]> code.delx.au - gnu-emacs/blobdiff - lisp/shell.el
Initial revision.
[gnu-emacs] / lisp / shell.el
index 369e0ac14d05f9ffdd1959274a6b99e3892510f6..0a4119f0f8d361266ec2c5d3085f86c667c981dc 100644 (file)
-;;; shell.el --- specialized comint.el for running the shell.
-;;; Copyright (C) 1988, 1993, 1994 Free Software Foundation, Inc.
+;;; shell.el --- specialized comint.el for running the shell
+
+;; 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
 
 ;; Keywords: processes
 
-;;; This file is part of GNU Emacs.
+;; This file is part of GNU Emacs.
 
 
-;;; GNU Emacs is free software; you can redistribute it and/or modify
-;;; it under the terms of the GNU General Public License as published by
-;;; the Free Software Foundation; either version 2, or (at your option)
-;;; any later version.
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
 
 
-;;; GNU Emacs is distributed in the hope that it will be useful,
-;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;;; GNU General Public License for more details.
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; 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.
+;; 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, 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 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-a     comint-bol                      Beginning of line; skip prompt
-;;; c-d            comint-delchar-or-maybe-eof     Delete char unless at end of buff.
-;;; 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
-;;;
-;;; 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))
+      "~/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.")
 
@@ -180,51 +196,86 @@ 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 "\\((.*)\\|[^;&|]\\)+"
-  "*Regexp to match shell commands.
-Elements of pipes are considered as separate commands, forks and redirections
-as part of one command.")
+(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)
 
 
-(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.")
+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).
 
   "*If non-nil, expand input command history references on completion.
 This mirrors the optional behavior of tcsh (its autoexpand and histlit).
 
@@ -234,7 +285,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.
@@ -246,56 +302,69 @@ Thus, this does not include the shell's current directory.")
 (defvar shell-last-dir nil
   "Keep track of last directory for ksh `cd -' command.")
 
 (defvar shell-last-dir nil
   "Keep track of last directory for ksh `cd -' command.")
 
-(defvar shell-dirstack-query "dirs"
+(defvar shell-dirstack-query nil
   "Command used by `shell-resync-dir' to query the shell.")
 
 (defvar shell-mode-map nil)
 (cond ((not shell-mode-map)
   "Command used by `shell-resync-dir' to query the shell.")
 
 (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)
-       ;; Undefine the general completion first.
-       (define-key shell-mode-map [menu-bar completion complete] nil)
-       (define-key shell-mode-map [menu-bar completion expand-directory]
-        '("Expand Directory Reference" . shell-replace-by-expanded-directory))
-       (define-key shell-mode-map [menu-bar completion complete-env-variable]
-        '("Complete Env. Variable Name" .
-          shell-dynamic-complete-environment-variable))
-       (define-key shell-mode-map [menu-bar completion complete-command]
-        '("Complete Command Name" . shell-dynamic-complete-command))
-       ;; Redefine (requires a new key) so that it is at the top.
-       (define-key shell-mode-map [menu-bar completion complete-shell]
-        '("Complete Before Point" . comint-dynamic-complete))
-       ))
-
-(defvar shell-mode-hook '()
-  "*Hook for customising Shell mode.")
-
-\f
+       (define-key shell-mode-map [menu-bar completion]
+        (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)
+        'complete-file)
+       (define-key-after (lookup-key shell-mode-map [menu-bar completion])
+        [expand-directory] '("Expand Directory Reference" .
+                             shell-replace-by-expanded-directory)
+        'complete-expand)))
+
+(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.")
+
 ;;; 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
-    the shell.  This is useful for entering passwords.
+\\[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'.
+
+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
@@ -303,10 +372,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
@@ -322,40 +391,74 @@ 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 (car (process-command (get-buffer-process (current-buffer))))))
+  (let ((shell (file-name-nondirectory (car
+                (process-command (get-buffer-process (current-buffer)))))))
     (setq comint-input-ring-file-name
          (or (getenv "HISTFILE")
     (setq comint-input-ring-file-name
          (or (getenv "HISTFILE")
-             (cond ((string-match "csh$" shell) "~/.history")
-                   ((string-match "bash$" shell) "~/.bash_history")
-                   ((string-match "ksh$" shell) "~/.sh_history")
-                   (t "~/.history")))))
-  (run-hooks 'shell-mode-hook)
+             (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"))))
   (comint-read-input-ring t))
   (comint-read-input-ring t))
-\f
+
+(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.
@@ -366,31 +469,46 @@ 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: " "*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"))))
+      (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
@@ -417,7 +535,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
@@ -435,39 +553,77 @@ 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 "^[; \t]*" 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 "[; \t]*" 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)))
@@ -480,12 +636,14 @@ Environment variables are expanded, see function `substitute-in-file-name'."
           (error "Couldn't popd")))))
 
 ;; Return DIR prefixed with comint-file-name-prefix as appropriate.
           (error "Couldn't popd")))))
 
 ;; Return DIR prefixed with comint-file-name-prefix as appropriate.
-(defsubst shell-prefixed-directory-name (dir)
-  (if (file-name-absolute-p dir)
-      ;; The name is absolute, so prepend the prefix.
-      (concat comint-file-name-prefix dir)
-    ;; For a relative name we assume default-directory already has the prefix.
-    (expand-file-name dir)))
+(defun shell-prefixed-directory-name (dir)
+  (if (= (length comint-file-name-prefix) 0)
+      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.
+      (expand-file-name dir))))
 
 ;;; cd [dir]
 (defun shell-process-cd (arg)
 
 ;;; cd [dir]
 (defun shell-process-cd (arg)
@@ -494,7 +652,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]
@@ -506,9 +664,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."))))
@@ -517,12 +674,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))
@@ -530,13 +687,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)))
@@ -548,19 +705,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.
@@ -574,7 +741,7 @@ 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
     (comint-send-string proc "\n")
     (set-marker pmark (point))
     (let ((pt (point))) ; wait for 1 line
@@ -599,10 +766,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)
@@ -614,31 +782,64 @@ 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'."
   (interactive "p")
   (let ((limit (save-excursion (end-of-line nil) (point))))
 (defun shell-forward-command (&optional arg)
   "Move forward across ARG shell command(s).  Does not cross lines.
 See `shell-command-regexp'."
   (interactive "p")
   (let ((limit (save-excursion (end-of-line nil) (point))))
-    (if (re-search-forward (concat shell-command-regexp "\\([;&|][\\s ]*\\)+")
+    (if (re-search-forward (concat shell-command-regexp "\\([;&|][\t ]*\\)+")
                           limit 'move arg)
        (skip-syntax-backward " "))))
 
                           limit 'move arg)
        (skip-syntax-backward " "))))
 
@@ -648,11 +849,11 @@ 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
     (skip-syntax-backward " " limit)
     (if (re-search-backward
-        (format "[;&|]+[\\s ]*\\(%s\\)" shell-command-regexp) limit 'move arg)
+        (format "[;&|]+[\t ]*\\(%s\\)" shell-command-regexp) limit 'move arg)
        (progn (goto-char (match-beginning 1))
               (skip-chars-forward ";&|")))))
 
        (progn (goto-char (match-beginning 1))
               (skip-chars-forward ";&|")))))
 
@@ -681,9 +882,7 @@ Returns t if successful."
 (defun shell-dynamic-complete-as-command ()
   "Dynamically complete at point as a command.
 See `shell-dynamic-complete-filename'.  Returns t if successful."
 (defun shell-dynamic-complete-as-command ()
   "Dynamically complete at point as a command.
 See `shell-dynamic-complete-filename'.  Returns t if successful."
-  (let* ((completion-ignore-case nil)
-        (success t)
-        (filename (or (comint-match-partial-filename) ""))
+  (let* ((filename (or (comint-match-partial-filename) ""))
         (pathnondir (file-name-nondirectory filename))
         (paths (cdr (reverse exec-path)))
         (cwd (file-name-as-directory (expand-file-name default-directory)))
         (pathnondir (file-name-nondirectory filename))
         (paths (cdr (reverse exec-path)))
         (cwd (file-name-as-directory (expand-file-name default-directory)))
@@ -702,7 +901,8 @@ See `shell-dynamic-complete-filename'.  Returns t if successful."
        (setq file (car comps-in-path)
              filepath (concat path file))
        (if (and (not (member file completions))
        (setq file (car comps-in-path)
              filepath (concat path file))
        (if (and (not (member file completions))
-                (not (string-match ignored-extensions file))
+                (not (and ignored-extensions
+                          (string-match ignored-extensions file)))
                 (or (string-equal path cwd)
                     (not (file-directory-p filepath)))
                 (or (null shell-completion-execonly)
                 (or (string-equal path cwd)
                     (not (file-directory-p filepath)))
                 (or (null shell-completion-execonly)
@@ -711,43 +911,16 @@ See `shell-dynamic-complete-filename'.  Returns t if successful."
        (setq comps-in-path (cdr comps-in-path)))
       (setq paths (cdr paths)))
     ;; OK, we've got a list of completions.
        (setq comps-in-path (cdr comps-in-path)))
       (setq paths (cdr paths)))
     ;; OK, we've got a list of completions.
-    (cond ((null completions)
-          (message "No completions of %s" filename)
-          (setq success nil))
-         ((= 1 (length completions))   ; Gotcha!
-          (let ((completion (car completions)))
-            (if (string-equal completion pathnondir)
-                (message "Sole completion")
-              (insert (substring (directory-file-name completion)
-                                 (length pathnondir)))
-              (message "Completed"))
-            (if comint-completion-addsuffix
-                (insert (if (file-directory-p completion) "/" " ")))))
-         (t                            ; There's no unique completion.
-          (let ((completion
-                 (try-completion pathnondir (mapcar (function (lambda (x)
-                                                                (list x)))
-                                                    completions))))
-            ;; Insert the longest substring.
-            (insert (substring (directory-file-name completion)
-                               (length pathnondir)))
-            (cond ((and comint-completion-recexact comint-completion-addsuffix
-                        (string-equal pathnondir completion)
-                        (member completion completions))
-                   ;; It's not unique, but user wants shortest match.
-                   (insert (if (file-directory-p completion) "/" " "))
-                   (message "Completed shortest"))
-                  ((or comint-completion-autolist
-                       (string-equal pathnondir completion))
-                   ;; It's not unique, list possible completions.
-                   (comint-dynamic-list-completions completions))
-                  (t
-                   (message "Partially completed"))))))
-    success))
+    (let ((success (let ((comint-completion-addsuffix nil))
+                    (comint-dynamic-simple-complete pathnondir completions))))
+      (if (and (memq success '(sole shortest)) comint-completion-addsuffix
+              (not (file-directory-p (comint-match-partial-filename))))
+         (insert " "))
+      success)))
 
 
 (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)
@@ -815,7 +988,6 @@ Returns t if successful."
   (interactive)
   (if (comint-match-partial-filename)
       (save-excursion
   (interactive)
   (if (comint-match-partial-filename)
       (save-excursion
-       (message "Expanding directory references...")
        (goto-char (match-beginning 0))
        (let ((stack (cons default-directory shell-dirstack))
              (index (cond ((looking-at "=-/?")
        (goto-char (match-beginning 0))
        (let ((stack (cons default-directory shell-dirstack))
              (index (cond ((looking-at "=-/?")
@@ -827,12 +999,12 @@ Returns t if successful."
          (cond ((null index)
                 nil)
                ((>= index (length stack))
          (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
+
 (provide 'shell)
 
 ;;; shell.el ends here
 (provide 'shell)
 
 ;;; shell.el ends here