diff options
| author | Eli Zaretskii | 2020-09-03 20:16:33 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2020-09-03 20:16:33 +0300 |
| commit | a4e45a13b65c496a0c53b58992a4be2e3d923325 (patch) | |
| tree | 6099898ab7c01152d43130432f4e2fccb7dac855 /src | |
| parent | 8cb15183aa8faba4af52d7b87e5ee4dcd3b1104f (diff) | |
| download | emacs-a4e45a13b65c496a0c53b58992a4be2e3d923325.tar.gz emacs-a4e45a13b65c496a0c53b58992a4be2e3d923325.zip | |
Fix 'expand-file-name' for remote files
This reverts most of commit 14fb657ba82da346d36f05f88da26f1c5498b798
and its followup fixes, and instead fixes the original bugs in a
different manner that doesn't affect any unrelated use cases. As
part of this, the code which caused 'expand-file-name' to enforce
a trailing slash on expanded directories is removed, as this kind
of semantic processing is outside of 'expand-file-name's scope.
* src/fileio.c (Fexpand_file_name): If expanding default_directory
yields a remote file name, call its handlers. (Bug#26911)
(Bug#34834)
* doc/lispref/files.texi (File Name Expansion): Remove the
requirement that expanding a directory name yields a directory
name, i.e. that the expansion must end in a slash.
* etc/NEWS: Remove the announcement of the changed behavior of
'expand-file-name' wrt trailing slashes.
* test/src/fileio-tests.el (fileio-tests--HOME-trailing-slash)
(fileio-tests--expand-file-name-trailing-slash): Remove tests.
* test/lisp/net/tramp-tests.el (tramp-test05-expand-file-name): No
need to expect different results in Emacs 28 and later.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fileio.c | 86 |
1 files changed, 30 insertions, 56 deletions
diff --git a/src/fileio.c b/src/fileio.c index c91af36fdf6..1e4ca82e5f3 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -827,9 +827,9 @@ the root directory. */) | |||
| 827 | ptrdiff_t tlen; | 827 | ptrdiff_t tlen; |
| 828 | #ifdef DOS_NT | 828 | #ifdef DOS_NT |
| 829 | int drive = 0; | 829 | int drive = 0; |
| 830 | bool collapse_newdir = true; | ||
| 830 | bool is_escaped = 0; | 831 | bool is_escaped = 0; |
| 831 | #endif /* DOS_NT */ | 832 | #endif /* DOS_NT */ |
| 832 | bool collapse_newdir = true; | ||
| 833 | ptrdiff_t length, nbytes; | 833 | ptrdiff_t length, nbytes; |
| 834 | Lisp_Object handler, result, handled_name; | 834 | Lisp_Object handler, result, handled_name; |
| 835 | bool multibyte; | 835 | bool multibyte; |
| @@ -947,6 +947,22 @@ the root directory. */) | |||
| 947 | ) | 947 | ) |
| 948 | { | 948 | { |
| 949 | default_directory = Fexpand_file_name (default_directory, Qnil); | 949 | default_directory = Fexpand_file_name (default_directory, Qnil); |
| 950 | |||
| 951 | /* The above expansion might have produced a remote file name, | ||
| 952 | so give the handlers one last chance to DTRT. This can | ||
| 953 | happen when both NAME and DEFAULT-DIRECTORY arguments are | ||
| 954 | relative file names, and the buffer's default-directory is | ||
| 955 | remote. */ | ||
| 956 | handler = Ffind_file_name_handler (default_directory, | ||
| 957 | Qexpand_file_name); | ||
| 958 | if (!NILP (handler)) | ||
| 959 | { | ||
| 960 | handled_name = call3 (handler, Qexpand_file_name, | ||
| 961 | name, default_directory); | ||
| 962 | if (STRINGP (handled_name)) | ||
| 963 | return handled_name; | ||
| 964 | error ("Invalid handler in `file-name-handler-alist'"); | ||
| 965 | } | ||
| 950 | } | 966 | } |
| 951 | } | 967 | } |
| 952 | multibyte = STRING_MULTIBYTE (name); | 968 | multibyte = STRING_MULTIBYTE (name); |
| @@ -1065,7 +1081,7 @@ the root directory. */) | |||
| 1065 | #endif /* WINDOWSNT */ | 1081 | #endif /* WINDOWSNT */ |
| 1066 | #endif /* DOS_NT */ | 1082 | #endif /* DOS_NT */ |
| 1067 | 1083 | ||
| 1068 | /* If nm is absolute, look for "/./" or "/../" or "//" sequences; if | 1084 | /* If nm is absolute, look for `/./' or `/../' or `//''sequences; if |
| 1069 | none are found, we can probably return right away. We will avoid | 1085 | none are found, we can probably return right away. We will avoid |
| 1070 | allocating a new string if name is already fully expanded. */ | 1086 | allocating a new string if name is already fully expanded. */ |
| 1071 | if ( | 1087 | if ( |
| @@ -1183,7 +1199,9 @@ the root directory. */) | |||
| 1183 | newdir = SSDATA (hdir); | 1199 | newdir = SSDATA (hdir); |
| 1184 | newdirlim = newdir + SBYTES (hdir); | 1200 | newdirlim = newdir + SBYTES (hdir); |
| 1185 | } | 1201 | } |
| 1202 | #ifdef DOS_NT | ||
| 1186 | collapse_newdir = false; | 1203 | collapse_newdir = false; |
| 1204 | #endif | ||
| 1187 | } | 1205 | } |
| 1188 | else /* ~user/filename */ | 1206 | else /* ~user/filename */ |
| 1189 | { | 1207 | { |
| @@ -1203,7 +1221,9 @@ the root directory. */) | |||
| 1203 | 1221 | ||
| 1204 | while (*++nm && !IS_DIRECTORY_SEP (*nm)) | 1222 | while (*++nm && !IS_DIRECTORY_SEP (*nm)) |
| 1205 | continue; | 1223 | continue; |
| 1224 | #ifdef DOS_NT | ||
| 1206 | collapse_newdir = false; | 1225 | collapse_newdir = false; |
| 1226 | #endif | ||
| 1207 | } | 1227 | } |
| 1208 | 1228 | ||
| 1209 | /* If we don't find a user of that name, leave the name | 1229 | /* If we don't find a user of that name, leave the name |
| @@ -1370,15 +1390,12 @@ the root directory. */) | |||
| 1370 | } | 1390 | } |
| 1371 | #endif /* DOS_NT */ | 1391 | #endif /* DOS_NT */ |
| 1372 | 1392 | ||
| 1373 | length = newdirlim - newdir; | ||
| 1374 | |||
| 1375 | #ifdef DOS_NT | ||
| 1376 | /* Ignore any slash at the end of newdir, unless newdir is | 1393 | /* Ignore any slash at the end of newdir, unless newdir is |
| 1377 | just "/" or "//". */ | 1394 | just "/" or "//". */ |
| 1395 | length = newdirlim - newdir; | ||
| 1378 | while (length > 1 && IS_DIRECTORY_SEP (newdir[length - 1]) | 1396 | while (length > 1 && IS_DIRECTORY_SEP (newdir[length - 1]) |
| 1379 | && ! (length == 2 && IS_DIRECTORY_SEP (newdir[0]))) | 1397 | && ! (length == 2 && IS_DIRECTORY_SEP (newdir[0]))) |
| 1380 | length--; | 1398 | length--; |
| 1381 | #endif | ||
| 1382 | 1399 | ||
| 1383 | /* Now concatenate the directory and name to new space in the stack frame. */ | 1400 | /* Now concatenate the directory and name to new space in the stack frame. */ |
| 1384 | tlen = length + file_name_as_directory_slop + (nmlim - nm) + 1; | 1401 | tlen = length + file_name_as_directory_slop + (nmlim - nm) + 1; |
| @@ -1392,16 +1409,12 @@ the root directory. */) | |||
| 1392 | #else /* not DOS_NT */ | 1409 | #else /* not DOS_NT */ |
| 1393 | target = SAFE_ALLOCA (tlen); | 1410 | target = SAFE_ALLOCA (tlen); |
| 1394 | #endif /* not DOS_NT */ | 1411 | #endif /* not DOS_NT */ |
| 1412 | *target = 0; | ||
| 1395 | nbytes = 0; | 1413 | nbytes = 0; |
| 1396 | 1414 | ||
| 1397 | if (newdir) | 1415 | if (newdir) |
| 1398 | { | 1416 | { |
| 1399 | #ifndef DOS_NT | 1417 | if (nm[0] == 0 || IS_DIRECTORY_SEP (nm[0])) |
| 1400 | bool treat_as_absolute = !collapse_newdir; | ||
| 1401 | #else | ||
| 1402 | bool treat_as_absolute = !nm[0] || IS_DIRECTORY_SEP (nm[0]); | ||
| 1403 | #endif | ||
| 1404 | if (treat_as_absolute) | ||
| 1405 | { | 1418 | { |
| 1406 | #ifdef DOS_NT | 1419 | #ifdef DOS_NT |
| 1407 | /* If newdir is effectively "C:/", then the drive letter will have | 1420 | /* If newdir is effectively "C:/", then the drive letter will have |
| @@ -1413,23 +1426,13 @@ the root directory. */) | |||
| 1413 | && newdir[1] == '\0')) | 1426 | && newdir[1] == '\0')) |
| 1414 | #endif | 1427 | #endif |
| 1415 | { | 1428 | { |
| 1416 | /* With ~ or ~user, leave NEWDIR as-is to avoid transforming | ||
| 1417 | it from a symlink (or a regular file!) into a directory. */ | ||
| 1418 | memcpy (target, newdir, length); | 1429 | memcpy (target, newdir, length); |
| 1430 | target[length] = 0; | ||
| 1419 | nbytes = length; | 1431 | nbytes = length; |
| 1420 | } | 1432 | } |
| 1421 | } | 1433 | } |
| 1422 | else | 1434 | else |
| 1423 | nbytes = file_name_as_directory (target, newdir, length, multibyte); | 1435 | nbytes = file_name_as_directory (target, newdir, length, multibyte); |
| 1424 | |||
| 1425 | #ifndef DOS_NT | ||
| 1426 | /* If TARGET ends in a directory separator, omit leading | ||
| 1427 | directory separators from NM so that concatenating a TARGET "/" | ||
| 1428 | to an NM "/foo" does not result in the incorrect "//foo". */ | ||
| 1429 | if (nbytes && IS_DIRECTORY_SEP (target[nbytes - 1])) | ||
| 1430 | while (IS_DIRECTORY_SEP (nm[0])) | ||
| 1431 | nm++; | ||
| 1432 | #endif | ||
| 1433 | } | 1436 | } |
| 1434 | 1437 | ||
| 1435 | memcpy (target + nbytes, nm, nmlim - nm + 1); | 1438 | memcpy (target + nbytes, nm, nmlim - nm + 1); |
| @@ -1446,20 +1449,6 @@ the root directory. */) | |||
| 1446 | { | 1449 | { |
| 1447 | *o++ = *p++; | 1450 | *o++ = *p++; |
| 1448 | } | 1451 | } |
| 1449 | #ifndef DOS_NT | ||
| 1450 | else if (p[1] == '.' && IS_DIRECTORY_SEP (p[2])) | ||
| 1451 | { | ||
| 1452 | /* Replace "/./" with "/". */ | ||
| 1453 | p += 2; | ||
| 1454 | } | ||
| 1455 | else if (p[1] == '.' && !p[2]) | ||
| 1456 | { | ||
| 1457 | /* At the end of the file name, replace "/." with "/". | ||
| 1458 | The trailing "/" is for symlinks. */ | ||
| 1459 | *o++ = *p; | ||
| 1460 | p += 2; | ||
| 1461 | } | ||
| 1462 | #else | ||
| 1463 | else if (p[1] == '.' | 1452 | else if (p[1] == '.' |
| 1464 | && (IS_DIRECTORY_SEP (p[2]) | 1453 | && (IS_DIRECTORY_SEP (p[2]) |
| 1465 | || p[2] == 0)) | 1454 | || p[2] == 0)) |
| @@ -1470,7 +1459,6 @@ the root directory. */) | |||
| 1470 | *o++ = *p; | 1459 | *o++ = *p; |
| 1471 | p += 2; | 1460 | p += 2; |
| 1472 | } | 1461 | } |
| 1473 | #endif | ||
| 1474 | else if (p[1] == '.' && p[2] == '.' | 1462 | else if (p[1] == '.' && p[2] == '.' |
| 1475 | /* `/../' is the "superroot" on certain file systems. | 1463 | /* `/../' is the "superroot" on certain file systems. |
| 1476 | Turned off on DOS_NT systems because they have no | 1464 | Turned off on DOS_NT systems because they have no |
| @@ -1484,35 +1472,21 @@ the root directory. */) | |||
| 1484 | #endif | 1472 | #endif |
| 1485 | && (IS_DIRECTORY_SEP (p[3]) || p[3] == 0)) | 1473 | && (IS_DIRECTORY_SEP (p[3]) || p[3] == 0)) |
| 1486 | { | 1474 | { |
| 1487 | #ifndef DOS_NT | 1475 | #ifdef WINDOWSNT |
| 1488 | while (o != target) | ||
| 1489 | { | ||
| 1490 | o--; | ||
| 1491 | if (IS_DIRECTORY_SEP (*o)) | ||
| 1492 | { | ||
| 1493 | /* Keep "/" at the end of the name, for symlinks. */ | ||
| 1494 | o += p[3] == 0; | ||
| 1495 | |||
| 1496 | break; | ||
| 1497 | } | ||
| 1498 | } | ||
| 1499 | #else | ||
| 1500 | # ifdef WINDOWSNT | ||
| 1501 | char *prev_o = o; | 1476 | char *prev_o = o; |
| 1502 | # endif | 1477 | #endif |
| 1503 | while (o != target && (--o, !IS_DIRECTORY_SEP (*o))) | 1478 | while (o != target && (--o, !IS_DIRECTORY_SEP (*o))) |
| 1504 | continue; | 1479 | continue; |
| 1505 | # ifdef WINDOWSNT | 1480 | #ifdef WINDOWSNT |
| 1506 | /* Don't go below server level in UNC filenames. */ | 1481 | /* Don't go below server level in UNC filenames. */ |
| 1507 | if (o == target + 1 && IS_DIRECTORY_SEP (*o) | 1482 | if (o == target + 1 && IS_DIRECTORY_SEP (*o) |
| 1508 | && IS_DIRECTORY_SEP (*target)) | 1483 | && IS_DIRECTORY_SEP (*target)) |
| 1509 | o = prev_o; | 1484 | o = prev_o; |
| 1510 | else | 1485 | else |
| 1511 | # endif | 1486 | #endif |
| 1512 | /* Keep initial / only if this is the whole name. */ | 1487 | /* Keep initial / only if this is the whole name. */ |
| 1513 | if (o == target && IS_ANY_SEP (*o) && p[3] == 0) | 1488 | if (o == target && IS_ANY_SEP (*o) && p[3] == 0) |
| 1514 | ++o; | 1489 | ++o; |
| 1515 | #endif | ||
| 1516 | p += 3; | 1490 | p += 3; |
| 1517 | } | 1491 | } |
| 1518 | else if (IS_DIRECTORY_SEP (p[1]) | 1492 | else if (IS_DIRECTORY_SEP (p[1]) |