diff options
| author | Paul Eggert | 2020-08-26 13:25:35 -0700 |
|---|---|---|
| committer | Paul Eggert | 2020-08-26 13:27:56 -0700 |
| commit | 14fb657ba82da346d36f05f88da26f1c5498b798 (patch) | |
| tree | ca60dbe7a621ad7a5d00b1a28f489caf15e4896a /src | |
| parent | ff864be694247e5f6c8732afcbaeb1c0a8a8a124 (diff) | |
| download | emacs-14fb657ba82da346d36f05f88da26f1c5498b798.tar.gz emacs-14fb657ba82da346d36f05f88da26f1c5498b798.zip | |
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')
| -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. */) | |||
| 1065 | #endif /* WINDOWSNT */ | 1065 | #endif /* WINDOWSNT */ |
| 1066 | #endif /* DOS_NT */ | 1066 | #endif /* DOS_NT */ |
| 1067 | 1067 | ||
| 1068 | /* If nm is absolute, look for `/./' or `/../' or `//''sequences; if | 1068 | /* If nm is absolute, look for "/./" or "/../" or "//" sequences; if |
| 1069 | none are found, we can probably return right away. We will avoid | 1069 | none are found, we can probably return right away. We will avoid |
| 1070 | allocating a new string if name is already fully expanded. */ | 1070 | allocating a new string if name is already fully expanded. */ |
| 1071 | if ( | 1071 | if ( |
| @@ -1398,7 +1398,7 @@ the root directory. */) | |||
| 1398 | 1398 | ||
| 1399 | if (newdir) | 1399 | if (newdir) |
| 1400 | { | 1400 | { |
| 1401 | if (nm[0] == 0 || IS_DIRECTORY_SEP (nm[0])) | 1401 | if (IS_DIRECTORY_SEP (nm[0])) |
| 1402 | { | 1402 | { |
| 1403 | #ifdef DOS_NT | 1403 | #ifdef DOS_NT |
| 1404 | /* If newdir is effectively "C:/", then the drive letter will have | 1404 | /* If newdir is effectively "C:/", then the drive letter will have |
| @@ -1433,14 +1433,16 @@ the root directory. */) | |||
| 1433 | { | 1433 | { |
| 1434 | *o++ = *p++; | 1434 | *o++ = *p++; |
| 1435 | } | 1435 | } |
| 1436 | else if (p[1] == '.' | 1436 | else if (p[1] == '.' && IS_DIRECTORY_SEP (p[2])) |
| 1437 | && (IS_DIRECTORY_SEP (p[2]) | ||
| 1438 | || p[2] == 0)) | ||
| 1439 | { | 1437 | { |
| 1440 | /* If "/." is the entire filename, keep the "/". Otherwise, | 1438 | /* Replace "/./" with "/". */ |
| 1441 | just delete the whole "/.". */ | 1439 | p += 2; |
| 1442 | if (o == target && p[2] == '\0') | 1440 | } |
| 1443 | *o++ = *p; | 1441 | else if (p[1] == '.' && !p[2]) |
| 1442 | { | ||
| 1443 | /* At the end of the file name, replace "/." with "/". | ||
| 1444 | The trailing "/" is for symlinks. */ | ||
| 1445 | *o++ = *p; | ||
| 1444 | p += 2; | 1446 | p += 2; |
| 1445 | } | 1447 | } |
| 1446 | else if (p[1] == '.' && p[2] == '.' | 1448 | else if (p[1] == '.' && p[2] == '.' |
| @@ -1459,18 +1461,23 @@ the root directory. */) | |||
| 1459 | #ifdef WINDOWSNT | 1461 | #ifdef WINDOWSNT |
| 1460 | char *prev_o = o; | 1462 | char *prev_o = o; |
| 1461 | #endif | 1463 | #endif |
| 1462 | while (o != target && (--o, !IS_DIRECTORY_SEP (*o))) | 1464 | while (o != target) |
| 1463 | continue; | 1465 | { |
| 1466 | o--; | ||
| 1467 | if (IS_DIRECTORY_SEP (*o)) | ||
| 1468 | { | ||
| 1469 | /* Keep "/" at the end of the name, for symlinks. */ | ||
| 1470 | o += p[3] == 0; | ||
| 1471 | |||
| 1472 | break; | ||
| 1473 | } | ||
| 1474 | } | ||
| 1464 | #ifdef WINDOWSNT | 1475 | #ifdef WINDOWSNT |
| 1465 | /* Don't go below server level in UNC filenames. */ | 1476 | /* Don't go below server level in UNC filenames. */ |
| 1466 | if (o == target + 1 && IS_DIRECTORY_SEP (*o) | 1477 | if (o == target + 1 && IS_DIRECTORY_SEP (*o) |
| 1467 | && IS_DIRECTORY_SEP (*target)) | 1478 | && IS_DIRECTORY_SEP (*target)) |
| 1468 | o = prev_o; | 1479 | o = prev_o; |
| 1469 | else | ||
| 1470 | #endif | 1480 | #endif |
| 1471 | /* Keep initial / only if this is the whole name. */ | ||
| 1472 | if (o == target && IS_ANY_SEP (*o) && p[3] == 0) | ||
| 1473 | ++o; | ||
| 1474 | p += 3; | 1481 | p += 3; |
| 1475 | } | 1482 | } |
| 1476 | else if (IS_DIRECTORY_SEP (p[1]) | 1483 | else if (IS_DIRECTORY_SEP (p[1]) |