summaryrefslogtreecommitdiff
path: root/src/fileio.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2020-08-26 13:25:35 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2020-08-26 13:27:56 -0700
commit14fb657ba82da346d36f05f88da26f1c5498b798 (patch)
treeca60dbe7a621ad7a5d00b1a28f489caf15e4896a /src/fileio.c
parentff864be694247e5f6c8732afcbaeb1c0a8a8a124 (diff)
downloademacs-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.c37
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])