diff options
| author | Paul Eggert | 2019-07-24 14:28:13 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-07-24 14:33:02 -0700 |
| commit | a5063aa8b174db286a0e83b8ffdd4e65c521f733 (patch) | |
| tree | dc439d78ecef638817f98cb7e43868fa2ab7b856 /src | |
| parent | e089c3141a51bf70b91da21a01cdb4be0b63c08d (diff) | |
| download | emacs-a5063aa8b174db286a0e83b8ffdd4e65c521f733.tar.gz emacs-a5063aa8b174db286a0e83b8ffdd4e65c521f733.zip | |
Do not treat ~nosuchuser as an absolute file name
Derived from Ken Brown’s patch (Bug#36502#97).
* doc/lispref/files.texi (Relative File Names):
* etc/NEWS: Document this.
* src/fileio.c (user_homedir): New function.
(Fexpand_file_name, file_name_absolute_p): Use it.
(search_embedded_absfilename): Simplify via file_name_absolute_p.
* test/src/fileio-tests.el (fileio-tests--no-such-user): New test.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fileio.c | 121 |
1 files changed, 55 insertions, 66 deletions
diff --git a/src/fileio.c b/src/fileio.c index e4269b96a37..d1a7f39ac95 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -744,6 +744,31 @@ file_name_absolute_no_tilde_p (Lisp_Object name) | |||
| 744 | return IS_ABSOLUTE_FILE_NAME (SSDATA (name)); | 744 | return IS_ABSOLUTE_FILE_NAME (SSDATA (name)); |
| 745 | } | 745 | } |
| 746 | 746 | ||
| 747 | /* Return the home directory of the user NAME, or a null pointer if | ||
| 748 | NAME is empty or the user does not exist or the user's home | ||
| 749 | directory is not an absolute file name. NAME is an array of bytes | ||
| 750 | that continues up to (but not including) the next NUL byte or | ||
| 751 | directory separator. The returned string lives in storage good | ||
| 752 | until the next call to this or similar functions. */ | ||
| 753 | static char * | ||
| 754 | user_homedir (char const *name) | ||
| 755 | { | ||
| 756 | ptrdiff_t length; | ||
| 757 | for (length = 0; name[length] && !IS_DIRECTORY_SEP (name[length]); length++) | ||
| 758 | continue; | ||
| 759 | if (length == 0) | ||
| 760 | return NULL; | ||
| 761 | USE_SAFE_ALLOCA; | ||
| 762 | char *p = SAFE_ALLOCA (length + 1); | ||
| 763 | memcpy (p, name, length); | ||
| 764 | p[length] = 0; | ||
| 765 | struct passwd *pw = getpwnam (p); | ||
| 766 | SAFE_FREE (); | ||
| 767 | if (!pw || (pw->pw_dir && !IS_ABSOLUTE_FILE_NAME (pw->pw_dir))) | ||
| 768 | return NULL; | ||
| 769 | return pw->pw_dir; | ||
| 770 | } | ||
| 771 | |||
| 747 | DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0, | 772 | DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0, |
| 748 | doc: /* Convert filename NAME to absolute, and canonicalize it. | 773 | doc: /* Convert filename NAME to absolute, and canonicalize it. |
| 749 | Second arg DEFAULT-DIRECTORY is directory to start with if NAME is relative | 774 | Second arg DEFAULT-DIRECTORY is directory to start with if NAME is relative |
| @@ -788,7 +813,6 @@ the root directory. */) | |||
| 788 | char *target; | 813 | char *target; |
| 789 | 814 | ||
| 790 | ptrdiff_t tlen; | 815 | ptrdiff_t tlen; |
| 791 | struct passwd *pw; | ||
| 792 | #ifdef DOS_NT | 816 | #ifdef DOS_NT |
| 793 | int drive = 0; | 817 | int drive = 0; |
| 794 | bool collapse_newdir = true; | 818 | bool collapse_newdir = true; |
| @@ -1153,39 +1177,29 @@ the root directory. */) | |||
| 1153 | } | 1177 | } |
| 1154 | else /* ~user/filename */ | 1178 | else /* ~user/filename */ |
| 1155 | { | 1179 | { |
| 1156 | char *o, *p; | 1180 | char *nmhome = user_homedir (nm + 1); |
| 1157 | for (p = nm; *p && !IS_DIRECTORY_SEP (*p); p++) | 1181 | if (nmhome) |
| 1158 | continue; | ||
| 1159 | o = SAFE_ALLOCA (p - nm + 1); | ||
| 1160 | memcpy (o, nm, p - nm); | ||
| 1161 | o[p - nm] = 0; | ||
| 1162 | |||
| 1163 | block_input (); | ||
| 1164 | pw = getpwnam (o + 1); | ||
| 1165 | unblock_input (); | ||
| 1166 | if (pw) | ||
| 1167 | { | 1182 | { |
| 1168 | Lisp_Object tem; | 1183 | ptrdiff_t nmhomelen = strlen (nmhome); |
| 1169 | 1184 | newdir = nmhome; | |
| 1170 | newdir = pw->pw_dir; | 1185 | newdirlim = newdir + nmhomelen; |
| 1171 | /* `getpwnam' may return a unibyte string, which will | 1186 | if (multibyte) |
| 1172 | bite us when we expect the directory to be multibyte. */ | ||
| 1173 | tem = make_unibyte_string (newdir, strlen (newdir)); | ||
| 1174 | newdirlim = newdir + SBYTES (tem); | ||
| 1175 | if (multibyte && !STRING_MULTIBYTE (tem)) | ||
| 1176 | { | 1187 | { |
| 1177 | hdir = DECODE_FILE (tem); | 1188 | AUTO_STRING_WITH_LEN (lisp_nmhome, nmhome, nmhomelen); |
| 1189 | hdir = DECODE_FILE (lisp_nmhome); | ||
| 1178 | newdir = SSDATA (hdir); | 1190 | newdir = SSDATA (hdir); |
| 1179 | newdirlim = newdir + SBYTES (hdir); | 1191 | newdirlim = newdir + SBYTES (hdir); |
| 1180 | } | 1192 | } |
| 1181 | nm = p; | 1193 | |
| 1194 | while (*++nm && !IS_DIRECTORY_SEP (*nm)) | ||
| 1195 | continue; | ||
| 1182 | #ifdef DOS_NT | 1196 | #ifdef DOS_NT |
| 1183 | collapse_newdir = false; | 1197 | collapse_newdir = false; |
| 1184 | #endif | 1198 | #endif |
| 1185 | } | 1199 | } |
| 1186 | 1200 | ||
| 1187 | /* If we don't find a user of that name, leave the name | 1201 | /* If we don't find a user of that name, leave the name |
| 1188 | unchanged; don't move nm forward to p. */ | 1202 | unchanged. */ |
| 1189 | } | 1203 | } |
| 1190 | } | 1204 | } |
| 1191 | 1205 | ||
| @@ -1667,18 +1681,6 @@ See also the function `substitute-in-file-name'.") | |||
| 1667 | } | 1681 | } |
| 1668 | #endif | 1682 | #endif |
| 1669 | 1683 | ||
| 1670 | bool | ||
| 1671 | file_name_absolute_p (const char *filename) | ||
| 1672 | { | ||
| 1673 | return | ||
| 1674 | (IS_DIRECTORY_SEP (*filename) || *filename == '~' | ||
| 1675 | #ifdef DOS_NT | ||
| 1676 | || (IS_DRIVE (*filename) && IS_DEVICE_SEP (filename[1]) | ||
| 1677 | && IS_DIRECTORY_SEP (filename[2])) | ||
| 1678 | #endif | ||
| 1679 | ); | ||
| 1680 | } | ||
| 1681 | |||
| 1682 | /* Put into BUF the concatenation of DIR and FILE, with an intervening | 1684 | /* Put into BUF the concatenation of DIR and FILE, with an intervening |
| 1683 | directory separator if needed. Return a pointer to the NUL byte | 1685 | directory separator if needed. Return a pointer to the NUL byte |
| 1684 | at the end of the concatenated string. */ | 1686 | at the end of the concatenated string. */ |
| @@ -1774,7 +1776,10 @@ get_homedir (void) | |||
| 1774 | return ahome; | 1776 | return ahome; |
| 1775 | } | 1777 | } |
| 1776 | 1778 | ||
| 1777 | /* If /~ or // appears, discard everything through first slash. */ | 1779 | /* If a directory separator followed by an absolute file name (e.g., |
| 1780 | "//foo", "/~", "/~someuser") appears in NM, return the address of | ||
| 1781 | the absolute file name. Otherwise return NULL. ENDP is the | ||
| 1782 | address of the null byte at the end of NM. */ | ||
| 1778 | static char * | 1783 | static char * |
| 1779 | search_embedded_absfilename (char *nm, char *endp) | 1784 | search_embedded_absfilename (char *nm, char *endp) |
| 1780 | { | 1785 | { |
| @@ -1784,34 +1789,8 @@ search_embedded_absfilename (char *nm, char *endp) | |||
| 1784 | && !IS_DIRECTORY_SEP (p[1])); | 1789 | && !IS_DIRECTORY_SEP (p[1])); |
| 1785 | #endif | 1790 | #endif |
| 1786 | for (; p < endp; p++) | 1791 | for (; p < endp; p++) |
| 1787 | { | 1792 | if (IS_DIRECTORY_SEP (p[-1]) && file_name_absolute_p (p)) |
| 1788 | if (IS_DIRECTORY_SEP (p[-1]) && file_name_absolute_p (p)) | 1793 | return p; |
| 1789 | { | ||
| 1790 | char *s; | ||
| 1791 | for (s = p; *s && !IS_DIRECTORY_SEP (*s); s++) | ||
| 1792 | continue; | ||
| 1793 | if (p[0] == '~' && s > p + 1) /* We've got "/~something/". */ | ||
| 1794 | { | ||
| 1795 | USE_SAFE_ALLOCA; | ||
| 1796 | char *o = SAFE_ALLOCA (s - p + 1); | ||
| 1797 | struct passwd *pw; | ||
| 1798 | memcpy (o, p, s - p); | ||
| 1799 | o [s - p] = 0; | ||
| 1800 | |||
| 1801 | /* If we have ~user and `user' exists, discard | ||
| 1802 | everything up to ~. But if `user' does not exist, leave | ||
| 1803 | ~user alone, it might be a literal file name. */ | ||
| 1804 | block_input (); | ||
| 1805 | pw = getpwnam (o + 1); | ||
| 1806 | unblock_input (); | ||
| 1807 | SAFE_FREE (); | ||
| 1808 | if (pw) | ||
| 1809 | return p; | ||
| 1810 | } | ||
| 1811 | else | ||
| 1812 | return p; | ||
| 1813 | } | ||
| 1814 | } | ||
| 1815 | return NULL; | 1794 | return NULL; |
| 1816 | } | 1795 | } |
| 1817 | 1796 | ||
| @@ -2696,13 +2675,23 @@ This happens for interactive use with M-x. */) | |||
| 2696 | 2675 | ||
| 2697 | DEFUN ("file-name-absolute-p", Ffile_name_absolute_p, Sfile_name_absolute_p, | 2676 | DEFUN ("file-name-absolute-p", Ffile_name_absolute_p, Sfile_name_absolute_p, |
| 2698 | 1, 1, 0, | 2677 | 1, 1, 0, |
| 2699 | doc: /* Return t if FILENAME is an absolute file name or starts with `~'. | 2678 | doc: /* Return t if FILENAME is an absolute file name. |
| 2700 | On Unix, absolute file names start with `/'. */) | 2679 | On Unix, absolute file names start with `/'. In Emacs, an absolute |
| 2680 | file name can also start with an initial `~' or `~USER' component, | ||
| 2681 | where USER is a valid login name. */) | ||
| 2701 | (Lisp_Object filename) | 2682 | (Lisp_Object filename) |
| 2702 | { | 2683 | { |
| 2703 | CHECK_STRING (filename); | 2684 | CHECK_STRING (filename); |
| 2704 | return file_name_absolute_p (SSDATA (filename)) ? Qt : Qnil; | 2685 | return file_name_absolute_p (SSDATA (filename)) ? Qt : Qnil; |
| 2705 | } | 2686 | } |
| 2687 | |||
| 2688 | bool | ||
| 2689 | file_name_absolute_p (char const *filename) | ||
| 2690 | { | ||
| 2691 | return (IS_ABSOLUTE_FILE_NAME (filename) | ||
| 2692 | || (filename[0] == '~' | ||
| 2693 | && (!filename[1] || user_homedir (&filename[1])))); | ||
| 2694 | } | ||
| 2706 | 2695 | ||
| 2707 | DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0, | 2696 | DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0, |
| 2708 | doc: /* Return t if file FILENAME exists (whether or not you can read it). | 2697 | doc: /* Return t if file FILENAME exists (whether or not you can read it). |