comparison lisp/comint/shell.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 0293115a14e9
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; shell.el --- specialized comint.el for running the shell.
2
3 ;; Copyright (C) 1988, 1993, 1994, 1995 Free Software Foundation, Inc.
4
5 ;; Author: Olin Shivers <shivers@cs.cmu.edu>
6 ;; Maintainer: Simon Marshall <simon@gnu.ai.mit.edu>
7 ;; Keywords: processes
8
9 ;; This file is part of XEmacs.
10
11 ;; XEmacs is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; XEmacs is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with XEmacs; see the file COPYING. If not, write to the Free
23 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24
25 ;;; Synched up with: FSF 19.30.
26
27 ;;; Commentary:
28
29 ;;; Please send me bug reports, bug fixes, and extensions, so that I can
30 ;;; merge them into the master source.
31 ;;; - Olin Shivers (shivers@cs.cmu.edu)
32 ;;; - Simon Marshall (simon@gnu.ai.mit.edu)
33
34 ;;; This file defines a a shell-in-a-buffer package (shell mode) built
35 ;;; on top of comint mode. This is actually cmushell with things
36 ;;; renamed to replace its counterpart in Emacs 18. cmushell is more
37 ;;; featureful, robust, and uniform than the Emacs 18 version.
38
39 ;;; Since this mode is built on top of the general command-interpreter-in-
40 ;;; a-buffer mode (comint mode), it shares a common base functionality,
41 ;;; and a common set of bindings, with all modes derived from comint mode.
42 ;;; This makes these modes easier to use.
43
44 ;;; For documentation on the functionality provided by comint mode, and
45 ;;; the hooks available for customising it, see the file comint.el.
46 ;;; For further information on shell mode, see the comments below.
47
48 ;;; Needs fixin:
49 ;;; When sending text from a source file to a subprocess, the process-mark can
50 ;;; move off the window, so you can lose sight of the process interactions.
51 ;;; Maybe I should ensure the process mark is in the window when I send
52 ;;; text to the process? Switch selectable?
53
54 ;; YOUR .EMACS FILE
55 ;;=============================================================================
56 ;; Some suggestions for your .emacs file.
57 ;;
58 ;; ;; Define M-# to run some strange command:
59 ;; (eval-after-load "shell"
60 ;; '(define-key shell-mode-map "\M-#" 'shells-dynamic-spell))
61
62 ;;; Brief Command Documentation:
63 ;;;============================================================================
64 ;;; Comint Mode Commands: (common to shell and all comint-derived modes)
65 ;;;
66 ;;; m-p comint-previous-input Cycle backwards in input history
67 ;;; m-n comint-next-input Cycle forwards
68 ;;; m-r comint-previous-matching-input Previous input matching a regexp
69 ;;; m-s comint-next-matching-input Next input that matches
70 ;;; m-c-l comint-show-output Show last batch of process output
71 ;;; return comint-send-input
72 ;;; c-d comint-delchar-or-maybe-eof Delete char unless at end of buff.
73 ;;; c-c c-a comint-bol Beginning of line; skip prompt
74 ;;; c-c c-u comint-kill-input ^u
75 ;;; c-c c-w backward-kill-word ^w
76 ;;; c-c c-c comint-interrupt-subjob ^c
77 ;;; c-c c-z comint-stop-subjob ^z
78 ;;; c-c c-\ comint-quit-subjob ^\
79 ;;; c-c c-o comint-kill-output Delete last batch of process output
80 ;;; c-c c-r comint-show-output Show last batch of process output
81 ;;; c-c c-h comint-dynamic-list-input-ring List input history
82 ;;; send-invisible Read line w/o echo & send to proc
83 ;;; comint-continue-subjob Useful if you accidentally suspend
84 ;;; top-level job
85 ;;; comint-mode-hook is the comint mode hook.
86
87 ;;; Shell Mode Commands:
88 ;;; shell Fires up the shell process
89 ;;; tab comint-dynamic-complete Complete filename/command/history
90 ;;; m-? comint-dynamic-list-filename-completions
91 ;;; List completions in help buffer
92 ;;; m-c-f shell-forward-command Forward a shell command
93 ;;; m-c-b shell-backward-command Backward a shell command
94 ;;; dirs Resync the buffer's dir stack
95 ;;; dirtrack-toggle Turn dir tracking on/off
96 ;;; comint-strip-ctrl-m Remove trailing ^Ms from output
97 ;;;
98 ;;; The shell mode hook is shell-mode-hook
99 ;;; comint-prompt-regexp is initialised to shell-prompt-pattern, for backwards
100 ;;; compatibility.
101
102 ;;; Read the rest of this file for more information.
103
104 ;;; Customization and Buffer Variables
105 ;;; ===========================================================================
106 ;;;
107
108 ;;; Code:
109
110 (require 'comint)
111
112 ;;;###autoload
113 (defvar shell-prompt-pattern (purecopy "^[^#$%>\n]*[#$%>] *")
114 "Regexp to match prompts in the inferior shell.
115 Defaults to \"^[^#$%>\\n]*[#$%>] *\", which works pretty well.
116 This variable is used to initialise `comint-prompt-regexp' in the
117 shell buffer.
118
119 The pattern should probably not match more than one line. If it does,
120 shell-mode may become confused trying to distinguish prompt from input
121 on lines which don't start with a prompt.
122
123 This is a fine thing to set in your `.emacs' file.")
124
125 (defvar shell-completion-fignore nil
126 "*List of suffixes to be disregarded during file/command completion.
127 This variable is used to initialize `comint-completion-fignore' in the shell
128 buffer. The default is nil, for compatibility with most shells.
129 Some people like (\"~\" \"#\" \"%\").
130
131 This is a fine thing to set in your `.emacs' file.")
132
133 ;jwz: turned this off; it's way too broken.
134 (defvar shell-delimiter-argument-list nil ;'(?\| ?& ?< ?> ?\( ?\) ?\;
135 "List of characters to recognise as separate arguments.
136 This variable is used to initialize `comint-delimiter-argument-list' in the
137 shell buffer. The default is (?\\| ?& ?< ?> ?\\( ?\\) ?\\;).
138
139 This is a fine thing to set in your `.emacs' file.")
140
141 (defvar shell-file-name-quote-list
142 (append shell-delimiter-argument-list '(?\ ?\* ?\! ?\" ?\' ?\`))
143 "List of characters to quote when in a file name.
144 This variable is used to initialize `comint-file-name-quote-list' in the
145 shell buffer. The default is (?\ ?\* ?\! ?\" ?\' ?\`) plus characters
146 in `shell-delimiter-argument-list'.
147
148 This is a fine thing to set in your `.emacs' file.")
149
150 (defvar shell-dynamic-complete-functions
151 '(comint-replace-by-expanded-history
152 shell-dynamic-complete-environment-variable
153 shell-dynamic-complete-command
154 shell-replace-by-expanded-directory
155 comint-dynamic-complete-filename)
156 "List of functions called to perform completion.
157 This variable is used to initialise `comint-dynamic-complete-functions' in the
158 shell buffer.
159
160 This is a fine thing to set in your `.emacs' file.")
161
162 (defvar shell-command-regexp "[^;&|\n]+"
163 "*Regexp to match a single command within a pipeline.
164 This is used for directory tracking and does not do a perfect job.")
165
166 (defvar shell-completion-execonly t
167 "*If non-nil, use executable files only for completion candidates.
168 This mirrors the optional behavior of tcsh.
169
170 Detecting executability of files may slow command completion considerably.")
171
172 (defvar shell-multiple-shells nil
173 "*If non-nil, each time shell mode is invoked, a new shell is made")
174
175 (defvar shell-popd-regexp "popd"
176 "*Regexp to match subshell commands equivalent to popd.")
177
178 (defvar shell-pushd-regexp "pushd"
179 "*Regexp to match subshell commands equivalent to pushd.")
180
181 (defvar shell-pushd-tohome nil
182 "*If non-nil, make pushd with no arg behave as \"pushd ~\" (like cd).
183 This mirrors the optional behavior of tcsh.")
184
185 (defvar shell-pushd-dextract nil
186 "*If non-nil, make \"pushd +n\" pop the nth dir to the stack top.
187 This mirrors the optional behavior of tcsh.")
188
189 (defvar shell-pushd-dunique nil
190 "*If non-nil, make pushd only add unique directories to the stack.
191 This mirrors the optional behavior of tcsh.")
192
193 (defvar shell-cd-regexp "cd"
194 "*Regexp to match subshell commands equivalent to cd.")
195
196 (defvar explicit-shell-file-name nil
197 "*If non-nil, is file name to use for explicitly requested inferior shell.")
198
199 (defvar explicit-csh-args
200 (if (eq system-type 'hpux)
201 ;; -T persuades HP's csh not to think it is smarter
202 ;; than us about what terminal modes to use.
203 '("-i" "-T")
204 '("-i"))
205 "*Args passed to inferior shell by M-x shell, if the shell is csh.
206 Value is a list of strings, which may be nil.")
207
208 (defvar shell-input-autoexpand 'history
209 "*If non-nil, expand input command history references on completion.
210 This mirrors the optional behavior of tcsh (its autoexpand and histlit).
211
212 If the value is `input', then the expansion is seen on input.
213 If the value is `history', then the expansion is only when inserting
214 into the buffer's input ring. See also `comint-magic-space' and
215 `comint-dynamic-complete'.
216
217 This variable supplies a default for `comint-input-autoexpand',
218 for Shell mode only.")
219
220 (defvar shell-dirstack nil
221 "List of directories saved by pushd in this buffer's shell.
222 Thus, this does not include the shell's current directory.")
223
224 (defvar shell-dirtrackp t
225 "Non-nil in a shell buffer means directory tracking is enabled.")
226
227 (defvar shell-last-dir nil
228 "Keep track of last directory for ksh `cd -' command.")
229
230 (defvar shell-dirstack-query nil
231 "Command used by `shell-resync-dirs' to query the shell.")
232
233 (defvar shell-mode-map nil)
234 (if (not shell-mode-map)
235 (let ((map (make-keymap)))
236 (set-keymap-parents map (list comint-mode-map))
237 (set-keymap-name map 'shell-mode-map)
238 (define-key map "\C-c\C-f" 'shell-forward-command)
239 (define-key map "\C-c\C-b" 'shell-backward-command)
240 (define-key map "\t" 'comint-dynamic-complete)
241 (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
242 ;; XEmacs: this is a pretty common operation for those of us
243 ;; who use directory aliases ... someone shoot me if they
244 ;; don't like this binding. Another possibility is C-c C-s
245 ;; but that's way awkward.
246 (define-key map "\M-\C-m" 'shell-resync-dirs)
247 (setq shell-mode-map map)))
248
249 (defvar shell-mode-hook nil
250 "*Hook for customising Shell mode.")
251
252 (defvar shell-font-lock-keywords
253 (list (cons shell-prompt-pattern 'font-lock-keyword-face)
254 '("[ \t]\\([+-][^ \t\n]+\\)" 1 font-lock-comment-face)
255 '("^[^ \t\n]+:.*" . font-lock-string-face)
256 '("^\\[[1-9][0-9]*\\]" . font-lock-string-face))
257 "Additional expressions to highlight in Shell mode.")
258 (put 'shell-mode 'font-lock-defaults '(shell-font-lock-keywords t))
259
260 ;;; Basic Procedures
261 ;;; ===========================================================================
262 ;;;
263
264 (defun shell-mode ()
265 "Major mode for interacting with an inferior shell.
266 \\<shell-mode-map>\\[comint-send-input] after the end of the process' output sends the text from
267 the end of process to the end of the current line.
268 \\[comint-send-input] before end of process output copies the current line minus the
269 prompt to the end of the buffer and sends it (\\[comint-copy-old-input] just copies
270 the current line).
271 \\[send-invisible] reads a line of text without echoing it, and sends it to
272 the shell. This is useful for entering passwords. Or, add the function
273 `comint-watch-for-password-prompt' to `comint-output-filter-functions'.
274
275 If you want to make multiple shell buffers, rename the `*shell*' buffer
276 using \\[rename-buffer] or \\[rename-uniquely] and start a new shell.
277
278 If you want to make shell buffers limited in length, add the function
279 `comint-truncate-buffer' to `comint-output-filter-functions'.
280
281 If you accidentally suspend your process, use \\[comint-continue-subjob]
282 to continue it.
283
284 `cd', `pushd' and `popd' commands given to the shell are watched by Emacs to
285 keep this buffer's default directory the same as the shell's working directory.
286 While directory tracking is enabled, the shell's working directory is displayed
287 by \\[list-buffers] or \\[mouse-buffer-menu] in the `File' field.
288 \\[shell-resync-dirs] queries the shell and resyncs Emacs' idea of what the
289 current directory stack is.
290 \\[shell-dirtrack-toggle] turns directory tracking on and off.
291
292 \\{shell-mode-map}
293 Customization: Entry to this mode runs the hooks on `comint-mode-hook' and
294 `shell-mode-hook' (in that order). Before each input, the hooks on
295 `comint-input-filter-functions' are run. After each shell output, the hooks
296 on `comint-output-filter-functions' are run.
297
298 Variable `shell-multiple-shells' will automatically generate a new shell each
299 time it is invoked.
300
301 Variables `shell-cd-regexp', `shell-pushd-regexp' and `shell-popd-regexp'
302 are used to match their respective commands, while `shell-pushd-tohome',
303 `shell-pushd-dextract' and `shell-pushd-dunique' control the behavior of the
304 relevant command.
305
306 Variables `comint-completion-autolist', `comint-completion-addsuffix',
307 `comint-completion-recexact' and `comint-completion-fignore' control the
308 behavior of file name, command name and variable name completion. Variable
309 `shell-completion-execonly' controls the behavior of command name completion.
310 Variable `shell-completion-fignore' is used to initialise the value of
311 `comint-completion-fignore'.
312
313 Variables `comint-input-ring-file-name' and `comint-input-autoexpand' control
314 the initialisation of the input ring history, and history expansion.
315
316 Variables `comint-output-filter-functions', a hook, and
317 `comint-scroll-to-bottom-on-input' and `comint-scroll-to-bottom-on-output'
318 control whether input and output cause the window to scroll to the end of the
319 buffer."
320 (interactive)
321 (comint-mode)
322 (setq major-mode 'shell-mode)
323 (setq mode-name "Shell")
324 (use-local-map shell-mode-map)
325 (make-local-variable 'comint-prompt-regexp)
326 (setq comint-prompt-regexp shell-prompt-pattern)
327 (setq comint-completion-fignore shell-completion-fignore)
328 (make-local-variable 'comint-delimiter-argument-list)
329 (setq comint-delimiter-argument-list shell-delimiter-argument-list)
330 (make-local-variable 'comint-after-partial-filename-command)
331 (setq comint-after-partial-filename-command 'shell-after-partial-filename)
332 (make-local-variable 'comint-get-current-command)
333 (setq comint-get-current-command 'shell-get-current-command)
334 (make-local-variable 'comint-dynamic-complete-command-command)
335 (setq comint-dynamic-complete-command-command 'shell-dynamic-complete-command)
336 (setq comint-file-name-quote-list shell-file-name-quote-list)
337 (setq comint-dynamic-complete-functions shell-dynamic-complete-functions)
338 (make-local-variable 'paragraph-start)
339 (setq paragraph-start comint-prompt-regexp)
340 (make-local-variable 'shell-dirstack)
341 (setq shell-dirstack nil)
342 (make-local-variable 'shell-last-dir)
343 (setq shell-last-dir nil)
344 (make-local-variable 'shell-dirtrackp)
345 (setq shell-dirtrackp t)
346 (add-hook 'comint-input-filter-functions 'shell-directory-tracker nil t)
347 (setq comint-input-autoexpand shell-input-autoexpand)
348 (make-local-variable 'list-buffers-directory)
349 (setq list-buffers-directory (expand-file-name default-directory))
350 ;; shell-dependent assignments.
351 (let ((shell (file-name-nondirectory (car
352 (process-command (get-buffer-process (current-buffer)))))))
353 (setq comint-input-ring-file-name
354 (or (getenv "HISTFILE")
355 (cond ((string-equal shell "bash") "~/.bash_history")
356 ((string-equal shell "ksh") "~/.sh_history")
357 (t "~/.history"))))
358 (if (or (equal comint-input-ring-file-name "")
359 (equal (file-truename comint-input-ring-file-name) "/dev/null"))
360 (setq comint-input-ring-file-name nil))
361 (setq shell-dirstack-query
362 (if (string-match "^k?sh$" shell) "pwd" "dirs")))
363 (run-hooks 'shell-mode-hook)
364 (comint-read-input-ring t)
365 (shell-dirstack-message))
366
367
368 ;;;###autoload
369 (defun shell ()
370 "Run an inferior shell, with I/O through buffer *shell*.
371 If buffer exists but shell process is not running, make new shell.
372 If buffer exists and shell process is running,
373 just switch to buffer `*shell*'.
374 Program used comes from variable `explicit-shell-file-name',
375 or (if that is nil) from the ESHELL environment variable,
376 or else from SHELL if there is no ESHELL.
377 If a file `~/.emacs_SHELLNAME' exists, it is given as initial input
378 (Note that this may lose due to a timing error if the shell
379 discards input when it starts up.)
380 The buffer is put in Shell mode, giving commands for sending input
381 and controlling the subjobs of the shell. See `shell-mode'.
382 See also the variable `shell-prompt-pattern'.
383
384 The shell file name (sans directories) is used to make a symbol name
385 such as `explicit-csh-args'. If that symbol is a variable,
386 its value is used as a list of arguments when invoking the shell.
387 Otherwise, one argument `-i' is passed to the shell.
388
389 \(Type \\[describe-mode] in the shell buffer for a list of commands.)"
390 (interactive)
391 (let ((buffer "*shell*")
392 (buffer-name (if shell-multiple-shells
393 "*shell*"
394 "shell")))
395 (cond ((or shell-multiple-shells
396 (not (comint-check-proc buffer)))
397 (let* ((prog (or explicit-shell-file-name
398 (getenv "ESHELL")
399 (getenv "SHELL")
400 "/bin/sh"))
401 (name (file-name-nondirectory prog))
402 (startfile (concat "~/.emacs_" name))
403 (xargs-name (intern-soft (concat "explicit-" name "-args"))))
404 (setq buffer (set-buffer (apply 'make-comint buffer-name prog
405 (if (file-exists-p startfile)
406 startfile)
407 (if (and xargs-name
408 (boundp xargs-name))
409 (symbol-value xargs-name)
410 '("-i")))))
411 (shell-mode))))
412 (pop-to-buffer buffer)
413 (if shell-multiple-shells
414 (rename-buffer (generate-new-buffer-name "*shell*")))
415 ))
416
417 ;;; Don't do this when shell.el is loaded, only while dumping.
418 ;;;###autoload (add-hook 'same-window-buffer-names "*shell*")
419
420 ;;; Directory tracking
421 ;;; ===========================================================================
422 ;;; This code provides the shell mode input sentinel
423 ;;; SHELL-DIRECTORY-TRACKER
424 ;;; that tracks cd, pushd, and popd commands issued to the shell, and
425 ;;; changes the current directory of the shell buffer accordingly.
426 ;;;
427 ;;; This is basically a fragile hack, although it's more accurate than
428 ;;; the version in Emacs 18's shell.el. It has the following failings:
429 ;;; 1. It doesn't know about the cdpath shell variable.
430 ;;; 2. It cannot infallibly deal with command sequences, though it does well
431 ;;; with these and with ignoring commands forked in another shell with ()s.
432 ;;; 3. More generally, any complex command is going to throw it. Otherwise,
433 ;;; you'd have to build an entire shell interpreter in emacs lisp. Failing
434 ;;; that, there's no way to catch shell commands where cd's are buried
435 ;;; inside conditional expressions, aliases, and so forth.
436 ;;;
437 ;;; The whole approach is a crock. Shell aliases mess it up. File sourcing
438 ;;; messes it up. You run other processes under the shell; these each have
439 ;;; separate working directories, and some have commands for manipulating
440 ;;; their w.d.'s (e.g., the lcd command in ftp). Some of these programs have
441 ;;; commands that do *not* affect the current w.d. at all, but look like they
442 ;;; do (e.g., the cd command in ftp). In shells that allow you job
443 ;;; control, you can switch between jobs, all having different w.d.'s. So
444 ;;; simply saying %3 can shift your w.d..
445 ;;;
446 ;;; The solution is to relax, not stress out about it, and settle for
447 ;;; a hack that works pretty well in typical circumstances. Remember
448 ;;; that a half-assed solution is more in keeping with the spirit of Unix,
449 ;;; anyway. Blech.
450 ;;;
451 ;;; One good hack not implemented here for users of programmable shells
452 ;;; is to program up the shell w.d. manipulation commands to output
453 ;;; a coded command sequence to the tty. Something like
454 ;;; ESC | <cwd> |
455 ;;; where <cwd> is the new current working directory. Then trash the
456 ;;; directory tracking machinery currently used in this package, and
457 ;;; replace it with a process filter that watches for and strips out
458 ;;; these messages.
459
460 (defun shell-directory-tracker (str)
461 "Tracks cd, pushd and popd commands issued to the shell.
462 This function is called on each input passed to the shell.
463 It watches for cd, pushd and popd commands and sets the buffer's
464 default directory to track these commands.
465
466 You may toggle this tracking on and off with \\[shell-dirtrack-toggle].
467 If emacs gets confused, you can resync with the shell
468 with \\[shell-resync-dirs].
469
470 See variables `shell-cd-regexp', `shell-pushd-regexp', and `shell-popd-regexp',
471 while `shell-pushd-tohome', `shell-pushd-dextract' and `shell-pushd-dunique'
472 control the behavior of the relevant command.
473
474 Environment variables are expanded, see function `substitute-in-file-name'."
475 (if shell-dirtrackp
476 ;; We fail gracefully if we think the command will fail in the shell.
477 (condition-case err
478 (let ((start (progn (string-match "^[; \t]*" str) ; skip whitespace
479 (match-end 0)))
480 end cmd arg1)
481 (while (string-match shell-command-regexp str start)
482 (setq end (match-end 0)
483 cmd (comint-arguments (substring str start end) 0 0)
484 arg1 (comint-arguments (substring str start end) 1 1))
485 (cond ((string-match (concat "\\`\\(" shell-popd-regexp
486 "\\)\\($\\|[ \t]\\)")
487 cmd)
488 (shell-process-popd (substitute-in-file-name arg1)))
489 ((string-match (concat "\\`\\(" shell-pushd-regexp
490 "\\)\\($\\|[ \t]\\)")
491 cmd)
492 (shell-process-pushd (substitute-in-file-name arg1)))
493 ((string-match (concat "\\`\\(" shell-cd-regexp
494 "\\)\\($\\|[ \t]\\)")
495 cmd)
496 (shell-process-cd (substitute-in-file-name arg1))))
497 (setq start (progn (string-match "[; \t]*" str end) ; skip again
498 (match-end 0)))))
499 (error
500 ;; XEmacs change
501 (message nil)
502 (display-error err t)))))
503
504 ;; Like `cd', but prepends comint-file-name-prefix to absolute names.
505 (defun shell-cd-1 (dir dirstack)
506 (if shell-dirtrackp
507 (setq list-buffers-directory (file-name-as-directory
508 (expand-file-name dir))))
509 (condition-case nil
510 (progn (if (file-name-absolute-p dir)
511 (cd-absolute (concat comint-file-name-prefix dir))
512 (cd dir))
513 (setq shell-dirstack dirstack)
514 (shell-dirstack-message))
515 (file-error (message "Couldn't cd."))))
516
517 ;;; popd [+n]
518 (defun shell-process-popd (arg)
519 (let ((num (or (shell-extract-num arg) 0)))
520 (cond ((and num (= num 0) shell-dirstack)
521 (shell-cd-1 (car shell-dirstack) (cdr shell-dirstack)))
522 ((and num (> num 0) (<= num (length shell-dirstack)))
523 (let* ((ds (cons nil shell-dirstack))
524 (cell (nthcdr (1- num) ds)))
525 (rplacd cell (cdr (cdr cell)))
526 (setq shell-dirstack (cdr ds))
527 (shell-dirstack-message)))
528 (t
529 (error "Couldn't popd")))))
530
531 ;; Return DIR prefixed with comint-file-name-prefix as appropriate.
532 (defun shell-prefixed-directory-name (dir)
533 (if (= (length comint-file-name-prefix) 0)
534 dir
535 (if (file-name-absolute-p dir)
536 ;; The name is absolute, so prepend the prefix.
537 (concat comint-file-name-prefix dir)
538 ;; For relative name we assume default-directory already has the prefix.
539 (expand-file-name dir))))
540
541 ;;; cd [dir]
542 (defun shell-process-cd (arg)
543 (let ((new-dir (cond ((zerop (length arg)) (concat comint-file-name-prefix
544 "~"))
545 ((string-equal "-" arg) shell-last-dir)
546 (t (shell-prefixed-directory-name arg)))))
547 (setq shell-last-dir default-directory)
548 (shell-cd-1 new-dir shell-dirstack)))
549
550 ;;; pushd [+n | dir]
551 (defun shell-process-pushd (arg)
552 (let ((num (shell-extract-num arg)))
553 (cond ((zerop (length arg))
554 ;; no arg -- swap pwd and car of stack unless shell-pushd-tohome
555 (cond (shell-pushd-tohome
556 (shell-process-pushd (concat comint-file-name-prefix "~")))
557 (shell-dirstack
558 (let ((old default-directory))
559 (shell-cd-1 (car shell-dirstack)
560 (cons old (cdr shell-dirstack)))))
561 (t
562 (message "Directory stack empty."))))
563 ((numberp num)
564 ;; pushd +n
565 (cond ((> num (length shell-dirstack))
566 (message "Directory stack not that deep."))
567 ((= num 0)
568 (error (message "Couldn't cd.")))
569 (shell-pushd-dextract
570 (let ((dir (nth (1- num) shell-dirstack)))
571 (shell-process-popd arg)
572 (shell-process-pushd default-directory)
573 (shell-cd-1 dir shell-dirstack)))
574 (t
575 (let* ((ds (cons default-directory shell-dirstack))
576 (dslen (length ds))
577 (front (nthcdr num ds))
578 (back (reverse (nthcdr (- dslen num) (reverse ds))))
579 (new-ds (append front back)))
580 (shell-cd-1 (car new-ds) (cdr new-ds))))))
581 (t
582 ;; pushd <dir>
583 (let ((old-wd default-directory))
584 (shell-cd-1 (shell-prefixed-directory-name arg)
585 (if (or (null shell-pushd-dunique)
586 (not (member old-wd shell-dirstack)))
587 (cons old-wd shell-dirstack)
588 shell-dirstack)))))))
589
590 ;; If STR is of the form +n, for n>0, return n. Otherwise, nil.
591 (defun shell-extract-num (str)
592 (and (string-match "^\\+[1-9][0-9]*$" str)
593 (string-to-int str)))
594
595
596 (defun shell-dirtrack-toggle ()
597 "Turn directory tracking on and off in a shell buffer."
598 (interactive)
599 (if (setq shell-dirtrackp (not shell-dirtrackp))
600 (setq list-buffers-directory default-directory)
601 (setq list-buffers-directory nil))
602 (message "Directory tracking %s" (if shell-dirtrackp "ON" "OFF")))
603
604 ;;; For your typing convenience:
605 ;; XEmacs: removed this because then `M-x dir' doesn't complete to `dired'
606 ;;(define-function 'dirtrack-toggle 'shell-dirtrack-toggle)
607
608 (defun shell-cd (dir)
609 "Do normal `cd' to DIR, and set `list-buffers-directory'."
610 (if shell-dirtrackp
611 (setq list-buffers-directory (file-name-as-directory
612 (expand-file-name dir))))
613 (cd dir))
614
615 (defun shell-resync-dirs ()
616 "Resync the buffer's idea of the current directory stack.
617 This command queries the shell with the command bound to
618 `shell-dirstack-query' (default \"dirs\"), reads the next
619 line output and parses it to form the new directory stack.
620 DON'T issue this command unless the buffer is at a shell prompt.
621 Also, note that if some other subprocess decides to do output
622 immediately after the query, its output will be taken as the
623 new directory stack -- you lose. If this happens, just do the
624 command again."
625 (interactive)
626 (let* ((proc (get-buffer-process (current-buffer)))
627 (pmark (process-mark proc)))
628 (goto-char pmark)
629 (insert shell-dirstack-query) (insert "\n")
630 (sit-for 0) ; force redisplay
631 (comint-send-string proc shell-dirstack-query)
632 (comint-send-string proc "\n")
633 (set-marker pmark (point))
634 (let ((pt (point))) ; wait for 1 line
635 ;; This extra newline prevents the user's pending input from spoofing us.
636 (insert "\n") (backward-char 1)
637 (while (not (looking-at ".+\n"))
638 (accept-process-output proc)
639 (goto-char pt)
640 ;; kludge to cope with shells that have "stty echo" turned on.
641 ;; of course this will lose if there is only one dir on the stack
642 ;; and it is named "dirs"... -jwz
643 (if (looking-at "^dirs\r?\n") (delete-region (point) (match-end 0)))
644 ))
645 (goto-char pmark) (delete-char 1) ; remove the extra newline
646 ;; That's the dirlist. grab it & parse it.
647 (let* ((dl (buffer-substring (match-beginning 0) (1- (match-end 0))))
648 (dl-len (length dl))
649 (ds '()) ; new dir stack
650 (i 0))
651 (while (< i dl-len)
652 ;; regexp = optional whitespace, (non-whitespace), optional whitespace
653 (string-match "\\s *\\(\\S +\\)\\s *" dl i) ; pick off next dir
654 (setq ds (cons (concat comint-file-name-prefix
655 (substring dl (match-beginning 1)
656 (match-end 1)))
657 ds))
658 (setq i (match-end 0)))
659 (let ((ds (reverse ds)))
660 (shell-cd-1 (car ds) (cdr ds))))))
661
662 ;;; For your typing convenience:
663 ;; XEmacs: removed this because then `M-x dir' doesn't complete to `dired'
664 ;(define-function 'dirs 'shell-resync-dirs)
665
666 ;; XEmacs addition
667 (defvar shell-dirstack-message-hook nil
668 "Hook to run after a cd, pushd or popd event")
669
670 ;;; Show the current dirstack on the message line.
671 ;;; Pretty up dirs a bit by changing "/usr/jqr/foo" to "~/foo".
672 ;;; (This isn't necessary if the dirlisting is generated with a simple "dirs".)
673 ;;; All the commands that mung the buffer's dirstack finish by calling
674 ;;; this guy.
675 (defun shell-dirstack-message ()
676 (let* ((msg "")
677 (ds (cons default-directory shell-dirstack))
678 (home (format "^%s\\(/\\|$\\)" (regexp-quote (getenv "HOME"))))
679 (prefix (and comint-file-name-prefix
680 ;; XEmacs addition: don't turn "/foo" into "foo" !!
681 (not (= 0 (length comint-file-name-prefix)))
682 (format "^%s\\(/\\|$\\)"
683 (regexp-quote comint-file-name-prefix)))))
684 (while ds
685 (let ((dir (car ds)))
686 (if (string-match home dir)
687 (setq dir (concat "~/" (substring dir (match-end 0)))))
688 ;; Strip off comint-file-name-prefix if present.
689 (and prefix (string-match prefix dir)
690 (setq dir (substring dir (match-end 0)))
691 (setcar ds dir)
692 )
693 (setq msg (concat msg dir " "))
694 (setq ds (cdr ds))))
695 ;; XEmacs change
696 (run-hooks 'shell-dirstack-message-hook)
697 (message msg)))
698
699
700 (defun shell-forward-command (&optional arg)
701 "Move forward across ARG shell command(s). Does not cross lines.
702 See `shell-command-regexp'."
703 (interactive "p")
704 (let ((limit (save-excursion (end-of-line nil) (point))))
705 (if (re-search-forward (concat shell-command-regexp "\\([;&|][\t ]*\\)+")
706 limit 'move arg)
707 (skip-syntax-backward " "))))
708
709
710 (defun shell-backward-command (&optional arg)
711 "Move backward across ARG shell command(s). Does not cross lines.
712 See `shell-command-regexp'."
713 (interactive "p")
714 (let ((limit (save-excursion (comint-bol nil) (point))))
715 (if (> limit (point))
716 (save-excursion (beginning-of-line) (setq limit (point))))
717 (skip-syntax-backward " " limit)
718 (if (re-search-backward
719 (format "[;&|]+[\t ]*\\(%s\\)" shell-command-regexp) limit 'move arg)
720 (progn (goto-char (match-beginning 1))
721 (skip-chars-forward ";&|")))))
722
723
724 (defun shell-dynamic-complete-command ()
725 "Dynamically complete the command at point.
726 This function is similar to `comint-dynamic-complete-filename', except that it
727 searches `exec-path' (minus the trailing emacs library path) for completion
728 candidates. Note that this may not be the same as the shell's idea of the
729 path.
730
731 Completion is dependent on the value of `shell-completion-execonly', plus
732 those that effect file completion. See `shell-dynamic-complete-as-command'.
733
734 Returns t if successful."
735 (interactive)
736 (let ((filename (comint-match-partial-filename)))
737 (if (and filename
738 (save-match-data (not (string-match "[~/]" filename)))
739 (eq (match-beginning 0)
740 (save-excursion (shell-backward-command 1) (point))))
741 (prog2 (message "Completing command name...")
742 (shell-dynamic-complete-as-command)))))
743
744
745 (defun shell-dynamic-complete-as-command ()
746 "Dynamically complete at point as a command.
747 See `shell-dynamic-complete-filename'. Returns t if successful."
748 (let* ((filename (or (comint-match-partial-filename) ""))
749 (pathnondir (file-name-nondirectory filename))
750 (paths (cdr (reverse exec-path)))
751 (cwd (file-name-as-directory (expand-file-name default-directory)))
752 (ignored-extensions
753 (and comint-completion-fignore
754 (mapconcat (function (lambda (x) (concat (regexp-quote x) "$")))
755 comint-completion-fignore "\\|")))
756 (path "") (comps-in-path ()) (file "") (filepath "") (completions ()))
757 ;; Go thru each path in the search path, finding completions.
758 (while paths
759 (setq path (file-name-as-directory (comint-directory (or (car paths) ".")))
760 comps-in-path (and (file-accessible-directory-p path)
761 (file-name-all-completions pathnondir path)))
762 ;; Go thru each completion found, to see whether it should be used.
763 (while comps-in-path
764 (setq file (car comps-in-path)
765 filepath (concat path file))
766 (if (and (not (member file completions))
767 (not (and ignored-extensions
768 (string-match ignored-extensions file)))
769 (or (string-equal path cwd)
770 (not (file-directory-p filepath)))
771 (or (null shell-completion-execonly)
772 (file-executable-p filepath)))
773 (setq completions (cons file completions)))
774 (setq comps-in-path (cdr comps-in-path)))
775 (setq paths (cdr paths)))
776 ;; OK, we've got a list of completions.
777 (let ((success (let ((comint-completion-addsuffix nil))
778 (comint-dynamic-simple-complete pathnondir completions))))
779 (if (and (memq success '(sole shortest)) comint-completion-addsuffix
780 (not (file-directory-p (comint-match-partial-filename))))
781 (insert " "))
782 success)))
783
784
785 (defun shell-match-partial-variable ()
786 "Return the variable at point, or nil if non is found."
787 (save-excursion
788 (let ((limit (point)))
789 (if (re-search-backward "[^A-Za-z0-9_{}]" nil 'move)
790 (or (looking-at "\\$") (forward-char 1)))
791 ;; Anchor the search forwards.
792 (if (or (eolp) (looking-at "[^A-Za-z0-9_{}$]"))
793 nil
794 (re-search-forward "\\$?{?[A-Za-z0-9_]*}?" limit)
795 (buffer-substring (match-beginning 0) (match-end 0))))))
796
797
798 (defun shell-dynamic-complete-environment-variable ()
799 "Dynamically complete the environment variable at point.
800 Completes if after a variable, i.e., if it starts with a \"$\".
801 See `shell-dynamic-complete-as-environment-variable'.
802
803 This function is similar to `comint-dynamic-complete-filename', except that it
804 searches `process-environment' for completion candidates. Note that this may
805 not be the same as the interpreter's idea of variable names. The main problem
806 with this type of completion is that `process-environment' is the environment
807 which Emacs started with. Emacs does not track changes to the environment made
808 by the interpreter. Perhaps it would be more accurate if this function was
809 called `shell-dynamic-complete-process-environment-variable'.
810
811 Returns non-nil if successful."
812 (interactive)
813 (let ((variable (shell-match-partial-variable)))
814 (if (and variable (string-match "^\\$" variable))
815 (prog2 (message "Completing variable name...")
816 (shell-dynamic-complete-as-environment-variable)))))
817
818
819 (defun shell-dynamic-complete-as-environment-variable ()
820 "Dynamically complete at point as an environment variable.
821 Used by `shell-dynamic-complete-environment-variable'.
822 Uses `comint-dynamic-simple-complete'."
823 (let* ((var (or (shell-match-partial-variable) ""))
824 (variable (substring var (or (string-match "[^$({]\\|$" var) 0)))
825 (variables (mapcar (function (lambda (x)
826 (substring x 0 (string-match "=" x))))
827 process-environment))
828 (addsuffix comint-completion-addsuffix)
829 (comint-completion-addsuffix nil)
830 (success (comint-dynamic-simple-complete variable variables)))
831 (if (memq success '(sole shortest))
832 (let* ((var (shell-match-partial-variable))
833 (variable (substring var (string-match "[^$({]" var)))
834 (protection (cond ((string-match "{" var) "}")
835 ((string-match "(" var) ")")
836 (t "")))
837 (suffix (cond ((null addsuffix) "")
838 ((file-directory-p
839 (comint-directory (getenv variable))) "/")
840 (t " "))))
841 (insert protection suffix)))
842 success))
843
844
845 (defun shell-replace-by-expanded-directory ()
846 "Expand directory stack reference before point.
847 Directory stack references are of the form \"=digit\" or \"=-\".
848 See `default-directory' and `shell-dirstack'.
849
850 Returns t if successful."
851 (interactive)
852 (if (comint-match-partial-filename)
853 (save-excursion
854 (goto-char (match-beginning 0))
855 (let ((stack (cons default-directory shell-dirstack))
856 (index (cond ((looking-at "=-/?")
857 (length shell-dirstack))
858 ((looking-at "=\\([0-9]+\\)")
859 (string-to-number
860 (buffer-substring
861 (match-beginning 1) (match-end 1)))))))
862 (cond ((null index)
863 nil)
864 ((>= index (length stack))
865 (error "Directory stack not that deep."))
866 (t
867 (replace-match (file-name-as-directory (nth index stack)) t t)
868 (message "Directory item: %d" index)
869 t))))))
870
871 (provide 'shell)
872
873 ;;; shell.el ends here