diff options
| -rw-r--r-- | doc/emacs/files.texi | 8 | ||||
| -rw-r--r-- | doc/lispref/files.texi | 11 | ||||
| -rw-r--r-- | etc/NEWS | 24 | ||||
| -rw-r--r-- | lisp/dired.el | 5 | ||||
| -rw-r--r-- | lisp/files.el | 31 | ||||
| -rw-r--r-- | src/fileio.c | 28 | ||||
| -rw-r--r-- | test/src/fileio-tests.el | 21 |
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 |
| 1612 | the name @var{target} is nonexistent at that time. This command does | 1612 | the name @var{target} is nonexistent at that time. This command does |
| 1613 | not expand the argument @var{target}, so that it allows you to specify | 1613 | not expand the argument @var{target}, so that it allows you to specify |
| 1614 | a relative name as the target of the link. On MS-Windows, this | 1614 | a relative name as the target of the link. However, this command |
| 1615 | command works only on MS Windows Vista and later. On remote systems, | 1615 | does expand leading @samp{~} in @var{target} so that you can easily |
| 1616 | specify home directories, and strips leading @samp{/:} so that you can | ||
| 1617 | specify relative names beginning with literal @samp{~} or @samp{/:}. | ||
| 1618 | @xref{Quoted File Names}. On MS-Windows, this command works only on | ||
| 1619 | MS Windows Vista and later. When @var{new} is remote, | ||
| 1616 | it works depending on the system type. | 1620 | it 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 | |||
| 1726 | SELinux context are not copied over in either case. | 1726 | SELinux 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 |
| 1732 | This command makes a symbolic link to @var{filename}, named | 1732 | This 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 |
| 1735 | is treated only as a string; it need not name an existing file. | 1735 | is treated only as a string; it need not name an existing file. |
| 1736 | If @var{filename} is a relative file name, the resulting symbolic link | 1736 | If @var{ok-if-already-exists} is an integer, indicating interactive |
| 1737 | use, then leading @samp{~} is expanded and leading @samp{/:} is | ||
| 1738 | stripped in the @var{target} string. | ||
| 1739 | If @var{target} is a relative file name, the resulting symbolic link | ||
| 1737 | is interpreted relative to the directory containing the symbolic link. | 1740 | is interpreted relative to the directory containing the symbolic link. |
| 1738 | @xref{Relative File Names}. | 1741 | @xref{Relative File Names}. |
| 1739 | 1742 | ||
| @@ -1228,6 +1228,30 @@ instead of to utf-8. Before this change, Emacs would sometimes | |||
| 1228 | mishandle file names containing these control characters. | 1228 | mishandle file names containing these control characters. |
| 1229 | 1229 | ||
| 1230 | +++ | 1230 | +++ |
| 1231 | ** 'file-attributes', 'file-symlink-p' and 'make-symbolic-link' no | ||
| 1232 | longer quietly mutate the target of a local symbolic link, so that | ||
| 1233 | Emacs can access and copy them reliably regardless of their contents. | ||
| 1234 | The following changes are involved. | ||
| 1235 | |||
| 1236 | *** 'file-attributes' and 'file-symlink-p' no longer prepend "/:" to | ||
| 1237 | symbolic links whose targets begin with "/" and contain ":". For | ||
| 1238 | example, 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 | ||
| 1242 | creating 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 "~" | ||
| 1246 | only when the optional third arg is an integer, as when invoked | ||
| 1247 | interactively. For example, (make-symbolic-link "~y" "x") now creates | ||
| 1248 | a link with target the literal string "~y"; to get the old behavior, | ||
| 1249 | use (make-symbolic-link (expand-file-name "~y") "x"). To avoid this | ||
| 1250 | expansion in interactive use, you can now prefix the link target with | ||
| 1251 | "/:". For example, (make-symbolic-link "/:~y" "x" 1) now creates a | ||
| 1252 | link to literal "~y". | ||
| 1253 | |||
| 1254 | +++ | ||
| 1231 | ** Module functions are now implemented slightly differently; in | 1255 | ** Module functions are now implemented slightly differently; in |
| 1232 | particular, the function 'internal--module-call' has been removed. | 1256 | particular, the function 'internal--module-call' has been removed. |
| 1233 | Code that depends on undocumented internals of the module system might | 1257 | Code 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. | ||
| 1151 | This is like `file-name-absolute-p', except that it returns nil for | ||
| 1152 | names 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. |
| 1151 | If FILENAME is not absolute, first expands it against `default-directory'. | 1158 | If 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, | |||
| 2413 | Both args must be strings. | 2413 | Both args must be strings. |
| 2414 | Signal a `file-already-exists' error if a file LINKNAME already exists | 2414 | Signal a `file-already-exists' error if a file LINKNAME already exists |
| 2415 | unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. | 2415 | unless optional third argument OK-IF-ALREADY-EXISTS is non-nil. |
| 2416 | An integer third arg means request confirmation if LINKNAME already exists. | 2416 | An integer third arg means request confirmation if LINKNAME already |
| 2417 | exists, and expand leading "~" or strip leading "/:" in TARGET. | ||
| 2417 | This happens for interactive use with M-x. */) | 2418 | This 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 () |