From a13f8f50d4cc544d3bbfa78568e82ce09e68bded Mon Sep 17 00:00:00 2001 From: Karoly Lorentey Date: Fri, 26 May 2006 17:37:25 +0000 Subject: [PATCH] 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 --- README.multi-tty | 1 + lisp/env.el | 193 ++++++++++++++++++++------------------------ lisp/frame.el | 3 + src/callproc.c | 206 +++++++++++++---------------------------------- src/emacs.c | 2 +- 5 files changed, 149 insertions(+), 256 deletions(-) diff --git a/README.multi-tty b/README.multi-tty index 9cce8f8dfa..12bc2c2558 100644 --- a/README.multi-tty +++ b/README.multi-tty @@ -40,6 +40,7 @@ Damien Cassou Robert J. Chassell Romain Francoise Ami Fischman +Noah Friedman Friedrich Delgado Friedrichs IRIE Tetsuya Yoshiaki Kasahara diff --git a/lisp/env.el b/lisp/env.el index d0c2208fc6..96ff1f37c3 100644 --- a/lisp/env.el +++ b/lisp/env.el @@ -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) diff --git a/lisp/frame.el b/lisp/frame.el index 0f255ac77a..f5d3f4b0c3 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -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) diff --git a/src/callproc.c b/src/callproc.c index fb187d757d..b2352e9bd5 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -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 + /* Try to find VAR in Vprocess_environment first. */ + for (scan = Vprocess_environment; 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) + /* NT environment variables are case insensitive. */ + && ! strnicmp (SDATA (entry), var, varlen) #else /* not WINDOWSNT */ - && ! bcmp (SDATA (entry), var, varlen) + && ! bcmp (SDATA (entry), var, varlen) #endif /* not WINDOWSNT */ - ) - { - 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; + 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); - 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; - } - } - } - } + CHECK_FRAME (frame); + frame = Fframe_with_environment (frame); - 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 diff --git a/src/emacs.c b/src/emacs.c index e9fb4e3cb8..ac6bf57c2c 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -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. */ -- 2.20.1