(select-safe-coding-system): Check coding-category-list more rigidly.
[bpt/emacs.git] / lisp / shell.el
index 1d4dfce..d3ec4ee 100644 (file)
@@ -1,9 +1,10 @@
 ;;; shell.el --- specialized comint.el for running the shell.
 
-;; Copyright (C) 1988, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+;; Copyright (C) 1988, 93, 94, 95, 96, 1997, 2000 Free Software Foundation, Inc.
 
-;; Author: Olin Shivers <shivers@cs.cmu.edu>
-;; Maintainer: Simon Marshall <simon@gnu.ai.mit.edu>
+;; Author: Olin Shivers <shivers@cs.cmu.edu> then
+;;     Simon Marshall <simon@gnu.org>
+;; Maintainer: FSF
 ;; Keywords: processes
 
 ;; This file is part of GNU Emacs.
@@ -28,7 +29,7 @@
 ;; Please send me bug reports, bug fixes, and extensions, so that I can
 ;; merge them into the master source.
 ;;     - Olin Shivers (shivers@cs.cmu.edu)
-;;     - Simon Marshall (simon@gnu.ai.mit.edu)
+;;     - Simon Marshall (simon@gnu.org)
 
 ;; This file defines a a shell-in-a-buffer package (shell mode) built
 ;; on top of comint mode.  This is actually cmushell with things
 ;;============================================================================
 ;; Comint Mode Commands: (common to shell and all comint-derived modes)
 ;;
-;; m-p     comint-previous-input           Cycle backwards in input history
-;; m-n     comint-next-input               Cycle forwards
+;; m-p    comint-previous-input           Cycle backwards in input history
+;; m-n    comint-next-input               Cycle forwards
 ;; m-r     comint-previous-matching-input  Previous input matching a regexp
 ;; m-s     comint-next-matching-input      Next input that matches
-;; m-c-l   comint-show-output              Show last batch of process output
+;; m-c-l   comint-show-output             Show last batch of process output
 ;; return  comint-send-input
-;; c-d     comint-delchar-or-maybe-eof     Delete char unless at end of buff.
+;; c-d    comint-delchar-or-maybe-eof     Delete char unless at end of buff.
 ;; c-c c-a comint-bol                      Beginning of line; skip prompt
-;; c-c c-u comint-kill-input               ^u
-;; c-c c-w backward-kill-word              ^w
-;; c-c c-c comint-interrupt-subjob         ^c
-;; c-c c-z comint-stop-subjob              ^z
-;; c-c c-\ comint-quit-subjob              ^\
-;; c-c c-o comint-kill-output              Delete last batch of process output
-;; c-c c-r comint-show-output              Show last batch of process output
-;; c-c c-h comint-dynamic-list-input-ring  List input history
+;; c-c c-u comint-kill-input              ^u
+;; c-c c-w backward-kill-word             ^w
+;; c-c c-c comint-interrupt-subjob        ^c
+;; c-c c-z comint-stop-subjob             ^z
+;; c-c c-\ comint-quit-subjob             ^\
+;; c-c c-o comint-kill-output             Delete last batch of process output
+;; c-c c-r comint-show-output             Show last batch of process output
+;; c-c c-l comint-dynamic-list-input-ring  List input history
 ;;         send-invisible                  Read line w/o echo & send to proc
-;;         comint-continue-subjob          Useful if you accidentally suspend
+;;         comint-continue-subjob         Useful if you accidentally suspend
 ;;                                             top-level job
 ;; comint-mode-hook is the comint mode hook.
 
@@ -90,8 +91,8 @@
 ;;                                     List completions in help buffer
 ;; m-c-f   shell-forward-command       Forward a shell command
 ;; m-c-b   shell-backward-command      Backward a shell command
-;;         dirs                        Resync the buffer's dir stack
-;;         dirtrack-toggle             Turn dir tracking on/off
+;;        dirs                         Resync the buffer's dir stack
+;;        dirtrack-mode                Turn dir tracking on/off
 ;;         comint-strip-ctrl-m         Remove trailing ^Ms from output
 ;;
 ;; The shell mode hook is shell-mode-hook
 
 ;;; Customization and Buffer Variables
 
+(defgroup shell nil
+  "Running shell from within Emacs buffers"
+  :group 'processes
+  :group 'unix)
+
+(defgroup shell-directories nil
+  "Directory support in shell mode"
+  :group 'shell)
+
+(defgroup shell-faces nil
+  "Faces in shell buffers"
+  :group 'shell)
+
 ;;;###autoload
-(defvar shell-prompt-pattern "^[^#$%>\n]*[#$%>] *"
+(defcustom shell-dumb-shell-regexp "cmd\\(proxy\\)?\\.exe"
+  "Regexp to match shells that don't save their command history.
+For shells that match this regexp, Emacs will write out the
+command history when the shell finishes."
+  :type 'regexp
+  :group 'shell)
+
+(defcustom shell-prompt-pattern "^[^#$%>\n]*[#$%>] *"
   "Regexp to match prompts in the inferior shell.
 Defaults to \"^[^#$%>\\n]*[#$%>] *\", which works pretty well.
 This variable is used to initialise `comint-prompt-regexp' in the 
 shell buffer.
 
+This variable is only used if the variable
+`comint-use-prompt-regexp-instead-of-fields' is non-nil.
+
 The pattern should probably not match more than one line.  If it does,
 Shell mode may become confused trying to distinguish prompt from input
 on lines which don't start with a prompt.
 
-This is a fine thing to set in your `.emacs' file.")
+This is a fine thing to set in your `.emacs' file."
+  :type 'regexp
+  :group 'shell)
 
-(defvar shell-completion-fignore nil
+(defcustom shell-completion-fignore nil
   "*List of suffixes to be disregarded during file/command completion.
 This variable is used to initialize `comint-completion-fignore' in the shell
 buffer.  The default is nil, for compatibility with most shells.
 Some people like (\"~\" \"#\" \"%\").
 
-This is a fine thing to set in your `.emacs' file.")  
+This is a fine thing to set in your `.emacs' file."
+  :type '(repeat (string :tag "Suffix"))
+  :group 'shell)
 
 (defvar shell-delimiter-argument-list '(?\| ?& ?< ?> ?\( ?\) ?\;)
   "List of characters to recognise as separate arguments.
@@ -136,7 +164,7 @@ This is a fine thing to set in your `.emacs' file.")
 
 (defvar shell-file-name-chars
   (if (memq system-type '(ms-dos windows-nt))
-      "~/A-Za-z0-9_^$!#%&{}@`'.()-"
+      "~/A-Za-z0-9_^$!#%&{}@`'.,:()-"
     "~/A-Za-z0-9+@:_.$#%,={}-")
   "String of characters valid in a file name.
 This variable is used to initialize `comint-file-name-chars' in the
@@ -147,7 +175,7 @@ This is a fine thing to set in your `.emacs' file.")
 (defvar shell-file-name-quote-list
   (if (memq system-type '(ms-dos windows-nt))
       nil
-    (append shell-delimiter-argument-list '(?\  ?\* ?\! ?\" ?\' ?\`)))
+    (append shell-delimiter-argument-list '(?\  ?\* ?\! ?\" ?\' ?\` ?\#)))
   "List of characters to quote when in a file name.
 This variable is used to initialize `comint-file-name-quote-list' in the
 shell buffer.  The value may depend on the operating system or shell.
@@ -166,57 +194,86 @@ shell buffer.
 
 This is a fine thing to set in your `.emacs' file.")
 
-(defvar shell-command-regexp "[^;&|\n]+"
+(defcustom shell-command-regexp "[^;&|\n]+"
   "*Regexp to match a single command within a pipeline.
-This is used for directory tracking and does not do a perfect job.")
+This is used for directory tracking and does not do a perfect job."
+  :type 'regexp
+  :group 'shell)
 
-(defvar shell-completion-execonly t
+(defcustom shell-completion-execonly t
   "*If non-nil, use executable files only for completion candidates.
 This mirrors the optional behavior of tcsh.
 
-Detecting executability of files may slow command completion considerably.")
+Detecting executability of files may slow command completion considerably."
+  :type 'boolean
+  :group 'shell)
 
-(defvar shell-popd-regexp "popd"
-  "*Regexp to match subshell commands equivalent to popd.")
+(defcustom shell-popd-regexp "popd"
+  "*Regexp to match subshell commands equivalent to popd."
+  :type 'regexp
+  :group 'shell-directories)
 
-(defvar shell-pushd-regexp "pushd"
-  "*Regexp to match subshell commands equivalent to pushd.")
+(defcustom shell-pushd-regexp "pushd"
+  "*Regexp to match subshell commands equivalent to pushd."
+  :type 'regexp
+  :group 'shell-directories)
 
-(defvar shell-pushd-tohome nil
+(defcustom shell-pushd-tohome nil
   "*If non-nil, make pushd with no arg behave as \"pushd ~\" (like cd).
-This mirrors the optional behavior of tcsh.")
+This mirrors the optional behavior of tcsh."
+  :type 'boolean
+  :group 'shell-directories)
 
-(defvar shell-pushd-dextract nil
+(defcustom shell-pushd-dextract nil
   "*If non-nil, make \"pushd +n\" pop the nth dir to the stack top.
-This mirrors the optional behavior of tcsh.")
+This mirrors the optional behavior of tcsh."
+  :type 'boolean
+  :group 'shell-directories)
 
-(defvar shell-pushd-dunique nil
+(defcustom shell-pushd-dunique nil
   "*If non-nil, make pushd only add unique directories to the stack.
-This mirrors the optional behavior of tcsh.")
+This mirrors the optional behavior of tcsh."
+  :type 'boolean
+  :group 'shell-directories)
 
-(defvar shell-cd-regexp "cd"
-  "*Regexp to match subshell commands equivalent to cd.")
+(defcustom shell-cd-regexp "cd"
+  "*Regexp to match subshell commands equivalent to cd."
+  :type 'regexp
+  :group 'shell-directories)
 
-(defvar shell-chdrive-regexp
+(defcustom shell-chdrive-regexp
   (if (memq system-type '(ms-dos windows-nt)) 
       ; NetWare allows the five chars between upper and lower alphabetics.
       "[]a-zA-Z^_`\\[\\\\]:"
     nil)
-  "*If non-nil, is regexp used to track drive changes.")
-
-(defvar explicit-shell-file-name nil
-  "*If non-nil, is file name to use for explicitly requested inferior shell.")
-
-(defvar explicit-csh-args
+  "*If non-nil, is regexp used to track drive changes."
+  :type '(choice regexp
+                (const nil))
+  :group 'shell-directories)
+
+(defcustom shell-dirtrack-verbose t
+  "*If non-nil, show the directory stack following directory change.
+This is effective only if directory tracking is enabled."
+  :type 'boolean
+  :group 'shell-directories)
+
+(defcustom explicit-shell-file-name nil
+  "*If non-nil, is file name to use for explicitly requested inferior shell."
+  :type '(choice (const :tag "None" nil) file)
+  :group 'shell)
+
+(defcustom explicit-csh-args
   (if (eq system-type 'hpux)
       ;; -T persuades HP's csh not to think it is smarter
       ;; than us about what terminal modes to use.
       '("-i" "-T")
     '("-i"))
   "*Args passed to inferior shell by M-x shell, if the shell is csh.
-Value is a list of strings, which may be nil.")
+Value is a list of strings, which may be nil."
+  :type '(repeat (string :tag "Argument"))
+  :group 'shell)
 
-(defvar shell-input-autoexpand 'history
+(defcustom shell-input-autoexpand 'history
   "*If non-nil, expand input command history references on completion.
 This mirrors the optional behavior of tcsh (its autoexpand and histlit).
 
@@ -226,7 +283,12 @@ into the buffer's input ring.  See also `comint-magic-space' and
 `comint-dynamic-complete'.
 
 This variable supplies a default for `comint-input-autoexpand',
-for Shell mode only.")
+for Shell mode only."
+  :type '(choice (const :tag "off" nil)
+                (const input)
+                (const history)
+                (const :tag "on" t))
+  :group 'shell)
 
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
@@ -260,19 +322,22 @@ Thus, this does not include the shell's current directory.")
                              shell-replace-by-expanded-directory)
         'complete-expand)))
 
-(defvar shell-mode-hook '()
-  "*Hook for customising Shell mode.")
+(defcustom shell-mode-hook '()
+  "*Hook for customising Shell mode."
+  :type 'hook
+  :group 'shell)
 
 (defvar shell-font-lock-keywords
-  (list (cons shell-prompt-pattern 'font-lock-keyword-face)
-       '("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-comment-face)
-       '("^[^ \t\n]+:.*" . font-lock-string-face)
-       '("^\\[[1-9][0-9]*\\]" . font-lock-string-face))
+  '(("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-comment-face)
+    ("^[^ \t\n]+:.*" . font-lock-string-face)
+    ("^\\[[1-9][0-9]*\\]" . font-lock-string-face))
   "Additional expressions to highlight in Shell mode.")
 \f
 ;;; Basic Procedures
 
-(defun shell-mode ()
+(put 'shell-mode 'mode-class 'special)
+
+(define-derived-mode shell-mode comint-mode "Shell"
   "Major mode for interacting with an inferior shell.
 \\[comint-send-input] after the end of the process' output sends the text from
     the end of process to the end of the current line.
@@ -297,7 +362,7 @@ While directory tracking is enabled, the shell's working directory is displayed
 by \\[list-buffers] or \\[mouse-buffer-menu] in the `File' field.
 \\[dirs] queries the shell and resyncs Emacs' idea of what the current 
     directory stack is.
-\\[dirtrack-toggle] turns directory tracking on and off.
+\\[dirtrack-mode] turns directory tracking on and off.
 
 \\{shell-mode-map}
 Customization: Entry to this mode runs the hooks on `comint-mode-hook' and
@@ -324,11 +389,6 @@ Variables `comint-output-filter-functions', a hook, and
 `comint-scroll-to-bottom-on-input' and `comint-scroll-to-bottom-on-output'
 control whether input and output cause the window to scroll to the end of the
 buffer."
-  (interactive)
-  (comint-mode)
-  (setq major-mode 'shell-mode)
-  (setq mode-name "Shell")
-  (use-local-map shell-mode-map)
   (setq comint-prompt-regexp shell-prompt-pattern)
   (setq comint-completion-fignore shell-completion-fignore)
   (setq comint-delimiter-argument-list shell-delimiter-argument-list)
@@ -341,11 +401,14 @@ buffer."
   (setq font-lock-defaults '(shell-font-lock-keywords t))
   (make-local-variable 'shell-dirstack)
   (setq shell-dirstack nil)
+  (make-local-variable 'shell-last-dir)
   (setq shell-last-dir nil)
   (make-local-variable 'shell-dirtrackp)
   (setq shell-dirtrackp t)
   (add-hook 'comint-input-filter-functions 'shell-directory-tracker nil t)
   (setq comint-input-autoexpand shell-input-autoexpand)
+  ;; This is not really correct, since the shell buffer does not really
+  ;; edit this directory.  But it is useful in the buffer list and menus.
   (make-local-variable 'list-buffers-directory)
   (setq list-buffers-directory (expand-file-name default-directory))
   ;; shell-dependent assignments.
@@ -360,18 +423,38 @@ buffer."
            (equal (file-truename comint-input-ring-file-name)
                   (file-truename "/dev/null")))
        (setq comint-input-ring-file-name nil))
+    ;; Arrange to write out the input ring on exit, if the shell doesn't
+    ;; do this itself.
+    (if (and comint-input-ring-file-name
+            (string-match shell-dumb-shell-regexp shell))
+       (set-process-sentinel (get-buffer-process (current-buffer))
+                             #'shell-write-history-on-exit))
     (setq shell-dirstack-query
          (cond ((string-equal shell "sh") "pwd")
                ((string-equal shell "ksh") "echo $PWD ~-")
                (t "dirs"))))
-  (run-hooks 'shell-mode-hook)
   (comint-read-input-ring t))
+
+(defun shell-write-history-on-exit (process event)
+  "Called when the shell process is stopped.
+
+Writes the input history to a history file
+`comint-comint-input-ring-file-name' using `comint-write-input-ring'
+and inserts a short message in the shell buffer.
+
+This function is a sentinel watching the shell interpreter process.
+Sentinels will always get the two parameters PROCESS and EVENT."
+  ;; Write history.
+  (comint-write-input-ring)
+  (if (buffer-live-p (process-buffer process))
+      (insert (format "\nProcess %s %s\n" process event))))
 \f
 ;;;###autoload
-(defun shell ()
-  "Run an inferior shell, with I/O through buffer *shell*.
-If buffer exists but shell process is not running, make new shell.
-If buffer exists and shell process is running, just switch to buffer `*shell*'.
+(defun shell (&optional buffer)
+  "Run an inferior shell, with I/O through BUFFER (which defaults to `*shell*').
+Interactively, a prefix arg means to prompt for BUFFER.
+If BUFFER exists but shell process is not running, make new shell.
+If BUFFER exists and shell process is running, just switch to BUFFER.
 Program used comes from variable `explicit-shell-file-name',
  or (if that is nil) from the ESHELL environment variable,
  or else from SHELL if there is no ESHELL.
@@ -382,14 +465,26 @@ The buffer is put in Shell mode, giving commands for sending input
 and controlling the subjobs of the shell.  See `shell-mode'.
 See also the variable `shell-prompt-pattern'.
 
+To specify a coding system for converting non-ASCII characters
+in the input and output to the shell, use \\[universal-coding-system-argument]
+before \\[shell].  You can also specify this with \\[set-buffer-process-coding-system]
+in the shell buffer, after you start the shell.
+The default comes from `process-coding-system-alist' and
+`default-process-coding-system'.
+
 The shell file name (sans directories) is used to make a symbol name
 such as `explicit-csh-args'.  If that symbol is a variable,
 its value is used as a list of arguments when invoking the shell.
 Otherwise, one argument `-i' is passed to the shell.
 
 \(Type \\[describe-mode] in the shell buffer for a list of commands.)"
-  (interactive)
-  (if (not (comint-check-proc "*shell*"))
+  (interactive
+   (list
+    (and current-prefix-arg
+        (read-buffer "Shell buffer: " "*shell*"))))
+  (when (null buffer)
+    (setq buffer "*shell*"))
+  (if (not (comint-check-proc buffer))
       (let* ((prog (or explicit-shell-file-name
                       (getenv "ESHELL")
                       (getenv "SHELL")
@@ -399,7 +494,7 @@ Otherwise, one argument `-i' is passed to the shell.
             (xargs-name (intern-soft (concat "explicit-" name "-args")))
             shell-buffer)
        (save-excursion
-         (set-buffer (apply 'make-comint "shell" prog
+         (set-buffer (apply 'make-comint-in-buffer "shell" buffer prog
                             (if (file-exists-p startfile) startfile)
                             (if (and xargs-name (boundp xargs-name))
                                 (symbol-value xargs-name)
@@ -407,7 +502,7 @@ Otherwise, one argument `-i' is passed to the shell.
          (setq shell-buffer (current-buffer))
          (shell-mode))
        (pop-to-buffer shell-buffer))
-    (pop-to-buffer "*shell*")))
+    (pop-to-buffer buffer)))
 
 ;;; Don't do this when shell.el is loaded, only while dumping.
 ;;;###autoload (add-hook 'same-window-buffer-names "*shell*")
@@ -458,7 +553,7 @@ This function is called on each input passed to the shell.
 It watches for cd, pushd and popd commands and sets the buffer's
 default directory to track these commands.
 
-You may toggle this tracking on and off with M-x dirtrack-toggle.
+You may toggle this tracking on and off with M-x dirtrack-mode.
 If emacs gets confused, you can resync with the shell with M-x dirs.
 
 See variables `shell-cd-regexp', `shell-chdrive-regexp', `shell-pushd-regexp',
@@ -476,6 +571,8 @@ Environment variables are expanded, see function `substitute-in-file-name'."
              (setq end (match-end 0)
                    cmd (comint-arguments (substring str start end) 0 0)
                    arg1 (comint-arguments (substring str start end) 1 1))
+             (if arg1
+                 (setq arg1 (shell-unquote-argument arg1)))
              (cond ((string-match (concat "\\`\\(" shell-popd-regexp
                                           "\\)\\($\\|[ \t]\\)")
                                   cmd)
@@ -497,6 +594,25 @@ Environment variables are expanded, see function `substitute-in-file-name'."
                                 (match-end 0)))))
        (error "Couldn't cd"))))
 
+(defun shell-unquote-argument (string)
+  "Remove all kinds of shell quoting from STRING."
+  (save-match-data
+    (let ((idx 0) next inside)
+      (while (and (< idx (length string))
+                 (setq next (string-match "[\\'`\"]" string next)))
+       (cond ((= (aref string next) ?\\)
+              (setq string (replace-match "" nil nil string))
+              (setq next (1+ next)))
+             ((and inside (= (aref string next) inside))
+              (setq string (replace-match "" nil nil string))
+              (setq inside nil))
+             (inside
+              (setq next (1+ next)))
+             (t
+              (setq inside (aref string next))
+              (setq string (replace-match "" nil nil string)))))
+      string)))
+
 ;;; popd [+n]
 (defun shell-process-popd (arg)
   (let ((num (or (shell-extract-num arg) 0)))
@@ -583,7 +699,7 @@ Environment variables are expanded, see function `substitute-in-file-name'."
        (string-to-int str)))
 
 
-(defun shell-dirtrack-toggle ()
+(defun shell-dirtrack-mode ()
   "Turn directory tracking on and off in a shell buffer."
   (interactive)
   (if (setq shell-dirtrackp (not shell-dirtrackp))
@@ -592,7 +708,9 @@ Environment variables are expanded, see function `substitute-in-file-name'."
   (message "Directory tracking %s" (if shell-dirtrackp "ON" "OFF")))
 
 ;;; For your typing convenience:
-(defalias 'dirtrack-toggle 'shell-dirtrack-toggle)
+(defalias 'shell-dirtrack-toggle 'shell-dirtrack-mode)
+(defalias 'dirtrack-toggle 'shell-dirtrack-mode)
+(defalias 'dirtrack-mode 'shell-dirtrack-mode)
 
 (defun shell-cd (dir)
   "Do normal `cd' to DIR, and set `list-buffers-directory'."
@@ -658,24 +776,57 @@ command again."
 ;;; All the commands that mung the buffer's dirstack finish by calling
 ;;; this guy.
 (defun shell-dirstack-message ()
-  (let* ((msg "")
-        (ds (cons default-directory shell-dirstack))
-        (home (expand-file-name (concat comint-file-name-prefix "~/")))
-        (homelen (length home)))
-    (while ds
-      (let ((dir (car ds)))
-       (and (>= (length dir) homelen) (string= home (substring dir 0 homelen))
-           (setq dir (concat "~/" (substring dir homelen))))
-       ;; Strip off comint-file-name-prefix if present.
-       (and comint-file-name-prefix
-            (>= (length dir) (length comint-file-name-prefix))
-            (string= comint-file-name-prefix
-                     (substring dir 0 (length comint-file-name-prefix)))
-            (setq dir (substring dir (length comint-file-name-prefix)))
-            (setcar ds dir))
-       (setq msg (concat msg (directory-file-name dir) " "))
-       (setq ds (cdr ds))))
-    (message "%s" msg)))
+  (when shell-dirtrack-verbose
+    (let* ((msg "")
+          (ds (cons default-directory shell-dirstack))
+          (home (expand-file-name (concat comint-file-name-prefix "~/")))
+          (homelen (length home)))
+      (while ds
+       (let ((dir (car ds)))
+         (and (>= (length dir) homelen)
+              (string= home (substring dir 0 homelen))
+              (setq dir (concat "~/" (substring dir homelen))))
+         ;; Strip off comint-file-name-prefix if present.
+         (and comint-file-name-prefix
+              (>= (length dir) (length comint-file-name-prefix))
+              (string= comint-file-name-prefix
+                       (substring dir 0 (length comint-file-name-prefix)))
+              (setq dir (substring dir (length comint-file-name-prefix)))
+              (setcar ds dir))
+         (setq msg (concat msg (directory-file-name dir) " "))
+         (setq ds (cdr ds))))
+      (message "%s" msg))))
+\f
+;; This was mostly copied from shell-resync-dirs.
+(defun shell-snarf-envar (var)
+  "Return as a string the shell's value of environment variable VAR."
+  (let* ((cmd (format "printenv '%s'\n" var))
+        (proc (get-buffer-process (current-buffer)))
+        (pmark (process-mark proc)))
+    (goto-char pmark)
+    (insert cmd)
+    (sit-for 0)                                ; force redisplay
+    (comint-send-string proc cmd)
+    (set-marker pmark (point))
+    (let ((pt (point)))                        ; wait for 1 line
+      ;; This extra newline prevents the user's pending input from spoofing us.
+      (insert "\n") (backward-char 1)
+      (while (not (looking-at ".+\n"))
+       (accept-process-output proc)
+       (goto-char pt)))
+    (goto-char pmark) (delete-char 1)  ; remove the extra newline
+    (buffer-substring (match-beginning 0) (1- (match-end 0)))))
+
+(defun shell-copy-environment-variable (variable)
+  "Copy the environment variable VARIABLE from the subshell to Emacs.
+This command reads the value of the specified environment variable
+in the shell, and sets the same environment variable in Emacs
+\(what `getenv' in Emacs would return) to that value.
+That value will affect any new subprocesses that you subsequently start
+from Emacs."
+  (interactive (list (read-envvar-name "\
+Copy Shell environment variable to Emacs: ")))
+  (setenv variable (shell-snarf-envar variable)))
 \f
 (defun shell-forward-command (&optional arg)
   "Move forward across ARG shell command(s).  Does not cross lines.
@@ -692,8 +843,8 @@ See `shell-command-regexp'."
 See `shell-command-regexp'."
   (interactive "p")
   (let ((limit (save-excursion (comint-bol nil) (point))))
-    (if (> limit (point))
-       (save-excursion (beginning-of-line) (setq limit (point))))
+    (when (> limit (point))
+      (setq limit (line-beginning-position)))
     (skip-syntax-backward " " limit)
     (if (re-search-backward
         (format "[;&|]+[\t ]*\\(%s\\)" shell-command-regexp) limit 'move arg)