Function: substitute-in-file-name

substitute-in-file-name is a function defined in fileio.c.

Signature

(substitute-in-file-name FILENAME)

Documentation

Substitute environment variables referred to in FILENAME.

$FOO where FOO is an environment variable name means to substitute
the value of that variable. The variable name should be terminated with a character not a letter, digit or underscore; otherwise, enclose the entire variable name in braces.

If FOO is not defined in the environment, $FOO is left unchanged in the value of this function.

If /~ appears, all of FILENAME through that / is discarded. If // appears, everything up to and including the first of those / is discarded.

Other relevant functions are documented in the file-name group.

View in manual

Probably introduced at or before Emacs version 1.6.

Shortdoc

;; file-name
(substitute-in-file-name "$HOME/foo")
    => "/root/foo"

Source Code

// Defined in /usr/src/emacs/src/fileio.c
{
  char *nm, *p, *x, *endp;
  bool substituted = false;
  bool multibyte;
  char *xnm;
  Lisp_Object handler;

  CHECK_STRING (filename);

  multibyte = STRING_MULTIBYTE (filename);

  /* If the file name has special constructs in it,
     call the corresponding file name handler.  */
  handler = Ffind_file_name_handler (filename, Qsubstitute_in_file_name);
  if (!NILP (handler))
    {
      Lisp_Object handled_name = call2 (handler, Qsubstitute_in_file_name,
					filename);
      if (STRINGP (handled_name))
	return handled_name;
      error ("Invalid handler in `file-name-handler-alist'");
    }

  /* Always work on a copy of the string, in case GC happens during
     decode of environment variables, causing the original Lisp_String
     data to be relocated.  */
  USE_SAFE_ALLOCA;
  SAFE_ALLOCA_STRING (nm, filename);

#ifdef DOS_NT
  dostounix_filename (nm);
  substituted = (memcmp (nm, SDATA (filename), SBYTES (filename)) != 0);
#endif
  endp = nm + SBYTES (filename);

  /* If /~ or // appears, discard everything through first slash.  */
  p = search_embedded_absfilename (nm, endp);
  if (p)
    /* Start over with the new string, so we check the file-name-handler
       again.  Important with filenames like "/home/foo//:/hello///there"
       which would substitute to "/:/hello///there" rather than "/there".  */
    {
      Lisp_Object result
	= (Fsubstitute_in_file_name
	   (make_specified_string (p, -1, endp - p, multibyte)));
      SAFE_FREE ();
      return result;
    }

  /* See if any variables are substituted into the string.  */

  if (!NILP (Ffboundp (Qsubstitute_env_in_file_name)))
    {
      Lisp_Object name
	= (!substituted ? filename
	   : make_specified_string (nm, -1, endp - nm, multibyte));
      Lisp_Object tmp = call1 (Qsubstitute_env_in_file_name, name);
      CHECK_STRING (tmp);
      if (!EQ (tmp, name))
	substituted = true;
      filename = tmp;
    }

  if (!substituted)
    {
#ifdef WINDOWSNT
      if (!NILP (Vw32_downcase_file_names))
	filename = Fdowncase (filename);
#endif
      SAFE_FREE ();
      return filename;
    }

  xnm = SSDATA (filename);
  x = xnm + SBYTES (filename);

  /* If /~ or // appears, discard everything through first slash.  */
  while ((p = search_embedded_absfilename (xnm, x)) != NULL)
    /* This time we do not start over because we've already expanded envvars
       and replaced $$ with $.  Maybe we should start over as well, but we'd
       need to quote some $ to $$ first.  */
    xnm = p;

#ifdef WINDOWSNT
  if (!NILP (Vw32_downcase_file_names))
    {
      Lisp_Object xname = make_specified_string (xnm, -1, x - xnm, multibyte);

      filename = Fdowncase (xname);
    }
  else
#endif
  if (xnm != SSDATA (filename))
    filename = make_specified_string (xnm, -1, x - xnm, multibyte);
  SAFE_FREE ();
  return filename;
}