]> code.delx.au - gnu-emacs/blobdiff - lisp/shell.el
(Converting Representations): Update the description of what
[gnu-emacs] / lisp / shell.el
index 966afc0a6600f45d12b6e9dc10d4a3ed9208c32f..ec52a2f3624cbb5004f78fb18bca6c563eb67bfa 100644 (file)
@@ -1,8 +1,8 @@
-;;; shell.el --- specialized comint.el for running the shell.
+;;; 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> then
+;; Author: Olin Shivers <shivers@cs.cmu.edu>
 ;;     Simon Marshall <simon@gnu.org>
 ;; Maintainer: FSF
 ;; Keywords: processes
@@ -37,7 +37,7 @@
 ;; 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, 
+;; 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.
 
@@ -46,7 +46,7 @@
 ;; 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 
+;; 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?
@@ -58,7 +58,7 @@
 ;; ;; Define M-# to run some strange command:
 ;; (eval-after-load "shell"
 ;;  '(define-key shell-mode-map "\M-#" 'shells-dynamic-spell))
-\f
+
 ;; Brief Command Documentation:
 ;;============================================================================
 ;; Comint Mode Commands: (common to shell and all comint-derived modes)
 ;; compatibility.
 
 ;; Read the rest of this file for more information.
-\f
+
 ;;; Code:
 
 (require 'comint)
 
 ;;;###autoload
 (defcustom shell-dumb-shell-regexp "cmd\\(proxy\\)?\\.exe"
-  "Regexp to match shells that don't save their command history.
-For shells that match this regexp, Emacs will write out the
-command history when the shell finishes."
+  "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.
-This variable is used to initialise `comint-prompt-regexp' in the 
+This variable is used to initialise `comint-prompt-regexp' in the
 shell buffer.
 
 This variable is only used if the variable
@@ -242,7 +244,7 @@ This mirrors the optional behavior of tcsh."
   :group 'shell-directories)
 
 (defcustom shell-chdrive-regexp
-  (if (memq system-type '(ms-dos windows-nt)) 
+  (if (memq system-type '(ms-dos windows-nt))
       ; NetWare allows the five chars between upper and lower alphabetics.
       "[]a-zA-Z^_`\\[\\\\]:"
     nil)
@@ -312,7 +314,8 @@ Thus, this does not include the shell's current directory.")
        (define-key shell-mode-map "\M-?"
         'comint-dynamic-list-filename-completions)
        (define-key shell-mode-map [menu-bar completion]
-        (copy-keymap (lookup-key comint-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)
@@ -332,12 +335,12 @@ Thus, this does not include the shell's current directory.")
     ("^[^ \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 ()
+(define-derived-mode shell-mode comint-mode "Shell"
   "Major mode for interacting with an inferior shell.
 \\[comint-send-input] after the end of the process' output sends the text from
     the end of process to the end of the current line.
@@ -360,7 +363,7 @@ to continue it.
 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 
+\\[dirs] queries the shell and resyncs Emacs' idea of what the current
     directory stack is.
 \\[dirtrack-mode] turns directory tracking on and off.
 
@@ -370,9 +373,9 @@ 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.
 
-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' 
+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',
@@ -389,11 +392,6 @@ Variables `comint-output-filter-functions', a hook, and
 `comint-scroll-to-bottom-on-input' and `comint-scroll-to-bottom-on-output'
 control whether input and output cause the window to scroll to the end of the
 buffer."
-  (interactive)
-  (comint-mode)
-  (setq major-mode 'shell-mode)
-  (setq mode-name "Shell")
-  (use-local-map shell-mode-map)
   (setq comint-prompt-regexp shell-prompt-pattern)
   (setq comint-completion-fignore shell-completion-fignore)
   (setq comint-delimiter-argument-list shell-delimiter-argument-list)
@@ -417,44 +415,46 @@ buffer."
   (make-local-variable 'list-buffers-directory)
   (setq list-buffers-directory (expand-file-name default-directory))
   ;; 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 (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"))))
-  (run-hooks 'shell-mode-hook)
-  (comint-read-input-ring t))
+  (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"))))
+    (comint-read-input-ring t)))
 
 (defun shell-write-history-on-exit (process event)
   "Called when the shell process is stopped.
 
 Writes the input history to a history file
-`comint-comint-input-ring-file-name' using `comint-write-input-ring'
+`comint-input-ring-file-name' using `comint-write-input-ring'
 and inserts a short message in the shell buffer.
 
 This function is a sentinel watching the shell interpreter process.
 Sentinels will always get the two parameters PROCESS and EVENT."
   ;; Write history.
   (comint-write-input-ring)
-  (if (buffer-live-p (process-buffer process))
-      (insert (format "\nProcess %s %s\n" process event))))
-\f
+  (let ((buf (process-buffer process)))
+    (when (buffer-live-p buf)
+      (with-current-buffer buf
+        (insert (format "\nProcess %s %s\n" process event))))))
+
 ;;;###autoload
 (defun shell (&optional buffer)
   "Run an inferior shell, with I/O through BUFFER (which defaults to `*shell*').
@@ -488,31 +488,27 @@ Otherwise, one argument `-i' is passed to the shell.
    (list
     (and current-prefix-arg
         (read-buffer "Shell buffer: " "*shell*"))))
-  (when (null buffer)
-    (setq buffer "*shell*"))
-  (if (not (comint-check-proc buffer))
-      (let* ((prog (or explicit-shell-file-name
-                      (getenv "ESHELL")
-                      (getenv "SHELL")
-                      "/bin/sh"))                   
-            (name (file-name-nondirectory prog))
-            (startfile (concat "~/.emacs_" name))
-            (xargs-name (intern-soft (concat "explicit-" name "-args")))
-            shell-buffer)
-       (save-excursion
-         (set-buffer (apply 'make-comint-in-buffer "shell" buffer prog
-                            (if (file-exists-p startfile) startfile)
-                            (if (and xargs-name (boundp xargs-name))
-                                (symbol-value xargs-name)
-                              '("-i"))))
-         (setq shell-buffer (current-buffer))
-         (shell-mode))
-       (pop-to-buffer shell-buffer))
-    (pop-to-buffer buffer)))
+  (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*")
-\f
+
 ;;; Directory tracking
 ;;;
 ;;; This code provides the shell mode input sentinel
@@ -541,7 +537,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
-;;; 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
@@ -563,7 +559,7 @@ 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',
-and  `shell-popd-regexp', while `shell-pushd-tohome', `shell-pushd-dextract', 
+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'."
@@ -603,9 +599,15 @@ Environment variables are expanded, see function `substitute-in-file-name'."
 (defun shell-unquote-argument (string)
   "Remove all kinds of shell quoting from STRING."
   (save-match-data
-    (let ((idx 0) next inside)
+    (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 "[\\'`\"]" string next)))
+                 (setq next (string-match quote-chars string next)))
        (cond ((= (aref string next) ?\\)
               (setq string (replace-match "" nil nil string))
               (setq next (1+ next)))
@@ -674,7 +676,7 @@ Environment variables are expanded, see function `substitute-in-file-name'."
           (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)
@@ -727,7 +729,7 @@ Environment variables are expanded, see function `substitute-in-file-name'."
 
 (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.
@@ -741,7 +743,7 @@ command again."
     (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
@@ -770,7 +772,7 @@ command again."
                   (setq shell-dirstack (cdr ds)
                         shell-last-dir (car shell-dirstack))
                   (shell-dirstack-message))
-         (error (message "Couldn't cd.")))))))
+         (error (message "Couldn't cd")))))))
 
 ;;; For your typing convenience:
 (defalias 'dirs 'shell-resync-dirs)
@@ -802,7 +804,7 @@ command again."
          (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."
@@ -833,7 +835,7 @@ 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.
 See `shell-command-regexp'."
@@ -857,7 +859,6 @@ See `shell-command-regexp'."
        (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
@@ -992,19 +993,19 @@ Returns t if successful."
        (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))
-                (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))))))
-\f
+
 (provide 'shell)
 
 ;;; shell.el ends here