diff options
| author | Paul Eggert | 2020-08-27 14:46:52 -0700 |
|---|---|---|
| committer | Paul Eggert | 2020-08-27 14:49:38 -0700 |
| commit | 0bbc84630f12e848e19c39dce01f3d14559bf70b (patch) | |
| tree | 8c2e252c0ea6ef65667f4df46c468ef2f0b50bc6 /src | |
| parent | 1153b238aef5a48bbecd5a58cd6a14dae9ec1d2f (diff) | |
| download | emacs-0bbc84630f12e848e19c39dce01f3d14559bf70b.tar.gz emacs-0bbc84630f12e848e19c39dce01f3d14559bf70b.zip | |
Fix recently-introduced expand-file-name bug
The bug was that (expand-file-name "~") returned something
like "/home/eggert/" instead of "/home/eggert".
Problem reported by Mattias Engdegård (Bug#26911#27).
* src/fileio.c (Fexpand_file_name): When concatenating NEWDIR to
NM, instead of stripping trailing slashes from NEWDIR (which can
turn non-symlinks into symlinks), strip leading slashes from NM.
This also simplifies the code by removing no-longer-needed DOS_NT
special-casing. Also, remove an unnecessary ‘target[length] = 0;’
as that byte will be overwritten by the next memcpy anyway.
* test/src/fileio-tests.el (fileio-tests--HOME-trailing-slash):
New test.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fileio.c | 38 |
1 files changed, 13 insertions, 25 deletions
diff --git a/src/fileio.c b/src/fileio.c index b70dff1c22c..47e5e46a003 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; | ||
| 831 | bool is_escaped = 0; | 830 | bool is_escaped = 0; |
| 832 | #endif /* DOS_NT */ | 831 | #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; |
| @@ -1183,9 +1183,7 @@ the root directory. */) | |||
| 1183 | newdir = SSDATA (hdir); | 1183 | newdir = SSDATA (hdir); |
| 1184 | newdirlim = newdir + SBYTES (hdir); | 1184 | newdirlim = newdir + SBYTES (hdir); |
| 1185 | } | 1185 | } |
| 1186 | #ifdef DOS_NT | ||
| 1187 | collapse_newdir = false; | 1186 | collapse_newdir = false; |
| 1188 | #endif | ||
| 1189 | } | 1187 | } |
| 1190 | else /* ~user/filename */ | 1188 | else /* ~user/filename */ |
| 1191 | { | 1189 | { |
| @@ -1205,9 +1203,7 @@ the root directory. */) | |||
| 1205 | 1203 | ||
| 1206 | while (*++nm && !IS_DIRECTORY_SEP (*nm)) | 1204 | while (*++nm && !IS_DIRECTORY_SEP (*nm)) |
| 1207 | continue; | 1205 | continue; |
| 1208 | #ifdef DOS_NT | ||
| 1209 | collapse_newdir = false; | 1206 | collapse_newdir = false; |
| 1210 | #endif | ||
| 1211 | } | 1207 | } |
| 1212 | 1208 | ||
| 1213 | /* If we don't find a user of that name, leave the name | 1209 | /* If we don't find a user of that name, leave the name |
| @@ -1374,12 +1370,7 @@ the root directory. */) | |||
| 1374 | } | 1370 | } |
| 1375 | #endif /* DOS_NT */ | 1371 | #endif /* DOS_NT */ |
| 1376 | 1372 | ||
| 1377 | /* Ignore any slash at the end of newdir, unless newdir is | ||
| 1378 | just "/" or "//". */ | ||
| 1379 | length = newdirlim - newdir; | 1373 | length = newdirlim - newdir; |
| 1380 | while (length > 1 && IS_DIRECTORY_SEP (newdir[length - 1]) | ||
| 1381 | && ! (length == 2 && IS_DIRECTORY_SEP (newdir[0]))) | ||
| 1382 | length--; | ||
| 1383 | 1374 | ||
| 1384 | /* Now concatenate the directory and name to new space in the stack frame. */ | 1375 | /* Now concatenate the directory and name to new space in the stack frame. */ |
| 1385 | tlen = length + file_name_as_directory_slop + (nmlim - nm) + 1; | 1376 | tlen = length + file_name_as_directory_slop + (nmlim - nm) + 1; |
| @@ -1398,25 +1389,22 @@ the root directory. */) | |||
| 1398 | 1389 | ||
| 1399 | if (newdir) | 1390 | if (newdir) |
| 1400 | { | 1391 | { |
| 1401 | if (IS_DIRECTORY_SEP (nm[0])) | 1392 | if (!collapse_newdir) |
| 1402 | { | 1393 | { |
| 1403 | #ifdef DOS_NT | 1394 | /* With ~ or ~user, leave NEWDIR as-is to avoid transforming |
| 1404 | /* If newdir is effectively "C:/", then the drive letter will have | 1395 | it from a symlink (or a regular file!) into a directory. */ |
| 1405 | been stripped and newdir will be "/". Concatenating with an | 1396 | memcpy (target, newdir, length); |
| 1406 | absolute directory in nm produces "//", which will then be | 1397 | nbytes = length; |
| 1407 | incorrectly treated as a network share. Ignore newdir in | ||
| 1408 | this case (keeping the drive letter). */ | ||
| 1409 | if (!(drive && nm[0] && IS_DIRECTORY_SEP (newdir[0]) | ||
| 1410 | && newdir[1] == '\0')) | ||
| 1411 | #endif | ||
| 1412 | { | ||
| 1413 | memcpy (target, newdir, length); | ||
| 1414 | target[length] = 0; | ||
| 1415 | nbytes = length; | ||
| 1416 | } | ||
| 1417 | } | 1398 | } |
| 1418 | else | 1399 | else |
| 1419 | nbytes = file_name_as_directory (target, newdir, length, multibyte); | 1400 | nbytes = file_name_as_directory (target, newdir, length, multibyte); |
| 1401 | |||
| 1402 | /* If TARGET ends in a directory separator, omit leading | ||
| 1403 | directory separators from NM so that concatenating a TARGET "/" | ||
| 1404 | to an NM "/foo" does not result in the incorrect "//foo". */ | ||
| 1405 | if (nbytes && IS_DIRECTORY_SEP (target[nbytes - 1])) | ||
| 1406 | while (IS_DIRECTORY_SEP (nm[0])) | ||
| 1407 | nm++; | ||
| 1420 | } | 1408 | } |
| 1421 | 1409 | ||
| 1422 | memcpy (target + nbytes, nm, nmlim - nm + 1); | 1410 | memcpy (target + nbytes, nm, nmlim - nm + 1); |