From: Paul Eggert <eggert@cs.ucla.edu>
To: Ken Brown <kbrown@cornell.edu>, Eli Zaretskii <eliz@gnu.org>
Cc: "rms@gnu.org" <rms@gnu.org>, "schwab@suse.de" <schwab@suse.de>,
"36502-done@debbugs.gnu.org" <36502@debbugs.gnu.org>,
"npostavs@gmail.com" <npostavs@gmail.com>,
"monnier@iro.umontreal.ca" <monnier@iro.umontreal.ca>,
"dan@dpsutton.com" <dan@dpsutton.com>
Subject: bug#36502: Fwd: bug#36502: 27.0.50; infinite loop in file-name-case-insensitive-p
Date: Wed, 24 Jul 2019 14:36:25 -0700 [thread overview]
Message-ID: <90f841a9-4ec2-14f2-846c-fbfc31b9ad42@cs.ucla.edu> (raw)
In-Reply-To: <22127ce6-182a-38ac-acc1-dfd09d727f18@cornell.edu>
[-- Attachment #1: Type: text/plain, Size: 194 bytes --]
Thanks for the patch. I improved it, wrote a NEWS entry and a test case for it,
and installed the attached. I think this finishes off the issues in Bug#36502
and so am closing the bug report.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Do-not-treat-nosuchuser-as-an-absolute-file-name.patch --]
[-- Type: text/x-patch; name="0001-Do-not-treat-nosuchuser-as-an-absolute-file-name.patch", Size: 9392 bytes --]
From a5063aa8b174db286a0e83b8ffdd4e65c521f733 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Wed, 24 Jul 2019 14:28:13 -0700
Subject: [PATCH] Do not treat ~nosuchuser as an absolute file name
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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.
---
doc/lispref/files.texi | 10 +++-
etc/NEWS | 3 +
src/fileio.c | 121 ++++++++++++++++++---------------------
test/src/fileio-tests.el | 11 ++++
4 files changed, 78 insertions(+), 67 deletions(-)
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index 0519f787dc..0ea8a4f0a1 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -2154,7 +2154,11 @@ Relative File Names
@defun file-name-absolute-p filename
This function returns @code{t} if file @var{filename} is an absolute
-file name or begins with @samp{~}, @code{nil} otherwise.
+file name, @code{nil} otherwise. A file name is considered to be
+absolute if its first component is @samp{~}, or is @samp{~@var{user}}
+where @var{user} is a valid login name. In the following examples,
+assume that there is a user named @samp{rms} but no user named
+@samp{nosuchuser}.
@example
@group
@@ -2162,6 +2166,10 @@ Relative File Names
@result{} t
@end group
@group
+(file-name-absolute-p "~nosuchuser/foo")
+ @result{} nil
+@end group
+@group
(file-name-absolute-p "rms/foo")
@result{} nil
@end group
diff --git a/etc/NEWS b/etc/NEWS
index 5313270411..08f0e654f7 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1815,6 +1815,9 @@ relative to the 'default-directory' of the current buffer. We recommend
always setting "$HOME" to an absolute file name, so that its meaning is
independent of where Emacs was started.
+** file-name-absolute-p no longer considers "~foo" to be an absolute
+file name if there is no user named "foo".
+
** The FILENAME argument to 'file-name-base' is now mandatory and no
longer defaults to 'buffer-file-name'.
diff --git a/src/fileio.c b/src/fileio.c
index e4269b96a3..d1a7f39ac9 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -744,6 +744,31 @@ file_name_absolute_no_tilde_p (Lisp_Object name)
return IS_ABSOLUTE_FILE_NAME (SSDATA (name));
}
+/* Return the home directory of the user NAME, or a null pointer if
+ NAME is empty or the user does not exist or the user's home
+ directory is not an absolute file name. NAME is an array of bytes
+ that continues up to (but not including) the next NUL byte or
+ directory separator. The returned string lives in storage good
+ until the next call to this or similar functions. */
+static char *
+user_homedir (char const *name)
+{
+ ptrdiff_t length;
+ for (length = 0; name[length] && !IS_DIRECTORY_SEP (name[length]); length++)
+ continue;
+ if (length == 0)
+ return NULL;
+ USE_SAFE_ALLOCA;
+ char *p = SAFE_ALLOCA (length + 1);
+ memcpy (p, name, length);
+ p[length] = 0;
+ struct passwd *pw = getpwnam (p);
+ SAFE_FREE ();
+ if (!pw || (pw->pw_dir && !IS_ABSOLUTE_FILE_NAME (pw->pw_dir)))
+ return NULL;
+ return pw->pw_dir;
+}
+
DEFUN ("expand-file-name", Fexpand_file_name, Sexpand_file_name, 1, 2, 0,
doc: /* Convert filename NAME to absolute, and canonicalize it.
Second arg DEFAULT-DIRECTORY is directory to start with if NAME is relative
@@ -788,7 +813,6 @@ the root directory. */)
char *target;
ptrdiff_t tlen;
- struct passwd *pw;
#ifdef DOS_NT
int drive = 0;
bool collapse_newdir = true;
@@ -1153,39 +1177,29 @@ the root directory. */)
}
else /* ~user/filename */
{
- char *o, *p;
- for (p = nm; *p && !IS_DIRECTORY_SEP (*p); p++)
- continue;
- o = SAFE_ALLOCA (p - nm + 1);
- memcpy (o, nm, p - nm);
- o[p - nm] = 0;
-
- block_input ();
- pw = getpwnam (o + 1);
- unblock_input ();
- if (pw)
+ char *nmhome = user_homedir (nm + 1);
+ if (nmhome)
{
- Lisp_Object tem;
-
- newdir = pw->pw_dir;
- /* `getpwnam' may return a unibyte string, which will
- bite us when we expect the directory to be multibyte. */
- tem = make_unibyte_string (newdir, strlen (newdir));
- newdirlim = newdir + SBYTES (tem);
- if (multibyte && !STRING_MULTIBYTE (tem))
+ ptrdiff_t nmhomelen = strlen (nmhome);
+ newdir = nmhome;
+ newdirlim = newdir + nmhomelen;
+ if (multibyte)
{
- hdir = DECODE_FILE (tem);
+ AUTO_STRING_WITH_LEN (lisp_nmhome, nmhome, nmhomelen);
+ hdir = DECODE_FILE (lisp_nmhome);
newdir = SSDATA (hdir);
newdirlim = newdir + SBYTES (hdir);
}
- nm = p;
+
+ 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
- unchanged; don't move nm forward to p. */
+ unchanged. */
}
}
@@ -1667,18 +1681,6 @@ See also the function `substitute-in-file-name'.")
}
#endif
\f
-bool
-file_name_absolute_p (const char *filename)
-{
- return
- (IS_DIRECTORY_SEP (*filename) || *filename == '~'
-#ifdef DOS_NT
- || (IS_DRIVE (*filename) && IS_DEVICE_SEP (filename[1])
- && IS_DIRECTORY_SEP (filename[2]))
-#endif
- );
-}
-
/* Put into BUF the concatenation of DIR and FILE, with an intervening
directory separator if needed. Return a pointer to the NUL byte
at the end of the concatenated string. */
@@ -1774,7 +1776,10 @@ get_homedir (void)
return ahome;
}
-/* If /~ or // appears, discard everything through first slash. */
+/* If a directory separator followed by an absolute file name (e.g.,
+ "//foo", "/~", "/~someuser") appears in NM, return the address of
+ the absolute file name. Otherwise return NULL. ENDP is the
+ address of the null byte at the end of NM. */
static char *
search_embedded_absfilename (char *nm, char *endp)
{
@@ -1784,34 +1789,8 @@ search_embedded_absfilename (char *nm, char *endp)
&& !IS_DIRECTORY_SEP (p[1]));
#endif
for (; p < endp; p++)
- {
- if (IS_DIRECTORY_SEP (p[-1]) && file_name_absolute_p (p))
- {
- char *s;
- for (s = p; *s && !IS_DIRECTORY_SEP (*s); s++)
- continue;
- if (p[0] == '~' && s > p + 1) /* We've got "/~something/". */
- {
- USE_SAFE_ALLOCA;
- char *o = SAFE_ALLOCA (s - p + 1);
- struct passwd *pw;
- memcpy (o, p, s - p);
- o [s - p] = 0;
-
- /* If we have ~user and `user' exists, discard
- everything up to ~. But if `user' does not exist, leave
- ~user alone, it might be a literal file name. */
- block_input ();
- pw = getpwnam (o + 1);
- unblock_input ();
- SAFE_FREE ();
- if (pw)
- return p;
- }
- else
- return p;
- }
- }
+ if (IS_DIRECTORY_SEP (p[-1]) && file_name_absolute_p (p))
+ return p;
return NULL;
}
@@ -2696,13 +2675,23 @@ This happens for interactive use with M-x. */)
\f
DEFUN ("file-name-absolute-p", Ffile_name_absolute_p, Sfile_name_absolute_p,
1, 1, 0,
- doc: /* Return t if FILENAME is an absolute file name or starts with `~'.
-On Unix, absolute file names start with `/'. */)
+ doc: /* Return t if FILENAME is an absolute file name.
+On Unix, absolute file names start with `/'. In Emacs, an absolute
+file name can also start with an initial `~' or `~USER' component,
+where USER is a valid login name. */)
(Lisp_Object filename)
{
CHECK_STRING (filename);
return file_name_absolute_p (SSDATA (filename)) ? Qt : Qnil;
}
+
+bool
+file_name_absolute_p (char const *filename)
+{
+ return (IS_ABSOLUTE_FILE_NAME (filename)
+ || (filename[0] == '~'
+ && (!filename[1] || user_homedir (&filename[1]))));
+}
\f
DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0,
doc: /* Return t if file FILENAME exists (whether or not you can read it).
diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el
index 813ee5f798..09a5b147e1 100644
--- a/test/src/fileio-tests.el
+++ b/test/src/fileio-tests.el
@@ -136,3 +136,14 @@ fileio-tests--symlink-failure
(name (expand-file-name "bar")))
(should (and (file-name-absolute-p name)
(not (eq (aref name 0) ?~))))))
+
+(ert-deftest fileio-tests--no-such-user ()
+ "Test file-name-absolute-p on ~nosuchuser."
+ (unless (user-full-name "nosuchuser")
+ (should (not (file-name-absolute-p "~nosuchuser")))
+ (should (not (file-name-absolute-p "~nosuchuser/")))
+ (should (not (file-name-absolute-p "~nosuchuser//")))
+ (should (not (file-name-absolute-p "~nosuchuser/foo")))
+ (should (not (file-name-absolute-p "~nosuchuser/foo/")))
+ (should (not (file-name-absolute-p "~nosuchuser/foo//")))
+ (should (not (file-name-absolute-p "~nosuchuser/foo/bar")))))
--
2.17.1
next prev parent reply other threads:[~2019-07-24 21:36 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-07-04 16:48 bug#36502: 27.0.50; infinite loop in file-name-case-insensitive-p Daniel Sutton
2019-07-04 18:08 ` Ken Brown
[not found] ` <CAMfzp7bhcmeY7QP4-ALfmBE4OojJthcYEVLR79zj-FrGx5s+WA@mail.gmail.com>
2019-07-05 1:32 ` Ken Brown
[not found] ` <CAMfzp7brsFLdpi04pDAL+O_yVuF7=EERzinVBKoQyTaLUtgwDA@mail.gmail.com>
[not found] ` <CAMfzp7Y=wA8_V=Tvm1iOtyXM-kqKZyx41Q4phJfnwmygHhJWLA@mail.gmail.com>
2019-07-05 3:05 ` bug#36502: Fwd: " Daniel Sutton
2019-07-06 12:49 ` Ken Brown
2019-07-06 13:27 ` Noam Postavsky
2019-07-06 15:38 ` Ken Brown
2019-07-06 16:14 ` Eli Zaretskii
2019-07-07 14:09 ` Ken Brown
2019-07-07 14:37 ` Eli Zaretskii
2019-07-07 19:30 ` Ken Brown
2019-07-08 12:25 ` Eli Zaretskii
2019-07-08 13:36 ` Ken Brown
2019-07-08 13:59 ` Eli Zaretskii
2019-07-08 15:17 ` Ken Brown
2019-07-08 16:44 ` Ken Brown
2019-07-08 17:23 ` Eli Zaretskii
2019-07-10 21:57 ` Ken Brown
2019-07-11 23:36 ` Richard Stallman
2019-07-12 6:41 ` Eli Zaretskii
2019-07-12 20:18 ` Ken Brown
2019-07-15 13:39 ` Ken Brown
2019-07-19 7:00 ` Eli Zaretskii
2019-07-20 9:19 ` Eli Zaretskii
2019-07-20 14:27 ` Ken Brown
2019-07-20 15:52 ` Eli Zaretskii
2019-07-21 2:32 ` Paul Eggert
2019-07-21 14:21 ` Eli Zaretskii
2019-07-21 14:30 ` Ken Brown
2019-07-22 2:16 ` Ken Brown
2019-07-24 21:36 ` Paul Eggert [this message]
2019-07-24 22:47 ` Ken Brown
2019-07-26 11:04 ` Andy Moreton
2019-07-08 14:37 ` Andreas Schwab
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=90f841a9-4ec2-14f2-846c-fbfc31b9ad42@cs.ucla.edu \
--to=eggert@cs.ucla.edu \
--cc=36502@debbugs.gnu.org \
--cc=dan@dpsutton.com \
--cc=eliz@gnu.org \
--cc=kbrown@cornell.edu \
--cc=monnier@iro.umontreal.ca \
--cc=npostavs@gmail.com \
--cc=rms@gnu.org \
--cc=schwab@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).