-;;; env.el --- functions to manipulate environment variables
+;;; env.el --- functions to manipulate environment variables -*- lexical-binding:t -*-
;; Copyright (C) 1991, 1994, 2000-2013 Free Software Foundation, Inc.
(defconst env--substitute-vars-regexp
"\\$\\(?:\\(?1:[[:alnum:]_]+\\)\\|{\\(?1:[^{}]+\\)}\\|\\$\\)")
-(defun substitute-env-vars (string &optional only-defined)
+(defun substitute-env-vars (string &optional when-undefined)
"Substitute environment variables referred to in STRING.
`$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. For instance, in `ab$cd-x',
`$cd' is treated as an environment variable.
-If ONLY-DEFINED is nil, references to undefined environment variables
-are replaced by the empty string; if it is non-nil, they are left unchanged.
+
+If WHEN-DEFINED is nil, references to undefined environment variables
+are replaced by the empty string; if it is a function, the function is called
+with the variable name as argument and should return the text with which
+to replace it or nil to leave it unchanged.
+If it is non-nil and not a function, references to undefined variables are
+left unchanged.
Use `$$' to insert a single dollar sign."
(let ((start 0))
(while (string-match env--substitute-vars-regexp string start)
(cond ((match-beginning 1)
- (let ((value (getenv (match-string 1 string))))
- (if (and (null value) only-defined)
+ (let* ((var (match-string 1 string))
+ (value (getenv var)))
+ (if (and (null value)
+ (if (functionp when-undefined)
+ (null (setq value (funcall when-undefined var)))
+ when-undefined))
(setq start (match-end 0))
- (setq string (replace-match (or value "") t t string)
+ (setq string (replace-match (or value "") t t string)
start (+ (match-beginning 0) (length value))))))
(t
(setq string (replace-match "$" t t string)
start (+ (match-beginning 0) 1)))))
string))
+(defun substitute-env-in-file-name (filename)
+ (substitute-env-vars filename
+ ;; How 'bout we lookup other tables than the env?
+ ;; E.g. we could accept bookmark names as well!
+ (if (memq system-type '(windows-nt ms-dos))
+ (lambda (var) (getenv (upcase var)))
+ t)))
(defun setenv-internal (env variable value keep-empty)
"Set VARIABLE to VALUE in ENV, adding empty entries if KEEP-EMPTY.
/* Lisp function for recursively deleting directories. */
static Lisp_Object Qdelete_directory;
+static Lisp_Object Qsubstitute_env_in_file_name;
+
#ifdef WINDOWSNT
#endif
those `/' is discarded. */)
(Lisp_Object filename)
{
- char *nm, *s, *p, *o, *x, *endp;
- char *target = NULL;
- ptrdiff_t total = 0;
- bool substituted = 0;
+ char *nm, *p, *x, *endp;
+ bool substituted = false;
bool multibyte;
char *xnm;
Lisp_Object handler;
return Fsubstitute_in_file_name
(make_specified_string (p, -1, endp - p, multibyte));
- /* See if any variables are substituted into the string
- and find the total length of their values in `total'. */
-
- for (p = nm; p != endp;)
- if (*p != '$')
- p++;
- else
- {
- p++;
- if (p == endp)
- goto badsubst;
- else if (*p == '$')
- {
- /* "$$" means a single "$". */
- p++;
- total -= 1;
- substituted = 1;
- continue;
- }
- else if (*p == '{')
- {
- o = ++p;
- p = memchr (p, '}', endp - p);
- if (! p)
- goto missingclose;
- s = p;
- }
- else
- {
- o = p;
- while (p != endp && (c_isalnum (*p) || *p == '_')) p++;
- s = p;
- }
-
- /* Copy out the variable name. */
- target = alloca (s - o + 1);
- memcpy (target, o, s - o);
- target[s - o] = 0;
-#ifdef DOS_NT
- strupr (target); /* $home == $HOME etc. */
-#endif /* DOS_NT */
+ /* See if any variables are substituted into the string. */
- /* Get variable value. */
- o = egetenv (target);
- if (o)
- {
- /* Don't try to guess a maximum length - UTF8 can use up to
- four bytes per character. This code is unlikely to run
- in a situation that requires performance, so decoding the
- env variables twice should be acceptable. Note that
- decoding may cause a garbage collect. */
- Lisp_Object orig, decoded;
- orig = build_unibyte_string (o);
- decoded = DECODE_FILE (orig);
- total += SBYTES (decoded);
- substituted = 1;
- }
- else if (*p == '}')
- goto badvar;
- }
+ 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)
{
return filename;
}
- /* If substitution required, recopy the string and do it. */
- /* Make space in stack frame for the new copy. */
- xnm = alloca (SBYTES (filename) + total + 1);
- x = xnm;
-
- /* Copy the rest of the name through, replacing $ constructs with values. */
- for (p = nm; *p;)
- if (*p != '$')
- *x++ = *p++;
- else
- {
- p++;
- if (p == endp)
- goto badsubst;
- else if (*p == '$')
- {
- *x++ = *p++;
- continue;
- }
- else if (*p == '{')
- {
- o = ++p;
- p = memchr (p, '}', endp - p);
- if (! p)
- goto missingclose;
- s = p++;
- }
- else
- {
- o = p;
- while (p != endp && (c_isalnum (*p) || *p == '_')) p++;
- s = p;
- }
-
- /* Copy out the variable name. */
- target = alloca (s - o + 1);
- memcpy (target, o, s - o);
- target[s - o] = 0;
-
- /* Get variable value. */
- o = egetenv (target);
- if (!o)
- {
- *x++ = '$';
- strcpy (x, target); x+= strlen (target);
- }
- else
- {
- Lisp_Object orig, decoded;
- ptrdiff_t orig_length, decoded_length;
- orig_length = strlen (o);
- orig = make_unibyte_string (o, orig_length);
- decoded = DECODE_FILE (orig);
- decoded_length = SBYTES (decoded);
- memcpy (x, SDATA (decoded), decoded_length);
- x += decoded_length;
-
- /* If environment variable needed decoding, return value
- needs to be multibyte. */
- if (decoded_length != orig_length
- || memcmp (SDATA (decoded), o, orig_length))
- multibyte = 1;
- }
- }
-
- *x = 0;
-
+ 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
}
else
#endif
- return make_specified_string (xnm, -1, x - xnm, multibyte);
-
- badsubst:
- error ("Bad format environment-variable substitution");
- missingclose:
- error ("Missing \"}\" in environment-variable substitution");
- badvar:
- error ("Substituting nonexistent environment variable \"%s\"", target);
+ return (xnm == SSDATA (filename)
+ ? filename
+ : make_specified_string (xnm, -1, x - xnm, multibyte));
}
\f
/* A slightly faster and more convenient way to get
DEFSYM (Qmove_file_to_trash, "move-file-to-trash");
DEFSYM (Qcopy_directory, "copy-directory");
DEFSYM (Qdelete_directory, "delete-directory");
+ DEFSYM (Qsubstitute_env_in_file_name, "substitute-env-in-file-name");
defsubr (&Sfind_file_name_handler);
defsubr (&Sfile_name_directory);