;;; shell.el --- specialized comint.el for running the shell.
-;; Copyright (C) 1988, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+;; Copyright (C) 1988, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
-;; Author: Olin Shivers <shivers@cs.cmu.edu>
-;; Maintainer: Simon Marshall <simon@gnu.ai.mit.edu>
+;; Author: Olin Shivers <shivers@cs.cmu.edu> then
+;; Simon Marshall <simon@gnu.ai.mit.edu>
+;; Maintainer: FSF
;; Keywords: processes
;; This file is part of GNU Emacs.
;;============================================================================
;; Comint Mode Commands: (common to shell and all comint-derived modes)
;;
-;; m-p comint-previous-input Cycle backwards in input history
-;; m-n comint-next-input Cycle forwards
+;; m-p comint-previous-input Cycle backwards in input history
+;; m-n comint-next-input Cycle forwards
;; m-r comint-previous-matching-input Previous input matching a regexp
;; m-s comint-next-matching-input Next input that matches
-;; m-c-l comint-show-output Show last batch of process output
+;; m-c-l comint-show-output Show last batch of process output
;; return comint-send-input
-;; c-d comint-delchar-or-maybe-eof Delete char unless at end of buff.
+;; c-d comint-delchar-or-maybe-eof Delete char unless at end of buff.
;; c-c c-a comint-bol Beginning of line; skip prompt
-;; c-c c-u comint-kill-input ^u
-;; c-c c-w backward-kill-word ^w
-;; c-c c-c comint-interrupt-subjob ^c
-;; c-c c-z comint-stop-subjob ^z
-;; c-c c-\ comint-quit-subjob ^\
-;; c-c c-o comint-kill-output Delete last batch of process output
-;; c-c c-r comint-show-output Show last batch of process output
+;; c-c c-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
+;; comint-continue-subjob Useful if you accidentally suspend
;; top-level job
;; comint-mode-hook is the comint mode hook.
;; List completions in help buffer
;; m-c-f shell-forward-command Forward a shell command
;; m-c-b shell-backward-command Backward a shell command
-;; dirs Resync the buffer's dir stack
-;; dirtrack-toggle Turn dir tracking on/off
+;; dirs Resync the buffer's dir stack
+;; dirtrack-mode Turn dir tracking on/off
;; comint-strip-ctrl-m Remove trailing ^Ms from output
;;
;; The shell mode hook is shell-mode-hook
;;; Customization and Buffer Variables
+(defgroup shell nil
+ "Running shell from within Emacs buffers"
+ :group 'processes
+ :group 'unix)
+
+(defgroup shell-directories nil
+ "Directory support in shell mode"
+ :group 'shell)
+
+(defgroup shell-faces nil
+ "Faces in shell buffers"
+ :group 'shell)
+
;;;###autoload
-(defvar shell-prompt-pattern "^[^#$%>\n]*[#$%>] *"
+(defcustom shell-prompt-pattern "^[^#$%>\n]*[#$%>] *"
"Regexp to match prompts in the inferior shell.
Defaults to \"^[^#$%>\\n]*[#$%>] *\", which works pretty well.
This variable is used to initialise `comint-prompt-regexp' in the
Shell mode may become confused trying to distinguish prompt from input
on lines which don't start with a prompt.
-This is a fine thing to set in your `.emacs' file.")
+This is a fine thing to set in your `.emacs' file."
+ :type 'regexp
+ :group 'shell)
-(defvar shell-completion-fignore nil
+(defcustom shell-completion-fignore nil
"*List of suffixes to be disregarded during file/command completion.
This variable is used to initialize `comint-completion-fignore' in the shell
buffer. The default is nil, for compatibility with most shells.
Some people like (\"~\" \"#\" \"%\").
-This is a fine thing to set in your `.emacs' file.")
+This is a fine thing to set in your `.emacs' file."
+ :type '(repeat (string :tag "Suffix"))
+ :group 'shell)
(defvar shell-delimiter-argument-list '(?\| ?& ?< ?> ?\( ?\) ?\;)
"List of characters to recognise as separate arguments.
(defvar shell-file-name-chars
(if (memq system-type '(ms-dos windows-nt))
- "~/A-Za-z0-9_^$!#%&{}@`'.()-"
+ "~/A-Za-z0-9_^$!#%&{}@`'.,:()-"
"~/A-Za-z0-9+@:_.$#%,={}-")
"String of characters valid in a file name.
This variable is used to initialize `comint-file-name-chars' in the
(defvar shell-file-name-quote-list
(if (memq system-type '(ms-dos windows-nt))
nil
- (append shell-delimiter-argument-list '(?\ ?\* ?\! ?\" ?\' ?\`)))
+ (append shell-delimiter-argument-list '(?\ ?\* ?\! ?\" ?\' ?\` ?\#)))
"List of characters to quote when in a file name.
This variable is used to initialize `comint-file-name-quote-list' in the
shell buffer. The value may depend on the operating system or shell.
This is a fine thing to set in your `.emacs' file.")
-(defvar shell-command-regexp "[^;&|\n]+"
+(defcustom shell-command-regexp "[^;&|\n]+"
"*Regexp to match a single command within a pipeline.
-This is used for directory tracking and does not do a perfect job.")
+This is used for directory tracking and does not do a perfect job."
+ :type 'regexp
+ :group 'shell)
-(defvar shell-completion-execonly t
+(defcustom shell-completion-execonly t
"*If non-nil, use executable files only for completion candidates.
This mirrors the optional behavior of tcsh.
-Detecting executability of files may slow command completion considerably.")
+Detecting executability of files may slow command completion considerably."
+ :type 'boolean
+ :group 'shell)
-(defvar shell-popd-regexp "popd"
- "*Regexp to match subshell commands equivalent to popd.")
+(defcustom shell-popd-regexp "popd"
+ "*Regexp to match subshell commands equivalent to popd."
+ :type 'regexp
+ :group 'shell-directories)
-(defvar shell-pushd-regexp "pushd"
- "*Regexp to match subshell commands equivalent to pushd.")
+(defcustom shell-pushd-regexp "pushd"
+ "*Regexp to match subshell commands equivalent to pushd."
+ :type 'regexp
+ :group 'shell-directories)
-(defvar shell-pushd-tohome nil
+(defcustom shell-pushd-tohome nil
"*If non-nil, make pushd with no arg behave as \"pushd ~\" (like cd).
-This mirrors the optional behavior of tcsh.")
+This mirrors the optional behavior of tcsh."
+ :type 'boolean
+ :group 'shell-directories)
-(defvar shell-pushd-dextract nil
+(defcustom shell-pushd-dextract nil
"*If non-nil, make \"pushd +n\" pop the nth dir to the stack top.
-This mirrors the optional behavior of tcsh.")
+This mirrors the optional behavior of tcsh."
+ :type 'boolean
+ :group 'shell-directories)
-(defvar shell-pushd-dunique nil
+(defcustom shell-pushd-dunique nil
"*If non-nil, make pushd only add unique directories to the stack.
-This mirrors the optional behavior of tcsh.")
+This mirrors the optional behavior of tcsh."
+ :type 'boolean
+ :group 'shell-directories)
-(defvar shell-cd-regexp "cd"
- "*Regexp to match subshell commands equivalent to cd.")
+(defcustom shell-cd-regexp "cd"
+ "*Regexp to match subshell commands equivalent to cd."
+ :type 'regexp
+ :group 'shell-directories)
-(defvar shell-chdrive-regexp
+(defcustom shell-chdrive-regexp
(if (memq system-type '(ms-dos windows-nt))
; NetWare allows the five chars between upper and lower alphabetics.
"[]a-zA-Z^_`\\[\\\\]:"
nil)
- "*If non-nil, is regexp used to track drive changes.")
-
-(defvar explicit-shell-file-name nil
- "*If non-nil, is file name to use for explicitly requested inferior shell.")
-
-(defvar explicit-csh-args
+ "*If non-nil, is regexp used to track drive changes."
+ :type '(choice regexp
+ (const nil))
+ :group 'shell-directories)
+
+(defcustom shell-dirtrack-verbose t
+ "*If non-nil, show the directory stack following directory change.
+This is effective only if directory tracking is enabled."
+ :type 'boolean
+ :group 'shell-directories)
+
+(defcustom explicit-shell-file-name nil
+ "*If non-nil, is file name to use for explicitly requested inferior shell."
+ :type '(choice (const :tag "None" nil) file)
+ :group 'shell)
+
+(defcustom explicit-csh-args
(if (eq system-type 'hpux)
;; -T persuades HP's csh not to think it is smarter
;; than us about what terminal modes to use.
'("-i" "-T")
'("-i"))
"*Args passed to inferior shell by M-x shell, if the shell is csh.
-Value is a list of strings, which may be nil.")
+Value is a list of strings, which may be nil."
+ :type '(repeat (string :tag "Argument"))
+ :group 'shell)
-(defvar shell-input-autoexpand 'history
+(defcustom shell-input-autoexpand 'history
"*If non-nil, expand input command history references on completion.
This mirrors the optional behavior of tcsh (its autoexpand and histlit).
`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.
shell-replace-by-expanded-directory)
'complete-expand)))
-(defvar shell-mode-hook '()
- "*Hook for customising Shell mode.")
+(defcustom shell-mode-hook '()
+ "*Hook for customising Shell mode."
+ :type 'hook
+ :group 'shell)
(defvar shell-font-lock-keywords
- (list (cons shell-prompt-pattern 'font-lock-keyword-face)
- '("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-comment-face)
- '("^[^ \t\n]+:.*" . font-lock-string-face)
- '("^\\[[1-9][0-9]*\\]" . font-lock-string-face))
+ '((eval . (cons shell-prompt-pattern 'font-lock-warning-face))
+ ("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-comment-face)
+ ("^[^ \t\n]+:.*" . font-lock-string-face)
+ ("^\\[[1-9][0-9]*\\]" . font-lock-string-face))
"Additional expressions to highlight in Shell mode.")
\f
;;; Basic Procedures
+(put 'shell-mode 'mode-class 'special)
+
(defun shell-mode ()
"Major mode for interacting with an inferior shell.
\\[comint-send-input] after the end of the process' output sends the text from
by \\[list-buffers] or \\[mouse-buffer-menu] in the `File' field.
\\[dirs] queries the shell and resyncs Emacs' idea of what the current
directory stack is.
-\\[dirtrack-toggle] turns directory tracking on and off.
+\\[dirtrack-mode] turns directory tracking on and off.
\\{shell-mode-map}
Customization: Entry to this mode runs the hooks on `comint-mode-hook' and
(setq font-lock-defaults '(shell-font-lock-keywords t))
(make-local-variable 'shell-dirstack)
(setq shell-dirstack nil)
+ (make-local-variable 'shell-last-dir)
(setq shell-last-dir nil)
(make-local-variable 'shell-dirtrackp)
(setq shell-dirtrackp t)
(add-hook 'comint-input-filter-functions 'shell-directory-tracker nil t)
(setq comint-input-autoexpand shell-input-autoexpand)
+ ;; This is not really correct, since the shell buffer does not really
+ ;; edit this directory. But it is useful in the buffer list and menus.
(make-local-variable 'list-buffers-directory)
(setq list-buffers-directory (expand-file-name default-directory))
;; shell-dependent assignments.
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.
It watches for cd, pushd and popd commands and sets the buffer's
default directory to track these commands.
-You may toggle this tracking on and off with M-x dirtrack-toggle.
+You may toggle this tracking on and off with M-x dirtrack-mode.
If emacs gets confused, you can resync with the shell with M-x dirs.
See variables `shell-cd-regexp', `shell-chdrive-regexp', `shell-pushd-regexp',
(string-to-int str)))
-(defun shell-dirtrack-toggle ()
+(defun shell-dirtrack-mode ()
"Turn directory tracking on and off in a shell buffer."
(interactive)
(if (setq shell-dirtrackp (not shell-dirtrackp))
(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'."
;;; All the commands that mung the buffer's dirstack finish by calling
;;; this guy.
(defun shell-dirstack-message ()
- (let* ((msg "")
- (ds (cons default-directory shell-dirstack))
- (home (expand-file-name (concat comint-file-name-prefix "~/")))
- (homelen (length home)))
- (while ds
- (let ((dir (car ds)))
- (and (>= (length dir) homelen) (string= home (substring dir 0 homelen))
- (setq dir (concat "~/" (substring dir homelen))))
- ;; Strip off comint-file-name-prefix if present.
- (and comint-file-name-prefix
- (>= (length dir) (length comint-file-name-prefix))
- (string= comint-file-name-prefix
- (substring dir 0 (length comint-file-name-prefix)))
- (setq dir (substring dir (length comint-file-name-prefix)))
- (setcar ds dir))
- (setq msg (concat msg (directory-file-name dir) " "))
- (setq ds (cdr ds))))
- (message "%s" msg)))
+ (when shell-dirtrack-verbose
+ (let* ((msg "")
+ (ds (cons default-directory shell-dirstack))
+ (home (expand-file-name (concat comint-file-name-prefix "~/")))
+ (homelen (length home)))
+ (while ds
+ (let ((dir (car ds)))
+ (and (>= (length dir) homelen)
+ (string= home (substring dir 0 homelen))
+ (setq dir (concat "~/" (substring dir homelen))))
+ ;; Strip off comint-file-name-prefix if present.
+ (and comint-file-name-prefix
+ (>= (length dir) (length comint-file-name-prefix))
+ (string= comint-file-name-prefix
+ (substring dir 0 (length comint-file-name-prefix)))
+ (setq dir (substring dir (length comint-file-name-prefix)))
+ (setcar ds dir))
+ (setq msg (concat msg (directory-file-name dir) " "))
+ (setq ds (cdr ds))))
+ (message "%s" msg))))
+\f
+;; This was mostly copied from shell-resync-dirs.
+(defun shell-snarf-envar (var)
+ "Return as a string the shell's value of environment variable VAR."
+ (let* ((cmd (format "printenv '%s'\n" var))
+ (proc (get-buffer-process (current-buffer)))
+ (pmark (process-mark proc)))
+ (goto-char pmark)
+ (insert cmd)
+ (sit-for 0) ; force redisplay
+ (comint-send-string proc cmd)
+ (set-marker pmark (point))
+ (let ((pt (point))) ; wait for 1 line
+ ;; This extra newline prevents the user's pending input from spoofing us.
+ (insert "\n") (backward-char 1)
+ (while (not (looking-at ".+\n"))
+ (accept-process-output proc)
+ (goto-char pt)))
+ (goto-char pmark) (delete-char 1) ; remove the extra newline
+ (buffer-substring (match-beginning 0) (1- (match-end 0)))))
+
+(defun shell-copy-environment-variable (variable)
+ "Copy the environment variable VARIABLE from the subshell to Emacs.
+This command reads the value of the specified environment variable
+in the shell, and sets the same environment variable in Emacs
+\(what `getenv' in Emacs would return) to that value.
+That value will affect any new subprocesses that you subsequently start
+from Emacs."
+ (interactive (list (read-envvar-name "\
+Copy Shell environment variable to Emacs: ")))
+ (setenv variable (shell-snarf-envar variable)))
\f
(defun shell-forward-command (&optional arg)
"Move forward across ARG shell command(s). Does not cross lines.