1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-30 12:21:25 -08:00

Rework environment variable support. (Reported by Kalle Olavi Niemitalo and Noah Friedman.)

* src/callproc.c (Vglobal_environment, Vlocal_environment_variables): Remove.
  (getenv_internal, child_setup): Don't look at global-environment or
  local-environment-variables.
  (Fgetenv_internal): Update docs.
  (set_initial_environment): Rename from set_global_environment.  Store
  Emacs environment in initial frame parameter.
  (syms_of_callproc): Remove obsolete defvars.  Update docs.

* lisp/env.el (read-envvar-name): Remove reference to global-environment.
  (setenv-internal): New function.
  (setenv): Use it.  Always set process-environment.  Update docs.
  (getenv): Update docs.
  (environment): Rewrite for the new environment design.  Update docs.

* lisp/frame.el (frame-initialize): Copy the environment from the initial frame.

* src/emacs.c (main): Call set_initial_environment, not set_global_environment.

git-archimport-id: lorentey@elte.hu--2004/emacs--multi-tty--0--patch-569
This commit is contained in:
Karoly Lorentey 2006-05-26 17:37:25 +00:00
parent 16b555334a
commit a13f8f50d4
5 changed files with 151 additions and 258 deletions

View file

@ -40,6 +40,7 @@ Damien Cassou <damien.cassou@laposte.net>
Robert J. Chassell <bob@rattlesnake.com>
Romain Francoise <romain@orebokech.com>
Ami Fischman <ami@fischman.org>
Noah Friedman <friedman@splode.com>
Friedrich Delgado Friedrichs <friedel@nomaden.org>
IRIE Tetsuya <irie@t.email.ne.jp>
Yoshiaki Kasahara <kasahara@nc.kyushu-u.ac.jp>

View file

@ -55,8 +55,7 @@ If it is also not t, RET does not exit if it does non-null completion."
(substring enventry 0
(string-match "=" enventry)))))
(append process-environment
(frame-parameter (frame-with-environment) 'environment)
global-environment))
(frame-parameter (frame-with-environment) 'environment)))
nil mustmatch nil 'read-envvar-name-history))
;; History list for VALUE argument to setenv.
@ -92,6 +91,40 @@ Use `$$' to insert a single dollar sign."
start (+ (match-beginning 0) 1)))))
string))
(defun setenv-internal (env variable value keep-empty)
"Set VARIABLE to VALUE in ENV, adding empty entries if KEEP-EMPTY.
Changes ENV by side-effect, and returns its new value."
(let ((pattern (concat "\\`" (regexp-quote variable) "\\(=\\|\\'\\)"))
(case-fold-search nil)
(scan env)
prev found)
;; Handle deletions from the beginning of the list specially.
(if (and (null value)
(not keep-empty)
env
(stringp (car env))
(string-match pattern (car env)))
(cdr env)
;; Try to find existing entry for VARIABLE in ENV.
(while (and scan (stringp (car scan)))
(when (string-match pattern (car scan))
(if value
(setcar scan (concat variable "=" value))
(if keep-empty
(setcar scan variable)
(setcdr prev (cdr scan))))
(setq found t
scan nil))
(setq prev scan
scan (cdr scan)))
(if (and (not found) (or value keep-empty))
(cons (if value
(concat variable "=" value)
variable)
env)
env))))
;; Fixme: Should the environment be recoded if LC_CTYPE &c is set?
(defun setenv (variable &optional value substitute-env-vars frame)
@ -105,26 +138,23 @@ the front of the history list when you type in the new value.
This function always replaces environment variables in the new
value when called interactively.
If VARIABLE is set in `process-environment', then this function
modifies its value there. Otherwise, this function works by
modifying either `global-environment' or the environment
belonging to the selected frame, depending on the value of
`local-environment-variables'.
SUBSTITUTE-ENV-VARS, if non-nil, means to substitute environment
variables in VALUE with `substitute-env-vars', which see.
This is normally used only for interactive calls.
If optional parameter FRAME is non-nil, this function modifies
only the frame-local value of VARIABLE on FRAME, ignoring
`process-environment'. Note that frames on the same terminal
device usually share their environment, so calling `setenv' on
one of them affects the others as well.
If FRAME is nil, `setenv' changes the global value of VARIABLE by
modifying `process-environment'. Note that the global value
overrides any frame-local values.
The return value is the new value of VARIABLE, or nil if
it was removed from the environment.
If optional parameter FRAME is non-nil, then it should be a a
frame. If the specified frame has its own set of environment
variables, this function will modify VARIABLE in it. Note that
frames on the same terminal device usually share their
environment, so calling `setenv' on one of them affects the
others as well.
As a special case, setting variable `TZ' calls `set-time-zone-rule' as
a side-effect."
(interactive
@ -155,57 +185,16 @@ a side-effect."
(setq value (encode-coding-string value locale-coding-system)))
(if (string-match "=" variable)
(error "Environment variable name `%s' contains `='" variable))
(let ((pattern (concat "\\`" (regexp-quote variable) "\\(=\\|\\'\\)"))
(case-fold-search nil)
(frame-env (frame-parameter (frame-with-environment frame) 'environment))
(frame-forced (not frame))
(scan process-environment)
found)
(if (string-equal "TZ" variable)
(set-time-zone-rule value))
(if (null frame)
(setq process-environment (setenv-internal process-environment
variable value t))
(setq frame (frame-with-environment frame))
(if (string-equal "TZ" variable)
(set-time-zone-rule value))
(block nil
;; Look for an existing entry for VARIABLE; try `process-environment' first.
(while (and scan (stringp (car scan)))
(when (string-match pattern (car scan))
(if value
(setcar scan (concat variable "=" value))
;; Leave unset variables in `process-environment',
;; otherwise the overridden value in `global-environment'
;; or frame-env would become unmasked.
(setcar scan variable))
(return value))
(setq scan (cdr scan)))
;; Look in the local or global environment, whichever is relevant.
(let ((local-var-p (and frame-env
(or frame-forced
(eq t local-environment-variables)
(member variable local-environment-variables)))))
(setq scan (if local-var-p
frame-env
global-environment))
(while scan
(when (string-match pattern (car scan))
(if value
(setcar scan (concat variable "=" value))
(if local-var-p
(set-frame-parameter frame 'environment
(delq (car scan) frame-env))
(setq global-environment (delq (car scan) global-environment))))
(return value))
(setq scan (cdr scan)))
;; VARIABLE is not in any environment list.
(if value
(if local-var-p
(set-frame-parameter frame 'environment
(cons (concat variable "=" value)
frame-env))
(setq global-environment
(cons (concat variable "=" value)
global-environment))))
(return value)))))
(set-frame-parameter frame 'environment
(setenv-internal (frame-parameter frame 'environment)
variable value nil)))
value)
(defun getenv (variable &optional frame)
"Get the value of environment variable VARIABLE.
@ -213,14 +202,12 @@ VARIABLE should be a string. Value is nil if VARIABLE is undefined in
the environment. Otherwise, value is a string.
If optional parameter FRAME is non-nil, then it should be a
frame. If that frame has its own set of environment variables,
this function will look up VARIABLE in there.
frame. This function will look up VARIABLE in its 'environment
parameter.
Otherwise, this function searches `process-environment' for
VARIABLE. If it is not found there, then it continues the
search in either `global-environment' or the environment list of
the selected frame, depending on the value of
`local-environment-variables'."
VARIABLE. If it is not found there, then it continues the search
in the environment list of the selected frame."
(interactive (list (read-envvar-name "Get environment variable: " t)))
(let ((value (getenv-internal (if (multibyte-string-p variable)
(encode-coding-string
@ -239,47 +226,43 @@ Each entry in the list is a string of the form NAME=VALUE.
The returned list can not be used to change environment
variables, only read them. See `setenv' to do that.
The list is constructed from elements of `process-environment',
`global-environment' and the local environment list of the
selected frame, as specified by `local-environment-variables'.
The list is constructed by concatenating the elements of
`process-environment' and the 'environment parameter of the
selected frame, and removing duplicated and empty values.
Non-ASCII characters are encoded according to the initial value of
`locale-coding-system', i.e. the elements must normally be decoded for use.
See `setenv' and `getenv'."
(let ((env (let ((local-env (frame-parameter (frame-with-environment)
'environment)))
(cond ((or (not local-environment-variables)
(not local-env))
(append process-environment global-environment nil))
((consp local-environment-variables)
(let ((e (reverse process-environment)))
(dolist (entry local-environment-variables)
(setq e (cons (getenv entry) e)))
(append (nreverse e) global-environment nil)))
(t
(append process-environment local-env nil)))))
scan seen)
;; Find the first valid entry in env.
(while (and env (stringp (car env))
(or (not (string-match "=" (car env)))
(member (substring (car env) 0 (string-match "=" (car env))) seen)))
(setq seen (cons (car env) seen)
env (cdr env)))
(setq scan env)
(while (and (cdr scan) (stringp (cadr scan)))
(let* ((match (string-match "=" (cadr scan)))
(name (substring (cadr scan) 0 match)))
(cond ((not match)
(let* ((env (append process-environment
(frame-parameter (frame-with-environment)
'environment)
nil))
(scan env)
prev seen)
;; Remove unset variables from the beginning of the list.
(while (and env
(or (not (stringp (car env)))
(not (string-match "=" (car env)))))
(or (member (car env) seen)
(setq seen (cons (car env) seen)))
(setq env (cdr env)
scan env))
(let (name)
(while scan
(cond ((or (not (stringp (car scan)))
(not (string-match "=" (car scan))))
;; Unset variable.
(setq seen (cons name seen))
(setcdr scan (cddr scan)))
((member name seen)
;; Duplicate variable.
(setcdr scan (cddr scan)))
(or (member (car scan) seen)
(setq seen (cons (car scan) seen)))
(setcdr prev (cdr scan)))
((member (setq name (substring (car scan) 0 (string-match "=" (car scan)))) seen)
;; Duplicated variable.
(setcdr prev (cdr scan)))
(t
;; New variable.
(setq seen (cons name seen)
scan (cdr scan))))))
(setq seen (cons name seen))))
(setq prev scan
scan (cdr scan))))
env))
(defmacro let-environment (varlist &rest body)

View file

@ -238,6 +238,9 @@ Pass it BUFFER as first arg, and (cdr ARGS) gives the rest of the args."
;; because that would override explicit user resizing.
(setq initial-frame-alist
(frame-remove-geometry-params initial-frame-alist))))
;; Copy the environment of the Emacs process into the new frame.
(set-frame-parameter frame-initial-frame 'environment
(frame-parameter terminal-frame 'environment))
;; At this point, we know that we have a frame open, so we
;; can delete the terminal frame.
(delete-frame terminal-frame)

View file

@ -113,7 +113,6 @@ Lisp_Object Vtemp_file_name_pattern;
Lisp_Object Vshell_file_name;
Lisp_Object Vglobal_environment;
Lisp_Object Vprocess_environment;
#ifdef DOS_NT
@ -134,9 +133,6 @@ int synch_process_termsig;
this is exit code of synchronous subprocess. */
int synch_process_retcode;
/* List of environment variables to look up in emacsclient. */
Lisp_Object Vlocal_environment_variables;
/* Clean up when exiting Fcall_process.
On MSDOS, delete the temporary file on any kind of termination.
@ -1321,8 +1317,8 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
register char **new_env;
char **p, **q;
register int new_length;
Lisp_Object environment = Vglobal_environment;
Lisp_Object local;
Lisp_Object local = get_frame_param (XFRAME (Fframe_with_environment (selected_frame)),
Qenvironment);
new_length = 0;
@ -1331,20 +1327,7 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
tem = XCDR (tem))
new_length++;
if (!NILP (Vlocal_environment_variables))
{
local = get_frame_param (XFRAME (Fframe_with_environment (selected_frame)),
Qenvironment);
if (EQ (Vlocal_environment_variables, Qt)
&& !NILP (local))
environment = local;
else if (CONSP (local))
{
new_length += Fsafe_length (Vlocal_environment_variables);
}
}
for (tem = environment;
for (tem = local;
CONSP (tem) && STRINGP (XCAR (tem));
tem = XCDR (tem))
new_length++;
@ -1354,7 +1337,7 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
/* If we have a PWD envvar, pass one down,
but with corrected value. */
if (getenv ("PWD"))
if (egetenv ("PWD"))
*new_env++ = pwd_var;
/* Overrides. */
@ -1363,17 +1346,10 @@ child_setup (in, out, err, new_argv, set_pgrp, current_dir)
tem = XCDR (tem))
new_env = add_env (env, new_env, SDATA (XCAR (tem)));
/* Local part of environment, if Vlocal_environment_variables is a list. */
for (tem = Vlocal_environment_variables;
/* Local part of environment. */
for (tem = local;
CONSP (tem) && STRINGP (XCAR (tem));
tem = XCDR (tem))
new_env = add_env (env, new_env, egetenv (SDATA (XCAR (tem))));
/* The rest of the environment (either Vglobal_environment or the
'environment frame parameter). */
for (tem = environment;
CONSP (tem) && STRINGP (XCAR (tem));
tem = XCDR (tem))
new_env = add_env (env, new_env, SDATA (XCAR (tem)));
*new_env = 0;
@ -1510,79 +1486,47 @@ getenv_internal (var, varlen, value, valuelen, frame)
Lisp_Object frame;
{
Lisp_Object scan;
Lisp_Object environment = Vglobal_environment;
/* Try to find VAR in Vprocess_environment first. */
for (scan = Vprocess_environment; CONSP (scan); scan = XCDR (scan))
if (NILP (frame))
{
Lisp_Object entry = XCAR (scan);
if (STRINGP (entry)
&& SBYTES (entry) >= varlen
#ifdef WINDOWSNT
/* NT environment variables are case insensitive. */
&& ! strnicmp (SDATA (entry), var, varlen)
#else /* not WINDOWSNT */
&& ! bcmp (SDATA (entry), var, varlen)
#endif /* not WINDOWSNT */
)
/* Try to find VAR in Vprocess_environment first. */
for (scan = Vprocess_environment; CONSP (scan); scan = XCDR (scan))
{
if (SBYTES (entry) > varlen && SREF (entry, varlen) == '=')
Lisp_Object entry = XCAR (scan);
if (STRINGP (entry)
&& SBYTES (entry) >= varlen
#ifdef WINDOWSNT
/* NT environment variables are case insensitive. */
&& ! strnicmp (SDATA (entry), var, varlen)
#else /* not WINDOWSNT */
&& ! bcmp (SDATA (entry), var, varlen)
#endif /* not WINDOWSNT */
)
{
*value = (char *) SDATA (entry) + (varlen + 1);
*valuelen = SBYTES (entry) - (varlen + 1);
return 1;
}
else if (SBYTES (entry) == varlen)
{
/* Lone variable names in Vprocess_environment mean that
variable should be removed from the environment. */
return 0;
if (SBYTES (entry) > varlen && SREF (entry, varlen) == '=')
{
*value = (char *) SDATA (entry) + (varlen + 1);
*valuelen = SBYTES (entry) - (varlen + 1);
return 1;
}
else if (SBYTES (entry) == varlen)
{
/* Lone variable names in Vprocess_environment mean that
variable should be removed from the environment. */
return 0;
}
}
}
frame = selected_frame;
}
/* Find the environment in which to search the variable. */
if (!NILP (frame))
{
Lisp_Object local;
CHECK_FRAME (frame);
frame = Fframe_with_environment (frame);
CHECK_FRAME (frame);
frame = Fframe_with_environment (frame);
local = get_frame_param (XFRAME (frame), Qenvironment);
/* Use Vglobal_environment if there is no local environment. */
if (!NILP (local))
environment = local;
}
else if (!NILP (Vlocal_environment_variables))
{
Lisp_Object local = get_frame_param (XFRAME (Fframe_with_environment (selected_frame)),
Qenvironment);
if (EQ (Vlocal_environment_variables, Qt)
&& !NILP (local))
environment = local;
else if (CONSP (local))
{
for (scan = Vlocal_environment_variables; CONSP (scan); scan = XCDR (scan))
{
Lisp_Object entry = XCAR (scan);
if (STRINGP (entry)
&& SBYTES (entry) == varlen
#ifdef WINDOWSNT
/* NT environment variables are case insensitive. */
&& ! strnicmp (SDATA (entry), var, varlen)
#else /* not WINDOWSNT */
&& ! bcmp (SDATA (entry), var, varlen)
#endif /* not WINDOWSNT */
)
{
environment = local;
break;
}
}
}
}
for (scan = environment; CONSP (scan); scan = XCDR (scan))
for (scan = get_frame_param (XFRAME (frame), Qenvironment);
CONSP (scan);
scan = XCDR (scan))
{
Lisp_Object entry;
@ -1612,14 +1556,13 @@ DEFUN ("getenv-internal", Fgetenv_internal, Sgetenv_internal, 1, 2, 0,
VARIABLE should be a string. Value is nil if VARIABLE is undefined in
the environment. Otherwise, value is a string.
If optional parameter FRAME is non-nil, then it should be a frame. If
that frame has its own set of environment variables, this function
will look up VARIABLE in there.
This function searches `process-environment' for VARIABLE. If it is
not found there, then it continues the search in the environment list
of the selected frame.
Otherwise, this function searches `process-environment' for VARIABLE.
If it is not found there, then it continues the search in either
`global-environment' or the environment list of the selected frame,
depending on the value of `local-environment-variables'. */)
If optional parameter FRAME is non-nil, then this function will ignore
`process-environment' and will simply look up the variable in that
frame's environment. */)
(variable, frame)
Lisp_Object variable, frame;
{
@ -1766,8 +1709,8 @@ init_callproc ()
{
char *dir = getenv ("TMPDIR");
Vtemp_file_name_pattern
= Fexpand_file_name (build_string ("emacsXXXXXX"),
build_string (dir));
= Fexpand_file_name (build_string ("emacsXXXXXX"),
build_string (dir));
}
else
Vtemp_file_name_pattern = build_string ("/tmp/emacsXXXXXX");
@ -1783,17 +1726,18 @@ init_callproc ()
}
void
set_global_environment ()
set_initial_environment ()
{
register char **envp;
Vglobal_environment = Qnil;
Lisp_Object env = Qnil;
#ifndef CANNOT_DUMP
if (initialized)
#endif
for (envp = environ; *envp; envp++)
Vglobal_environment = Fcons (build_string (*envp),
Vglobal_environment);
{
for (envp = environ; *envp; envp++)
env = Fcons (build_string (*envp), env);
store_frame_param (SELECTED_FRAME(), Qenvironment, env);
}
}
void
@ -1851,37 +1795,16 @@ If this variable is nil, then Emacs is unable to use a shared directory. */);
This is used by `call-process-region'. */);
/* This variable is initialized in init_callproc. */
DEFVAR_LISP ("global-environment", &Vglobal_environment,
doc: /* Global list of environment variables for subprocesses to inherit.
Each element should be a string of the form ENVVARNAME=VALUE.
The environment which Emacs inherits is placed in this variable when
Emacs starts.
Some frames may have their own local list of environment variables in
their 'environment parameter, which may override this global list; see
`local-environment-variables' and `frame-with-environment'. See
`process-environment' for a way to modify an environment variable on
all frames.
If multiple entries define the same variable, the first one always
takes precedence.
Non-ASCII characters are encoded according to the initial value of
`locale-coding-system', i.e. the elements must normally be decoded for use.
See `setenv' and `getenv'. */);
DEFVAR_LISP ("process-environment", &Vprocess_environment,
doc: /* List of overridden environment variables for subprocesses to inherit.
Each element should be a string of the form ENVVARNAME=VALUE.
Entries in this list take precedence to those in `global-environment'
or the frame-local environments. (See `local-environment-variables'
and `frame-with-environment'.) Therefore, let-binding
`process-environment' is an easy way to temporarily change the value
of an environment variable, irrespective of where it comes from. To
use `process-environment' to remove an environment variable, include
only its name in the list, without "=VALUE".
Entries in this list take precedence to those in the frame-local
environments. Therefore, let-binding `process-environment' is an easy
way to temporarily change the value of an environment variable,
irrespective of where it comes from. To use `process-environment' to
remove an environment variable, include only its name in the list,
without "=VALUE".
This variable is set to nil when Emacs starts.
@ -1900,23 +1823,6 @@ See `setenv' and `getenv'. */);
defsubr (&Sgetenv_internal);
#endif
defsubr (&Scall_process_region);
DEFVAR_LISP ("local-environment-variables", &Vlocal_environment_variables,
doc: /* Enable or disable frame-local environment variables.
If set to t, `getenv', `setenv' and subprocess creation functions use
the local environment of the selected frame, ignoring
`global-environment'.
If set to nil, Emacs uses `global-environment' and ignores the
frame-local environment.
Otherwise, `local-environment-variables' should be a list of variable
names (represented by Lisp strings) to look up in the frame's
environment. The rest will come from `global-environment'.
The frame-local environment is stored in the 'environment frame
parameter. See `frame-with-environment'. */);
Vlocal_environment_variables = Qt;
}
/* arch-tag: 769b8045-1df7-4d2b-8968-e3fb49017f95

View file

@ -1518,7 +1518,7 @@ main (argc, argv
/* egetenv is a pretty low-level facility, which may get called in
many circumstances; it seems flimsy to put off initializing it
until calling init_callproc. */
set_global_environment ();
set_initial_environment ();
/* AIX crashes are reported in system versions 3.2.3 and 3.2.4
if this is not done. Do it after set_global_environment so that we
don't pollute Vglobal_environment. */