aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/emacs/files.texi8
-rw-r--r--doc/lispref/files.texi11
-rw-r--r--etc/NEWS24
-rw-r--r--lisp/dired.el5
-rw-r--r--lisp/files.el31
-rw-r--r--src/fileio.c28
-rw-r--r--test/src/fileio-tests.el21
7 files changed, 78 insertions, 50 deletions
diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi
index 9195bc47efe..fa1f9e53165 100644
--- a/doc/emacs/files.texi
+++ b/doc/emacs/files.texi
@@ -1611,8 +1611,12 @@ attempts to open file @var{new} will refer to whatever file is named
1611@var{target} at the time the opening is done, or will get an error if 1611@var{target} at the time the opening is done, or will get an error if
1612the name @var{target} is nonexistent at that time. This command does 1612the name @var{target} is nonexistent at that time. This command does
1613not expand the argument @var{target}, so that it allows you to specify 1613not expand the argument @var{target}, so that it allows you to specify
1614a relative name as the target of the link. On MS-Windows, this 1614a relative name as the target of the link. However, this command
1615command works only on MS Windows Vista and later. On remote systems, 1615does expand leading @samp{~} in @var{target} so that you can easily
1616specify home directories, and strips leading @samp{/:} so that you can
1617specify relative names beginning with literal @samp{~} or @samp{/:}.
1618@xref{Quoted File Names}. On MS-Windows, this command works only on
1619MS Windows Vista and later. When @var{new} is remote,
1616it works depending on the system type. 1620it works depending on the system type.
1617 1621
1618@node Misc File Ops 1622@node Misc File Ops
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index f701d683703..06466c9bba8 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -1726,14 +1726,17 @@ default file permissions (see @code{set-default-file-modes} below), if
1726SELinux context are not copied over in either case. 1726SELinux context are not copied over in either case.
1727@end deffn 1727@end deffn
1728 1728
1729@deffn Command make-symbolic-link filename newname &optional ok-if-already-exists 1729@deffn Command make-symbolic-link target newname &optional ok-if-already-exists
1730@pindex ln 1730@pindex ln
1731@kindex file-already-exists 1731@kindex file-already-exists
1732This command makes a symbolic link to @var{filename}, named 1732This command makes a symbolic link to @var{target}, named
1733@var{newname}. This is like the shell command @samp{ln -s 1733@var{newname}. This is like the shell command @samp{ln -s
1734@var{filename} @var{newname}}. The @var{filename} argument 1734@var{target} @var{newname}}. The @var{target} argument
1735is treated only as a string; it need not name an existing file. 1735is treated only as a string; it need not name an existing file.
1736If @var{filename} is a relative file name, the resulting symbolic link 1736If @var{ok-if-already-exists} is an integer, indicating interactive
1737use, then leading @samp{~} is expanded and leading @samp{/:} is
1738stripped in the @var{target} string.
1739If @var{target} is a relative file name, the resulting symbolic link
1737is interpreted relative to the directory containing the symbolic link. 1740is interpreted relative to the directory containing the symbolic link.
1738@xref{Relative File Names}. 1741@xref{Relative File Names}.
1739 1742
diff --git a/etc/NEWS b/etc/NEWS
index 02de66b355f..d53e0d25f78 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1228,6 +1228,30 @@ instead of to utf-8. Before this change, Emacs would sometimes
1228mishandle file names containing these control characters. 1228mishandle file names containing these control characters.
1229 1229
1230+++ 1230+++
1231** 'file-attributes', 'file-symlink-p' and 'make-symbolic-link' no
1232longer quietly mutate the target of a local symbolic link, so that
1233Emacs can access and copy them reliably regardless of their contents.
1234The following changes are involved.
1235
1236*** 'file-attributes' and 'file-symlink-p' no longer prepend "/:" to
1237symbolic links whose targets begin with "/" and contain ":". For
1238example, if a symbolic link "x" has a target "/y:z", (file-symlink-p
1239"x") now returns "/y:z" rather than "/:/y:z".
1240
1241*** 'make-symbolic-link' no longer looks for file name handlers when
1242creating a local symbolic link. For example, (make-symbolic-link
1243"/y:z" "x") now creates a symlink to "/y:z" instead of failing.
1244
1245*** 'make-symbolic-link' now expands a link target with leading "~"
1246only when the optional third arg is an integer, as when invoked
1247interactively. For example, (make-symbolic-link "~y" "x") now creates
1248a link with target the literal string "~y"; to get the old behavior,
1249use (make-symbolic-link (expand-file-name "~y") "x"). To avoid this
1250expansion in interactive use, you can now prefix the link target with
1251"/:". For example, (make-symbolic-link "/:~y" "x" 1) now creates a
1252link to literal "~y".
1253
1254+++
1231** Module functions are now implemented slightly differently; in 1255** Module functions are now implemented slightly differently; in
1232particular, the function 'internal--module-call' has been removed. 1256particular, the function 'internal--module-call' has been removed.
1233Code that depends on undocumented internals of the module system might 1257Code that depends on undocumented internals of the module system might
diff --git a/lisp/dired.el b/lisp/dired.el
index 0455f3d1378..ff62183f091 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -2332,10 +2332,7 @@ Otherwise, an error occurs in these cases."
2332 (if (and enable-multibyte-characters 2332 (if (and enable-multibyte-characters
2333 (not (multibyte-string-p file))) 2333 (not (multibyte-string-p file)))
2334 (setq file (string-to-multibyte file))))) 2334 (setq file (string-to-multibyte file)))))
2335 (and file (file-name-absolute-p file) 2335 (and file (files--name-absolute-system-p file)
2336 ;; A relative file name can start with ~.
2337 ;; Don't treat it as absolute in this context.
2338 (not (eq (aref file 0) ?~))
2339 (setq already-absolute t)) 2336 (setq already-absolute t))
2340 (cond 2337 (cond
2341 ((null file) 2338 ((null file)
diff --git a/lisp/files.el b/lisp/files.el
index ca3b055d7a6..872fc46e87a 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -1146,6 +1146,13 @@ accessible."
1146 (funcall handler 'file-local-copy file) 1146 (funcall handler 'file-local-copy file)
1147 nil))) 1147 nil)))
1148 1148
1149(defun files--name-absolute-system-p (file)
1150 "Return non-nil if FILE is an absolute name to the operating system.
1151This is like `file-name-absolute-p', except that it returns nil for
1152names beginning with `~'."
1153 (and (file-name-absolute-p file)
1154 (not (eq (aref file 0) ?~))))
1155
1149(defun file-truename (filename &optional counter prev-dirs) 1156(defun file-truename (filename &optional counter prev-dirs)
1150 "Return the truename of FILENAME. 1157 "Return the truename of FILENAME.
1151If FILENAME is not absolute, first expands it against `default-directory'. 1158If FILENAME is not absolute, first expands it against `default-directory'.
@@ -1247,9 +1254,9 @@ containing it, until no links are left at any level.
1247 ;; since target might look like foo/../bar where foo 1254 ;; since target might look like foo/../bar where foo
1248 ;; is itself a link. Instead, we handle . and .. above. 1255 ;; is itself a link. Instead, we handle . and .. above.
1249 (setq filename 1256 (setq filename
1250 (if (file-name-absolute-p target) 1257 (concat (if (files--name-absolute-system-p target)
1251 target 1258 "/:" dir)
1252 (concat dir target)) 1259 target)
1253 done nil) 1260 done nil)
1254 ;; No, we are done! 1261 ;; No, we are done!
1255 (setq done t)))))))) 1262 (setq done t))))))))
@@ -1284,7 +1291,10 @@ it means chase no more than that many links and then stop."
1284 (directory-file-name (file-name-directory newname)))) 1291 (directory-file-name (file-name-directory newname))))
1285 ;; Now find the parent of that dir. 1292 ;; Now find the parent of that dir.
1286 (setq newname (file-name-directory newname))) 1293 (setq newname (file-name-directory newname)))
1287 (setq newname (expand-file-name tem (file-name-directory newname))) 1294 (setq newname (concat (if (files--name-absolute-system-p tem)
1295 "/:"
1296 (file-name-directory newname))
1297 tem))
1288 (setq count (1+ count)))) 1298 (setq count (1+ count))))
1289 newname)) 1299 newname))
1290 1300
@@ -5504,10 +5514,10 @@ directly into NEWNAME instead."
5504 ;; If NEWNAME is an existing directory and COPY-CONTENTS 5514 ;; If NEWNAME is an existing directory and COPY-CONTENTS
5505 ;; is nil, copy into NEWNAME/[DIRECTORY-BASENAME]. 5515 ;; is nil, copy into NEWNAME/[DIRECTORY-BASENAME].
5506 ((not copy-contents) 5516 ((not copy-contents)
5507 (setq newname (expand-file-name 5517 (setq newname (concat
5518 (file-name-as-directory newname)
5508 (file-name-nondirectory 5519 (file-name-nondirectory
5509 (directory-file-name directory)) 5520 (directory-file-name directory))))
5510 newname))
5511 (and (file-exists-p newname) 5521 (and (file-exists-p newname)
5512 (not (file-directory-p newname)) 5522 (not (file-directory-p newname))
5513 (error "Cannot overwrite non-directory %s with a directory" 5523 (error "Cannot overwrite non-directory %s with a directory"
@@ -5519,7 +5529,8 @@ directly into NEWNAME instead."
5519 ;; We do not want to copy "." and "..". 5529 ;; We do not want to copy "." and "..".
5520 (directory-files directory 'full 5530 (directory-files directory 'full
5521 directory-files-no-dot-files-regexp)) 5531 directory-files-no-dot-files-regexp))
5522 (let ((target (expand-file-name (file-name-nondirectory file) newname)) 5532 (let ((target (concat (file-name-as-directory newname)
5533 (file-name-nondirectory file)))
5523 (filetype (car (file-attributes file)))) 5534 (filetype (car (file-attributes file))))
5524 (cond 5535 (cond
5525 ((eq filetype t) ; Directory but not a symlink. 5536 ((eq filetype t) ; Directory but not a symlink.
@@ -7149,8 +7160,8 @@ Otherwise, trash FILENAME using the freedesktop.org conventions,
7149 ;; If `trash-directory' is non-nil, move the file there. 7160 ;; If `trash-directory' is non-nil, move the file there.
7150 (let* ((trash-dir (expand-file-name trash-directory)) 7161 (let* ((trash-dir (expand-file-name trash-directory))
7151 (fn (directory-file-name (expand-file-name filename))) 7162 (fn (directory-file-name (expand-file-name filename)))
7152 (new-fn (expand-file-name (file-name-nondirectory fn) 7163 (new-fn (concat (file-name-as-directory trash-dir)
7153 trash-dir))) 7164 (file-name-nondirectory fn))))
7154 ;; We can't trash a parent directory of trash-directory. 7165 ;; We can't trash a parent directory of trash-directory.
7155 (if (string-prefix-p fn trash-dir) 7166 (if (string-prefix-p fn trash-dir)
7156 (error "Trash directory `%s' is a subdirectory of `%s'" 7167 (error "Trash directory `%s' is a subdirectory of `%s'"
diff --git a/src/fileio.c b/src/fileio.c
index fa694249cb7..bbd1a4ef69c 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -2413,7 +2413,8 @@ DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3,
2413Both args must be strings. 2413Both args must be strings.
2414Signal a `file-already-exists' error if a file LINKNAME already exists 2414Signal a `file-already-exists' error if a file LINKNAME already exists
2415unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. 2415unless optional third argument OK-IF-ALREADY-EXISTS is non-nil.
2416An integer third arg means request confirmation if LINKNAME already exists. 2416An integer third arg means request confirmation if LINKNAME already
2417exists, and expand leading "~" or strip leading "/:" in TARGET.
2417This happens for interactive use with M-x. */) 2418This happens for interactive use with M-x. */)
2418 (Lisp_Object target, Lisp_Object linkname, Lisp_Object ok_if_already_exists) 2419 (Lisp_Object target, Lisp_Object linkname, Lisp_Object ok_if_already_exists)
2419{ 2420{
@@ -2421,21 +2422,15 @@ This happens for interactive use with M-x. */)
2421 Lisp_Object encoded_target, encoded_linkname; 2422 Lisp_Object encoded_target, encoded_linkname;
2422 2423
2423 CHECK_STRING (target); 2424 CHECK_STRING (target);
2424 /* If the link target has a ~, we must expand it to get 2425 if (INTEGERP (ok_if_already_exists))
2425 a truly valid file name. Otherwise, do not expand; 2426 {
2426 we want to permit links to relative file names. */ 2427 if (SREF (target, 0) == '~')
2427 if (SREF (target, 0) == '~') 2428 target = Fexpand_file_name (target, Qnil);
2428 target = Fexpand_file_name (target, Qnil); 2429 else if (SREF (target, 0) == '/' && SREF (target, 1) == ':')
2429 2430 target = Fsubstring_no_properties (target, make_number (2), Qnil);
2431 }
2430 linkname = expand_cp_target (target, linkname); 2432 linkname = expand_cp_target (target, linkname);
2431 2433
2432 /* If the file name has special constructs in it,
2433 call the corresponding file handler. */
2434 handler = Ffind_file_name_handler (target, Qmake_symbolic_link);
2435 if (!NILP (handler))
2436 return call4 (handler, Qmake_symbolic_link, target,
2437 linkname, ok_if_already_exists);
2438
2439 /* If the new link name has special constructs in it, 2434 /* If the new link name has special constructs in it,
2440 call the corresponding file handler. */ 2435 call the corresponding file handler. */
2441 handler = Ffind_file_name_handler (linkname, Qmake_symbolic_link); 2436 handler = Ffind_file_name_handler (linkname, Qmake_symbolic_link);
@@ -2633,11 +2628,6 @@ emacs_readlinkat (int fd, char const *filename)
2633 return Qnil; 2628 return Qnil;
2634 2629
2635 val = build_unibyte_string (buf); 2630 val = build_unibyte_string (buf);
2636 if (buf[0] == '/' && strchr (buf, ':'))
2637 {
2638 AUTO_STRING (slash_colon, "/:");
2639 val = concat2 (slash_colon, val);
2640 }
2641 if (buf != readlink_buf) 2631 if (buf != readlink_buf)
2642 xfree (buf); 2632 xfree (buf);
2643 val = DECODE_FILE (val); 2633 val = DECODE_FILE (val);
diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el
index 2ef1b553ab4..5103d2f21e6 100644
--- a/test/src/fileio-tests.el
+++ b/test/src/fileio-tests.el
@@ -19,14 +19,13 @@
19 19
20(require 'ert) 20(require 'ert)
21 21
22(defun try-char (char link) 22(defun try-link (target link)
23 (let ((target (string char))) 23 (make-symbolic-link target link)
24 (make-symbolic-link target link) 24 (let* ((read-link (file-symlink-p link))
25 (let* ((read-link (file-symlink-p link)) 25 (failure (unless (string-equal target read-link)
26 (failure (unless (string-equal target read-link) 26 (list 'string-equal target read-link))))
27 (list 'string-equal target read-link)))) 27 (delete-file link)
28 (delete-file link) 28 failure))
29 failure)))
30 29
31(defun fileio-tests--symlink-failure () 30(defun fileio-tests--symlink-failure ()
32 (let* ((dir (make-temp-file "fileio" t)) 31 (let* ((dir (make-temp-file "fileio" t))
@@ -36,9 +35,9 @@
36 (char 0)) 35 (char 0))
37 (while (and (not failure) (< char 127)) 36 (while (and (not failure) (< char 127))
38 (setq char (1+ char)) 37 (setq char (1+ char))
39 (unless (= char ?~) 38 (setq failure (try-link (string char) link)))
40 (setq failure (try-char char link)))) 39 (or failure
41 failure) 40 (try-link "/:" link)))
42 (delete-directory dir t)))) 41 (delete-directory dir t))))
43 42
44(ert-deftest fileio-tests--odd-symlink-chars () 43(ert-deftest fileio-tests--odd-symlink-chars ()