From: Paul Eggert Date: Thu, 27 Aug 2020 21:46:52 +0000 (-0700) Subject: Fix recently-introduced expand-file-name bug X-Git-Tag: emacs-28.0.90~6349 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=0bbc84630f12e848e19c39dce01f3d14559bf70b;p=emacs.git 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. --- 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. */) ptrdiff_t tlen; #ifdef DOS_NT int drive = 0; - bool collapse_newdir = true; bool is_escaped = 0; #endif /* DOS_NT */ + bool collapse_newdir = true; ptrdiff_t length, nbytes; Lisp_Object handler, result, handled_name; bool multibyte; @@ -1183,9 +1183,7 @@ the root directory. */) newdir = SSDATA (hdir); newdirlim = newdir + SBYTES (hdir); } -#ifdef DOS_NT collapse_newdir = false; -#endif } else /* ~user/filename */ { @@ -1205,9 +1203,7 @@ the root directory. */) while (*++nm && !IS_DIRECTORY_SEP (*nm)) continue; -#ifdef DOS_NT collapse_newdir = false; -#endif } /* If we don't find a user of that name, leave the name @@ -1374,12 +1370,7 @@ the root directory. */) } #endif /* DOS_NT */ - /* Ignore any slash at the end of newdir, unless newdir is - just "/" or "//". */ length = newdirlim - newdir; - while (length > 1 && IS_DIRECTORY_SEP (newdir[length - 1]) - && ! (length == 2 && IS_DIRECTORY_SEP (newdir[0]))) - length--; /* Now concatenate the directory and name to new space in the stack frame. */ tlen = length + file_name_as_directory_slop + (nmlim - nm) + 1; @@ -1398,25 +1389,22 @@ the root directory. */) if (newdir) { - if (IS_DIRECTORY_SEP (nm[0])) + if (!collapse_newdir) { -#ifdef DOS_NT - /* If newdir is effectively "C:/", then the drive letter will have - been stripped and newdir will be "/". Concatenating with an - absolute directory in nm produces "//", which will then be - incorrectly treated as a network share. Ignore newdir in - this case (keeping the drive letter). */ - if (!(drive && nm[0] && IS_DIRECTORY_SEP (newdir[0]) - && newdir[1] == '\0')) -#endif - { - memcpy (target, newdir, length); - target[length] = 0; - nbytes = length; - } + /* With ~ or ~user, leave NEWDIR as-is to avoid transforming + it from a symlink (or a regular file!) into a directory. */ + memcpy (target, newdir, length); + nbytes = length; } else nbytes = file_name_as_directory (target, newdir, length, multibyte); + + /* If TARGET ends in a directory separator, omit leading + directory separators from NM so that concatenating a TARGET "/" + to an NM "/foo" does not result in the incorrect "//foo". */ + if (nbytes && IS_DIRECTORY_SEP (target[nbytes - 1])) + while (IS_DIRECTORY_SEP (nm[0])) + nm++; } memcpy (target + nbytes, nm, nmlim - nm + 1); diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el index 1516590795e..8b76912f5e1 100644 --- a/test/src/fileio-tests.el +++ b/test/src/fileio-tests.el @@ -108,6 +108,14 @@ Also check that an encoding error can appear in a symlink." (should (equal (expand-file-name "~/bar") "x:/foo/bar"))) (setenv "HOME" old-home))) +(ert-deftest fileio-tests--HOME-trailing-slash () + "Test that expand-file-name of \"~\" respects trailing slash." + (let ((old-home (getenv "HOME"))) + (dolist (home '("/a/b/c" "/a/b/c/")) + (setenv "HOME" home) + (should (equal (expand-file-name "~") (expand-file-name home)))) + (setenv "HOME" old-home))) + (ert-deftest fileio-tests--expand-file-name-trailing-slash () (dolist (fooslashalias '("foo/" "foo//" "foo/." "foo//." "foo///././." "foo/a/.."))