*** empty log message ***
[bpt/emacs.git] / lisp / shell.el
index 3a50263..1817a1f 100644 (file)
@@ -1,9 +1,10 @@
-;;; shell.el --- specialized comint.el for running the shell.
+;;; shell.el --- specialized comint.el for running the shell
 
 
-;; Copyright (C) 1988, 1993, 1994 Free Software Foundation, Inc.
+;; Copyright (C) 1988, 93, 94, 95, 96, 1997, 2000 Free Software Foundation, Inc.
 
 ;; Author: Olin Shivers <shivers@cs.cmu.edu>
 
 ;; Author: Olin Shivers <shivers@cs.cmu.edu>
-;; Maintainer: Simon Marshall <s.marshall@dcs.hull.ac.uk>
+;;     Simon Marshall <simon@gnu.org>
+;; Maintainer: FSF
 ;; Keywords: processes
 
 ;; This file is part of GNU Emacs.
 ;; Keywords: processes
 
 ;; This file is part of GNU Emacs.
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
 
 ;;; Commentary:
 
-;;; 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 (s.marshall@dcs.hull.ac.uk)
+;; 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.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
-;;; renamed to replace its counterpart in Emacs 18.  cmushell is more
-;;; featureful, robust, and uniform than the Emacs 18 version.
+;; This file defines a shell-in-a-buffer package (shell mode) built on
+;; top of comint mode.  This is actually cmushell with things renamed
+;; to replace its counterpart in Emacs 18.  cmushell is more
+;; featureful, robust, and uniform than the Emacs 18 version.
 
 
-;;; Since this mode is built on top of the general command-interpreter-in-
-;;; a-buffer mode (comint mode), it shares a common base functionality, 
-;;; and a common set of bindings, with all modes derived from comint mode.
-;;; This makes these modes easier to use.
+;; Since this mode is built on top of the general command-interpreter-in-
+;; a-buffer mode (comint mode), it shares a common base functionality,
+;; and a common set of bindings, with all modes derived from comint mode.
+;; This makes these modes easier to use.
 
 
-;;; For documentation on the functionality provided by comint mode, and
-;;; the hooks available for customising it, see the file comint.el.
-;;; For further information on shell mode, see the comments below.
+;; For documentation on the functionality provided by comint mode, and
+;; the hooks available for customising it, see the file comint.el.
+;; For further information on shell mode, see the comments below.
 
 
-;;; Needs fixin:
-;;; When sending text from a source file to a subprocess, the process-mark can 
-;;; move off the window, so you can lose sight of the process interactions.
-;;; Maybe I should ensure the process mark is in the window when I send
-;;; text to the process? Switch selectable?
+;; Needs fixin:
+;; When sending text from a source file to a subprocess, the process-mark can
+;; move off the window, so you can lose sight of the process interactions.
+;; Maybe I should ensure the process mark is in the window when I send
+;; text to the process? Switch selectable?
 
 ;; YOUR .EMACS FILE
 ;;=============================================================================
 ;; Some suggestions for your .emacs file.
 ;;
 
 ;; YOUR .EMACS FILE
 ;;=============================================================================
 ;; Some suggestions for your .emacs file.
 ;;
-;; ;; Define C-c t to run my favorite command in shell mode:
-;; (setq shell-mode-hook
-;;       '((lambda () 
-;;           (define-key shell-mode-map "\C-ct" 'favorite-cmd))))
-
-\f
-;;; Brief Command Documentation:
-;;;============================================================================
-;;; 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-r     comint-previous-matching-input  Previous input matching a regexp
-;;; m-R     comint-previous-matching-input-from-input -"- matching input
-;;; m-s     comint-next-matching-input      Next input that matches
-;;; m-S     comint-next-matching-input-from-input     -"- matching input
-;;; 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-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
-;;;         send-invisible                  Read line w/o echo & send to proc
-;;;         comint-continue-subjob         Useful if you accidentally suspend
-;;;                                            top-level job
-;;; comint-mode-hook is the comint mode hook.
-
-;;; Shell Mode Commands:
-;;;         shell                          Fires up the shell process
-;;; tab     comint-dynamic-complete        Complete filename/command/history
-;;; m-?     comint-dynamic-list-filename-completions 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
-;;;        shell-strip-ctrl-m              Remove trailing ^Ms from output
-;;;
-;;; The shell mode hook is shell-mode-hook
-;;; comint-prompt-regexp is initialised to shell-prompt-pattern, for backwards
-;;; compatibility.
-
-;;; Read the rest of this file for more information.
-\f
-;;; SHELL.EL COMPATIBILITY
-;;; Notes from when this was called cmushell, and was not the standard emacs
-;;; shell package.
-;;;============================================================================
-;;; In brief: this package should have no trouble coexisting with shell.el.
-;;; 
-;;; Most customising variables -- e.g., explicit-shell-file-name -- are the
-;;; same, so the users shouldn't have much trouble. Hooks have different
-;;; names, however, so you can customise shell mode differently from cmushell
-;;; mode. You basically just have to remember to type M-x cmushell instead of
-;;; M-x shell.
-;;; 
-;;; It would be nice if this file was completely plug-compatible with the old
-;;; shell package -- if you could just name this file shell.el, and have it
-;;; transparently replace the old one. But you can't.  Several other packages
-;;; (tex-mode, background, dbx, gdb, kermit, monkey, prolog, telnet) are also
-;;; clients of shell mode. These packages assume detailed knowledge of shell
-;;; mode internals in ways that are incompatible with cmushell mode (mostly
-;;; because of cmushell mode's greater functionality).  So, unless we are
-;;; willing to port all of these packages, we can't have this file be a
-;;; complete replacement for shell.el -- that is, we can't name this file
-;;; shell.el, and its main entry point (shell), because dbx.el will break
-;;; when it loads it in and tries to use it.
-;;; 
-;;; There are two ways to fix this. One: rewrite these other modes to use the
-;;; new package. This is a win, but can't be assumed. The other, backwards
-;;; compatible route, is to make this package non-conflict with shell.el, so
-;;; both files can be loaded in at the same time. And *that* is why some
-;;; functions and variables have different names: (cmushell),
-;;; cmushell-mode-map, that sort of thing. All the names have been carefully
-;;; chosen so that shell.el and cmushell.el won't tromp on each other.
-\f
-;;; Customization and Buffer Variables
-;;; ===========================================================================
-;;; 
+;; ;; Define M-# to run some strange command:
+;; (eval-after-load "shell"
+;;  '(define-key shell-mode-map "\M-#" 'shells-dynamic-spell))
+
+;; Brief Command Documentation:
+;;============================================================================
+;; 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-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
+;; return  comint-send-input
+;; 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-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
+;;                                             top-level job
+;; comint-mode-hook is the comint mode hook.
+
+;; Shell Mode Commands:
+;;         shell                       Fires up the shell process
+;; tab     comint-dynamic-complete     Complete filename/command/history
+;; m-?     comint-dynamic-list-filename-completions
+;;                                     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-mode                Turn dir tracking on/off
+;;         comint-strip-ctrl-m         Remove trailing ^Ms from output
+;;
+;; The shell mode hook is shell-mode-hook
+;; comint-prompt-regexp is initialised to shell-prompt-pattern, for backwards
+;; compatibility.
+
+;; Read the rest of this file for more information.
 
 ;;; Code:
 
 (require 'comint)
 
 
 ;;; Code:
 
 (require 'comint)
 
+;;; 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
 ;;;###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, and
+don't handle the backslash as a quote character.  For shells that
+match this regexp, Emacs will write out the command history when the
+shell finishes, and won't remove backslashes when it unquotes shell
+arguments."
+  :type 'regexp
+  :group 'shell)
+
+(defcustom shell-prompt-pattern "^[^#$%>\n]*[#$%>] *"
   "Regexp to match prompts in the inferior shell.
 Defaults to \"^[^#$%>\\n]*[#$%>] *\", which works pretty well.
   "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 
+This variable is used to initialise `comint-prompt-regexp' in the
 shell buffer.
 
 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,
 The pattern should probably not match more than one line.  If it does,
-shell-mode may become confused trying to distinguish prompt from input
+Shell mode may become confused trying to distinguish prompt from input
 on lines which don't start with a prompt.
 
 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 (\"~\" \"#\" \"%\").
 
   "*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.
 This variable is used to initialize `comint-delimiter-argument-list' in the
 
 (defvar shell-delimiter-argument-list '(?\| ?& ?< ?> ?\( ?\) ?\;)
   "List of characters to recognise as separate arguments.
 This variable is used to initialize `comint-delimiter-argument-list' in the
-shell buffer.  The default is (?\\| ?& ?< ?> ?\\( ?\\) ?\\;).
+shell buffer.  The value may depend on the operating system or shell.
+
+This is a fine thing to set in your `.emacs' file.")
+
+(defvar shell-file-name-chars
+  (if (memq system-type '(ms-dos windows-nt cygwin))
+      "~/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
+shell buffer.  The value may depend on the operating system or shell.
+
+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 '(?\  ?\* ?\! ?\" ?\' ?\` ?\# ?\\)))
+  "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.
 
 This is a fine thing to set in your `.emacs' file.")
 
 
 This is a fine thing to set in your `.emacs' file.")
 
@@ -182,50 +196,110 @@ shell buffer.
 
 This is a fine thing to set in your `.emacs' file.")
 
 
 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."
+  :type 'regexp
+  :group 'shell)
+
+(defcustom shell-command-separator-regexp "[;&|\n \t]*"
   "*Regexp to match a single command within a pipeline.
   "*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.
 
   "*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).
   "*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.
   "*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.
   "*If non-nil, make pushd only add unique directories to the stack.
-This mirrors the optional behavior of tcsh.")
-
-(defvar shell-cd-regexp "cd"
-  "*Regexp to match subshell commands equivalent to cd.")
-
-(defvar explicit-shell-file-name nil
-  "*If non-nil, is file name to use for explicitly requested inferior shell.")
-
-(defvar explicit-csh-args
+This mirrors the optional behavior of tcsh."
+  :type 'boolean
+  :group 'shell-directories)
+
+(defcustom shell-cd-regexp "cd"
+  "*Regexp to match subshell commands equivalent to cd."
+  :type 'regexp
+  :group 'shell-directories)
+
+(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."
+  :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.
   (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.")
-
-(defvar shell-input-autoexpand 'history
+Value is a list of strings, which may be nil."
+  :type '(repeat (string :tag "Argument"))
+  :group 'shell)
+
+(defcustom explicit-bash-args
+  ;; Tell bash not to use readline, except for bash 1.x which doesn't grook --noediting.
+  ;; Bash 1.x has -nolineediting, but process-send-eof cannot terminate bash if we use it.
+  (let* ((prog (or (and (boundp 'explicit-shell-file-name) explicit-shell-file-name)
+                  (getenv "ESHELL") shell-file-name))
+        (name (file-name-nondirectory prog)))
+    (if (and (not purify-flag)
+            (equal name "bash")
+            (file-executable-p prog)
+            (string-match "bad option"
+                          (shell-command-to-string (concat prog " --noediting"))))
+       '("-i")
+      '("--noediting" "-i")))
+  "*Args passed to inferior shell by M-x shell, if the shell is bash.
+Value is a list of strings, which may be nil."
+  :type '(repeat (string :tag "Argument"))
+  :group 'shell)
+
+(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).
 
   "*If non-nil, expand input command history references on completion.
 This mirrors the optional behavior of tcsh (its autoexpand and histlit).
 
@@ -235,7 +309,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',
 `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.
 
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
@@ -252,12 +331,15 @@ Thus, this does not include the shell's current directory.")
 
 (defvar shell-mode-map nil)
 (cond ((not shell-mode-map)
 
 (defvar shell-mode-map nil)
 (cond ((not shell-mode-map)
-       (setq shell-mode-map (copy-keymap comint-mode-map))
+       (setq shell-mode-map (nconc (make-sparse-keymap) comint-mode-map))
        (define-key shell-mode-map "\C-c\C-f" 'shell-forward-command)
        (define-key shell-mode-map "\C-c\C-b" 'shell-backward-command)
        (define-key shell-mode-map "\t" 'comint-dynamic-complete)
        (define-key shell-mode-map "\M-?"
         'comint-dynamic-list-filename-completions)
        (define-key shell-mode-map "\C-c\C-f" 'shell-forward-command)
        (define-key shell-mode-map "\C-c\C-b" 'shell-backward-command)
        (define-key shell-mode-map "\t" 'comint-dynamic-complete)
        (define-key shell-mode-map "\M-?"
         'comint-dynamic-list-filename-completions)
+       (define-key shell-mode-map [menu-bar completion]
+        (cons "Complete"
+              (copy-keymap (lookup-key comint-mode-map [menu-bar completion]))))
        (define-key-after (lookup-key shell-mode-map [menu-bar completion])
         [complete-env-variable] '("Complete Env. Variable Name" .
                                   shell-dynamic-complete-environment-variable)
        (define-key-after (lookup-key shell-mode-map [menu-bar completion])
         [complete-env-variable] '("Complete Env. Variable Name" .
                                   shell-dynamic-complete-environment-variable)
@@ -267,32 +349,47 @@ Thus, this does not include the shell's current directory.")
                              shell-replace-by-expanded-directory)
         'complete-expand)))
 
                              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
+  '(("[ \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
 ;;; 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.
   "Major mode for interacting with an inferior shell.
-Return after the end of the process' output sends the text from the 
-    end of process to the end of the current line.
-Return before end of process output copies the current line (except
-    for the prompt) to the end of the buffer and sends it.
-M-x send-invisible reads a line of text without echoing it, and sends it to
+\\[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.
+\\[comint-send-input] before end of process output copies the current line minus the prompt to
+    the end of the buffer and sends it (\\[comint-copy-old-input] just copies the current line).
+\\[send-invisible] reads a line of text without echoing it, and sends it to
     the shell.  This is useful for entering passwords.  Or, add the function
     `comint-watch-for-password-prompt' to `comint-output-filter-functions'.
 
     the shell.  This is useful for entering passwords.  Or, add the function
     `comint-watch-for-password-prompt' to `comint-output-filter-functions'.
 
+If you want to make multiple shell buffers, rename the `*shell*' buffer
+using \\[rename-buffer] or \\[rename-uniquely] and start a new shell.
+
+If you want to make shell buffers limited in length, add the function
+`comint-truncate-buffer' to `comint-output-filter-functions'.
+
 If you accidentally suspend your process, use \\[comint-continue-subjob]
 to continue it.
 
 If you accidentally suspend your process, use \\[comint-continue-subjob]
 to continue it.
 
-cd, pushd and popd commands given to the shell are watched by Emacs to keep
-this buffer's default directory the same as the shell's working directory.
-M-x dirs queries the shell and resyncs Emacs' idea of what the current 
+`cd', `pushd' and `popd' commands given to the shell are watched by Emacs to
+keep this buffer's default directory the same as the shell's working directory.
+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.
     directory stack is.
-M-x 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
 
 \\{shell-mode-map}
 Customization: Entry to this mode runs the hooks on `comint-mode-hook' and
@@ -300,10 +397,10 @@ Customization: Entry to this mode runs the hooks on `comint-mode-hook' and
 `comint-input-filter-functions' are run.  After each shell output, the hooks
 on `comint-output-filter-functions' are run.
 
 `comint-input-filter-functions' are run.  After each shell output, the hooks
 on `comint-output-filter-functions' are run.
 
-Variables `shell-cd-regexp', `shell-pushd-regexp' and `shell-popd-regexp'
-are used to match their respective commands, while `shell-pushd-tohome',
-`shell-pushd-dextract' and `shell-pushd-dunique' control the behavior of the
-relevant command.
+Variables `shell-cd-regexp', `shell-chdrive-regexp', `shell-pushd-regexp'
+and `shell-popd-regexp' are used to match their respective commands,
+while `shell-pushd-tohome', `shell-pushd-dextract' and `shell-pushd-dunique'
+control the behavior of the relevant command.
 
 Variables `comint-completion-autolist', `comint-completion-addsuffix',
 `comint-completion-recexact' and `comint-completion-fignore' control the
 
 Variables `comint-completion-autolist', `comint-completion-addsuffix',
 `comint-completion-recexact' and `comint-completion-fignore' control the
@@ -319,44 +416,97 @@ 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."
 `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)
   (setq comint-prompt-regexp shell-prompt-pattern)
   (setq comint-completion-fignore shell-completion-fignore)
   (setq comint-delimiter-argument-list shell-delimiter-argument-list)
+  (setq comint-file-name-chars shell-file-name-chars)
+  (setq comint-file-name-quote-list shell-file-name-quote-list)
   (setq comint-dynamic-complete-functions shell-dynamic-complete-functions)
   (make-local-variable 'paragraph-start)
   (setq paragraph-start comint-prompt-regexp)
   (setq comint-dynamic-complete-functions shell-dynamic-complete-functions)
   (make-local-variable 'paragraph-start)
   (setq paragraph-start comint-prompt-regexp)
+  (make-local-variable 'font-lock-defaults)
+  (setq font-lock-defaults '(shell-font-lock-keywords t))
   (make-local-variable 'shell-dirstack)
   (setq shell-dirstack nil)
   (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)
   (setq shell-last-dir nil)
   (make-local-variable 'shell-dirtrackp)
   (setq shell-dirtrackp t)
-  (add-hook 'comint-input-filter-functions 'shell-directory-tracker)
+  (add-hook 'comint-input-filter-functions 'shell-directory-tracker nil t)
   (setq comint-input-autoexpand shell-input-autoexpand)
   (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.
   ;; shell-dependent assignments.
-  (let ((shell (file-name-nondirectory (car
-                (process-command (get-buffer-process (current-buffer)))))))
-    (setq comint-input-ring-file-name
-         (or (getenv "HISTFILE")
-             (cond ((string-equal shell "bash") "~/.bash_history")
-                   ((string-equal shell "ksh") "~/.sh_history")
-                   (t "~/.history"))))
-    (if (equal comint-input-ring-file-name "/dev/null")
-       (setq comint-input-ring-file-name nil))
-    (setq shell-dirstack-query
-         (if (string-match "^k?sh$" shell) "pwd" "dirs")))
-  (run-hooks 'shell-mode-hook)
-  (comint-read-input-ring t))
-\f
+  (when (ring-empty-p comint-input-ring)
+    (let ((shell (file-name-nondirectory (car
+                  (process-command (get-buffer-process (current-buffer)))))))
+      (setq comint-input-ring-file-name
+           (or (getenv "HISTFILE")
+               (cond ((string-equal shell "bash") "~/.bash_history")
+                     ((string-equal shell "ksh") "~/.sh_history")
+                     (t "~/.history"))))
+      (if (or (equal comint-input-ring-file-name "")
+             (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")))
+      ;; Bypass a bug in certain versions of bash.
+      (when (string-equal shell "bash")
+        (add-hook 'comint-output-filter-functions
+                  'shell-filter-ctrl-a-ctrl-b nil t)))
+    (comint-read-input-ring t)))
+
+(defun shell-filter-ctrl-a-ctrl-b (string)
+  "Remove `^A' and `^B' characters from comint output.
+
+Bash uses these characters as internal quoting characters in its
+prompt.  Due to a bug in some bash versions (including 2.03,
+2.04, and 2.05b), they may erroneously show up when bash is
+started with the `--noediting' option and Select Graphic
+Rendition (SGR) control sequences (formerly known as ANSI escape
+sequences) are used to color the prompt.
+
+This function can be put on `comint-output-filter-functions'.
+The argument STRING is ignored."
+  (let ((pmark (process-mark (get-buffer-process (current-buffer)))))
+    (save-excursion
+      (goto-char (or comint-last-output-start (point-min)))
+      (while (re-search-forward "[\C-a\C-b]" pmark t)
+        (replace-match "")))))
+
+(defun shell-write-history-on-exit (process event)
+  "Called when the shell process is stopped.
+
+Writes the input history to a history file
+`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)
+  (let ((buf (process-buffer process)))
+    (when (buffer-live-p buf)
+      (with-current-buffer buf
+        (insert (format "\nProcess %s %s\n" process event))))))
+
 ;;;###autoload
 ;;;###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.
 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.
@@ -367,31 +517,49 @@ 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'.
 
 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.)"
 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*"))
-      (let* ((prog (or explicit-shell-file-name
-                      (getenv "ESHELL")
-                      (getenv "SHELL")
-                      "/bin/sh"))                   
-            (name (file-name-nondirectory prog))
-            (startfile (concat "~/.emacs_" name))
-            (xargs-name (intern-soft (concat "explicit-" name "-args"))))
-       (set-buffer (apply 'make-comint "shell" prog
-                          (if (file-exists-p startfile) startfile)
-                          (if (and xargs-name (boundp xargs-name))
-                              (symbol-value xargs-name)
-                            '("-i"))))
-       (shell-mode)))
-  (switch-to-buffer "*shell*"))
-\f
+  (interactive
+   (list
+    (and current-prefix-arg
+        (read-buffer "Shell buffer: "
+                     (generate-new-buffer-name "*shell*")))))
+  (setq buffer (get-buffer-create (or buffer "*shell*")))
+  ;; Pop to buffer, so that the buffer's window will be correctly set
+  ;; when we call comint (so that comint sets the COLUMNS env var properly).
+  (pop-to-buffer buffer)
+  (unless (comint-check-proc buffer)
+    (let* ((prog (or explicit-shell-file-name
+                    (getenv "ESHELL") shell-file-name))
+          (name (file-name-nondirectory prog))
+          (startfile (concat "~/.emacs_" name))
+          (xargs-name (intern-soft (concat "explicit-" name "-args"))))
+      (if (not (file-exists-p startfile))
+         (setq startfile (concat "~/.emacs.d/.emacs_" name)))
+      (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)
+              '("-i")))
+      (shell-mode)))
+  buffer)
+
+;;; Don't do this when shell.el is loaded, only while dumping.
+;;;###autoload (add-hook 'same-window-buffer-names "*shell*")
+
 ;;; Directory tracking
 ;;; Directory tracking
-;;; ===========================================================================
+;;;
 ;;; This code provides the shell mode input sentinel
 ;;;     SHELL-DIRECTORY-TRACKER
 ;;; that tracks cd, pushd, and popd commands issued to the shell, and
 ;;; This code provides the shell mode input sentinel
 ;;;     SHELL-DIRECTORY-TRACKER
 ;;; that tracks cd, pushd, and popd commands issued to the shell, and
@@ -418,7 +586,7 @@ Otherwise, one argument `-i' is passed to the shell.
 ;;;
 ;;; The solution is to relax, not stress out about it, and settle for
 ;;; a hack that works pretty well in typical circumstances. Remember
 ;;;
 ;;; The solution is to relax, not stress out about it, and settle for
 ;;; a hack that works pretty well in typical circumstances. Remember
-;;; that a half-assed solution is more in keeping with the spirit of Unix, 
+;;; that a half-assed solution is more in keeping with the spirit of Unix,
 ;;; anyway. Blech.
 ;;;
 ;;; One good hack not implemented here for users of programmable shells
 ;;; anyway. Blech.
 ;;;
 ;;; One good hack not implemented here for users of programmable shells
@@ -436,39 +604,81 @@ 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.
 
 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.
 
 If emacs gets confused, you can resync with the shell with M-x dirs.
 
-See variables `shell-cd-regexp', `shell-pushd-regexp', and `shell-popd-regexp',
-while `shell-pushd-tohome', `shell-pushd-dextract' and `shell-pushd-dunique'
-control the behavior of the relevant command.
+See variables `shell-cd-regexp', `shell-chdrive-regexp', `shell-pushd-regexp',
+and  `shell-popd-regexp', while `shell-pushd-tohome', `shell-pushd-dextract',
+and `shell-pushd-dunique' control the behavior of the relevant command.
 
 Environment variables are expanded, see function `substitute-in-file-name'."
   (if shell-dirtrackp
       ;; We fail gracefully if we think the command will fail in the shell.
       (condition-case chdir-failure
 
 Environment variables are expanded, see function `substitute-in-file-name'."
   (if shell-dirtrackp
       ;; We fail gracefully if we think the command will fail in the shell.
       (condition-case chdir-failure
-         (let ((start (progn (string-match "^[;\\s ]*" str) ; skip whitespace
+         (let ((start (progn (string-match
+                              (concat "^" shell-command-separator-regexp)
+                              str) ; skip whitespace
                              (match-end 0)))
                end cmd arg1)
            (while (string-match shell-command-regexp str start)
              (setq end (match-end 0)
                    cmd (comint-arguments (substring str start end) 0 0)
                    arg1 (comint-arguments (substring str start end) 1 1))
                              (match-end 0)))
                end cmd arg1)
            (while (string-match shell-command-regexp str start)
              (setq end (match-end 0)
                    cmd (comint-arguments (substring str start end) 0 0)
                    arg1 (comint-arguments (substring str start end) 1 1))
-             (cond ((eq (string-match shell-popd-regexp cmd) 0)
-                    (shell-process-popd (substitute-in-file-name arg1)))
-                   ((eq (string-match shell-pushd-regexp cmd) 0)
-                    (shell-process-pushd (substitute-in-file-name arg1)))
-                   ((eq (string-match shell-cd-regexp cmd) 0)
-                    (shell-process-cd (substitute-in-file-name arg1))))
-             (setq start (progn (string-match "[;\\s ]*" str end) ; skip again
+             (if arg1
+                 (setq arg1 (shell-unquote-argument arg1)))
+             (cond ((string-match (concat "\\`\\(" shell-popd-regexp
+                                          "\\)\\($\\|[ \t]\\)")
+                                  cmd)
+                    (shell-process-popd (comint-substitute-in-file-name arg1)))
+                   ((string-match (concat "\\`\\(" shell-pushd-regexp
+                                          "\\)\\($\\|[ \t]\\)")
+                                  cmd)
+                    (shell-process-pushd (comint-substitute-in-file-name arg1)))
+                   ((string-match (concat "\\`\\(" shell-cd-regexp
+                                          "\\)\\($\\|[ \t]\\)")
+                                  cmd)
+                    (shell-process-cd (comint-substitute-in-file-name arg1)))
+                   ((and shell-chdrive-regexp
+                         (string-match (concat "\\`\\(" shell-chdrive-regexp
+                                               "\\)\\($\\|[ \t]\\)")
+                                       cmd))
+                    (shell-process-cd (comint-substitute-in-file-name cmd))))
+             (setq start (progn (string-match shell-command-separator-regexp
+                                              str end)
+                                ;; skip again
                                 (match-end 0)))))
        (error "Couldn't cd"))))
 
                                 (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
+         (quote-chars
+          (if (string-match shell-dumb-shell-regexp
+                            (file-name-nondirectory
+                             (car (process-command (get-buffer-process (current-buffer))))))
+              "['`\"]"
+            "[\\'`\"]")))
+      (while (and (< idx (length string))
+                 (setq next (string-match quote-chars 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)))
     (cond ((and num (= num 0) shell-dirstack)
 ;;; popd [+n]
 (defun shell-process-popd (arg)
   (let ((num (or (shell-extract-num arg) 0)))
     (cond ((and num (= num 0) shell-dirstack)
-          (cd (car shell-dirstack))
+          (shell-cd (car shell-dirstack))
           (setq shell-dirstack (cdr shell-dirstack))
           (shell-dirstack-message))
          ((and num (> num 0) (<= num (length shell-dirstack)))
           (setq shell-dirstack (cdr shell-dirstack))
           (shell-dirstack-message))
          ((and num (> num 0) (<= num (length shell-dirstack)))
@@ -487,7 +697,7 @@ Environment variables are expanded, see function `substitute-in-file-name'."
     (if (file-name-absolute-p dir)
        ;; The name is absolute, so prepend the prefix.
        (concat comint-file-name-prefix dir)
     (if (file-name-absolute-p dir)
        ;; The name is absolute, so prepend the prefix.
        (concat comint-file-name-prefix dir)
-      ;; For relative name we assume default-directory already has the prefix.
+      ;; For relative name we assume default-directory already has the prefix.
       (expand-file-name dir))))
 
 ;;; cd [dir]
       (expand-file-name dir))))
 
 ;;; cd [dir]
@@ -497,7 +707,7 @@ Environment variables are expanded, see function `substitute-in-file-name'."
                       ((string-equal "-" arg) shell-last-dir)
                       (t (shell-prefixed-directory-name arg)))))
     (setq shell-last-dir default-directory)
                       ((string-equal "-" arg) shell-last-dir)
                       (t (shell-prefixed-directory-name arg)))))
     (setq shell-last-dir default-directory)
-    (cd new-dir)
+    (shell-cd new-dir)
     (shell-dirstack-message)))
 
 ;;; pushd [+n | dir]
     (shell-dirstack-message)))
 
 ;;; pushd [+n | dir]
@@ -509,9 +719,8 @@ Environment variables are expanded, see function `substitute-in-file-name'."
                  (shell-process-pushd (concat comint-file-name-prefix "~")))
                 (shell-dirstack
                  (let ((old default-directory))
                  (shell-process-pushd (concat comint-file-name-prefix "~")))
                 (shell-dirstack
                  (let ((old default-directory))
-                   (cd (car shell-dirstack))
-                   (setq shell-dirstack
-                         (cons old (cdr shell-dirstack)))
+                   (shell-cd (car shell-dirstack))
+                   (setq shell-dirstack (cons old (cdr shell-dirstack)))
                    (shell-dirstack-message)))
                 (t
                  (message "Directory stack empty."))))
                    (shell-dirstack-message)))
                 (t
                  (message "Directory stack empty."))))
@@ -520,12 +729,12 @@ Environment variables are expanded, see function `substitute-in-file-name'."
           (cond ((> num (length shell-dirstack))
                  (message "Directory stack not that deep."))
                 ((= num 0)
           (cond ((> num (length shell-dirstack))
                  (message "Directory stack not that deep."))
                 ((= num 0)
-                 (error (message "Couldn't cd.")))
+                 (error (message "Couldn't cd")))
                 (shell-pushd-dextract
                  (let ((dir (nth (1- num) shell-dirstack)))
                    (shell-process-popd arg)
                    (shell-process-pushd default-directory)
                 (shell-pushd-dextract
                  (let ((dir (nth (1- num) shell-dirstack)))
                    (shell-process-popd arg)
                    (shell-process-pushd default-directory)
-                   (cd dir)
+                   (shell-cd dir)
                    (shell-dirstack-message)))
                 (t
                  (let* ((ds (cons default-directory shell-dirstack))
                    (shell-dirstack-message)))
                 (t
                  (let* ((ds (cons default-directory shell-dirstack))
@@ -533,13 +742,13 @@ Environment variables are expanded, see function `substitute-in-file-name'."
                         (front (nthcdr num ds))
                         (back (reverse (nthcdr (- dslen num) (reverse ds))))
                         (new-ds (append front back)))
                         (front (nthcdr num ds))
                         (back (reverse (nthcdr (- dslen num) (reverse ds))))
                         (new-ds (append front back)))
-                   (cd (car new-ds))
+                   (shell-cd (car new-ds))
                    (setq shell-dirstack (cdr new-ds))
                    (shell-dirstack-message)))))
          (t
           ;; pushd <dir>
           (let ((old-wd default-directory))
                    (setq shell-dirstack (cdr new-ds))
                    (shell-dirstack-message)))))
          (t
           ;; pushd <dir>
           (let ((old-wd default-directory))
-            (cd (shell-prefixed-directory-name arg))
+            (shell-cd (shell-prefixed-directory-name arg))
             (if (or (null shell-pushd-dunique)
                     (not (member old-wd shell-dirstack)))
                 (setq shell-dirstack (cons old-wd shell-dirstack)))
             (if (or (null shell-pushd-dunique)
                     (not (member old-wd shell-dirstack)))
                 (setq shell-dirstack (cons old-wd shell-dirstack)))
@@ -551,19 +760,29 @@ Environment variables are expanded, see function `substitute-in-file-name'."
        (string-to-int str)))
 
 
        (string-to-int str)))
 
 
-(defun shell-dirtrack-toggle ()
+(defun shell-dirtrack-mode ()
   "Turn directory tracking on and off in a shell buffer."
   (interactive)
   "Turn directory tracking on and off in a shell buffer."
   (interactive)
-  (setq shell-dirtrackp (not shell-dirtrackp))
+  (if (setq shell-dirtrackp (not shell-dirtrackp))
+      (setq list-buffers-directory default-directory)
+    (setq list-buffers-directory nil))
   (message "Directory tracking %s" (if shell-dirtrackp "ON" "OFF")))
 
 ;;; For your typing convenience:
   (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'."
+  (if shell-dirtrackp
+      (setq list-buffers-directory (file-name-as-directory
+                                   (expand-file-name dir))))
+  (cd dir))
 
 (defun shell-resync-dirs ()
   "Resync the buffer's idea of the current directory stack.
 
 (defun shell-resync-dirs ()
   "Resync the buffer's idea of the current directory stack.
-This command queries the shell with the command bound to 
+This command queries the shell with the command bound to
 `shell-dirstack-query' (default \"dirs\"), reads the next
 line output and parses it to form the new directory stack.
 DON'T issue this command unless the buffer is at a shell prompt.
 `shell-dirstack-query' (default \"dirs\"), reads the next
 line output and parses it to form the new directory stack.
 DON'T issue this command unless the buffer is at a shell prompt.
@@ -577,18 +796,22 @@ command again."
     (goto-char pmark)
     (insert shell-dirstack-query) (insert "\n")
     (sit-for 0) ; force redisplay
     (goto-char pmark)
     (insert shell-dirstack-query) (insert "\n")
     (sit-for 0) ; force redisplay
-    (comint-send-string proc shell-dirstack-query) 
+    (comint-send-string proc shell-dirstack-query)
     (comint-send-string proc "\n")
     (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)
     (comint-send-string proc "\n")
     (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"))
+      (while (not (looking-at
+                  (concat "\\(" ; skip literal echo in case of stty echo
+                          (regexp-quote shell-dirstack-query)
+                          "\n\\)?" ; skip if present
+                          "\\(" ".+\n" "\\)")) ) ; what to actually look for
        (accept-process-output proc)
        (goto-char pt)))
     (goto-char pmark) (delete-char 1) ; remove the extra newline
     ;; That's the dirlist. grab it & parse it.
        (accept-process-output proc)
        (goto-char pt)))
     (goto-char pmark) (delete-char 1) ; remove the extra newline
     ;; That's the dirlist. grab it & parse it.
-    (let* ((dl (buffer-substring (match-beginning 0) (1- (match-end 0))))
+    (let* ((dl (buffer-substring (match-beginning 2) (1- (match-end 2))))
           (dl-len (length dl))
           (ds '())                     ; new dir stack
           (i 0))
           (dl-len (length dl))
           (ds '())                     ; new dir stack
           (i 0))
@@ -602,10 +825,11 @@ command again."
        (setq i (match-end 0)))
       (let ((ds (nreverse ds)))
        (condition-case nil
        (setq i (match-end 0)))
       (let ((ds (nreverse ds)))
        (condition-case nil
-           (progn (cd (car ds))
-                  (setq shell-dirstack (cdr ds))
+           (progn (shell-cd (car ds))
+                  (setq shell-dirstack (cdr ds)
+                        shell-last-dir (car shell-dirstack))
                   (shell-dirstack-message))
                   (shell-dirstack-message))
-         (error (message "Couldn't cd.")))))))
+         (error (message "Couldn't cd")))))))
 
 ;;; For your typing convenience:
 (defalias 'dirs 'shell-resync-dirs)
 
 ;;; For your typing convenience:
 (defalias 'dirs 'shell-resync-dirs)
@@ -617,25 +841,58 @@ command again."
 ;;; All the commands that mung the buffer's dirstack finish by calling
 ;;; this guy.
 (defun shell-dirstack-message ()
 ;;; 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 msg)))
-\f
+  (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))))
+
+;; 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)))
+
 (defun shell-forward-command (&optional arg)
   "Move forward across ARG shell command(s).  Does not cross lines.
 See `shell-command-regexp'."
 (defun shell-forward-command (&optional arg)
   "Move forward across ARG shell command(s).  Does not cross lines.
 See `shell-command-regexp'."
@@ -651,15 +908,14 @@ See `shell-command-regexp'."
 See `shell-command-regexp'."
   (interactive "p")
   (let ((limit (save-excursion (comint-bol nil) (point))))
 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)
        (progn (goto-char (match-beginning 1))
               (skip-chars-forward ";&|")))))
 
     (skip-syntax-backward " " limit)
     (if (re-search-backward
         (format "[;&|]+[\t ]*\\(%s\\)" shell-command-regexp) limit 'move arg)
        (progn (goto-char (match-beginning 1))
               (skip-chars-forward ";&|")))))
 
-
 (defun shell-dynamic-complete-command ()
   "Dynamically complete the command at point.
 This function is similar to `comint-dynamic-complete-filename', except that it
 (defun shell-dynamic-complete-command ()
   "Dynamically complete the command at point.
 This function is similar to `comint-dynamic-complete-filename', except that it
@@ -685,36 +941,37 @@ Returns t if successful."
   "Dynamically complete at point as a command.
 See `shell-dynamic-complete-filename'.  Returns t if successful."
   (let* ((filename (or (comint-match-partial-filename) ""))
   "Dynamically complete at point as a command.
 See `shell-dynamic-complete-filename'.  Returns t if successful."
   (let* ((filename (or (comint-match-partial-filename) ""))
-        (pathnondir (file-name-nondirectory filename))
-        (paths (cdr (reverse exec-path)))
+        (filenondir (file-name-nondirectory filename))
+        (path-dirs (cdr (reverse exec-path)))
         (cwd (file-name-as-directory (expand-file-name default-directory)))
         (ignored-extensions
          (and comint-completion-fignore
               (mapconcat (function (lambda (x) (concat (regexp-quote x) "$")))
                          comint-completion-fignore "\\|")))
         (cwd (file-name-as-directory (expand-file-name default-directory)))
         (ignored-extensions
          (and comint-completion-fignore
               (mapconcat (function (lambda (x) (concat (regexp-quote x) "$")))
                          comint-completion-fignore "\\|")))
-        (path "") (comps-in-path ()) (file "") (filepath "") (completions ()))
-    ;; Go thru each path in the search path, finding completions.
-    (while paths
-      (setq path (file-name-as-directory (comint-directory (or (car paths) ".")))
-           comps-in-path (and (file-accessible-directory-p path)
-                              (file-name-all-completions pathnondir path)))
+        (dir "") (comps-in-dir ()) 
+        (file "") (abs-file-name "") (completions ()))
+    ;; Go thru each dir in the search path, finding completions.
+    (while path-dirs
+      (setq dir (file-name-as-directory (comint-directory (or (car path-dirs) ".")))
+           comps-in-dir (and (file-accessible-directory-p dir)
+                             (file-name-all-completions filenondir dir)))
       ;; Go thru each completion found, to see whether it should be used.
       ;; Go thru each completion found, to see whether it should be used.
-      (while comps-in-path
-       (setq file (car comps-in-path)
-             filepath (concat path file))
+      (while comps-in-dir
+       (setq file (car comps-in-dir)
+             abs-file-name (concat dir file))
        (if (and (not (member file completions))
                 (not (and ignored-extensions
                           (string-match ignored-extensions file)))
        (if (and (not (member file completions))
                 (not (and ignored-extensions
                           (string-match ignored-extensions file)))
-                (or (string-equal path cwd)
-                    (not (file-directory-p filepath)))
+                (or (string-equal dir cwd)
+                    (not (file-directory-p abs-file-name)))
                 (or (null shell-completion-execonly)
                 (or (null shell-completion-execonly)
-                    (file-executable-p filepath)))
+                    (file-executable-p abs-file-name)))
            (setq completions (cons file completions)))
            (setq completions (cons file completions)))
-       (setq comps-in-path (cdr comps-in-path)))
-      (setq paths (cdr paths)))
+       (setq comps-in-dir (cdr comps-in-dir)))
+      (setq path-dirs (cdr path-dirs)))
     ;; OK, we've got a list of completions.
     (let ((success (let ((comint-completion-addsuffix nil))
     ;; OK, we've got a list of completions.
     (let ((success (let ((comint-completion-addsuffix nil))
-                    (comint-dynamic-simple-complete pathnondir completions))))
+                    (comint-dynamic-simple-complete filenondir completions))))
       (if (and (memq success '(sole shortest)) comint-completion-addsuffix
               (not (file-directory-p (comint-match-partial-filename))))
          (insert " "))
       (if (and (memq success '(sole shortest)) comint-completion-addsuffix
               (not (file-directory-p (comint-match-partial-filename))))
          (insert " "))
@@ -722,7 +979,7 @@ See `shell-dynamic-complete-filename'.  Returns t if successful."
 
 
 (defun shell-match-partial-variable ()
 
 
 (defun shell-match-partial-variable ()
-  "Return the variable at point, or nil if non is found."
+  "Return the shell variable at point, or nil if none is found."
   (save-excursion
     (let ((limit (point)))
       (if (re-search-backward "[^A-Za-z0-9_{}]" nil 'move)
   (save-excursion
     (let ((limit (point)))
       (if (re-search-backward "[^A-Za-z0-9_{}]" nil 'move)
@@ -794,32 +1051,20 @@ Returns t if successful."
        (let ((stack (cons default-directory shell-dirstack))
              (index (cond ((looking-at "=-/?")
                            (length shell-dirstack))
        (let ((stack (cons default-directory shell-dirstack))
              (index (cond ((looking-at "=-/?")
                            (length shell-dirstack))
-                          ((looking-at "=\\([0-9]+\\)")
+                          ((looking-at "=\\([0-9]+\\)/?")
                            (string-to-number
                             (buffer-substring
                              (match-beginning 1) (match-end 1)))))))
          (cond ((null index)
                 nil)
                ((>= index (length stack))
                            (string-to-number
                             (buffer-substring
                              (match-beginning 1) (match-end 1)))))))
          (cond ((null index)
                 nil)
                ((>= index (length stack))
-                (error "Directory stack not that deep."))
+                (error "Directory stack not that deep"))
                (t
                 (replace-match (file-name-as-directory (nth index stack)) t t)
                 (message "Directory item: %d" index)
                 t))))))
                (t
                 (replace-match (file-name-as-directory (nth index stack)) t t)
                 (message "Directory item: %d" index)
                 t))))))
-\f
-(defun shell-strip-ctrl-m (&optional string)
-  "Strip trailing `^M' characters from the current output group.
 
 
-This function could be in the list `comint-output-filter-functions' or bound to
-a key."
-  (interactive)
-  (let ((pmark (process-mark (get-buffer-process (current-buffer)))))
-    (save-excursion
-      (goto-char
-       (if (interactive-p) comint-last-input-end comint-last-output-start))
-      (while (re-search-forward "\r+$" pmark t)
-       (replace-match "" t t)))))
-\f
 (provide 'shell)
 
 (provide 'shell)
 
+;;; arch-tag: bcb5f12a-c1f4-4aea-a809-2504bd5bd797
 ;;; shell.el ends here
 ;;; shell.el ends here