diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2020-08-26 13:25:35 -0700 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2020-08-26 13:27:56 -0700 |
commit | 14fb657ba82da346d36f05f88da26f1c5498b798 (patch) | |
tree | ca60dbe7a621ad7a5d00b1a28f489caf15e4896a /src/fileio.c | |
parent | ff864be694247e5f6c8732afcbaeb1c0a8a8a124 (diff) | |
download | emacs-14fb657ba82da346d36f05f88da26f1c5498b798.tar.gz |
Fix expand-file-name symlink-to-dir bug
Problem reported by Yegor Timoshenko (Bug#26911),
and I ran into it myself recently in normal-top-level.
* doc/lispref/files.texi (File Name Expansion), etc/NEWS: Mention this.
* src/fileio.c (Fexpand_file_name): Expand "/a/b/." to "/a/b/" not
"/a/b", to avoid misinterpreting a symlink "/a/b". Similarly,
expand "/a/b/c/.." to "/a/b/" not "/a/b".
* test/lisp/net/tramp-tests.el (tramp-test05-expand-file-name):
Adjust to match new behavior.
(tramp-test05-expand-file-name-relative): This test now succeeds,
at least on Fedora 31.
* test/src/fileio-tests.el:
(fileio-tests--expand-file-name-trailing-slash) New test.
Diffstat (limited to 'src/fileio.c')
-rw-r--r-- | src/fileio.c | 37 |
1 files changed, 22 insertions, 15 deletions
diff --git a/src/fileio.c b/src/fileio.c index 37072d9b6bd..b70dff1c22c 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -1065,7 +1065,7 @@ the root directory. */) #endif /* WINDOWSNT */ #endif /* DOS_NT */ - /* If nm is absolute, look for `/./' or `/../' or `//''sequences; if + /* If nm is absolute, look for "/./" or "/../" or "//" sequences; if none are found, we can probably return right away. We will avoid allocating a new string if name is already fully expanded. */ if ( @@ -1398,7 +1398,7 @@ the root directory. */) if (newdir) { - if (nm[0] == 0 || IS_DIRECTORY_SEP (nm[0])) + if (IS_DIRECTORY_SEP (nm[0])) { #ifdef DOS_NT /* If newdir is effectively "C:/", then the drive letter will have @@ -1433,14 +1433,16 @@ the root directory. */) { *o++ = *p++; } - else if (p[1] == '.' - && (IS_DIRECTORY_SEP (p[2]) - || p[2] == 0)) + else if (p[1] == '.' && IS_DIRECTORY_SEP (p[2])) { - /* If "/." is the entire filename, keep the "/". Otherwise, - just delete the whole "/.". */ - if (o == target && p[2] == '\0') - *o++ = *p; + /* Replace "/./" with "/". */ + p += 2; + } + else if (p[1] == '.' && !p[2]) + { + /* At the end of the file name, replace "/." with "/". + The trailing "/" is for symlinks. */ + *o++ = *p; p += 2; } else if (p[1] == '.' && p[2] == '.' @@ -1459,18 +1461,23 @@ the root directory. */) #ifdef WINDOWSNT char *prev_o = o; #endif - while (o != target && (--o, !IS_DIRECTORY_SEP (*o))) - continue; + while (o != target) + { + o--; + if (IS_DIRECTORY_SEP (*o)) + { + /* Keep "/" at the end of the name, for symlinks. */ + o += p[3] == 0; + + break; + } + } #ifdef WINDOWSNT /* Don't go below server level in UNC filenames. */ if (o == target + 1 && IS_DIRECTORY_SEP (*o) && IS_DIRECTORY_SEP (*target)) o = prev_o; - else #endif - /* Keep initial / only if this is the whole name. */ - if (o == target && IS_ANY_SEP (*o) && p[3] == 0) - ++o; p += 3; } else if (IS_DIRECTORY_SEP (p[1]) |