diff options
| author | Paul Eggert <eggert@cs.ucla.edu> | 2018-11-13 09:29:14 -0800 | 
|---|---|---|
| committer | Paul Eggert <eggert@cs.ucla.edu> | 2018-11-13 09:32:50 -0800 | 
| commit | 900276502fbb4dcabdabc5d7d24b4bc5645f2cf3 (patch) | |
| tree | 4d6c17f5b3cea0f4d5dfbc7243eb6495269a7e56 | |
| parent | ce1fb157e840fd292c3db4632831c4514a663890 (diff) | |
| download | emacs-900276502fbb4dcabdabc5d7d24b4bc5645f2cf3.tar.gz | |
Act like POSIX sh if $HOME is relative
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.
| -rwxr-xr-x | admin/merge-gnulib | 2 | ||||
| -rw-r--r-- | src/buffer.c | 28 | ||||
| -rw-r--r-- | src/emacs.c | 20 | ||||
| -rw-r--r-- | src/fileio.c | 62 | ||||
| -rw-r--r-- | src/lisp.h | 3 | ||||
| -rw-r--r-- | src/xrdb.c | 54 | ||||
| -rw-r--r-- | test/src/fileio-tests.el | 8 | 
7 files changed, 103 insertions, 74 deletions
| diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 575e3fa74a7..84dcb0b8752 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -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 diff --git a/src/buffer.c b/src/buffer.c index ac2de7d19f2..90ef886b229 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -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), diff --git a/src/emacs.c b/src/emacs.c index 512174d562e..acb4959bfea 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -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 */ diff --git a/src/fileio.c b/src/fileio.c index 7fb865809f5..e178c39fc18 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -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 -/* 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)  { diff --git a/src/lisp.h b/src/lisp.h index f8ffb33a641..7e7dba631f3 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -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 diff --git a/src/xrdb.c b/src/xrdb.c index 4abf1ad84ed..87c2faf6598 100644 --- a/src/xrdb.c +++ b/src/xrdb.c @@ -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);  	}      } diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el index 5d12685fa19..b7b78bbda09 100644 --- a/test/src/fileio-tests.el +++ b/test/src/fileio-tests.el @@ -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))) | 
