changeset 83421:bb2edc915032

Implement automatic terminal-local environment variables via `local-environment-variables'. * lisp/env.el (setenv, getenv): Add optional terminal parameter. Update docs. (setenv): Handle `local-environment-variables'. (read-envvar-name): Also allow (and complete) local environment variables on the current terminal. * src/callproc.c: Include frame.h and termhooks.h, for terminal parameters. (Qenvironment): New constant. (Vlocal_environment_variables): New variable. (syms_of_callproc): Register and initialize them. (child_setup): Handle Vlocal_environment_variables. (getenv_internal): Add terminal parameter. Handle Vlocal_environment_variables. (Fgetenv_internal): Add terminal parameter. * src/termhooks.h (get_terminal_param): Declare. * src/Makefile.in (callproc.o): Update dependencies. * mac/makefile.MPW (callproc.c.x): Update dependencies. * lisp/termdev.el (terminal-id): Make parameter optional. (terminal-getenv, terminal-setenv, with-terminal-environment): Disable functions. * lisp/mule-cmds.el (set-locale-environment): Convert `terminal-getenv' calls to `getenv'. * lisp/rxvt.el (rxvt-set-background-mode): Ditto. * lisp/x-win.el (x-initialize-window-system): Ditto. * lisp/xterm.el (terminal-init-xterm): Ditto. * lisp/server.el (server-process-filter): Fix reference to the 'display frame parameter. git-archimport-id: lorentey@elte.hu--2004/emacs--multi-tty--0--patch-461
author Karoly Lorentey <lorentey@elte.hu>
date Mon, 26 Dec 2005 02:14:10 +0000
parents 521d3f18b3d1
children 1f5d64c4df6f
files README.multi-tty lisp/env.el lisp/international/mule-cmds.el lisp/server.el lisp/term/rxvt.el lisp/term/x-win.el lisp/term/xterm.el lisp/termdev.el mac/makefile.MPW src/Makefile.in src/callproc.c src/termhooks.h
diffstat 12 files changed, 374 insertions(+), 205 deletions(-) [+]
line wrap: on
line diff
--- a/README.multi-tty	Sun Dec 25 20:06:58 2005 +0000
+++ b/README.multi-tty	Mon Dec 26 02:14:10 2005 +0000
@@ -401,28 +401,10 @@
 THINGS TO DO
 ------------
 
-** Implement automatic forwarding of client environment variables to
-   forked processes, as discussed on the multi-tty list.  Terminal
-   parameters are now accessible in C code, so the biggest obstacle is
-   gone.  The `getenv_internal' and `child_setup' functions in
-   callproc.c must be changed to support the following variable:
-
-	terminal-local-environment-variables is a variable defined in ...
-
-	Enable or disable terminal-local environment variables.
-
-	If set to t, `getenv', `setenv' and subprocess creation
-	functions use the environment variables of the emacsclient
-	process that created the selected frame, ignoring
-	`process-environment'.
-
-	If set to nil, Emacs uses `process-environment' and ignores
-	the client environment.
-
-	Otherwise, `terminal-local-environment-variables' should be a
-	list of variable names (represented by Lisp strings) to look
-	up in the client environment.  The rest will come from
-	`process-environment'.
+** Trouble: `setenv' doesn't actually set environment variables in the
+   Emacs process.  This defeats the purpose of the elaborate
+   `server-with-environment' magic around the `tgetent' call in
+   `init_tty'.  D'oh.
 
 ** (Possibly) create hooks in struct device for creating frames on a
    specific terminal, and eliminate the hackish terminal-related frame
@@ -1348,5 +1330,33 @@
 
    (Disabled in patch-450.)
 
+-- Implement automatic forwarding of client environment variables to
+   forked processes, as discussed on the multi-tty list.  Terminal
+   parameters are now accessible in C code, so the biggest obstacle is
+   gone.  The `getenv_internal' and `child_setup' functions in
+   callproc.c must be changed to support the following variable:
+
+	terminal-local-environment-variables is a variable defined in ...
+
+	Enable or disable terminal-local environment variables.
+
+	If set to t, `getenv', `setenv' and subprocess creation
+	functions use the environment variables of the emacsclient
+	process that created the selected frame, ignoring
+	`process-environment'.
+
+	If set to nil, Emacs uses `process-environment' and ignores
+	the client environment.
+
+	Otherwise, `terminal-local-environment-variables' should be a
+	list of variable names (represented by Lisp strings) to look
+	up in the client environment.  The rest will come from
+	`process-environment'.
+
+   (Implemented in patch-461; `terminal-getenv', `terminal-setenv' and
+   `with-terminal-environment' are now replaced by extensions to
+   `getenv' and `setenv', and the new `local-environment-variables'
+   facility.  Yay!)
+
 ;;; arch-tag: 8da1619e-2e79-41a8-9ac9-a0485daad17d
 
--- a/lisp/env.el	Sun Dec 25 20:06:58 2005 +0000
+++ b/lisp/env.el	Mon Dec 26 02:14:10 2005 +0000
@@ -52,7 +52,8 @@
 					locale-coding-system t)
 				     (substring enventry 0
 						(string-match "=" enventry)))))
-			   process-environment)
+			   (append (terminal-parameter nil 'environment)
+				   process-environment))
 		   nil mustmatch nil 'read-envvar-name-history))
 
 ;; History list for VALUE argument to setenv.
@@ -90,7 +91,7 @@
 
 ;; Fixme: Should `process-environment' be recoded if LC_CTYPE &c is set?
 
-(defun setenv (variable &optional value unset substitute-env-vars)
+(defun setenv (variable &optional value unset substitute-env-vars terminal)
   "Set the value of the environment variable named VARIABLE to VALUE.
 VARIABLE should be a string.  VALUE is optional; if not provided or
 nil, the environment variable VARIABLE will be removed.  UNSET
@@ -105,7 +106,14 @@
 appears at the front of the history list when you type in the new value.
 Interactively, always replace environment variables in the new value.
 
-This function works by modifying `process-environment'.
+If optional parameter TERMINAL is non-nil, then it should be a
+terminal id or a frame.  If the specified terminal device has its own
+set of environment variables, this function will modify VAR in it.
+
+Otherwise, this function works by modifying either
+`process-environment' or the environment belonging to the
+terminal device of the selected frame, depending on the value of
+`local-environment-variables'.
 
 As a special case, setting variable `TZ' calls `set-time-zone-rule' as
 a side-effect."
@@ -138,36 +146,58 @@
   (if (and value (multibyte-string-p value))
       (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 (concat variable "="))))
-	  (case-fold-search nil)
-	  (scan process-environment)
-	  found)
-      (if (string-equal "TZ" variable)
-	  (set-time-zone-rule value))
-      (while scan
-	(cond ((string-match pattern (car scan))
-	       (setq found t)
-	       (if (eq nil value)
+      (error "Environment variable name `%s' contains `='" variable))
+  (let* ((pattern (concat "\\`" (regexp-quote (concat variable "="))))
+	 (case-fold-search nil)
+	 (local-var-p (and (terminal-parameter terminal 'environment)
+			   (or terminal
+			       (eq t local-environment-variables)
+			       (member variable local-environment-variables))))
+	 (scan (if local-var-p
+		   (terminal-parameter terminal 'environment)
+		 process-environment))
+	 found)
+    (if (string-equal "TZ" variable)
+	(set-time-zone-rule value))
+    (while scan
+      (cond ((string-match pattern (car scan))
+	     (setq found t)
+	     (if (eq nil value)
+		 (if local-var-p
+		     (set-terminal-parameter terminal 'environment
+					     (delq (car scan)
+						   (terminal-parameter terminal 'environment)))
 		   (setq process-environment (delq (car scan)
-						   process-environment))
-		 (setcar scan (concat variable "=" value)))
-	       (setq scan nil)))
-	(setq scan (cdr scan)))
-      (or found
-	  (if value
+						   process-environment)))
+	       (setcar scan (concat variable "=" value)))
+	     (setq scan nil)))
+      (setq scan (cdr scan)))
+    (or found
+	(if value
+	    (if local-var-p
+		(set-terminal-parameter nil 'environment
+					(cons (concat variable "=" value)
+					      (terminal-parameter nil 'environment)))
 	      (setq process-environment
 		    (cons (concat variable "=" value)
 			  process-environment))))))
   value)
 
-(defun getenv (variable)
+(defun getenv (variable &optional terminal)
   "Get the value of environment variable VARIABLE.
 VARIABLE should be a string.  Value is nil if VARIABLE is undefined in
 the environment.  Otherwise, value is a string.
 
-This function consults the variable `process-environment'
-for its value."
+If optional parameter TERMINAL is non-nil, then it should be a
+terminal id or a frame.  If the specified terminal device has its own
+set of environment variables, this function will look up VAR in it.
+
+Otherwise, if `local-environment-variables' specifies that VAR is a
+local environment variable, then this function consults the
+environment variables belonging to the terminal device of the selected
+frame.
+
+Otherwise, the value of VAR will come from `process-environment'."
   (interactive (list (read-envvar-name "Get environment variable: " t)))
   (let ((value (getenv-internal (if (multibyte-string-p variable)
 				    (encode-coding-string
--- a/lisp/international/mule-cmds.el	Sun Dec 25 20:06:58 2005 +0000
+++ b/lisp/international/mule-cmds.el	Mon Dec 26 02:14:10 2005 +0000
@@ -2460,7 +2460,7 @@
       (let ((vars '("LC_ALL" "LC_CTYPE" "LANG")))
 	(while (and vars
 		    (= 0 (length locale))) ; nil or empty string
-	  (setq locale (terminal-getenv (pop vars))))))
+	  (setq locale (getenv (pop vars) display)))))
 
     (unless locale
       ;; The two tests are kept separate so the byte-compiler sees
@@ -2573,7 +2573,7 @@
       ;; Mac OS X's Terminal.app by default uses utf-8 regardless of
       ;; the locale.
       (when (and (null window-system)
-		 (equal (terminal-getenv "TERM_PROGRAM") "Apple_Terminal"))
+		 (equal (getenv "TERM_PROGRAM" display) "Apple_Terminal"))
 	(set-terminal-coding-system 'utf-8)
 	(set-keyboard-coding-system 'utf-8)))
 
@@ -2591,7 +2591,7 @@
 	      (setq ps-paper-type 'a4)))
 	  (let ((vars '("LC_ALL" "LC_PAPER" "LANG")))
 	    (while (and vars (= 0 (length locale)))
-	      (setq locale (terminal-getenv (pop vars)))))
+	      (setq locale (getenv (pop vars) display))))
 	  (when locale
 	    ;; As of glibc 2.2.5, these are the only US Letter locales,
 	    ;; and the rest are A4.
--- a/lisp/server.el	Sun Dec 25 20:06:58 2005 +0000
+++ b/lisp/server.el	Mon Dec 26 02:14:10 2005 +0000
@@ -624,7 +624,7 @@
 					(list (cons 'client proc)))))
 			  (setq frame (make-frame-on-display
 				       (or display
-					   (frame-parameter nil 'device)
+					   (frame-parameter nil 'display)
 					   (getenv "DISPLAY")
 					   (error "Please specify display"))
 				       params))
--- a/lisp/term/rxvt.el	Sun Dec 25 20:06:58 2005 +0000
+++ b/lisp/term/rxvt.el	Mon Dec 26 02:14:10 2005 +0000
@@ -291,7 +291,7 @@
 ;; intelligent way than the default guesswork in startup.el.
 (defun rxvt-set-background-mode ()
   "Set background mode as appropriate for the default rxvt colors."
-  (let ((fgbg (terminal-getenv "COLORFGBG"))
+  (let ((fgbg (getenv "COLORFGBG" (terminal-id)))
 	bg rgb)
     (setq default-frame-background-mode 'light)
     (when (and fgbg
--- a/lisp/term/x-win.el	Sun Dec 25 20:06:58 2005 +0000
+++ b/lisp/term/x-win.el	Mon Dec 26 02:14:10 2005 +0000
@@ -2407,7 +2407,8 @@
 	  (aset x-resource-name i ?-))))
 
   (x-open-connection (or x-display-name
-			 (setq x-display-name (terminal-getenv "DISPLAY" nil 'global-ok)))
+			 (setq x-display-name (or (getenv "DISPLAY" (terminal-id))
+						  (getenv "DISPLAY"))))
 		     x-command-line-resources
 		     ;; Exit Emacs with fatal error if this fails and we
 		     ;; are the initial display.
--- a/lisp/term/xterm.el	Sun Dec 25 20:06:58 2005 +0000
+++ b/lisp/term/xterm.el	Mon Dec 26 02:14:10 2005 +0000
@@ -192,8 +192,8 @@
   ;; rxvt terminals sometimes set the TERM variable to "xterm", but
   ;; rxvt's keybindings that are incompatible with xterm's. It is
   ;; better in that case to use rxvt's initializion function.
-  (if (and (terminal-getenv "COLORTERM")
-	   (string-match "\\`rxvt" (terminal-getenv "COLORTERM")))
+  (if (and (getenv "COLORTERM" (terminal-id))
+	   (string-match "\\`rxvt" (getenv "COLORTERM" (terminal-id))))
       (progn
 	(eval-and-compile (load "term/rxvt"))
 	(terminal-init-rxvt))
--- a/lisp/termdev.el	Sun Dec 25 20:06:58 2005 +0000
+++ b/lisp/termdev.el	Mon Dec 26 02:14:10 2005 +0000
@@ -25,7 +25,7 @@
 
 (substitute-key-definition 'suspend-emacs 'suspend-frame global-map)
 
-(defun terminal-id (terminal)
+(defun terminal-id (&optional terminal)
   "Return the numerical id of terminal TERMINAL.
 
 TERMINAL can be a terminal id (an integer), a frame, or
@@ -48,146 +48,146 @@
    (t
     (error "Invalid argument %s in `terminal-id'" terminal))))
 
-(defun terminal-getenv (variable &optional terminal global-ok)
-  "Get the value of VARIABLE in the client environment of TERMINAL.
-VARIABLE should be a string.  Value is nil if VARIABLE is undefined in
-the environment.  Otherwise, value is a string.
+;; (defun terminal-getenv (variable &optional terminal global-ok)
+;;   "Get the value of VARIABLE in the client environment of TERMINAL.
+;; VARIABLE should be a string.  Value is nil if VARIABLE is undefined in
+;; the environment.  Otherwise, value is a string.
 
-If TERMINAL has an associated emacsclient process, then
-`terminal-getenv' looks up VARIABLE in the environment of that
-process; otherwise the function consults the global environment,
-i.e., the environment of the Emacs process itself.
+;; If TERMINAL has an associated emacsclient process, then
+;; `terminal-getenv' looks up VARIABLE in the environment of that
+;; process; otherwise the function consults the global environment,
+;; i.e., the environment of the Emacs process itself.
 
-If GLOBAL-OK is non-nil, and VARIABLE is not defined in the
-terminal-local environment, then `terminal-getenv' will return
-its value in the global environment instead.
+;; If GLOBAL-OK is non-nil, and VARIABLE is not defined in the
+;; terminal-local environment, then `terminal-getenv' will return
+;; its value in the global environment instead.
 
-TERMINAL can be a terminal id, a frame, or nil (meaning the
-selected frame's terminal)."
-  (setq terminal (terminal-id terminal))
-  (if (null (terminal-parameter terminal 'environment))
-      (getenv variable)
-    (if (multibyte-string-p variable)
-	(setq variable (encode-coding-string variable locale-coding-system)))
-    (let ((env (terminal-parameter terminal 'environment))
-	  result entry)
-      (while (and env (null result))
-	(setq entry (car env)
-	      env (cdr env))
-	(if (and (> (length entry) (length variable))
-		 (eq ?= (aref entry (length variable)))
-		 (equal variable (substring entry 0 (length variable))))
-	    (setq result (substring entry (+ (length variable) 1)))))
-      (if (and global-ok (null result))
-	  (getenv variable)
-	(and result (decode-coding-string result locale-coding-system))))))
+;; TERMINAL can be a terminal id, a frame, or nil (meaning the
+;; selected frame's terminal)."
+;;   (setq terminal (terminal-id terminal))
+;;   (if (null (terminal-parameter terminal 'environment))
+;;       (getenv variable)
+;;     (if (multibyte-string-p variable)
+;; 	(setq variable (encode-coding-string variable locale-coding-system)))
+;;     (let ((env (terminal-parameter terminal 'environment))
+;; 	  result entry)
+;;       (while (and env (null result))
+;; 	(setq entry (car env)
+;; 	      env (cdr env))
+;; 	(if (and (> (length entry) (length variable))
+;; 		 (eq ?= (aref entry (length variable)))
+;; 		 (equal variable (substring entry 0 (length variable))))
+;; 	    (setq result (substring entry (+ (length variable) 1)))))
+;;       (if (and global-ok (null result))
+;; 	  (getenv variable)
+;; 	(and result (decode-coding-string result locale-coding-system))))))
 
-(defun terminal-setenv (variable &optional value terminal)
-  "Set the value of VARIABLE in the environment of TERMINAL.
-VARIABLE should be string.  VALUE is optional; if not provided or
-nil, the environment variable VARIABLE is removed.  Returned
-value is the new value of VARIABLE, or nil if it was removed from
-the environment.
+;; (defun terminal-setenv (variable &optional value terminal)
+;;   "Set the value of VARIABLE in the environment of TERMINAL.
+;; VARIABLE should be string.  VALUE is optional; if not provided or
+;; nil, the environment variable VARIABLE is removed.  Returned
+;; value is the new value of VARIABLE, or nil if it was removed from
+;; the environment.
 
-If TERMINAL was created by an emacsclient invocation, then the
-variable is set in the environment of the emacsclient process;
-otherwise the function changes the environment of the Emacs
-process itself.
+;; If TERMINAL was created by an emacsclient invocation, then the
+;; variable is set in the environment of the emacsclient process;
+;; otherwise the function changes the environment of the Emacs
+;; process itself.
 
-TERMINAL can be a terminal id, a frame, or nil (meaning the
-selected frame's terminal)."
-  (if (null (terminal-parameter terminal 'environment))
-      (setenv variable value)
-    (with-terminal-environment terminal variable
-      (setenv variable value))))
+;; TERMINAL can be a terminal id, a frame, or nil (meaning the
+;; selected frame's terminal)."
+;;   (if (null (terminal-parameter terminal 'environment))
+;;       (setenv variable value)
+;;     (with-terminal-environment terminal variable
+;;       (setenv variable value))))
 
-(defun terminal-setenv-internal (variable value terminal)
-  "Set the value of VARIABLE in the environment of TERMINAL.
-The caller is responsible to ensure that both VARIABLE and VALUE
-are usable in environment variables and that TERMINAL is a
-remote terminal."
-  (if (multibyte-string-p variable)
-      (setq variable (encode-coding-string variable locale-coding-system)))
-  (if (and value (multibyte-string-p value))
-      (setq value (encode-coding-string value locale-coding-system)))
-  (let ((env (terminal-parameter terminal 'environment))
-	found)
-    (while (and env (not found))
-      (if (and (> (length (car env)) (length variable))
-		 (eq ?= (aref (car env) (length variable)))
-		 (equal variable (substring (car env) 0 (length variable))))
-	  (progn
-	    (if value
-		(setcar env (concat variable "=" value))
-	      (set-terminal-parameter terminal 'environment
-				      (delq (car env)
-					    (terminal-parameter terminal
-								'environment))))
-	    (setq found t))
-	(setq env (cdr env))))
-    (cond
-     ((and value found)
-      (setcar env (concat variable "=" value)))
-     ((and value (not found))
-      (set-terminal-parameter terminal 'environment
-			      (cons (concat variable "=" value)
-				    (terminal-parameter terminal
-							'environment))))
-     ((and (not value) found)
-      (set-terminal-parameter terminal 'environment
-			      (delq (car env)
-				    (terminal-parameter terminal
-							'environment)))))))
+;; (defun terminal-setenv-internal (variable value terminal)
+;;   "Set the value of VARIABLE in the environment of TERMINAL.
+;; The caller is responsible to ensure that both VARIABLE and VALUE
+;; are usable in environment variables and that TERMINAL is a
+;; remote terminal."
+;;   (if (multibyte-string-p variable)
+;;       (setq variable (encode-coding-string variable locale-coding-system)))
+;;   (if (and value (multibyte-string-p value))
+;;       (setq value (encode-coding-string value locale-coding-system)))
+;;   (let ((env (terminal-parameter terminal 'environment))
+;; 	found)
+;;     (while (and env (not found))
+;;       (if (and (> (length (car env)) (length variable))
+;; 		 (eq ?= (aref (car env) (length variable)))
+;; 		 (equal variable (substring (car env) 0 (length variable))))
+;; 	  (progn
+;; 	    (if value
+;; 		(setcar env (concat variable "=" value))
+;; 	      (set-terminal-parameter terminal 'environment
+;; 				      (delq (car env)
+;; 					    (terminal-parameter terminal
+;; 								'environment))))
+;; 	    (setq found t))
+;; 	(setq env (cdr env))))
+;;     (cond
+;;      ((and value found)
+;;       (setcar env (concat variable "=" value)))
+;;      ((and value (not found))
+;;       (set-terminal-parameter terminal 'environment
+;; 			      (cons (concat variable "=" value)
+;; 				    (terminal-parameter terminal
+;; 							'environment))))
+;;      ((and (not value) found)
+;;       (set-terminal-parameter terminal 'environment
+;; 			      (delq (car env)
+;; 				    (terminal-parameter terminal
+;; 							'environment)))))))
 
-(defmacro with-terminal-environment (terminal vars &rest body)
-  "Evaluate BODY with environment variables VARS set to those of TERMINAL.
-The environment variables are then restored to their previous values.
+;; (defmacro with-terminal-environment (terminal vars &rest body)
+;;   "Evaluate BODY with environment variables VARS set to those of TERMINAL.
+;; The environment variables are then restored to their previous values.
 
-VARS should be a single string, a list of strings, or t for all
-environment variables.
+;; VARS should be a single string, a list of strings, or t for all
+;; environment variables.
 
-TERMINAL can be a terminal id, a frame, or nil (meaning the
-selected frame's terminal).
+;; TERMINAL can be a terminal id, a frame, or nil (meaning the
+;; selected frame's terminal).
 
-If BODY uses `setenv' to change environment variables in VARS,
-then the new variable values will be remembered for TERMINAL, and
-`terminal-getenv' will return them even outside BODY."
-  (declare (indent 2))
-  (let ((var (make-symbol "var"))
-	(term (make-symbol "term"))
-	(v (make-symbol "v"))
-	(old-env (make-symbol "old-env")))
-    `(let ((,term ,terminal)		; Evaluate arguments only once.
-	   (,v ,vars))
-       (if (stringp ,v)
-	   (setq ,v (list ,v)))
-       (cond
-	((null (terminal-parameter ,term 'environment))
-	 ;; Not a remote terminal; nothing to do.
-	 (progn ,@body))
-	((eq ,v t)
-	 ;; Switch the entire process-environment.
-	 (let (,old-env process-environment)
-	   (setq process-environment (terminal-parameter ,term 'environment))
-	   (unwind-protect
-	       (progn ,@body)
-	     (set-terminal-parameter ,term 'environment process-environment)
-	     (setq process-environment ,old-env))))
-	(t
-	 ;; Do only a set of variables.
-	 (let (,old-env)
-	   (dolist (,var ,v)
-	     (setq ,old-env (cons (cons ,var (getenv ,var)) ,old-env))
-	     (setenv ,var (terminal-getenv ,var ,term)))
-	   (unwind-protect
-	       (progn ,@body)
-	     ;; Split storing new values and restoring old ones so
-	     ;; that we DTRT even if a variable is specified twice in
-	     ;; VARS.
-	     (dolist (,var ,v)
-	       (terminal-setenv-internal ,var (getenv ,var) ,term))
-	     (dolist (,var ,old-env)
-	       (setenv (car ,var) (cdr ,var))))))))))
+;; If BODY uses `setenv' to change environment variables in VARS,
+;; then the new variable values will be remembered for TERMINAL, and
+;; `terminal-getenv' will return them even outside BODY."
+;;   (declare (indent 2))
+;;   (let ((var (make-symbol "var"))
+;; 	(term (make-symbol "term"))
+;; 	(v (make-symbol "v"))
+;; 	(old-env (make-symbol "old-env")))
+;;     `(let ((,term ,terminal)		; Evaluate arguments only once.
+;; 	   (,v ,vars))
+;;        (if (stringp ,v)
+;; 	   (setq ,v (list ,v)))
+;;        (cond
+;; 	((null (terminal-parameter ,term 'environment))
+;; 	 ;; Not a remote terminal; nothing to do.
+;; 	 (progn ,@body))
+;; 	((eq ,v t)
+;; 	 ;; Switch the entire process-environment.
+;; 	 (let (,old-env process-environment)
+;; 	   (setq process-environment (terminal-parameter ,term 'environment))
+;; 	   (unwind-protect
+;; 	       (progn ,@body)
+;; 	     (set-terminal-parameter ,term 'environment process-environment)
+;; 	     (setq process-environment ,old-env))))
+;; 	(t
+;; 	 ;; Do only a set of variables.
+;; 	 (let (,old-env)
+;; 	   (dolist (,var ,v)
+;; 	     (setq ,old-env (cons (cons ,var (getenv ,var)) ,old-env))
+;; 	     (setenv ,var (terminal-getenv ,var ,term)))
+;; 	   (unwind-protect
+;; 	       (progn ,@body)
+;; 	     ;; Split storing new values and restoring old ones so
+;; 	     ;; that we DTRT even if a variable is specified twice in
+;; 	     ;; VARS.
+;; 	     (dolist (,var ,v)
+;; 	       (terminal-setenv-internal ,var (getenv ,var) ,term))
+;; 	     (dolist (,var ,old-env)
+;; 	       (setenv (car ,var) (cdr ,var))))))))))
 
 (provide 'termdev)
 
--- a/mac/makefile.MPW	Sun Dec 25 20:06:58 2005 +0000
+++ b/mac/makefile.MPW	Mon Dec 26 02:14:10 2005 +0000
@@ -261,8 +261,8 @@
 	{CONFIG_H_GROUP} ¶
 	"{Includes}sys:types.h" ¶
 	"{Includes}sys:file.h" ¶
-		"{Includes}sys:types.h" ¶
-		"{Includes}sys:stat.h" ¶
+	"{Includes}sys:types.h" ¶
+	"{Includes}sys:stat.h" ¶
 	"{Src}lisp.h" ¶
 	"{Src}commands.h" ¶
 	"{Src}buffer.h" ¶
@@ -274,7 +274,9 @@
 	"{Src}process.h" ¶
 	"{Src}syssignal.h" ¶
 	"{Src}systty.h" ¶
-		"{Includes}termio.h"
+	"{Includes}termio.h" ¶
+	"{Src}frame.h" ¶
+	"{Src}termhooks.h"
 
 {Src}casefiddle Ä ¶
 	{CONFIG_H_GROUP} ¶
--- a/src/Makefile.in	Sun Dec 25 20:06:58 2005 +0000
+++ b/src/Makefile.in	Mon Dec 26 02:14:10 2005 +0000
@@ -1078,7 +1078,7 @@
    keyboard.h dispextern.h $(config_h)
 callproc.o: callproc.c epaths.h buffer.h commands.h $(config_h) \
 	process.h systty.h syssignal.h charset.h coding.h ccl.h msdos.h \
-        composite.h w32.h blockinput.h atimer.h systime.h
+        composite.h w32.h blockinput.h atimer.h systime.h frame.h termhooks.h
 casefiddle.o: casefiddle.c syntax.h commands.h buffer.h composite.h \
 	charset.h keymap.h $(config_h)
 casetab.o: casetab.c buffer.h $(config_h)
--- a/src/callproc.c	Sun Dec 25 20:06:58 2005 +0000
+++ b/src/callproc.c	Mon Dec 26 02:14:10 2005 +0000
@@ -84,6 +84,8 @@
 #include "syssignal.h"
 #include "systty.h"
 #include "blockinput.h"
+#include "frame.h"
+#include "termhooks.h"
 
 #ifdef MSDOS
 #include "msdos.h"
@@ -116,6 +118,7 @@
 #ifdef DOS_NT
 Lisp_Object Qbuffer_file_type;
 #endif /* DOS_NT */
+Lisp_Object Qenvironment;
 
 /* True iff we are about to fork off a synchronous process or if we
    are waiting for it.  */
@@ -130,6 +133,10 @@
 /* If synch_process_death is zero,
    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.
@@ -1264,9 +1271,25 @@
     register Lisp_Object tem;
     register char **new_env;
     register int new_length;
+    Lisp_Object environment = Vprocess_environment;
+    Lisp_Object local;
 
     new_length = 0;
-    for (tem = Vprocess_environment;
+
+    if (!NILP (Vlocal_environment_variables))
+      {
+        local = get_terminal_param (FRAME_DEVICE (XFRAME (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;
 	 CONSP (tem) && STRINGP (XCAR (tem));
 	 tem = XCDR (tem))
       new_length++;
@@ -1279,8 +1302,42 @@
     if (getenv ("PWD"))
       *new_env++ = pwd_var;
 
-    /* Copy the Vprocess_environment strings into new_env.  */
-    for (tem = Vprocess_environment;
+    /* Get the local environment variables first. */
+    for (tem = Vlocal_environment_variables;
+         CONSP (tem) && STRINGP (XCAR (tem));
+         tem = XCDR (tem))
+      {
+        char **ep = env;
+        char *string = egetenv (SDATA (XCAR (tem)));
+        int ok = 1;
+        if (string == NULL)
+          continue;
+
+	/* See if this string duplicates any string already in the env.
+	   If so, don't put it in.
+	   When an env var has multiple definitions,
+	   we keep the definition that comes first in process-environment.  */
+        for (; ep != new_env; ep++)
+          {
+	    char *p = *ep, *q = string;
+	    while (ok)
+	      {
+		if (*q == 0)
+                  /* The string is malformed; might as well drop it.  */
+                  ok = 0;
+		if (*q != *p)
+		  break;
+		if (*q == '=')
+                  ok = 0;
+		p++, q++;
+	      }
+          }
+        if (ok)
+          *new_env++ = string;
+      }
+
+    /* Copy the environment strings into new_env.  */
+    for (tem = environment;
 	 CONSP (tem) && STRINGP (XCAR (tem));
 	 tem = XCDR (tem))
       {
@@ -1423,29 +1480,68 @@
 }
 
 static int
-getenv_internal (var, varlen, value, valuelen)
+getenv_internal (var, varlen, value, valuelen, terminal)
      char *var;
      int varlen;
      char **value;
      int *valuelen;
+     Lisp_Object terminal;
 {
   Lisp_Object scan;
+  Lisp_Object environment = Vprocess_environment;
 
-  for (scan = Vprocess_environment; CONSP (scan); scan = XCDR (scan))
+  /* Find the environment in which to search the variable. */
+  if (!NILP (terminal))
+    {
+      Lisp_Object local = get_terminal_param (get_device (terminal, 1));
+      /* Use Vprocess_environment if there is no local environment.  */
+      if (!NILP (local))
+        environment = local;
+    }
+  else if (!NILP (Vlocal_environment_variables)) 
+    {
+      Lisp_Object local = get_terminal_param (FRAME_DEVICE (XFRAME (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))
     {
       Lisp_Object entry;
 
       entry = XCAR (scan);
       if (STRINGP (entry)
-	  && SBYTES (entry) > varlen
-	  && SREF (entry, varlen) == '='
+          && SBYTES (entry) > varlen
+          && SREF (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 */
-	  )
+          )
 	{
 	  *value    = (char *) SDATA (entry) + (varlen + 1);
 	  *valuelen = SBYTES (entry) - (varlen + 1);
@@ -1456,19 +1552,30 @@
   return 0;
 }
 
-DEFUN ("getenv-internal", Fgetenv_internal, Sgetenv_internal, 1, 1, 0,
+DEFUN ("getenv-internal", Fgetenv_internal, Sgetenv_internal, 1, 2, 0,
        doc: /* Return the value of environment variable VAR, as a string.
-VAR should be a string.  Value is nil if VAR is undefined in the environment.
-This function consults the variable ``process-environment'' for its value.  */)
-     (var)
-     Lisp_Object var;
+VAR should be a string.  Value is nil if VAR is undefined in the
+environment.
+
+If optional parameter TERMINAL is non-nil, then it should be a
+terminal id or a frame.  If the specified terminal device has its own
+set of environment variables, this function will look up VAR in it.
+
+Otherwise, if `local-environment-variables' specifies that VAR is a
+local environment variable, then this function consults the
+environment variables belonging to the terminal device of the selected
+frame.
+
+Otherwise, the value of VAR will come from `process-environment'.  */)
+     (var, terminal)
+     Lisp_Object var, terminal;
 {
   char *value;
   int valuelen;
 
   CHECK_STRING (var);
   if (getenv_internal (SDATA (var), SBYTES (var),
-		       &value, &valuelen))
+		       &value, &valuelen, terminal))
     return make_string (value, valuelen);
   else
     return Qnil;
@@ -1483,7 +1590,7 @@
   char *value;
   int valuelen;
 
-  if (getenv_internal (var, strlen (var), &value, &valuelen))
+  if (getenv_internal (var, strlen (var), &value, &valuelen, Qnil))
     return value;
   else
     return 0;
@@ -1707,6 +1814,23 @@
   defsubr (&Sgetenv_internal);
 #endif
   defsubr (&Scall_process_region);
+
+  DEFVAR_LISP ("local-environment-variables", &Vlocal_environment_variables,
+               doc: /* 	Enable or disable terminal-local environment variables.
+If set to t, `getenv', `setenv' and subprocess creation functions use
+the environment variables of the emacsclient process that created the
+selected frame, ignoring `process-environment'.
+
+If set to nil, Emacs uses `process-environment' and ignores the client
+environment.
+
+Otherwise, `terminal-local-environment-variables' should be a list of
+variable names (represented by Lisp strings) to look up in the client
+environment.  The rest will come from `process-environment'.  */);
+  Vlocal_environment_variables = Qnil;
+
+  Qenvironment = intern ("environment");
+  staticpro (&Qenvironment);
 }
 
 /* arch-tag: 769b8045-1df7-4d2b-8968-e3fb49017f95
--- a/src/termhooks.h	Sun Dec 25 20:06:58 2005 +0000
+++ b/src/termhooks.h	Mon Dec 26 02:14:10 2005 +0000
@@ -595,6 +595,8 @@
 /* Return true if the display device is not suspended. */
 #define DEVICE_ACTIVE_P(d) ((d)->type != output_termcap || (d)->display_info.tty->input)
 
+extern Lisp_Object get_terminal_param P_ ((struct device *, Lisp_Object));
+
 extern struct device *create_device P_ ((void));
 extern void delete_device P_ ((struct device *));