]> git.eshelyaron.com Git - emacs.git/commitdiff
Act like POSIX sh if $HOME is relative
authorPaul Eggert <eggert@cs.ucla.edu>
Tue, 13 Nov 2018 17:29:14 +0000 (09:29 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Tue, 13 Nov 2018 17:32:50 +0000 (09:32 -0800)
POSIX says sh ~/foo should act like $HOME/foo even if $HOME is
relative, so be consistent with that (Bug#33255).
* admin/merge-gnulib (GNULIB_MODULES): Add dosname.
* src/buffer.c (init_buffer): Use emacs_wd to get
initial working directory with slash appended if needed.
(default-directory): Say it must be absolute.
* src/emacs.c (emacs_wd): New global variable.
(init_cmdargs): Dir arg is now char const *.
(main): Set emacs_wd.
* src/emacs.c (main) [NS_IMPL_COCOA]:
* src/fileio.c (Fexpand_file_name):
Use get_homedir instead of egetenv ("HOME").
* src/fileio.c: Include dosname.h, for IS_ABSOLUTE_FILE_NAME.
(splice_dir_file, get_homedir): New functions.
* src/xrdb.c (gethomedir): Remove.  All callers changed
to use get_homedir and splice_dir_file.
* test/src/fileio-tests.el (fileio-tests--relative-HOME): New test.

admin/merge-gnulib
src/buffer.c
src/emacs.c
src/fileio.c
src/lisp.h
src/xrdb.c
test/src/fileio-tests.el

index 575e3fa74a7fa7fd087dcef3a2b904cf10f1f0f2..84dcb0b87523bb7f0ea990cfbe818d1cdd0b05af 100755 (executable)
@@ -30,7 +30,7 @@ GNULIB_MODULES='
   careadlinkat close-stream
   count-leading-zeros count-one-bits count-trailing-zeros
   crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer
-  d-type diffseq dtoastr dtotimespec dup2
+  d-type diffseq dosname dtoastr dtotimespec dup2
   environ execinfo explicit_bzero faccessat
   fcntl fcntl-h fdatasync fdopendir
   filemode filevercmp flexmember fpieee fstatat fsusage fsync
index ac2de7d19f23c93870f3a879efc75a8d924c2400..90ef886b22903288468103931c2a55a0b219adc1 100644 (file)
@@ -5268,9 +5268,7 @@ init_buffer_once (void)
 void
 init_buffer (int initialized)
 {
-  char *pwd;
   Lisp_Object temp;
-  ptrdiff_t len;
 
 #ifdef USE_MMAP_FOR_BUFFERS
   if (initialized)
@@ -5324,7 +5322,7 @@ init_buffer (int initialized)
   if (NILP (BVAR (&buffer_defaults, enable_multibyte_characters)))
     Fset_buffer_multibyte (Qnil);
 
-  pwd = emacs_get_current_dir_name ();
+  char const *pwd = emacs_wd;
 
   if (!pwd)
     {
@@ -5336,22 +5334,16 @@ init_buffer (int initialized)
     {
       /* Maybe this should really use some standard subroutine
          whose definition is filename syntax dependent.  */
-      len = strlen (pwd);
-      if (!(IS_DIRECTORY_SEP (pwd[len - 1])))
-        {
-          /* Grow buffer to add directory separator and '\0'.  */
-          pwd = realloc (pwd, len + 2);
-          if (!pwd)
-            fatal ("get_current_dir_name: %s\n", strerror (errno));
-          pwd[len] = DIRECTORY_SEP;
-          pwd[len + 1] = '\0';
-          len++;
-        }
+      ptrdiff_t len = strlen (pwd);
+      bool add_slash = ! IS_DIRECTORY_SEP (pwd[len - 1]);
 
       /* At this moment, we still don't know how to decode the directory
          name.  So, we keep the bytes in unibyte form so that file I/O
          routines correctly get the original bytes.  */
-      bset_directory (current_buffer, make_unibyte_string (pwd, len));
+      Lisp_Object dirname = make_unibyte_string (pwd, len + add_slash);
+      if (add_slash)
+       SSET (dirname, len, DIRECTORY_SEP);
+      bset_directory (current_buffer, dirname);
 
       /* Add /: to the front of the name
          if it would otherwise be treated as magic.  */
@@ -5372,8 +5364,6 @@ init_buffer (int initialized)
 
   temp = get_minibuffer (0);
   bset_directory (XBUFFER (temp), BVAR (current_buffer, directory));
-
-  free (pwd);
 }
 
 /* Similar to defvar_lisp but define a variable whose value is the
@@ -5706,8 +5696,8 @@ visual lines rather than logical lines.  See the documentation of
   DEFVAR_PER_BUFFER ("default-directory", &BVAR (current_buffer, directory),
                     Qstringp,
                     doc: /* Name of default directory of current buffer.
-It should be a directory name (as opposed to a directory file-name).
-On GNU and Unix systems, directory names end in a slash `/'.
+It should be an absolute directory name; on GNU and Unix systems,
+these names start with `/' or `~' and end with `/'.
 To interactively change the default directory, use command `cd'. */);
 
   DEFVAR_PER_BUFFER ("auto-fill-function", &BVAR (current_buffer, auto_fill_function),
index 512174d562e601d67caebc5a136e40b045bc1f1c..acb4959bfea0c07cb465857891c031b1d11c8705 100644 (file)
@@ -204,6 +204,9 @@ HANDLE w32_daemon_event;
 char **initial_argv;
 int initial_argc;
 
+/* The name of the working directory, or NULL if this info is unavailable.  */
+char const *emacs_wd;
+
 static void sort_args (int argc, char **argv);
 static void syms_of_emacs (void);
 
@@ -406,7 +409,7 @@ terminate_due_to_signal (int sig, int backtrace_limit)
 /* Code for dealing with Lisp access to the Unix command line.  */
 
 static void
-init_cmdargs (int argc, char **argv, int skip_args, char *original_pwd)
+init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
 {
   int i;
   Lisp_Object name, dir, handler;
@@ -694,7 +697,7 @@ main (int argc, char **argv)
   char *ch_to_dir = 0;
 
   /* If we use --chdir, this records the original directory.  */
-  char *original_pwd = 0;
+  char const *original_pwd = 0;
 
   /* Record (approximately) where the stack begins.  */
   stack_bottom = (char *) &stack_bottom_variable;
@@ -794,6 +797,8 @@ main (int argc, char **argv)
       exit (0);
     }
 
+  emacs_wd = emacs_get_current_dir_name ();
+
   if (argmatch (argv, argc, "-chdir", "--chdir", 4, &ch_to_dir, &skip_args))
     {
 #ifdef WINDOWSNT
@@ -804,13 +809,14 @@ main (int argc, char **argv)
       filename_from_ansi (ch_to_dir, newdir);
       ch_to_dir = newdir;
 #endif
-      original_pwd = emacs_get_current_dir_name ();
       if (chdir (ch_to_dir) != 0)
         {
           fprintf (stderr, "%s: Can't chdir to %s: %s\n",
                    argv[0], ch_to_dir, strerror (errno));
           exit (1);
         }
+      original_pwd = emacs_wd;
+      emacs_wd = emacs_get_current_dir_name ();
     }
 
 #if defined (HAVE_SETRLIMIT) && defined (RLIMIT_STACK) && !defined (CYGWIN)
@@ -1289,21 +1295,21 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
     {
 #ifdef NS_IMPL_COCOA
       /* Started from GUI? */
-      /* FIXME: Do the right thing if getenv returns NULL, or if
+      /* FIXME: Do the right thing if get_homedir returns "", or if
          chdir fails.  */
       if (! inhibit_window_system && ! isatty (STDIN_FILENO) && ! ch_to_dir)
-        chdir (getenv ("HOME"));
+        chdir (get_homedir ());
       if (skip_args < argc)
         {
           if (!strncmp (argv[skip_args], "-psn", 4))
             {
               skip_args += 1;
-              if (! ch_to_dir) chdir (getenv ("HOME"));
+              if (! ch_to_dir) chdir (get_homedir ());
             }
           else if (skip_args+1 < argc && !strncmp (argv[skip_args+1], "-psn", 4))
             {
               skip_args += 2;
-              if (! ch_to_dir) chdir (getenv ("HOME"));
+              if (! ch_to_dir) chdir (get_homedir ());
             }
         }
 #endif  /* COCOA */
index 7fb865809f51b7739c0c07edd813c53df6cec654..e178c39fc184a1179f72e92c8c197c684078b9a0 100644 (file)
@@ -96,6 +96,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include <acl.h>
 #include <allocator.h>
 #include <careadlinkat.h>
+#include <dosname.h>
 #include <fsusage.h>
 #include <stat-time.h>
 #include <tempname.h>
@@ -1093,8 +1094,7 @@ the root directory.  */)
        {
          Lisp_Object tem;
 
-         if (!(newdir = egetenv ("HOME")))
-           newdir = newdirlim = "";
+         newdir = get_homedir ();
          nm++;
 #ifdef WINDOWSNT
          if (newdir[0])
@@ -1109,7 +1109,7 @@ the root directory.  */)
 #endif
            tem = build_string (newdir);
          newdirlim = newdir + SBYTES (tem);
-         /* `egetenv' may return a unibyte string, which will bite us
+         /* get_homedir may return a unibyte string, which will bite us
             if we expect the directory to be multibyte.  */
          if (multibyte && !STRING_MULTIBYTE (tem))
            {
@@ -1637,7 +1637,6 @@ See also the function `substitute-in-file-name'.")
 }
 #endif
 \f
-/* If /~ or // appears, discard everything through first slash.  */
 static bool
 file_name_absolute_p (const char *filename)
 {
@@ -1650,6 +1649,61 @@ file_name_absolute_p (const char *filename)
      );
 }
 
+/* Put into BUF the concatenation of DIR and FILE, with an intervening
+   directory separator if needed.  Return a pointer to the null byte
+   at the end of the concatenated string.  */
+char *
+splice_dir_file (char *buf, char const *dir, char const *file)
+{
+  char *e = stpcpy (buf, dir);
+  *e = DIRECTORY_SEP;
+  e += ! (buf < e && IS_DIRECTORY_SEP (e[-1]));
+  return stpcpy (e, file);
+}
+
+/* Get the home directory, an absolute file name.  Return the empty
+   string on failure.  The returned value does not survive garbage
+   collection, calls to this function, or calls to the getpwnam class
+   of functions.  */
+char const *
+get_homedir (void)
+{
+  char const *home = egetenv ("HOME");
+  if (!home)
+    {
+      static char const *userenv[] = {"LOGNAME", "USER"};
+      struct passwd *pw = NULL;
+      for (int i = 0; i < ARRAYELTS (userenv); i++)
+       {
+         char *user = egetenv (userenv[i]);
+         if (user)
+           {
+             pw = getpwnam (user);
+             if (pw)
+               break;
+           }
+       }
+      if (!pw)
+       pw = getpwuid (getuid ());
+      if (pw)
+       home = pw->pw_dir;
+      if (!home)
+       return "";
+    }
+  if (IS_ABSOLUTE_FILE_NAME (home))
+    return home;
+  if (!emacs_wd)
+    error ("$HOME is relative to unknown directory");
+  static char *ahome;
+  static ptrdiff_t ahomesize;
+  ptrdiff_t ahomelenbound = strlen (emacs_wd) + 1 + strlen (home) + 1;
+  if (ahomesize <= ahomelenbound)
+    ahome = xpalloc (ahome, &ahomesize, ahomelenbound + 1 - ahomesize, -1, 1);
+  splice_dir_file (ahome, emacs_wd, home);
+  return ahome;
+}
+
+/* If /~ or // appears, discard everything through first slash.  */
 static char *
 search_embedded_absfilename (char *nm, char *endp)
 {
index f8ffb33a641de4ec37d4bea1f93abecc3683a3a9..7e7dba631f34ada04353ff3213ae64b4adeccbe0 100644 (file)
@@ -4061,6 +4061,8 @@ extern void syms_of_marker (void);
 
 /* Defined in fileio.c.  */
 
+extern char *splice_dir_file (char *, char const *, char const *);
+extern char const *get_homedir (void);
 extern Lisp_Object expand_and_dir_to_file (Lisp_Object);
 extern Lisp_Object write_region (Lisp_Object, Lisp_Object, Lisp_Object,
                                 Lisp_Object, Lisp_Object, Lisp_Object,
@@ -4185,6 +4187,7 @@ extern void syms_of_frame (void);
 /* Defined in emacs.c.  */
 extern char **initial_argv;
 extern int initial_argc;
+extern char const *emacs_wd;
 #if defined (HAVE_X_WINDOWS) || defined (HAVE_NS)
 extern bool display_arg;
 #endif
index 4abf1ad84edc43c2fe0f87aa10d39af5157c52cd..87c2faf6598b6092b484cacc6b823e6a6a82bc26 100644 (file)
@@ -202,35 +202,6 @@ magic_db (const char *string, ptrdiff_t string_len, const char *class,
 }
 
 
-static char *
-gethomedir (void)
-{
-  struct passwd *pw;
-  char *ptr;
-  char *copy;
-
-  if ((ptr = getenv ("HOME")) == NULL)
-    {
-      if ((ptr = getenv ("LOGNAME")) != NULL
-         || (ptr = getenv ("USER")) != NULL)
-       pw = getpwnam (ptr);
-      else
-       pw = getpwuid (getuid ());
-
-      if (pw)
-       ptr = pw->pw_dir;
-    }
-
-  if (ptr == NULL)
-    return xstrdup ("/");
-
-  ptrdiff_t len = strlen (ptr);
-  copy = xmalloc (len + 2);
-  strcpy (copy + len, "/");
-  return memcpy (copy, ptr, len);
-}
-
-
 /* Find the first element of SEARCH_PATH which exists and is readable,
    after expanding the %-escapes.  Return 0 if we didn't find any, and
    the path name of the one we found otherwise.  */
@@ -316,12 +287,11 @@ get_user_app (const char *class)
   if (! db)
     {
       /* Check in the home directory.  This is a bit of a hack; let's
-        hope one's home directory doesn't contain any %-escapes.  */
-      char *home = gethomedir ();
+        hope one's home directory doesn't contain ':' or '%'.  */
+      char const *home = get_homedir ();
       db = search_magic_path (home, class, "%L/%N");
       if (! db)
        db = search_magic_path (home, class, "%N");
-      xfree (home);
     }
 
   return db;
@@ -346,10 +316,9 @@ get_user_db (Display *display)
   else
     {
       /* Use ~/.Xdefaults.  */
-      char *home = gethomedir ();
-      ptrdiff_t homelen = strlen (home);
-      char *filename = xrealloc (home, homelen + sizeof xdefaults);
-      strcpy (filename + homelen, xdefaults);
+      char const *home = get_homedir ();
+      char *filename = xmalloc (strlen (home) + 1 + sizeof xdefaults);
+      splice_dir_file (filename, home, xdefaults);
       db = XrmGetFileDatabase (filename);
       xfree (filename);
     }
@@ -380,13 +349,12 @@ get_environ_db (void)
       if (STRINGP (system_name))
        {
          /* Use ~/.Xdefaults-HOSTNAME.  */
-         char *home = gethomedir ();
-         ptrdiff_t homelen = strlen (home);
-         ptrdiff_t filenamesize = (homelen + sizeof xdefaults
-                                   + 1 + SBYTES (system_name));
-         p = filename = xrealloc (home, filenamesize);
-         lispstpcpy (stpcpy (stpcpy (filename + homelen, xdefaults), "-"),
-                     system_name);
+         char const *home = get_homedir ();
+         p = filename = xmalloc (strlen (home) + 1 + sizeof xdefaults
+                                 + 1 + SBYTES (system_name));
+         char *e = splice_dir_file (p, home, xdefaults);
+         *e++ = '/';
+         lispstpcpy (e, system_name);
        }
     }
 
index 5d12685fa190c1fca39a21db9eebb297919f844c..b7b78bbda0910c9572833c699b09ef46535eea32 100644 (file)
@@ -95,3 +95,11 @@ Also check that an encoding error can appear in a symlink."
   (should (equal (file-name-as-directory "d:/abc/") "d:/abc/"))
   (should (equal (file-name-as-directory "D:\\abc/") "d:/abc/"))
   (should (equal (file-name-as-directory "D:/abc//") "d:/abc//")))
+
+(ert-deftest fileio-tests--relative-HOME ()
+  "Test that expand-file-name works even when HOME is relative."
+  (let ((old-home (getenv "HOME")))
+    (setenv "HOME" "a/b/c")
+    (should (equal (expand-file-name "~/foo")
+                   (expand-file-name "a/b/c/foo")))
+    (setenv "HOME" old-home)))