[comp.emacs] New process modes for shell, Lisp, Scheme, T, TeX

shivers@centro.soar.cs.cmu.edu (Olin Shivers) (02/10/89)

1. The standard anouncement header
2. The source for comint.el
(The entire package must be split -- otherwise notesfiles sites will not
be able to handle it.)
-------------------------------------------------------------------------------
I have written new gnu-emacs packages that provide shell, inferior lisp,
inferior scheme, and inferior T modes. These packages have the following
advantages over the standard released gnu packages:

- Input history is kept in all modes, traversed with M-p and M-n, just
  like on LispM's and various fancy shells.
- Filename completion and query is available in all modes.
- Keybindings are cross-compatible among all modes.
- Keybindings are compatible with Zwei and Hemlock.
- Directory tracking is more robust in shell mode, and is *only* active in
  shell mode. (Try entering "cd /" to an inferior lisp in the old package:
  Lisp barfs, but the inferior lisp mode goes ahead and changes the 
  buffer's directory.)
- Non-echoed text entry (for entering passwords) is available in all modes.
- Shell and inferior Lisp modes are backwards compatible with the standard
  gnu modes.
- One source for the inferior Lisp mode works in both emacs releases 18.4x and 
  18.5x. This has been the cause of confusing bugs for users who unwittingly
  tried to use an 18.4x version inferior Lisp mode in an 18.5x version emacs,
  and vice-versa.
- A full set of eval/compile-region/defun commands for the inferior Lisp,
  Scheme, and T modes.
- New modes are easily created for new types of processes.
===============================================================================

THE BAD NEWS:
It would be nice if the shell & inferior lisp package, cmushell.el, was
completely plug-compatible with the old package in shell.el -- if you could
just name the new version 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 the new mode (mostly because of the new mode's greater
functionality).  So, unless we are willing to port all of these packages, we
can't have the new shell package be a complete replacement for shell.el --
that is, we can't name the 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. This is what I have 
done. So the mode names and major functions have different names, e.g:
    shell.el    	cmushell.el
    --------    	----------
    M-x shell   	M-x cmushell	    -- Fire up a shell
    M-x run-lisp	M-x cmulisp 	    -- Fire up a lisp
    shell-mode-map  	cmushell-mode-map   -- Keybindings for [cmu]shell mode
All the names have been carefully chosen so that shell.el and cmushell.el 
won't tromp on each other -- that way dbx.el and friends can happily load
in shell.el without breaking the cmushell.el package, and vice versa.

With the exception of M-x cmushell and M-x cmulisp, however, most of the name
changes are invisible to the user. Further, most of the customising 
variables that are common in functionality have the same name:
    inferior-lisp-program, explicit-shell-file-name, et al.
Hook variables are different, so you can customise shell-mode and 
cmushell-mode differently, for instance.

By the way, it is rather easy to port the shell.el-dependent packages to use
the new stuff.  There are fairly complete comments in the relevant source
files showing how to do this.  Note that this backwards-compatibility hassle
*only* affects shell and inferior lisp mode; the other process-in-a-buffer
modes (Scheme, T, etc.) do not have this problem.
===============================================================================

GENERALIA:
The implementation strategy was to factor common process functionality
into a general command interpreter mode -- comint mode -- and then to
build all the specific modes on top. This provides uniform, integrated
functionality and interface across all the derived modes. Comint mode
provides the input history, filename completion and query,
non-echoed text entry, input editing, and process signalling 
(e.g., ^z, ^c, ...) commands. *Any* mode built on comint mode gets
all this stuff automatically. Additionally, comint mode has general
hooks for customising it to specific command interpreters, such as
Lisp, Scheme, csh, ML, etc..

This release includes the following files:
- comint.el 	comint mode
- cmushell.el	cmushell and cmulisp modes, built on comint mode.
- cmuscheme.el  inferior Scheme mode, built on comint mode.
- tea.el    	inferior T mode, built on comint mode.
- tea2.el   	Variant of tea.el
- cmutex.el 	tex-mode.el with rewritten process interaction code.
    	    	Some bugs also fixed.

These packages have been in daily use by a user community of about 10-20
at CMU since August; most bugs have been shaken out. cmutex.el is less
tested. Please notify me of bugs.

The files are *extensively* commented; this should serve as sufficient
documentation. Each file includes suggestions for your .emacs file
in comments at the top. On-line documentation (C-h C-m, C-h C-f, C-h C-v)
is available for modes, commands, and variables.

This source is available on an FSF-style basis: use it any way you like
as long as you don't charge money for it or change the basis of its
availability; I assume no liability for its use.
===============================================================================

INPUT HISTORY:
There are actually two different ways to retrieve old commands for
resubmission to a process. The standard way is to use the input history
mechanism. An internal list is kept in each process buffer of the last n
inputs (default: 30). The commands M-p and M-n move through this list.  This
is similar in functionality to the input history mechanisms provided by the
LispM, Hemlock, and various fancy shells (tcsh, cmucsh, ksh,...).  There is
also a command, bound to C-c r, which searches backwards through the input
history looking for a substring match.

RMS doesn't like this mechanism. He has suggested an input history mechanism
that operates by searching backwards (and forwards) through the buffer for
occurrences of the prompt.  The user can then resubmit the input by hitting
return.

I do not like this mechanism. If the prompt changes dynamically, you can miss
a command. False positives are also annoying. The screen jumps around a lot as
you scroll through your history. If you run a subprogram that has a null
prompt (like dc), prompt search will miss all its inputs. Etc.

However, you may try either of these mechanisms, and go with the style you
prefer. The RMS-style prompt-search stuff is available on M-N and M-P
(meta-shift-n and meta-shift-P); C-c R is bound to a command that searches for
specific commands (analogous to C-c r). If you use this stuff, you will
probably want to sharpen up the regular expression used to match the prompt in
each mode to match your particular prompt -- the default, general regexp used
in shell mode generates too many annoying false positives.  (It's local
variable comint-prompt-regexp -- you should set it in a hook).
===============================================================================

Source code follows. Please report bugs to me: shivers@cs.cmu.edu.
    -Olin
------
;;; -*-Emacs-Lisp-*- General command interpreter in a window stuff
;;; Copyright Olin Shivers (1988).
;;; Please imagine a long, tedious, legalistic 5-page gnu-style copyright
;;; notice appearing here to the effect that you may use this code any
;;; way you like, as long as you don't charge money for it, remove this
;;; notice, or hold me liable for its results.

;;; This hopefully generalises shell mode, lisp mode, tea mode, soar mode,...
;;; Hacked from tea.el and shell.el by Olin Shivers (shivers@cs.cmu.edu). 8/88

;;; This file defines a general command-interpreter-in-a-buffer package
;;; (comint mode). The idea is that you can build specific process-in-a-buffer
;;; modes on top of comint mode -- e.g., lisp, shell, scheme, T, soar, ....
;;; This way, all these specific packages share a common base functionality, 
;;; and a common set of bindings, which makes them easier to use (and
;;; saves code, implementation time, etc., etc.).
;;; 
;;; Several packages are already defined using comint mode:
;;; - The file cmushell.el defines cmushell and cmulisp mode.
;;; Cmushell and cmulisp mode are similar to, and intended to replace,
;;; their counterparts in the standard gnu emacs release (in shell.el). 
;;; These replacements are more featureful, robust, and uniform than the 
;;; released versions. The key bindings in lisp mode are also more compatible
;;; with the bindings of Hemlock and Zwei (the Lisp Machine emacs).
;;;
;;; - The file cmuscheme.el defines inferior-scheme mode.
;;; - The file tea.el tunes scheme and inferior-scheme modes for T.
;;; - The file soar.el tunes lisp and inferior-lisp modes for Soar.

;;; For documentation on the functionality provided by comint mode, and
;;; the hooks available for customising it, see the comments below.
;;; For further information on the standard derived modes (shell, 
;;; inferior-lisp, inferior-scheme, ...), see the relevant source files.

;;; Please send me bug reports, bug fixes, and extensions, so that I can
;;; merge them into the master source.

;;; For hints on converting existing process modes (e.g., tex-mode,
;;; background, dbx, gdb, kermit, prolog, telnet) to use comint-mode
;;; instead of shell-mode, see the notes at the end of this file.

(provide 'comint)


;;; Brief Command Documentation:
;;;============================================================================
;;; Comint Mode Commands: (common to all derived modes, like cmushell & cmulisp
;;; mode)
;;;
;;; m-p	    comint-previous-input    	    Cycle backwards in input history
;;; m-n	    comint-next-input  	    	    Cycle forwards
;;; c-c r   comint-previous-input-matching  Search backwards in input history
;;; 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
;;;
;;; Not bound by default in comint-mode
;;; send-invisible			Read a line w/o echo, and send to proc
;;; (These are bound in shell-mode)
;;; comint-dynamic-complete		Complete filename at point.
;;; comint-dynamic-list-completions	List completions in help buffer.
;;; comint-replace-by-expanded-filename	Expand and complete filename at point;
;;;					replace with expanded/completed name.
;;; comint-kill-subjob			No mercy.
;;; comint-continue-subjob		Send CONT signal to buffer's process
;;;					group. Useful if you accidentally
;;;					suspend your process (with C-c C-z).
;;;
;;; Bound for RMS -- I prefer the input history stuff, but you might like 'em.
;;; m-P	   comint-msearch-input		Search backwards for prompt
;;; m-N    comint-psearch-input		Search forwards for prompt
;;; C-cR   comint-msearch-input-matching Search backwards for prompt & string

;;; comint-mode-hook is the comint mode hook. Basically for your keybindings.
;;; comint-load-hook is run after loading in this package.


;;; Buffer Local Variables:
;;;============================================================================
;;; Comint mode buffer local variables:
;;;     comint-prompt-regexp    - string       comint-bol uses to match prompt.
;;;     comint-last-input-end   - marker       For comint-kill-output command
;;;     input-ring-size         - integer      For the input history
;;;     input-ring              - ring             mechanism
;;;     input-ring-index        - marker           ...
;;;     comint-last-input-match - string           ...
;;;     comint-get-old-input    - function     Hooks for specific 
;;;     comint-input-sentinel   - function         process-in-a-buffer
;;;     comint-input-filter     - function         modes.
(defvar comint-prompt-regexp "^"
  "Regexp to recognise prompts in the inferior process.
Defaults to \"^\", the null string at BOL.

Good choices:
  Canonical Lisp: \"^[^> ]*>+:? *\" (Lucid, franz, kcl, T, cscheme, oaklisp)
  Lucid Common Lisp: \"^\\(>\\|\\(->\\)+\\) *\"
  franz: \"^\\(->\\|<[0-9]*>:\\) *\"
  kcl: \"^>+ *\"
  shell: \"^[^#$%>]*[#$%>] *\"
  T: \"^>+ *\"

This is a good thing to set in mode hooks.")

(defvar input-ring-size 30
  "Size of input history ring.")

;;; Here are the per-interpreter hooks.
(defvar comint-get-old-input (function comint-get-old-input-default)
  "Function that submits old text in comint mode.
This function is called when return is typed while the point is in old text.
It returns the text to be submitted as process input.  The default is
comint-get-old-input-default, which grabs the current line, and strips off
leading text matching comint-prompt-regexp")

(defvar comint-input-sentinel (function ignore)
  "Called on each input submitted to comint mode process by comint-send-input.
Thus it can, for instance, track cd/pushd/popd commands issued to the csh.")

(defvar comint-input-filter
  (function (lambda (str) (not (string-match "\\`\\s *\\'" str))))
  "Predicate for filtering additions to input history.
Only inputs answering true to this function are saved on the input
history list. Default is to save anything that isn't all whitespace")

(defvar comint-mode-hook '()
  "Called upon entry into comint-mode")

(defvar comint-mode-map nil)

(defun comint-mode ()
  "Major mode for interacting with an inferior interpreter.
Interpreter name is same as buffer name, sans the asterisks.
Return at end of buffer sends line as input.
Return not at end copies rest of line to end and sends it.

This mode is typically customised to create inferior-lisp-mode,
shell-mode, etc.. This can be done by setting the hooks
comint-input-sentinel, comint-input-filter, and comint-get-old-input to
appropriate functions, and the variable comint-prompt-regexp to
the appropriate regular expression.

An input history is maintained of size input-ring-size, and
can be accessed with the commands comint-next-input [\\[comint-next-input]] and 
comint-previous-input [\\[comint-previous-input]]. Commands not keybound by
default are send-invisible, comint-dynamic-complete, and 
comint-list-dynamic-completions.
\\{comint-mode-map}
If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it.

Entry to this mode runs the hooks on comint-mode-hook"
  (interactive)
  (let ((old-ring (and (assq 'input-ring (buffer-local-variables))
		       (boundp 'input-ring)
		       input-ring)))
    (kill-all-local-variables)
    (setq major-mode 'comint-mode)
    (setq mode-name "Comint")
    (setq mode-line-process '(": %s"))
    (use-local-map comint-mode-map)
    (make-local-variable 'comint-last-input-end)
    (setq comint-last-input-end (make-marker))
    (make-local-variable 'comint-last-input-match)
    (setq comint-last-input-match "")
    (make-variable-buffer-local 'comint-prompt-regexp) ; Don't set; default
    (make-variable-buffer-local 'input-ring-size)      ; ...to global val.
    (make-local-variable 'input-ring)
    (make-local-variable 'input-ring-index)
    (setq input-ring-index 0)
    (make-variable-buffer-local 'comint-get-old-input)
    (make-variable-buffer-local 'comint-input-sentinel)
    (make-variable-buffer-local 'comint-input-filter)  
    (run-hooks 'comint-mode-hook)
    ;Do this after the hook so the user can mung INPUT-RING-SIZE w/his hook.
    ;The test is so we don't lose history if we run comint-mode twice in
    ;a buffer.
    (setq input-ring (if (ring-p old-ring) old-ring
			 (make-ring input-ring-size)))))

(if comint-mode-map
    nil
  (setq comint-mode-map (make-sparse-keymap))
  (define-key comint-mode-map "\ep" 'comint-previous-input)
  (define-key comint-mode-map "\en" 'comint-next-input)
  (define-key comint-mode-map "\C-m" 'comint-send-input)
  (define-key comint-mode-map "\C-d" 'comint-delchar-or-maybe-eof)
  (define-key comint-mode-map "\C-a" 'comint-bol)
  (define-key comint-mode-map "\C-c\C-u" 'comint-kill-input)
  (define-key comint-mode-map "\C-c\C-w" 'backward-kill-word)
  (define-key comint-mode-map "\C-c\C-c" 'comint-interrupt-subjob)
  (define-key comint-mode-map "\C-c\C-z" 'comint-stop-subjob)
  (define-key comint-mode-map "\C-c\C-\\" 'comint-quit-subjob)
  (define-key comint-mode-map "\C-c\C-o" 'comint-kill-output)
  (define-key comint-mode-map "\C-cr"    'comint-previous-input-matching)
  (define-key comint-mode-map "\C-c\C-r" 'comint-show-output)
  ;;; Here's the prompt-search stuff I installed for RMS to try...
  (define-key comint-mode-map "\eP" 'comint-msearch-input)
  (define-key comint-mode-map "\eN" 'comint-psearch-input)
  (define-key comint-mode-map "\C-cR" 'comint-msearch-input-matching))


;;; This function is used to make a full copy of the comint mode map,
;;; so that client modes won't interfere with each other. This function
;;; isn't necessary in emacs 18.5x, but we keep it around for 18.4x versions.
(defun full-copy-sparse-keymap (km)
  "Recursively copy the sparse keymap KM"
  (cond ((consp km)
	 (cons (full-copy-sparse-keymap (car km))
	       (full-copy-sparse-keymap (cdr km))))
	(t km)))

(defun comint-check-proc (buffer-name)
  "True if there is a process associated w/buffer BUFFER-NAME, and
it is alive (status RUN or STOP)."
  (let ((proc (get-buffer-process buffer-name)))
    (and proc (memq (process-status proc) '(run stop)))))

;;; Note that this guy, unlike shell.el's make-shell, barfs if you pass it ()
;;; for the second argument (program).
(defun make-comint (name program &optional startfile &rest switches)
  (let* ((buffer (get-buffer-create (concat "*" name "*")))
	 (proc (get-buffer-process buffer)))
    ;; If no process, or nuked process, crank up a new one and put buffer in
    ;; comint mode. Otherwise, leave buffer and existing process alone.
    (cond ((or (not proc) (not (memq (process-status proc) '(run stop))))
	   (comint-exec buffer name program startfile switches)
	   (save-excursion
	     (set-buffer buffer)
	     (comint-mode)))) ; Install local vars, mode, keymap, ...
    buffer))

(defun comint-exec (buffer name command startfile switches)
  "Fires up a process in buffer for comint modes.
Blasts any old process running in the buffer. Doesn't set the buffer mode.
You can use this to cheaply run a series of processes in the same comint
buffer."
  (save-excursion
    (set-buffer buffer)
    (let ((proc (get-buffer-process buffer)))	; Blast any old process.
      (if proc (delete-process proc)))
    ;; Crank up a new process
    (let ((proc (apply 'start-process name buffer (concat exec-directory "env")
		       (format "TERMCAP=emacs:co#%d:tc=unknown:"
			       (screen-width))
		       "TERM=emacs" "EMACS=t" "-" command switches)))
      ;; Feed it the startfile.
      (cond (startfile
	     ;;This is guaranteed to wait long enough
	     ;;but has bad results if the comint does not prompt at all
	     ;;	     (while (= size (buffer-size))
	     ;;	       (sleep-for 1))
	     ;;I hope 1 second is enough!
	     (sleep-for 1)
	     (goto-char (point-max))
	     (insert-file-contents startfile)
	     (setq startfile (buffer-substring (point) (point-max)))
	     (delete-region (point) (point-max))
	     (process-send-string proc startfile)))
      ;; Jump to the end, and set the process mark.
      (goto-char (point-max))
      (set-marker (process-mark proc) (point)))
    buffer))
      


;;; Ring Code
;;;============================================================================
;;; This code defines a ring data structure. A ring is a 
;;;     (hd-index tl-index . vector) 
;;; list. You can insert to, remove from, and rotate a ring. When the ring
;;; fills up, insertions cause the oldest elts to be quietly dropped.
;;;
;;; HEAD = index of the newest item on the ring.
;;; TAIL = index of the oldest item on the ring.
;;;
;;; These functions are used by the input history mechanism, but they can
;;; be used for other purposes as well.

(defun ring-p (x) 
  "T if X is a ring; NIL otherwise."
  (and (consp x) (integerp (car x))
       (consp (cdr x)) (integerp (car (cdr x)))
       (vectorp (cdr (cdr x)))))

(defun make-ring (size)
  "Make a ring that can contain SIZE elts"
  (cons 1 (cons 0 (make-vector (+ size 1) nil))))

(defun ring-plus1 (index veclen)
  "INDEX+1, with wraparound"
  (let ((new-index (+ index 1)))
    (if (= new-index veclen) 0 new-index)))

(defun ring-minus1 (index veclen)
  "INDEX-1, with wraparound"
  (- (if (= 0 index) veclen index) 1))

(defun ring-length (ring)
  (let ((hd (car ring)) (tl (car (cdr ring)))  (siz (length (cdr (cdr ring)))))
    (let ((len (if (<= hd tl) (+ 1 (- tl hd)) (+ 1 tl (- siz hd)))))
      (if (= len siz) 0 len))))

(defun ring-empty-p (ring)
  (= 0 (ring-length ring)))

(defun ring-insert (ring item)
  "Insert a new item onto the ring. If the ring is full, dump the oldest
item to make room."       
  (let* ((vec (cdr (cdr ring)))  (len (length vec))
	 (new-hd (ring-minus1 (car ring) len)))
      (setcar ring new-hd)
      (aset vec new-hd item)
      (if (ring-empty-p ring) ;overflow -- dump one off the tail.
	  (setcar (cdr ring) (ring-minus1 (car (cdr ring)) len)))))

(defun ring-remove (ring)
  "Remove the oldest item retained on the ring."
  (if (ring-empty-p ring) (error "Ring empty")
      (let ((tl (car (cdr ring)))  (vec (cdr (cdr ring))))
	(set-car (cdr ring) (ring-minus1 tl (length vec)))
	(aref vec tl))))

;;; This isn't actually used in this package. I just threw it in in case
;;; someone else wanted it. If you want rotating-ring behavior on your history
;;; retrieval (analagous to kill ring behavior), this function is what you
;;; need. I should write the yank-input and yank-pop-input-or-kill to go with
;;; this, and not bind it to a key by default, so it would be available to
;;; people who want to bind it to a key. But who would want it? Blech.
(defun ring-rotate (ring n)
  (if (not (= n 0))
      (if (ring-empty-p ring) ;Is this the right error check?
	  (error "ring empty")
	  (let ((hd (car ring))  (tl (car (cdr ring)))  (vec (cdr (cdr ring))))
	    (let ((len (length vec)))
	      (while (> n 0)
		(setq tl (ring-plus1 tl len))
		(aset ring tl (aref ring hd))
		(setq hd (ring-plus1 hd len))
		(setq n (- n 1)))
	      (while (< n 0)
		(setq hd (ring-minus1 hd len))
		(aset vec hd (aref vec tl))
		(setq tl (ring-minus1 tl len))
		(setq n (- n 1))))
	    (set-car ring hd)
	    (set-car (cdr ring) tl)))))

(defun comint-mod (n m)
  "Returns N mod M. M is positive. Answer is guaranteed to be non-negative, 
and less than m."
  (let ((n (% n m)))
    (if (>= n 0) n
	(+ n
	   (if (>= m 0) m (- m)))))) ; (abs m)

(defun ring-ref (ring index)
  (let ((numelts (ring-length ring)))
    (if (= numelts 0) (error "indexed empty ring")
	(let* ((hd (car ring))  (tl (car (cdr ring)))  (vec (cdr (cdr ring)))
	       (index (comint-mod index numelts))
	       (vec-index (comint-mod (+ index hd) 
				      (length vec))))
	  (aref vec vec-index)))))


;;; Input history retrieval commands
;;; M-p -- previous input    M-n -- next input
;;; C-c r -- previous input matching
;;; ===========================================================================

(defun comint-previous-input (arg)
  "Cycle backwards through input history."
  (interactive "*p")
  (let ((len (ring-length input-ring)))
    (cond ((<= len 0)
	   (message "Empty input ring")
	   (ding))
	  ((not (comint-after-pmark-p))
	   (message "Not after process mark")
	   (ding))
	  (t
	   (cond ((eq last-command 'comint-previous-input)
		  (delete-region (mark) (point))
		  (set-mark (point)))
		 (t                          
		  (setq input-ring-index
			(if (> arg 0) -1
			    (if (< arg 0) 1 0)))
		  (push-mark (point))))
	   (setq input-ring-index (comint-mod (+ input-ring-index arg) len))
	   (message "%d" (1+ input-ring-index))
	   (insert (ring-ref input-ring input-ring-index))
	   (setq this-command 'comint-previous-input))
	  (t (ding)))))
	 
(defun comint-next-input (arg)
  "Cycle forwards through input history."
  (interactive "*p")
  (comint-previous-input (- arg)))

(defvar comint-last-input-match ""
  "Last string searched for by comint input history search, for defaulting.
Buffer local variable.") 

(defun comint-previous-input-matching (str)
  "Searches backwards through input history for substring match"
  (interactive (let ((s (read-from-minibuffer 
			 (format "Command substring (default %s): "
				 comint-last-input-match))))
		 (list (if (string= s "") comint-last-input-match s))))
; (interactive "sCommand substring: ")
  (setq comint-last-input-match str) ; update default
  (let ((str (regexp-quote str))
        (len (ring-length input-ring))
	(n 0))
    (while (and (<= n len) (not (string-match str (ring-ref input-ring n))))
      (setq n (+ n 1)))
    (cond ((<= n len) (comint-previous-input (+ n 1)))
	  (t (error "Not found.")))))

;;; These next three commands are alternatives to the input history commands --
;;; comint-next-input, comint-previous-input and 
;;; comint-previous-input-matching. They search through the process buffer
;;; text looking for occurrences of the prompt. RMS likes them better;
;;; I don't. Bound to M-P, M-N, and C-c R (uppercase P, N, and R) for
;;; now. Try'em out. Go with what you like...

;;; comint-msearch-input-matching prompts for a string, not a regexp.
;;; This could be considered to be the wrong thing. I decided to keep it
;;; simple, and not make the user worry about regexps. This, of course,
;;; limits functionality.

(defun comint-psearch-input ()
  "Search forwards for next occurrence of prompt and skip to end of line.
\(prompt is anything matching regexp comint-prompt-regexp)"
  (interactive)
  (if (re-search-forward comint-prompt-regexp (point-max) t)
      (end-of-line)
      (error "No occurrence of prompt found")))

(defun comint-msearch-input ()
  "Search backwards for previous occurrence of prompt and skip to end of line.
Search starts from beginning of current line."
  (interactive)
  (let ((p (save-excursion
	     (beginning-of-line)
	     (cond ((re-search-backward comint-prompt-regexp (point-min) t)
		    (end-of-line)
		    (point))
		   (t nil)))))
    (if p (goto-char p)
	(error "No occurrence of prompt found"))))

(defun comint-msearch-input-matching (str)
  "Search backwards for occurrence of prompt followed by STRING.
STRING is prompted for, and is NOT a regular expression."
  (interactive (let ((s (read-from-minibuffer 
			 (format "Command (default %s): "
				 comint-last-input-match))))
		 (list (if (string= s "") comint-last-input-match s))))
; (interactive "sCommand: ")
  (setq comint-last-input-match str) ; update default
  (let* ((r (concat comint-prompt-regexp (regexp-quote str)))
	 (p (save-excursion
	      (beginning-of-line)
	      (cond ((re-search-backward r (point-min) t)
		     (end-of-line)
		     (point))
		    (t nil)))))
    (if p (goto-char p)
	(error "No match"))))

(defun comint-send-input () 
  "Send input to process.  After the process output mark, sends all text
from the process mark to point as input to the process.  Before the
process output mark, calls value of variable comint-get-old-input to retrieve
old input, copies it to the end of the buffer, and sends it.  A terminal
newline is also inserted into the buffer and sent to the process.  In either
case, value of variable comint-input-sentinel is called on the input before
sending it.  The input is entered into the input history ring, if value of
variable comint-input-filter returns T when called on the input.

comint-get-old-input, comint-input-sentinel, and comint-input-filter are chosen
according to the command interpreter running in the buffer. E.g.,
If the interpreter is the csh,
    comint-get-old-input is the default: take the current line, discard any
        initial string matching regexp comint-prompt-regexp.
    comint-input-sentinel monitors input for \"cd\", \"pushd\", and \"popd\" 
        commands. When it sees one, it cd's the buffer.
    comint-input-filter is the default: returns T if the input isn't all white
	space.

If the comint is Lucid Common Lisp, 
    comint-get-old-input snarfs the sexp ending at point.
    comint-input-sentinel does nothing.
    comint-input-filter returns NIL if the input matches input-filter-regexp,
        which matches (1) all whitespace (2) :a, :c, etc.

Similarly for Soar, Scheme, etc.."
  (interactive)
  ;; Note that the input string does not include its terminal newline.
  (let ((proc (get-buffer-process (current-buffer))))
    (if (not proc) (error "Current buffer has no process")
	(let* ((pmark (process-mark proc))
	       (pmark-val (marker-position pmark))
	       (input (if (>= (point) pmark-val)
			  (buffer-substring pmark (point))
			  (let ((copy (funcall comint-get-old-input)))
			    (goto-char pmark)
			    (insert copy)
			    copy))))
	  (insert ?\n)
	  (if (funcall comint-input-filter input) (ring-insert input-ring input))
	  (funcall comint-input-sentinel input)
	  (process-send-string proc input)
	  (process-send-string proc "\n")
	  (set-marker (process-mark proc) (point))
	  (set-marker comint-last-input-end (point))))))

(defun comint-get-old-input-default ()
  "Default for comint-get-old-input: take the current line, and discard
any initial text matching comint-prompt-regexp."
  (save-excursion
    (beginning-of-line)
    (comint-skip-prompt)
    (let ((beg (point)))
      (end-of-line)
      (buffer-substring beg (point)))))

(defun comint-skip-prompt ()
  "Skip past the text matching regexp comint-prompt-regexp. 
If this takes us past the end of the current line, don't skip at all."
  (let ((eol (save-excursion (end-of-line) (point))))
    (if (and (looking-at comint-prompt-regexp)
	     (<= (match-end 0) eol))
	(goto-char (match-end 0)))))


(defun comint-after-pmark-p ()
  "Is point after the process output marker?"
  ;; Since output could come into the buffer after we looked at the point
  ;; but before we looked at the process marker's value, we explicitly 
  ;; serialise. This is just because I don't know whether or not emacs
  ;; services input during execution of lisp commands.
  (let ((proc-pos (marker-position
		   (process-mark (get-buffer-process (current-buffer))))))
    (<= proc-pos (point))))

(defun comint-bol (arg)
  "Goes to the beginning of line, then skips past the prompt, if any.
If a prefix argument is given (\\[universal-argument]), then no prompt skip 
-- go straight to column 0.

The prompt skip is done by skipping text matching the regular expression
comint-prompt-regexp, a buffer local variable.

If you don't like this command, reset c-a to beginning-of-line 
in your hook, comint-mode-hook."
  (interactive "P")
  (beginning-of-line)
  (if (null arg) (comint-skip-prompt)))

;;; These two functions are for entering text you don't want echoed or
;;; saved -- typically passwords to ftp, telnet, or somesuch.
;;; Just enter m-x send-invisible and type in your line.

(defun comint-read-noecho (prompt)
  "Prompt the user with argument PROMPT. Read a single line of text
without echoing, and return it. Note that the keystrokes comprising
the text can still be recovered (temporarily) with \\[view-lossage]. This
may be a security bug for some applications."
  (let ((echo-keystrokes 0)
	(answ "")
	tem)
    (if (and (stringp prompt) (not (string= (message prompt) "")))
	(message prompt))
    (while (not(or  (= (setq tem (read-char)) ?\^m)
		    (= tem ?\n)))
      (setq answ (concat answ (char-to-string tem))))
    (message "")
    answ))

(defun send-invisible (str)
  "Read a string without echoing, and send it to the process running
in the current buffer. A new-line is additionally sent. String is not 
saved on comint input history list.
Security bug: your string can still be temporarily recovered with
\\[view-lossage]."
; (interactive (list (comint-read-noecho "Enter non-echoed text")))
  (interactive "P") ; Defeat snooping via C-x esc
  (let ((proc (get-buffer-process (current-buffer))))
    (if (not proc) (error "Current buffer has no process")
	(process-send-string proc
			     (if (stringp str) str
				 (comint-read-noecho "Enter non-echoed text")))
	(process-send-string proc "\n"))))

;;; Random input hackage

(defun comint-kill-output ()
  "Kill all output from interpreter since last input."
  (interactive)
  (let ((pmark (process-mark (get-buffer-process (current-buffer)))))
    (kill-region comint-last-input-end pmark)
    (goto-char pmark)    
    (insert "*** output flushed ***\n")
    (set-marker pmark (point))))

(defun comint-show-output ()
  "Display start of this batch of interpreter output at top of window.
Also put cursor there."
  (interactive)
  (goto-char comint-last-input-end)
  (backward-char)
  (beginning-of-line)
  (set-window-start (selected-window) (point))
  (end-of-line))

(defun comint-interrupt-subjob ()
  "Interrupt the current subjob."
  (interactive)
  (interrupt-process nil t))

(defun comint-kill-subjob ()
  "Send kill signal to the current subjob."
  (interactive)
  (kill-process nil t))

(defun comint-quit-subjob ()
  "Send quit signal to the current subjob."
  (interactive)
  (quit-process nil t))

(defun comint-stop-subjob ()
  "Stop the current subjob.
WARNING: if there is no current subjob, you can end up suspending
the top-level process running in the buffer. If you accidentally do
this, use \\[comint-continue-subjob] to resume the process. (This
is not a problem with most shells, since they ignore this signal.)"
  (interactive)
  (stop-process nil t))

(defun comint-continue-subjob ()
  "Send CONT signal to process buffer's process group.
Useful if you accidentally suspend the top-level process."
  (interactive)
  (continue-process nil t))

(defun comint-kill-input ()
  "Kill all text from last stuff output by interpreter to point."
  (interactive)
  (let* ((pmark (process-mark (get-buffer-process (current-buffer))))
	 (p-pos (marker-position pmark)))
    (if (> (point) p-pos)
	(kill-region pmark (point)))))

(defun comint-delchar-or-maybe-eof (arg)
  "Delete ARG characters forward, or send an EOF to lisp if at end of buffer."
  (interactive "p")
  (if (eobp)
      (process-send-eof)
      (delete-char arg)))




;;; Support for source-file processing commands.
;;;============================================================================
;;; Many command-interpreters (e.g., Lisp, Scheme, Soar) have
;;; commands that process files of source text (e.g. loading or compiling
;;; files). So the corresponding process-in-a-buffer modes have commands
;;; for doing this (e.g., lisp-load-file). The functions below are useful
;;; for defining these commands.
;;;
;;; Alas, these guys don't do exactly the right thing for Lisp, Scheme
;;; and Soar, in that they don't know anything about file extensions.
;;; So the compile/load interface gets the wrong default occasionally.
;;; The load-file/compile-file default mechanism could be smarter -- it
;;; doesn't know about the relationship between filename extensions and
;;; whether the file is source or executable. If you compile foo.lisp
;;; with compile-file, then the next load-file should use foo.bin for
;;; the default, not foo.lisp. This is tricky to do right, particularly
;;; because the extension for executable files varies so much (.o, .bin,
;;; .lbin, .mo, .vo, .ao, ...).


;;; COMINT-SOURCE-DEFAULT -- determines defaults for source-file processing
;;; commands.
;;;
;;; COMINT-CHECK-SOURCE -- if FNAME is in a modified buffer, asks you if you
;;; want to save the buffer before issuing any process requests to the command
;;; interpreter.
;;;
;;; COMINT-GET-SOURCE -- used by the source-file processing commands to prompt
;;; for the file to process.

;;; (COMINT-SOURCE-DEFAULT previous-dir/file source-modes)
;;;============================================================================
;;; This function computes the defaults for the load-file and compile-file
;;; commands for tea, soar, cmulisp, and cmuscheme modes. 
;;; 
;;; - PREVIOUS-DIR/FILE is a pair (directory . filename) from the last 
;;; source-file processing command. NIL if there hasn't been one yet.
;;; - SOURCE-MODES is a list used to determine what buffers contain source
;;; files: if the major mode of the buffer is in SOURCE-MODES, it's source.
;;; Typically, (lisp-mode) or (scheme-mode).
;;; 
;;; If the command is given in a file buffer whose major modes is in
;;; SOURCE-MODES, then the the filename is the default file, and the
;;; file's directory is the default directory.
;;; 
;;; If the buffer isn't a source file buffer (e.g., it's the process buffer),
;;; then the default directory & file are what was used in the last source-file
;;; processing command (i.e., PREVIOUS-DIR/FILE).  If this is the first time
;;; the command has been run (PREVIOUS-DIR/FILE is nil), the default directory
;;; is the cwd, with no default file. (\"no default file\" = nil)
;;; 
;;; SOURCE-REGEXP is typically going to be something like (tea-mode)
;;; for T programs, (lisp-mode) for Lisp programs, (soar-mode lisp-mode)
;;; for Soar programs, etc.
;;; 
;;; The function returns a pair: (default-directory . default-file).

(defun comint-source-default (previous-dir/file source-modes)
  (cond ((and buffer-file-name (memq major-mode source-modes))
	 (cons (file-name-directory    buffer-file-name)
	       (file-name-nondirectory buffer-file-name)))
	(previous-dir/file)
	(t
	 (cons default-directory nil))))


;;; (COMINT-CHECK-SOURCE fname)
;;;============================================================================
;;; Prior to loading or compiling (or otherwise processing) a file (in the CMU
;;; process-in-a-buffer modes), this function can be called on the filename.
;;; If the file is loaded into a buffer, and the buffer is modified, the user
;;; is queried to see if he wants to save the buffer before proceeding with
;;; the load or compile.

(defun comint-check-source (fname)
  (let ((buff (get-file-buffer fname)))
    (if (and buff
	     (buffer-modified-p buff)
	     (y-or-n-p (format "Save buffer %s first? "
			       (buffer-name buff))))
	;; save BUFF.
	(let ((old-buffer (current-buffer)))
	  (set-buffer buff)
	  (save-buffer)
	  (set-buffer old-buffer)))))


;;; (COMINT-GET-SOURCE prompt prev-dir/file source-modes mustmatch-p)
;;;============================================================================
;;; COMINT-GET-SOURCE is used to prompt for filenames in command-interpreter
;;; commands that process source files (like loading or compiling a file).
;;; It prompts for the filename, provides a default, if there is one,
;;; and returns the result filename.
;;; 
;;; See COMINT-SOURCE-DEFAULT for more on determining defaults.
;;; 
;;; PROMPT is the prompt string. PREV-DIR/FILE is the (directory . file) pair
;;; from the last source processing command.  SOURCE-MODES is a list of major
;;; modes used to determine what file buffers contain source files.  (These
;;; two arguments are used for determining defaults). If MUSTMATCH-P is true,
;;; then the filename reader will only accept a file that exists.
;;; 
;;; A typical use:
;;; (interactive (comint-get-source "Compile file: " prev-lisp-dir/file
;;;                                 "\\.lisp\\'" t))

(defun comint-get-source (prompt prev-dir/file source-regexp mustmatch-p)
  (let* ((def (comint-source-default prev-dir/file source-regexp))
	 (defdir  (car def))
	 (deffile (cdr def))
	 (ans (read-file-name (if deffile (format "%s(default %s) "
						  prompt    deffile)
				  prompt)
			      defdir
			      (concat defdir deffile)
			      mustmatch-p)))
    (list (expand-file-name (substitute-in-file-name ans)))))


;;; Filename completion in a buffer
;;; ===========================================================================
;;; Useful completion functions, courtesy of the Ergo group.
;;; M-<Tab> will complete the filename at the cursor as much as possible
;;; M-? will display a list of completions in the help buffer.

;;; Three commands:
;;; comint-dynamic-complete		Complete filename at point.
;;; comint-dynamic-list-completions	List completions in help buffer.
;;; comint-replace-by-expanded-filename	Expand and complete filename at point;
;;;					replace with expanded/completed name.

;;; These are not installed in the comint-mode keymap. But they are
;;; available for people who want them. Shell-mode installs them:
;;; (define-key cmushell-mode-map "\M-\t" 'comint-dynamic-complete)
;;; (define-key cmushell-mode-map "\M-?"  'comint-dynamic-list-completions)))
;;;
;;; Commands like this are fine things to put in load hooks if you
;;; want them present in specific modes. Example:
;;; (setq cmushell-load-hook
;;;       '((lambda () (define-key lisp-mode-map "\M-\t"
;;;				   'comint-replace-by-expanded-filename))))
;;;          


(defun comint-match-partial-pathname ()
  "Returns the string of an existing filename or causes and error."
  (if (save-excursion (backward-char 1) (looking-at "\\s ")) ""
      (save-excursion
	(re-search-backward "[^~/A-Za-z0-9---_.$#,]+")
	(re-search-forward "[~/A-Za-z0-9---_.$#,]+")
	(substitute-in-file-name 
	  (buffer-substring (match-beginning 0) (match-end 0))))))

(defun comint-replace-by-expanded-filename ()
  "Replace filename at point with expanded, completed name"
  (interactive)
  (let* ((pathname (comint-match-partial-pathname))
	 (pathdir (file-name-directory pathname))
	 (pathnondir (file-name-nondirectory pathname))
	 (completion (file-name-completion  pathnondir
					   (or pathdir default-directory))))
    (cond ((null completion)
	   (message "No completions of %s." pathname)
	   (ding))
	  ((eql completion t)
	   (message "Unique completion."))
	  (t				; this means a string was returned.
	   (delete-region (match-beginning 0) (match-end 0))
	   (insert pathdir completion)))))

(defun comint-dynamic-complete ()
  "Dynamically complete the filename at point."
  (interactive)
  (let* ((pathname (comint-match-partial-pathname))
	 (pathdir (file-name-directory pathname))
	 (pathnondir (file-name-nondirectory pathname))
	 (completion (file-name-completion  pathnondir
					   (or pathdir default-directory))))
    (cond ((null completion)
	   (message "No completions of %s." pathname)
	   (ding))
	  ((eql completion t)
	   (message "Unique completion."))
	  (t				; this means a string was returned.
	   (insert (substring completion (length pathnondir)))))))

(defun comint-dynamic-list-completions ()
  "List in help buffer all possible completions of the filename at point."
  (interactive)
  (let* ((pathname (comint-match-partial-pathname))
	 (pathdir (file-name-directory pathname))
	 (pathnondir (file-name-nondirectory pathname))
	 (completions
	  (file-name-all-completions pathnondir
				     (or pathdir default-directory))))
    (cond ((null completions)
	   (message "No completions of %s." pathname)
	   (ding))
	  (t (with-output-to-temp-buffer "*Help*"
	       (display-completion-list completions))))))

; Ergo bindings
; (global-set-key "\M-\t" 'comint-replace-by-expanded-filename)
; (global-set-key "\M-?" 'comint-dynamic-list-completions)
; (define-key shell-mode-map "\M-\t" 'comint-dynamic-complete)

;;; Log the user, so I know who's using the package during the beta test 
;;; period. This just inserts the user's name and current time into a 
;;; central file.
(defun comint-log-user ()
  (interactive)
  (if (file-writable-p "/afs/cs/user/shivers/lib/emacs/logdir/comint.log")
      (let ((u (getenv "USER"))
	    (old-buff (current-buffer)))
	(message "logging user in beta test database...")
	(find-file "/afs/cs/user/shivers/lib/emacs/logdir/comint.log")
	(cond ((search-forward u nil 'to-end)
	       (search-forward "| ")
	       (kill-line 1))
	      (t (insert (format "%s\t%s\t| " u (current-time-string)))))
	(insert (format "%s\n" (current-time-string)))
	(let ((make-backup-files nil)) (save-buffer))
	(kill-buffer (current-buffer))
	(set-buffer old-buff))))
(comint-log-user)


;;; Converting process modes to use comint mode
;;; ===========================================================================
;;; Several gnu packages (tex-mode, background, dbx, gdb, kermit, prolog, 
;;; telnet are some) use the shell package as clients. Most of them would
;;; be better off using the comint package, but they predate it. 
;;;
;;; Altering these packages to use comint mode should greatly
;;; improve their functionality, and is fairly easy.
;;; 
;;; Renaming variables
;;; Most of the work is renaming variables and functions. These are the common
;;; ones:
;;; Local variables:
;;; 	last-input-end		comint-last-input-end
;;;	last-input-start	<unnecessary>
;;;	shell-prompt-pattern	comint-prompt-regexp
;;;     shell-set-directory-error-hook <no equivalent>
;;; Miscellaneous:
;;;	shell-set-directory	<unnecessary>
;;; 	shell-mode-map		comint-mode-map
;;; Commands:
;;;	shell-send-input	comint-send-input
;;;	shell-send-eof		comint-delchar-or-maybe-eof
;;; 	kill-shell-input	comint-kill-input
;;;	interrupt-shell-subjob	comint-interrupt-subjob
;;;	stop-shell-subjob	comint-stop-subjob
;;;	quit-shell-subjob	comint-quit-subjob
;;;	kill-shell-subjob	comint-kill-subjob
;;;	kill-output-from-shell	comint-kill-output
;;;	show-output-from-shell	comint-show-output
;;;	copy-last-shell-input	Use comint-previous-input/comint-next-input
;;;
;;; LAST-INPUT-START is no longer necessary because inputs are stored on the
;;; input history ring. SHELL-SET-DIRECTORY is gone, its functionality taken
;;; over by SHELL-DIRECTORY-TRACKER, the shell mode's comint-input-sentinel.
;;; Comint mode does not provide functionality equivalent to 
;;; shell-set-directory-error-hook; it is gone.
;;; 
;;; If you are implementing some process-in-a-buffer mode, called foo-mode, do
;;; *not* create the comint-mode local variables in your foo-mode function.
;;; This is not modular.  Instead, call comint-mode, and let *it* create the
;;; necessary comint-specific local variables. Then create the
;;; foo-mode-specific local variables in foo-mode.  Set the buffer's keymap to
;;; be foo-mode-map, and it's mode to be foo-mode.  Set the comint-mode hooks
;;; (comint-prompt-regexp, comint-input-filter, comint-input-sentinel,
;;; comint-get-old-input) that need to be different from the defaults.  Call
;;; foo-mode-hook, and you're done. Don't run the comint-mode hook yourself;
;;; comint-mode will take care of it. The following example, from cmushell.el,
;;; is typical:
;;; 
;;; (defun shell-mode ()
;;;   (interactive)
;;;   (comint-mode)
;;;   (setq comint-prompt-regexp shell-prompt-pattern)
;;;   (setq major-mode 'shell-mode)
;;;   (setq mode-name "Shell")
;;;   (cond ((not shell-mode-map)
;;; 	     (setq shell-mode-map (full-copy-sparse-keymap comint-mode-map))
;;; 	     (define-key shell-mode-map "\M-\t" 'comint-dynamic-complete)
;;; 	     (define-key shell-mode-map "\M-?"
;;;                      'comint-dynamic-list-completions)))
;;;   (use-local-map shell-mode-map)
;;;   (make-local-variable 'shell-directory-stack)
;;;   (setq shell-directory-stack nil)
;;;   (setq comint-input-sentinel 'shell-directory-tracker)
;;;   (run-hooks 'shell-mode-hook))
;;;
;;;
;;; Note that make-comint is different from make-shell in that it
;;; doesn't have a default program argument. If you give make-shell
;;; a program name of NIL, it cleverly chooses one of explicit-shell-name,
;;; $ESHELL, $SHELL, or /bin/sh. If you give make-comint a program argument
;;; of NIL, it barfs. Adjust your code accordingly...
;;;

;;; Do the user's customisation...

(defvar comint-load-hook nil
  "This hook is run when comint is loaded in.
This is a good place to put keybindings.")
	
(run-hooks 'comint-load-hook)
-- 

shivers@centro.soar.cs.cmu.edu (Olin Shivers) (02/10/89)

This message contains:
1. The standard anouncement header
2. The source for cmushell.el
(The entire package must be split -- otherwise notesfiles sites will not
be able to handle it.)
-------------------------------------------------------------------------------
I have written new gnu-emacs packages that provide shell, inferior lisp,
inferior scheme, and inferior T modes. These packages have the following
advantages over the standard released gnu packages:

- Input history is kept in all modes, traversed with M-p and M-n, just
  like on LispM's and various fancy shells.
- Filename completion and query is available in all modes.
- Keybindings are cross-compatible among all modes.
- Keybindings are compatible with Zwei and Hemlock.
- Directory tracking is more robust in shell mode, and is *only* active in
  shell mode. (Try entering "cd /" to an inferior lisp in the old package:
  Lisp barfs, but the inferior lisp mode goes ahead and changes the 
  buffer's directory.)
- Non-echoed text entry (for entering passwords) is available in all modes.
- Shell and inferior Lisp modes are backwards compatible with the standard
  gnu modes.
- One source for the inferior Lisp mode works in both emacs releases 18.4x and 
  18.5x. This has been the cause of confusing bugs for users who unwittingly
  tried to use an 18.4x version inferior Lisp mode in an 18.5x version emacs,
  and vice-versa.
- A full set of eval/compile-region/defun commands for the inferior Lisp,
  Scheme, and T modes.
- New modes are easily created for new types of processes.
===============================================================================

THE BAD NEWS:
It would be nice if the shell & inferior lisp package, cmushell.el, was
completely plug-compatible with the old package in shell.el -- if you could
just name the new version 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 the new mode (mostly because of the new mode's greater
functionality).  So, unless we are willing to port all of these packages, we
can't have the new shell package be a complete replacement for shell.el --
that is, we can't name the 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. This is what I have 
done. So the mode names and major functions have different names, e.g:
    shell.el    	cmushell.el
    --------    	----------
    M-x shell   	M-x cmushell	    -- Fire up a shell
    M-x run-lisp	M-x cmulisp 	    -- Fire up a lisp
    shell-mode-map  	cmushell-mode-map   -- Keybindings for [cmu]shell mode
All the names have been carefully chosen so that shell.el and cmushell.el 
won't tromp on each other -- that way dbx.el and friends can happily load
in shell.el without breaking the cmushell.el package, and vice versa.

With the exception of M-x cmushell and M-x cmulisp, however, most of the name
changes are invisible to the user. Further, most of the customising 
variables that are common in functionality have the same name:
    inferior-lisp-program, explicit-shell-file-name, et al.
Hook variables are different, so you can customise shell-mode and 
cmushell-mode differently, for instance.

By the way, it is rather easy to port the shell.el-dependent packages to use
the new stuff.  There are fairly complete comments in the relevant source
files showing how to do this.  Note that this backwards-compatibility hassle
*only* affects shell and inferior lisp mode; the other process-in-a-buffer
modes (Scheme, T, etc.) do not have this problem.
===============================================================================

GENERALIA:
The implementation strategy was to factor common process functionality
into a general command interpreter mode -- comint mode -- and then to
build all the specific modes on top. This provides uniform, integrated
functionality and interface across all the derived modes. Comint mode
provides the input history, filename completion and query,
non-echoed text entry, input editing, and process signalling 
(e.g., ^z, ^c, ...) commands. *Any* mode built on comint mode gets
all this stuff automatically. Additionally, comint mode has general
hooks for customising it to specific command interpreters, such as
Lisp, Scheme, csh, ML, etc..

This release includes the following files:
- comint.el 	comint mode
- cmushell.el	cmushell and cmulisp modes, built on comint mode.
- cmuscheme.el  inferior Scheme mode, built on comint mode.
- tea.el    	inferior T mode, built on comint mode.
- tea2.el   	Variant of tea.el
- cmutex.el 	tex-mode.el with rewritten process interaction code.
    	    	Some bugs also fixed.

These packages have been in daily use by a user community of about 10-20
at CMU since August; most bugs have been shaken out. cmutex.el is less
tested. Please notify me of bugs.

The files are *extensively* commented; this should serve as sufficient
documentation. Each file includes suggestions for your .emacs file
in comments at the top. On-line documentation (C-h C-m, C-h C-f, C-h C-v)
is available for modes, commands, and variables.

This source is available on an FSF-style basis: use it any way you like
as long as you don't charge money for it or change the basis of its
availability; I assume no liability for its use.
===============================================================================

INPUT HISTORY:
There are actually two different ways to retrieve old commands for
resubmission to a process. The standard way is to use the input history
mechanism. An internal list is kept in each process buffer of the last n
inputs (default: 30). The commands M-p and M-n move through this list.  This
is similar in functionality to the input history mechanisms provided by the
LispM, Hemlock, and various fancy shells (tcsh, cmucsh, ksh,...).  There is
also a command, bound to C-c r, which searches backwards through the input
history looking for a substring match.

RMS doesn't like this mechanism. He has suggested an input history mechanism
that operates by searching backwards (and forwards) through the buffer for
occurrences of the prompt.  The user can then resubmit the input by hitting
return.

I do not like this mechanism. If the prompt changes dynamically, you can miss
a command. False positives are also annoying. The screen jumps around a lot as
you scroll through your history. If you run a subprogram that has a null
prompt (like dc), prompt search will miss all its inputs. Etc.

However, you may try either of these mechanisms, and go with the style you
prefer. The RMS-style prompt-search stuff is available on M-N and M-P
(meta-shift-n and meta-shift-P); C-c R is bound to a command that searches for
specific commands (analogous to C-c r). If you use this stuff, you will
probably want to sharpen up the regular expression used to match the prompt in
each mode to match your particular prompt -- the default, general regexp used
in shell mode generates too many annoying false positives.  (It's local
variable comint-prompt-regexp -- you should set it in a hook).
===============================================================================

Source code follows. Please report bugs to me: shivers@cs.cmu.edu.
    -Olin
------
;;; -*-Emacs-Lisp-*- General command interpreter in a window stuff
;;; Copyright Olin Shivers (1988).
;;; Please imagine a long, tedious, legalistic 5-page gnu-style copyright
;;; notice appearing here to the effect that you may use this code any
;;; way you like, as long as you don't charge money for it, remove this
;;; notice, or hold me liable for its results.

;;; This replaces the standard shell and inferior-lisp modes.
;;; Hacked from tea.el and shell.el by Olin Shivers (shivers@cs.cmu.edu). 8/88
;;; Please send me bug reports, bug fixes, and extensions, so that I can
;;; merge them into the master source.

;;; This file defines two packages:
;;; - A shell-in-a-buffer package (cmushell mode) built on top of comint mode.
;;; - A lisp-in-a-buffer package (cmulisp mode) built on top of comint
;;;   mode.
;;; 
;;; Cmushell and cmulisp modes are similar to, and intended to replace,
;;; their counterparts in the standard gnu emacs release. These replacements
;;; are more featureful, robust, and uniform than the released versions.
;;; The key bindings in lisp mode are also more compatible with the bindings
;;; of Hemlock and Zwei (the Lisp Machine emacs).

;;; Since these modes are built on top of the general command-interpreter-in-
;;; a-buffer mode (comint mode), they share a common base functionality, 
;;; and a common set of bindings, with all modes derived from comint mode.
;;; This makes them 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 the cmushell and cmulisp modes, see 
;;; the comments below.

;;; Needs fixin:
;;; The load-file/compile-file default mechanism could be smarter -- it
;;; doesn't know about the relationship between filename extensions and
;;; whether the file is source or executable. If you compile foo.lisp
;;; with compile-file, then the next load-file should use foo.bin for
;;; the default, not foo.lisp. This is tricky to do right, particularly
;;; because the extension for executable files varies so much (.o, .bin,
;;; .lbin, .mo, .vo, .ao, ...).
;;;
;;; It would be nice if cmulisp (and inferior scheme, T, ...) modes
;;; had a verbose minor mode wherein sending or compiling defuns, etc.
;;; would be reflected in the transcript with suitable comments, e.g.
;;; ";;; redefining fact". Several ways to do this. Which is right?
;;;
;;; 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?

(require 'comint)
(provide 'cmushell)

;; YOUR .EMACS FILE
;;=============================================================================
;; Some suggestions for your .emacs file.
;;
;; ; If cmushell lives in some non-standard directory, you must tell emacs
;; ; where to get it. This may or may not be necessary.
;; (setq load-path (cons (expand-file-name "~jones/lib/emacs") load-path))
;;
;; ; Autoload cmushell and cmulisp from file cmushell.el
;; (autoload 'cmushell "cmushell"
;;           "Run an inferior shell process."
;;           t)
;; (autoload 'cmulisp "cmushell"
;;           "Run an inferior lisp process."
;;           t)
;;
;; ; Define C-c C-t to run my favorite command in shell mode:
;; (setq cmushell-load-hook
;;       '((lambda () 
;;           (define-key cmushell-mode-map "\C-c\C-t" 'favorite-cmd))))


;;; Brief Command Documentation:
;;;============================================================================
;;; Comint Mode Commands: (common to cmushell and cmulisp modes)
;;;
;;; m-p	    comint-previous-input    	    Cycle backwards in input history
;;; m-n	    comint-next-input  	    	    Cycle forwards
;;; c-c r   comint-previous-input-matching  Search backwards in input history
;;; 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
;;;         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.

;;; CMU Lisp Mode Commands:
;;; c-m-x   lisp-send-defun     This binding is a gnu convention.
;;; c-c l   lisp-load-file  	Prompt for file name; tell Lisp to load it.
;;; c-c k   lisp-compile-file	Prompt for file name; tell Lisp to kompile it.
;;; 	    	    	    	Filename completion is available, of course.
;;;
;;; Additionally, these commands are added to the key bindings of Lisp mode:
;;; c-m-x   lisp-eval-defun         This binding is a gnu convention.
;;; c-c e   lisp-eval-defun 	    Send the current defun to Lisp process.
;;; c-c c-e lisp-eval-defun-and-go  After sending the defun, switch-to-lisp.
;;; c-c r   lisp-eval-region        Send the current region to Lisp process.
;;; c-c c-r lisp-eval-region-and-go After sending the region, switch-to-lisp.
;;; c-c c   lisp-compile-defun      Compile the current defun in Lisp process.
;;; c-c c-c lisp-compile-defun-and-go After compiling defun, switch-to-lisp.
;;; c-c z   switch-to-lisp          Switch to the Lisp process buffer.
;;; c-c l   lisp-load-file          (See above. In a lisp file buffer, default
;;; c-c k   lisp-compile-file        is to load/compile the current file.)
;;; cmulisp	    	    	    Fires up the Lisp process.
;;; lisp-compile-region     	    Compile all forms in the current region.
;;; lisp-compile-region-and-go      After compiling region, switch-to-lisp.
;;;
;;; CMU Lisp Mode Variables:
;;; cmulisp-filter-regexp	    Match this => don't get saved on input hist
;;; inferior-lisp-program   	    Name of Lisp program run-lisp executes
;;; inferior-lisp-load-command	    Customises lisp-load-file
;;; cmulisp-mode-hook  	    
;;; inferior-lisp-prompt	    Initialises comint-prompt-regexp.
;;;				    Backwards compatibility.
;;; lisp-source-modes               Anything loaded into a buffer that's in
;;;                                 one of these modes is considered lisp 
;;;                                 source by lisp-load/compile-file.

;;; Shell Mode Commands:
;;;         cmushell			    Fires up the shell process.
;;; m-tab   comint-dynamic-complete	    Complete a partial file name
;;; m-?     comint-dynamic-list-completions List completions in help buffer
;;;
;;; The cmushell mode hook is cmushell-mode-hook
;;; The cmushell-load-hook is run after this file is loaded.
;;; comint-prompt-regexp is initialised to shell-mode-pattern, for backwards
;;; compatibility.

;;; Read the rest of this file for more information.

;;; SHELL.EL COMPATIBILITY
;;;============================================================================
;;; In brief: this package should have no trouble coexisting with shell.el.
;;; 
;;; Most customising variables -- e.g., inferior-lisp-program,
;;; 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.

;;; Lisp stuff
;;;============================================================================
;;;

(defvar cmulisp-filter-regexp "\\`\\s *\\(:\\(\\w\\|\\s_\\)\\)?\\s *\\'"
  "*What not to save on inferior Lisp's input history
Input matching this regexp is not saved on the input history in cmulisp
mode. Default is whitespace followed by 0 or 1 single-letter :keyword 
(as in :a, :c, etc.)")

(defvar cmulisp-mode-map nil)
(cond ((not cmulisp-mode-map)
       (setq cmulisp-mode-map
	     (full-copy-sparse-keymap comint-mode-map))
       (lisp-mode-commands cmulisp-mode-map)
       (define-key cmulisp-mode-map "\C-cl"    'lisp-load-file)
       (define-key cmulisp-mode-map "\C-ck"    'lisp-compile-file)))

;;; These commands augment lisp mode, so you can process lisp code in
;;; the source files.
(define-key lisp-mode-map "\M-\C-x"  'lisp-eval-defun) ; Gnu convention
(define-key lisp-mode-map "\C-ce"    'lisp-eval-defun)
(define-key lisp-mode-map "\C-c\C-e" 'lisp-eval-defun-and-go)
(define-key lisp-mode-map "\C-cr"    'lisp-eval-region)
(define-key lisp-mode-map "\C-c\C-r" 'lisp-eval-region-and-go)
(define-key lisp-mode-map "\C-cc"    'lisp-compile-defun)
(define-key lisp-mode-map "\C-c\C-c" 'lisp-compile-defun-and-go)
(define-key lisp-mode-map "\C-cz"    'switch-to-lisp)
(define-key lisp-mode-map "\C-cl"    'lisp-load-file)
(define-key lisp-mode-map "\C-ck"    'lisp-compile-file)  ; "kompile" file

(defvar inferior-lisp-program "lisp"
  "*Program name for invoking an inferior Lisp with `cmulisp'.")

(defvar inferior-lisp-load-command "(load \"%s\")\n"
  "*Format-string for building a Lisp expression to load a file.
This format string should use %s to substitute a file name
and should result in a Lisp expression that will command the inferior Lisp
to load that file.  The default works acceptably on most Lisps.
The string \"(progn (load \\\"%s\\\" :verbose nil :print t) (values))\\\n\"
produces cosmetically superior output for this application,
but it works only in Common Lisp.")

(defvar inferior-lisp-prompt "^[^> ]*>+:? *"
  "Regexp to recognise prompts in the inferior Lisp.
Defaults to \"^[^> ]*>+:? *\", which works pretty good for Lucid, kcl,
and franz. This variable is used to initialise comint-prompt-regexp in the 
cmulisp buffer.

More precise choices:
Lucid Common Lisp: \"^\\(>\\|\\(->\\)+\\) *\"
franz: \"^\\(->\\|<[0-9]*>:\\) *\"
kcl: \"^>+ *\"

This is a fine thing to set in your .emacs file.")

(defvar cmulisp-mode-hook '()
  "*Hook for customising cmulisp mode")

(defun cmulisp-mode () 
  "Major mode for interacting with an inferior Lisp process.  
Runs a Lisp interpreter as a subprocess of Emacs, with Lisp I/O through an
Emacs buffer.  Variable inferior-lisp-program controls which Lisp interpreter
is run.  Variables inferior-lisp-prompt, cmulisp-filter-regexp and
inferior-lisp-load-command can customize this mode for different Lisp
interpreters.

\\{cmulisp-mode-map}

Customisation: Entry to this mode runs the hooks on comint-mode-hook and
cmulisp-mode-hook (in that order).

You can send text to the inferior Lisp process from other buffers containing
Lisp source.  
    switch-to-lisp switches the current buffer to the Lisp process buffer.
    lisp-eval-defun sends the current defun to the Lisp process.
    lisp-compile-defun compiles the current defun.
    lisp-eval-region sends the current region to the Lisp process.
    lisp-compile-region compiles the current region.

    lisp-eval-defun-and-go, lisp-compile-defun-and-go,
        lisp-eval-region-and-go, and lisp-compile-region-and-go
        switch to the Lisp process buffer after sending their text.

Commands:
Return after the end of the process' output sends the text from the 
    end of process to point.
Return before the end of the process' output copies the sexp ending at point
    to the end of the process' output, and sends it.
Delete converts tabs to spaces as it moves back.
Tab indents for Lisp; with argument, shifts rest
    of expression rigidly with the current line.
C-M-q does Tab on each line starting within following expression.
Paragraphs are separated only by blank lines.  Semicolons start comments.
If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it."
  (interactive)
  (comint-mode)
  (setq comint-prompt-regexp inferior-lisp-prompt)
  (setq major-mode 'cmulisp-mode)
  (setq mode-name "CMU Lisp")
  (setq mode-line-process '(": %s"))
  (if (string-match "^18.4" emacs-version) ; hack.
      (lisp-mode-variables)    ; This is right for 18.49  
      (lisp-mode-variables t)) ; This is right for 18.50
  (use-local-map cmulisp-mode-map)    ;c-c k for "kompile" file
  (setq comint-get-old-input (function lisp-get-old-input))
  (setq comint-input-filter (function lisp-input-filter))
  (setq comint-input-sentinel 'ignore)
  (run-hooks 'cmulisp-mode-hook))

(defun lisp-get-old-input ()
  "Snarf the sexp ending at point"
  (save-excursion
    (let ((end (point)))
      (backward-sexp)
      (buffer-substring (point) end))))

(defun lisp-input-filter (str)
  "Don't save anything matching cmulisp-filter-regexp"
  (not (string-match cmulisp-filter-regexp str)))

(defun cmulisp ()
  "Run an inferior Lisp process, input and output via buffer *cmulisp*.
If there is a process already running in *cmulisp*, just switch to that buffer.
Takes the program name from the variable inferior-lisp-program.
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
  (interactive)
  (cond ((not (comint-check-proc "*cmulisp*"))
	 (set-buffer (make-comint "cmulisp" inferior-lisp-program))
	 (cmulisp-mode)))
  (switch-to-buffer "*cmulisp*"))

(defun lisp-eval-region (start end)
  "Send the current region to the inferior Lisp process."
  (interactive "r")
  (send-region "cmulisp" start end)
  (send-string "cmulisp" "\n"))

(defun lisp-eval-defun ()
  "Send the current defun to the inferior Lisp process."
  (interactive)
  (save-excursion
   (end-of-defun)
   (let ((end (point)))
     (beginning-of-defun)
     (lisp-eval-region (point) end))))

;;; CommonLisp COMPILE sux. 
(defun lisp-compile-region (start end)
  "Compile the current region in the inferior Lisp process."
  (interactive "r")
  (save-excursion
    (process-send-string "cmulisp"
      (format "(funcall (compile nil `(lambda () (progn 'compile %s))))\n"
	      (buffer-substring start end)))))
			 
(defun lisp-compile-defun ()
  "Compile the current defun in the inferior Lisp process."
  (interactive)
  (save-excursion
    (end-of-defun)
    (let ((e (point)))
      (beginning-of-defun)
      (lisp-compile-region (point) e))))

(defun switch-to-lisp (eob-p)
  "Switch to the inferior Lisp process buffer.
With argument, positions cursor at end of buffer."
  (interactive "P")
  (pop-to-buffer "*cmulisp*")
  (cond (eob-p
	 (push-mark)
	 (goto-char (point-max)))))

(defun lisp-eval-region-and-go (start end)
  "Send the current region to the inferior Lisp, 
and switch to the process buffer."
  (interactive "r")
  (lisp-eval-region start end)
  (switch-to-lisp t))

(defun lisp-eval-defun-and-go ()
  "Send the current defun to the inferior Lisp, 
and switch to the process buffer."
  (interactive)
  (lisp-eval-defun)
  (switch-to-lisp t))

(defun lisp-compile-region-and-go (start end)
  "Compile the current region in the inferior Lisp, 
and switch to the process buffer."
  (interactive "r")
  (lisp-compile-region start end)
  (switch-to-lisp t))

(defun lisp-compile-defun-and-go ()
  "Compile the current defun in the inferior Lisp, 
and switch to the process buffer."
  (interactive)
  (lisp-compile-defun)
  (switch-to-lisp t))

;;; A version of the form in H. Shevis' soar-mode.el package. Less robust.
;(defun lisp-compile-sexp (start end)
;  "Compile the s-expression bounded by START and END in the inferior lisp.
;If the sexp isn't a DEFUN form, it is evaluated instead."
;    (cond ((looking-at "(defun\\s +")
;	   (goto-char (match-end 0))
;	   (let ((name-start (point)))
;	     (forward-sexp 1)
;	     (process-send-string "cmulisp" (format "(compile '%s #'(lambda "
;						 (buffer-substring name-start
;								   (point)))))
;	   (let ((body-start (point)))
;	     (goto-char start) (forward-sexp 1) ; Can't use end-of-defun.
;	     (process-send-region "cmulisp" (buffer-substring body-start (point))))
;	   (process-send-string "cmulisp" ")\n"))
;	  (t (lisp-eval-region start end)))))
;
;(defun lisp-compile-region (start end)
;  "Each s-expression in the current region is compiled (if a DEFUN)
;or evaluated (if not) in the inferior lisp."
;  (interactive "r")
;  (save-excursion
;    (goto-char start) (end-of-defun) (beginning-of-defun) ; error check
;    (if (< (point) start) (error "region begins in middle of defun"))
;    (goto-char start)
;    (let ((s start))
;      (end-of-defun)
;      (while (<= (point) end) ; Zip through
;	(lisp-compile-sexp s (point)) ; compiling up defun-sized chunks.
;	(setq s (point))
;	(end-of-defun))
;      (if (< s end) (lisp-compile-sexp s end)))))
;;;
;;; End of HS-style code


(defvar lisp-prev-l/c-dir/file nil
  "Saves the (directory . file) pair used in the last lisp-load-file or
lisp-compile-file command. Used for determining the default in the 
next one.")

(defvar lisp-source-modes '(lisp-mode)
  "*Used to determine if a buffer contains Lisp source code.
If it's loaded into a buffer that is in one of these major modes, it's
considered a lisp source file by lisp-load-file and lisp-compile-file.
Used by these commands to determine defaults.")

(defun lisp-load-file (file-name)
  "Load a lisp file into the inferior Lisp process."
  (interactive (comint-get-source "Load Lisp file: " lisp-prev-l/c-dir/file
				  lisp-source-modes nil)) ; NIL because LOAD
                                                   ; doesn't need an exact name
  (comint-check-source file-name) ; Check to see if buffer needs saved.
  (setq lisp-prev-l/c-dir/file (cons (file-name-directory    file-name)
				     (file-name-nondirectory file-name)))
  (send-string "cmulisp" (format inferior-lisp-load-command file-name))
  (switch-to-lisp t))


(defun lisp-compile-file (file-name)
  "Compile a Lisp file in the inferior Lisp process."
  (interactive (comint-get-source "Compile Lisp file: " lisp-prev-l/c-dir/file
				  lisp-source-modes nil)) ; NIL = don't need
                                                          ; suffix .lisp
  (comint-check-source file-name) ; Check to see if buffer needs saved.
  (setq lisp-prev-l/c-dir/file (cons (file-name-directory    file-name)
				     (file-name-nondirectory file-name)))
  (send-string "cmulisp" (concat "(compile-file \""
				 file-name
				 "\"\)\n"))
  (switch-to-lisp t))


;;; Shell stuff
;;;============================================================================
;;;

;In loaddefs.el now.
;(defconst shell-prompt-pattern
;  "^[^#$%>]*[#$%>] *"
;  "*Regexp used by Newline command to match subshell prompts.
;;; Change the doc string for shell-prompt-pattern:
(put 'shell-prompt-pattern 'variable-documentation
  "Regexp to match prompts in the inferior shell.
Defaults to \"^[^#$%>]*[#$%>] *\", which works pretty well.
This variable is used to initialise comint-prompt-regexp in the 
shell buffer.

This is a fine thing to set in your .emacs file.")

(defvar shell-directory-stack nil
  "List of directories saved by pushd in this buffer's shell.")

(defvar shell-popd-regexp "popd"
  "*Regexp to match subshell commands equivalent to popd.")

(defvar shell-pushd-regexp "pushd"
  "*Regexp to match subshell commands equivalent to pushd.")

(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
  (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 cmushell, if the shell is csh.
Value is a list of strings, which may be nil.")

(defvar cmushell-mode-map '())
(cond ((not cmushell-mode-map)
       (setq cmushell-mode-map (full-copy-sparse-keymap comint-mode-map))
       (define-key cmushell-mode-map "\M-\t" 'comint-dynamic-complete)
       (define-key cmushell-mode-map "\M-?"  'comint-dynamic-list-completions)))

(defvar cmushell-mode-hook '()
  "*Hook for customising cmushell mode")

(defun cmushell-mode ()
  "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 point.
Return before end of process output copies rest of line to end (skipping
    the prompt) and sends it.
send-invisible reads a line of text without echoing it, and sends it to
    the shell.

\\{cmushell-mode-map}
Customisation: Entry to this mode runs the hooks on comint-mode-hook and
cmushell-mode-hook (in that order).

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.
Variables shell-cd-regexp, shell-pushd-regexp and shell-popd-regexp are used
to match these command names.

If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it."
  (interactive)
  (comint-mode)
  (setq comint-prompt-regexp shell-prompt-pattern)
  (setq major-mode 'cmushell-mode)
  (setq mode-name "CMU shell")
  (use-local-map cmushell-mode-map)
  (make-local-variable 'shell-directory-stack)
  (setq shell-directory-stack nil)
  (setq comint-input-sentinel 'shell-directory-tracker)
  (run-hooks 'cmushell-mode-hook))


(defun cmushell ()
  "Run an inferior shell, with I/O through buffer *cmushell*.
If buffer exists but shell process is not running, make new shell.
If buffer exists and shell process is running, 
 just switch to buffer *cmushell*.
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.
If a file ~/.emacs_SHELLNAME exists, it is given as initial input
 (Note that this may lose due to a timing error if the shell
  discards input when it starts up.)
The buffer is put in cmushell-mode, giving commands for sending input
and controlling the subjobs of the shell.  See cmushell-mode.
See also variable shell-prompt-pattern.

The shell file name (sans directories) is used to make a symbol name
such as `explicit-csh-arguments'.  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)
  (cond ((not (comint-check-proc "*cmushell*"))
	 (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 "cmushell" prog
			      (if (file-exists-p startfile) startfile)
			      (if (and xargs-name (boundp xargs-name))
				  (symbol-value xargs-name)
				  '("-i"))))
	   (cmushell-mode))))
  (switch-to-buffer "*cmushell*"))


;;; Directory tracking
;;; ===========================================================================
;;; This code provides the cmushell mode input sentinel SHELL-DIRECTORY-TRACKER
;;; that tracks cd, pushd, and popd commands issued to the shell, and
;;; changes the current directory of the shell buffer accordingly.
;;;
;;; This is basically a fragile hack, although it's more accurate than
;;; the released version in shell.el. It has the following failings:
;;; 1. It doesn't know about the cdpath shell variable.
;;; 2. It doesn't implement the pushd and popd variants that take numerical
;;;    arguments. E.g., "popd 3" means pop 3 directories off the directory
;;;    stack.
;;; 3. It only spots the first command in a command sequence. E.g., it will
;;;    miss the cd in "ls; cd foo"
;;; 4. More generally, any complex command (like ";" sequencing) is going to
;;;    throw it. Otherwise, you'd have to build an entire shell interpreter in
;;;    emacs lisp.  Failing that, there's no way to catch shell commands where
;;;    cd's are buried inside conditional expressions, aliases, and so forth.
;;;
;;; The whole approach is a crock. The right way would be to hack the shell so
;;; that when it changes its pwd, for any reason, it notifies emacs via some
;;; ipc channel or something. However, that requires access to shell sources,
;;; isn't so portable, etc., etc.. The solution implemented here works
;;; amazingly well in most circumstances, and, being essentially half-assed,
;;; is more in keeping with the spirit of Unix. Blech.

;;;  Please let marick@gswd-vms.arpa know of any changes you
;;;  make. OK. I OUGHTA DO IT.


(defun shell-front-match (regexp str start)
  "REGEXP is a regular expression. STR is a string. START is a fixnum.
Returns T if REGEXP matches STR where the match is anchored to start
at position START in STR. Sort of like LOOKING-AT for strings."
  (eq start (string-match regexp str start)))

(defun shell-directory-tracker (str)
  (cond ((and (shell-front-match shell-popd-regexp str 0)
	      ;; Optional whitespace followed by either ";" or end-of-string
	      (shell-front-match "\\s *\\(\;\\|$\\)" str (match-end 0)))
         ;; popd
	 (cond (shell-directory-stack
		(cd (car shell-directory-stack))
		(setq shell-directory-stack (cdr shell-directory-stack)))))

	;; pushd 
	((shell-front-match shell-pushd-regexp str 0)
	 (shell-process-cd-or-pushd str nil))

	;; cd
	((shell-front-match shell-cd-regexp str 0)
	 (shell-process-cd-or-pushd str t))))


;;; This function parses and executes cd or pushd commands.
;;; SHELL-DIRECTORY-TRACKER calls this function after it matches the
;;; initial cd or pushd command on the input string. STR is
;;; the entire input string; we assume that (MATCH-END 0) indexes
;;; the string immediately after the end of the command. CD-P
;;; is true if the the command matched was a cd; false if the
;;; command was a pushd.

(defun shell-process-cd-or-pushd (str cd-p)
  ;; make sure the cd or pushd is followed by whitespace or a ";"
  (cond ((or (= (match-end 0) (length str))
	     (memq (aref str (match-end 0)) '(?\  ?\t  ?\;)))
	 (string-match "\\s *" str (match-end 0)) ; skip whitespace
	 (let ((i (match-end 0)))	; i points at the directory or ";"
	   (if (or (= i (length str)) (= '?\; (aref str i))) 
	       ;; The command was terminated by end-of-string or ";"
	       ;; so there was no argument. If cd, go home. If pushd,
	       ;; rotate the top of the directory stack.
	       (if cd-p (cd (getenv "HOME"))
		   (if shell-directory-stack
		       (let ((old default-directory))
			 (cd (car shell-directory-stack))
			 (setq shell-directory-stack
			       (cons old (cdr shell-directory-stack))))))

	       ;; pushd <directory> or cd <directory>
	       ;; The command came with an argument. If pushd, push 
	       ;; the current directory (DEFAULT-DIRECTORY) on the stack.
	       ;; In either case, cd to the argument.
	       (let ((dir (progn (string-match "[^ \t\;]*" str i) ; match dir
				 (expand-file-name
				  (substitute-in-file-name
				   (substring str i (match-end 0)))))))
		 (cond ((file-directory-p dir)
			(if (not cd-p)	; push the current directory
			    (setq shell-directory-stack
				  (cons default-directory
					shell-directory-stack)))
			(cd dir)))))))))


;;; Interfacing to client packages (and converting them)
;;;============================================================================
;;; Several gnu packages (tex-mode, background, dbx, gdb, kermit, prolog, 
;;; telnet are some) use the shell package as clients. Most of them would
;;; be better off using the comint package directly, but they predate it.
;;; The catch is that most of these packages (dbx, gdb, prolog, telnet)
;;; assume total knowledge of all the local variables that shell mode
;;; functions depend on. So they (kill-all-local-variables), then create
;;; the few local variables that shell.el functions depend on. Alas,
;;; cmushell.el functions depend on a different set of vars (for example,
;;; the input history ring is a local variable in cmushell.el's shell mode,
;;; whereas there is no input history ring in shell.el's shell mode).
;;; So we have a situation where the greater functionality of cmushell.el
;;; is biting us -- you can't just replace shell will cmushell.
;;;
;;; Altering these packages to use comint mode directly should *greatly*
;;; improve their functionality, and is actually pretty easy. It's
;;; mostly a matter of renaming a few variable names. See comint.el for more.
;;;     -Olin



;;; Do the user's customisation...
;;;===============================
(defvar cmushell-load-hook nil
  "This hook is run when cmushell is loaded in.
This is a good place to put keybindings.")
	
(run-hooks 'cmushell-load-hook)

-- 

shivers@centro.soar.cs.cmu.edu (Olin Shivers) (02/10/89)

This message contains:
1. The standard anouncement header
2. A shar file for cmuscheme.el, cmutex.el, tea.el, and tea2.el.
(The entire package must be split -- otherwise notesfiles sites will not
be able to handle it.)
-------------------------------------------------------------------------------
I have written new gnu-emacs packages that provide shell, inferior lisp,
inferior scheme, and inferior T modes. These packages have the following
advantages over the standard released gnu packages:

- Input history is kept in all modes, traversed with M-p and M-n, just
  like on LispM's and various fancy shells.
- Filename completion and query is available in all modes.
- Keybindings are cross-compatible among all modes.
- Keybindings are compatible with Zwei and Hemlock.
- Directory tracking is more robust in shell mode, and is *only* active in
  shell mode. (Try entering "cd /" to an inferior lisp in the old package:
  Lisp barfs, but the inferior lisp mode goes ahead and changes the 
  buffer's directory.)
- Non-echoed text entry (for entering passwords) is available in all modes.
- Shell and inferior Lisp modes are backwards compatible with the standard
  gnu modes.
- One source for the inferior Lisp mode works in both emacs releases 18.4x and 
  18.5x. This has been the cause of confusing bugs for users who unwittingly
  tried to use an 18.4x version inferior Lisp mode in an 18.5x version emacs,
  and vice-versa.
- A full set of eval/compile-region/defun commands for the inferior Lisp,
  Scheme, and T modes.
- New modes are easily created for new types of processes.
===============================================================================

THE BAD NEWS:
It would be nice if the shell & inferior lisp package, cmushell.el, was
completely plug-compatible with the old package in shell.el -- if you could
just name the new version 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 the new mode (mostly because of the new mode's greater
functionality).  So, unless we are willing to port all of these packages, we
can't have the new shell package be a complete replacement for shell.el --
that is, we can't name the 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. This is what I have 
done. So the mode names and major functions have different names, e.g:
    shell.el    	cmushell.el
    --------    	----------
    M-x shell   	M-x cmushell	    -- Fire up a shell
    M-x run-lisp	M-x cmulisp 	    -- Fire up a lisp
    shell-mode-map  	cmushell-mode-map   -- Keybindings for [cmu]shell mode
All the names have been carefully chosen so that shell.el and cmushell.el 
won't tromp on each other -- that way dbx.el and friends can happily load
in shell.el without breaking the cmushell.el package, and vice versa.

With the exception of M-x cmushell and M-x cmulisp, however, most of the name
changes are invisible to the user. Further, most of the customising 
variables that are common in functionality have the same name:
    inferior-lisp-program, explicit-shell-file-name, et al.
Hook variables are different, so you can customise shell-mode and 
cmushell-mode differently, for instance.

By the way, it is rather easy to port the shell.el-dependent packages to use
the new stuff.  There are fairly complete comments in the relevant source
files showing how to do this.  Note that this backwards-compatibility hassle
*only* affects shell and inferior lisp mode; the other process-in-a-buffer
modes (Scheme, T, etc.) do not have this problem.
===============================================================================

GENERALIA:
The implementation strategy was to factor common process functionality
into a general command interpreter mode -- comint mode -- and then to
build all the specific modes on top. This provides uniform, integrated
functionality and interface across all the derived modes. Comint mode
provides the input history, filename completion and query,
non-echoed text entry, input editing, and process signalling 
(e.g., ^z, ^c, ...) commands. *Any* mode built on comint mode gets
all this stuff automatically. Additionally, comint mode has general
hooks for customising it to specific command interpreters, such as
Lisp, Scheme, csh, ML, etc..

This release includes the following files:
- comint.el 	comint mode
- cmushell.el	cmushell and cmulisp modes, built on comint mode.
- cmuscheme.el  inferior Scheme mode, built on comint mode.
- tea.el    	inferior T mode, built on comint mode.
- tea2.el   	Variant of tea.el
- cmutex.el 	tex-mode.el with rewritten process interaction code.
    	    	Some bugs also fixed.

These packages have been in daily use by a user community of about 10-20
at CMU since August; most bugs have been shaken out. cmutex.el is less
tested. Please notify me of bugs.

The files are *extensively* commented; this should serve as sufficient
documentation. Each file includes suggestions for your .emacs file
in comments at the top. On-line documentation (C-h C-m, C-h C-f, C-h C-v)
is available for modes, commands, and variables.

This source is available on an FSF-style basis: use it any way you like
as long as you don't charge money for it or change the basis of its
availability; I assume no liability for its use.
===============================================================================

INPUT HISTORY:
There are actually two different ways to retrieve old commands for
resubmission to a process. The standard way is to use the input history
mechanism. An internal list is kept in each process buffer of the last n
inputs (default: 30). The commands M-p and M-n move through this list.  This
is similar in functionality to the input history mechanisms provided by the
LispM, Hemlock, and various fancy shells (tcsh, cmucsh, ksh,...).  There is
also a command, bound to C-c r, which searches backwards through the input
history looking for a substring match.

RMS doesn't like this mechanism. He has suggested an input history mechanism
that operates by searching backwards (and forwards) through the buffer for
occurrences of the prompt.  The user can then resubmit the input by hitting
return.

I do not like this mechanism. If the prompt changes dynamically, you can miss
a command. False positives are also annoying. The screen jumps around a lot as
you scroll through your history. If you run a subprogram that has a null
prompt (like dc), prompt search will miss all its inputs. Etc.

However, you may try either of these mechanisms, and go with the style you
prefer. The RMS-style prompt-search stuff is available on M-N and M-P
(meta-shift-n and meta-shift-P); C-c R is bound to a command that searches for
specific commands (analogous to C-c r). If you use this stuff, you will
probably want to sharpen up the regular expression used to match the prompt in
each mode to match your particular prompt -- the default, general regexp used
in shell mode generates too many annoying false positives.  (It's local
variable comint-prompt-regexp -- you should set it in a hook).
===============================================================================

Source code follows. Please report bugs to me: shivers@cs.cmu.edu.
    -Olin
------
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/post3.shar   to unpack this archive.
#
echo extracting cmuscheme.el...
cat >cmuscheme.el <<'!E!O!F!'
;;; cmuscheme.el -- Scheme process in a buffer. Adapted from tea.el.
;;; Copyright Olin Shivers (1988)
;;; Please imagine a long, tedious, legalistic 5-page gnu-style copyright
;;; notice appearing here to the effect that you may use this code any
;;; way you like, as long as you don't charge money for it, remove this
;;; notice, or hold me liable for its results.
;;;
;;;    This is a customisation of comint-mode (see comint.el)
;;;
;;; Written by Olin Shivers (olin.shivers@cs.cmu.edu). With bits and pieces
;;; lifted from scheme.el, shell.el, clisp.el, newclisp.el, cobol.el, et al..
;;; 8/88
;;;
;;; Please send me bug reports, bug fixes, and extensions, so that I can
;;; merge them into the master source.

;; YOUR .EMACS FILE
;;=============================================================================
;; Some suggestions for your .emacs file.
;;
;; ; If cmuscheme lives in some non-standard directory, you must tell emacs
;; ; where to get it. This may or may not be necessary.
;; (setq load-path (cons (expand-file-name "~jones/lib/emacs") load-path))
;;
;; ; Autoload run-scheme from file cmuscheme.el
;; (autoload 'run-scheme "cmuscheme"
;;           "Run an inferior Scheme process."
;;           t)
;;
;; ; Files ending in ".scm" are Scheme source, 
;; ; so put their buffers in scheme-mode.
;; (setq auto-mode-alist 
;;       (cons '("\\.scm$" . scheme-mode)  
;;             auto-mode-alist))
;;
;; ; Define C-c C-t to run my favorite command in inferior scheme mode:
;; (setq cmuscheme-load-hook
;;       '((lambda () (define-key inferior-scheme-mode-map "\C-c\C-t"
;;                                'favorite-cmd))))
;;;
;;; Unfortunately, scheme.el defines run-scheme to autoload from xscheme.el.
;;; This will womp your declaration to autoload run-scheme from cmuscheme.el
;;; if you haven't loaded cmuscheme in before scheme. Two fixes:
;;; - load cmuscheme.el in your .emacs: (load-library 'cmuscheme)
;;; - change autoload declaration in scheme.el to point to cmuscheme.el:
;;;   (autoload 'run-scheme "cmuscheme" "Run an inferior Scheme" t)
;;;   *or* just delete the autoload declaration from scheme.el altogether,
;;;   which will allow the autoload in your .emacs to have its say.

(provide 'cmuscheme)
(require 'scheme)
(require 'comint)

;;; INFERIOR SCHEME MODE STUFF
;;;============================================================================

(defvar inferior-scheme-mode-hook nil
  "*Hook for customising inferior-scheme mode.")
(defvar inferior-scheme-mode-map nil)

(cond ((not inferior-scheme-mode-map)
       (setq inferior-scheme-mode-map
	     (full-copy-sparse-keymap comint-mode-map))
       (define-key scheme-mode-map "\M-\C-x" ;gnu convention
	           'scheme-send-definition)
       (define-key inferior-scheme-mode-map "\C-cl"    'scheme-load-file)
       (define-key inferior-scheme-mode-map "\C-ck"    'scheme-compile-file)
       (scheme-mode-commands inferior-scheme-mode-map))) 

;; Install the process communication commands in the scheme-mode keymap.
(define-key scheme-mode-map "\M-\C-x" 'scheme-send-definition);gnu convention
(define-key scheme-mode-map "\C-ce"    'scheme-send-definition)
(define-key scheme-mode-map "\C-c\C-e" 'scheme-send-definition-and-go)
(define-key scheme-mode-map "\C-cr"    'scheme-send-region)
(define-key scheme-mode-map "\C-c\C-r" 'scheme-send-region-and-go)
(define-key scheme-mode-map "\C-cc"    'scheme-compile-definition)
(define-key scheme-mode-map "\C-c\C-c" 'scheme-compile-definition-and-go)
(define-key scheme-mode-map "\C-cz"    'switch-to-scheme)
(define-key scheme-mode-map "\C-cl"    'scheme-load-file)
(define-key scheme-mode-map "\C-ck"    'scheme-compile-file) ;k for "kompile"

(defun inferior-scheme-mode ()
  "Major mode for interacting with an inferior Scheme process.

The following commands are available:
\\{inferior-scheme-mode-map}

A Scheme process can be fired up with M-x run-scheme.

Customisation: Entry to this mode runs the hooks on comint-mode-hook and
inferior-scheme-mode-hook (in that order).

You can send text to the inferior Scheme process from other buffers containing
Scheme source.  
    switch-to-scheme switches the current buffer to the Scheme process buffer.
    scheme-send-definition sends the current definition to the Scheme process.
    scheme-compile-definition compiles the current definition.
    scheme-send-region sends the current region to the Scheme process.
    scheme-compile-region compiles the current region.

    scheme-send-definition-and-go, scheme-compile-definition-and-go,
        scheme-send-region-and-go, and scheme-compile-region-and-go
        switch to the Scheme process buffer after sending their text.

Commands:
Return after the end of the process' output sends the text from the 
    end of process to point.
Return before the end of the process' output copies the sexp ending at point
    to the end of the process' output, and sends it.
Delete converts tabs to spaces as it moves back.
Tab indents for Scheme; with argument, shifts rest
    of expression rigidly with the current line.
C-M-q does Tab on each line starting within following expression.
Paragraphs are separated only by blank lines.  Semicolons start comments.
If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it."
  (interactive)
  (comint-mode)
  ;; Customise in inferior-scheme-mode-hook
  (setq comint-prompt-regexp "^[^>]*>+ *") ; OK for cscheme, oaklisp, T,...
  (scheme-mode-variables)
  (setq major-mode 'inferior-scheme-mode)
  (setq mode-name "Inferior Scheme")
  (setq mode-line-process '(": %s"))
  (use-local-map inferior-scheme-mode-map)
  (setq comint-input-filter (function scheme-input-filter))
  (setq comint-input-sentinel (function ignore))
  (setq comint-get-old-input (function scheme-get-old-input))
  (run-hooks 'inferior-scheme-mode-hook))

(defun scheme-input-filter (str)
  "Don't save anything matching inferior-scheme-filter-regexp"
  (not (string-match inferior-scheme-filter-regexp str)))

(defvar inferior-scheme-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'"
  "*Input matching this regexp are not saved on the history list.
Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.")

(defun scheme-get-old-input ()
  "Snarf the sexp ending at point"
  (save-excursion
    (let ((end (point)))
      (backward-sexp)
      (buffer-substring (point) end))))

(defun scheme-args-to-list (string)
  (let ((where (string-match "[ \t]" string)))
    (cond ((null where) (list string))
	  ((not (= where 0))
	   (cons (substring string 0 where)
		 (scheme-args-to-list (substring string (+ 1 where)
						 (length string)))))
	  (t (let ((pos (string-match "[^ \t]" string)))
	       (if (null pos)
		   nil
		 (scheme-args-to-list (substring string pos
						 (length string)))))))))

(defvar scheme-program-name "scheme"
  "*Program invoked by the scheme and run-scheme commands")

(defun scheme (arg)
  "Like run-scheme, except prompts for a command line."
  (interactive "sExtra arguments to Scheme: ")
  (switch-to-buffer
   (apply 'make-comint (append (list "scheme" scheme-program-name nil)
			       (scheme-args-to-list arg)
			       '("-emacs"))))
  (inferior-scheme-mode))

(defun run-scheme (arg)
  "Run an inferior Scheme process, input and output via buffer *scheme*.
With argument, it asks for a command line.  Take the program name from the
variable scheme-program-name.  Runs the hooks from inferior-scheme-mode-hook
\(after the comint-mode-hook is run).
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
  (interactive "P")
  (if arg (call-interactively 'scheme)
      (scheme "")))

(defun scheme-send-region (start end)
  "Send the current region to the inferior Scheme process."
  (interactive "r")
  (send-region "scheme" start end)
  (send-string "scheme" "\n"))

(defun scheme-send-definition ()
  "Send the current definition to the inferior Scheme process."
  (interactive)
  (save-excursion
   (end-of-defun)
   (let ((end (point)))
     (beginning-of-defun)
     (scheme-send-region (point) end))))

(defvar scheme-compile-exp-command "(compile '%s)"
  "*Template for issuing commands to compile arbitrary Scheme expressions.")

(defun scheme-compile-region (start end)
  "Compile the current region in the inferior Scheme process
\(A BEGIN is wrapped around the region: (BEGIN <region>))"
  (interactive "r")
  (send-string "scheme" (format scheme-compile-exp-command
                                (format "(begin %s)"
				        (buffer-substring start end))))
  (send-string "scheme" "\n"))

(defun scheme-compile-definition ()
  "Compile the current definition in the inferior Scheme process."
  (interactive)
  (save-excursion
   (end-of-defun)
   (let ((end (point)))
     (beginning-of-defun)
     (scheme-compile-region (point) end))))

(defun switch-to-scheme (eob-p)
  "Switch to the *scheme* buffer.
With argument, positions cursor at end of buffer."
  (interactive "P")
  (pop-to-buffer "*scheme*")
  (cond (eob-p
	 (push-mark)
	 (goto-char (point-max)))))

(defun scheme-send-region-and-go (start end)
  "Send the current region to the inferior Scheme process,
and switch to the process buffer."
  (interactive "r")
  (scheme-send-region start end)
  (switch-to-scheme t))

(defun scheme-send-definition-and-go ()
  "Send the current definition to the inferior Scheme, 
and switch to the process buffer."
  (interactive)
  (scheme-send-definition)
  (switch-to-scheme t))

(defun scheme-compile-definition-and-go ()
  "Compile the current definition in the inferior Scheme, 
and switch to the process buffer."
  (interactive)
  (scheme-compile-definition)
  (switch-to-scheme t))

(defun scheme-compile-region-and-go (start end)
  "Compile the current region in the inferior Scheme, 
and switch to the process buffer."
  (interactive "r")
  (scheme-compile-region start end)
  (switch-to-scheme t))

(defvar scheme-source-modes '(scheme-mode)
  "*Used to determine if a buffer contains Scheme source code.
If it's loaded into a buffer that is in one of these major modes, it's
considered a scheme source file by scheme-load-file and scheme-compile-file.
Used by these commands to determine defaults.")

(defvar scheme-prev-l/c-dir/file nil
  "Caches the (directory . file) pair used in the last scheme-load-file or
scheme-compile-file command. Used for determining the default in the 
next one.")

(defun scheme-load-file (file-name)
  "Load a Scheme file into the inferior Scheme process."
  (interactive (comint-get-source "Load Scheme file: " scheme-prev-l/c-dir/file
				  scheme-source-modes t)) ; T because LOAD 
                                                          ; needs an exact name
  (comint-check-source file-name) ; Check to see if buffer needs saved.
  (setq scheme-prev-l/c-dir/file (cons (file-name-directory    file-name)
				       (file-name-nondirectory file-name)))
  (send-string "scheme" (concat "(load \""
				file-name
				"\"\)\n"))
  (switch-to-scheme t))

(defun scheme-compile-file (file-name)
  "Compile a Scheme file in the inferior Scheme process."
  (interactive (comint-get-source "Compile Scheme file: "
				  scheme-prev-l/c-dir/file
				  scheme-source-modes
				  nil)) ; NIL because COMPILE doesn't
                                        ; need an exact name.
  (comint-check-source file-name) ; Check to see if buffer needs saved.
  (setq scheme-prev-l/c-dir/file (cons (file-name-directory    file-name)
				       (file-name-nondirectory file-name)))
  (send-string "scheme" (concat "(compile-file \""
				file-name
				"\"\)\n"))
  (switch-to-scheme t))


;;; Do the user's customisation...

(defvar cmuscheme-load-hook nil
  "This hook is run when cmuscheme is loaded in.
This is a good place to put keybindings.")
	
(run-hooks 'cmuscheme-load-hook)
!E!O!F!
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/post3.shar   to unpack this archive.
#
echo extracting cmutex.el...
cat >cmutex.el <<'!E!O!F!'
;; TeX mode commands.
;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.
;;

; Some suggestions for your .emacs file:
; ======================================
; ;; Arrange for TeX-region to put it's temp file in the local directory, so
; ;; that you can invoke it on text that contains \input or \include.
; (setq TeX-directory "."    ; use the local directory
;       TeX-zap-file "#zap") ; and call the temp file #zap.tex
; ;; Arrange for tex m ode to autoload from cmutex.el, instead of the standard
; ;; package, tex-mode.el.
; (autoload 'tex-mode "cmutex" "Major mode for editing files of TeX or LaTeX."
; 	  t)
; ;; If cmutex.el or comint.el reside in a non-standard directory, you
; ;; must tell emacs where to look for them. This may not be necessary.
; (setq load-path (cons (expand-file-name "/afs/user/shivers/lib/emacs")
; 		      load-path))

;; See notes below to if you intend to use the TeX-region command.
;;
;; Rewritten following contributions by William F. Schelter
;; and Dick King (king@kestrel).
;; Modified August 1986 by Stephen Gildea <mit-erl!gildea> and
;; Michael Prange <mit-erl!prange> to add LaTeX support and enhance
;; TeX-region.
;; Added TeX-directory and reorganized somewhat  gildea 21 Nov 86
;;
;; Process stuff rewritten substantially. TeX processes are now run at
;; toplevel in a comint buffer. They are killed with the KILL signal,
;; so they die without core dumping. Comint mode provides better
;; process interaction than old shell mode. 
;; - suggest you arrange for TeX-directory to be "." so that
;;   \include and \input's work. See notes below.
;; - fixed TeX-region so it doesn't include the line before the header.
;;   I did not fix it so that if you TeX-region the entire buffer,
;;   the trailer won't get included twice (once from the buffer, and once
;;   courtesy of TeX-region). TeX works in the presence of this bug, so
;;   what the hell.
;; - changed TeX-buffer so it directly TeX's the file when appropriate.
;;   Also added TeX-last-processed-file so that TeX-print knows who to print.
;;   This is pretty winning.
;; - TeX-dvi-print-command is now a format string, with a %s where the filename
;;   goes. Now you can have print commands like: "dvi-ps %s | lpr".
;; Olin Shivers (shivers@cs.cmu.edu) 2/89
;;
;; This file is part of GNU Emacs.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY.  No author or distributor
;; accepts responsibility to anyone for the consequences of using it
;; or for whether it serves any particular purpose or works at all,
;; unless he says so in writing.  Refer to the GNU Emacs General Public
;; License for full details.

;; Everyone is granted permission to copy, modify and redistribute
;; GNU Emacs, but only under the conditions described in the
;; GNU Emacs General Public License.   A copy of this license is
;; supposed to have been given to you along with GNU Emacs so you
;; can know your rights and responsibilities.  It should be in a
;; file named COPYING.  Among other things, the copyright notice
;; and this notice must be preserved on all copies.

;; Still to do:
;;  Make TAB indent correctly for TeX code.  Then we can make linefeed
;;  do something more useful.
;;
;;  Have spell understand TeX instead of assuming the entire world
;;  uses nroff.
;;
;;  The code for finding matching $ needs to be fixed.

;; NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
;; NOTE
;; NOTE If you use the TeX-region command (C-c C-r) you may have problems with
;; NOTE the default setup. TeX-region creates a file in /tmp with a name like
;; NOTE /tmp/#tza0042.tex, and puts the region to be processed by TeX there.
;; NOTE IF YOUR FILE CONTAINS \input OR \include FORMS, TEX-REGION WILL BREAK.
;; NOTE If you, for example, have \input{decls} in your header, then when
;; NOTE TeX runs, it will look for decls.tex in /tmp, instead of looking
;; NOTE in the original directory your source file lives in. There isn't
;; NOTE any real clean way to fix this. A reasonable solution, if you intend
;; NOTE to use TeX-region on source that contains \input's and \include's
;; NOTE is to arrange for TeX-region to create the temp file in the local
;; NOTE directory. You can do this by putting the following form in your
;; NOTE .emacs file:
;; NOTE     (setq TeX-directory "."    ; use the local directory
;; NOTE           TeX-zap-file "#zap") ; and call the temp file #zap.tex
;; NOTE
;; NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE

(provide 'tex-mode)

(defvar TeX-directory "/tmp/"
  "*Directory in which to run TeX subjob.  Temporary files are
created in this directory.")
(defvar TeX-dvi-print-command "dvi-ps %s | lpr"
  "*Command string used by \\[TeX-print] to print a .dvi file.")
(defvar TeX-last-processed-file nil
  "The last file that was TeX'ed by \\[TeX-region] or \\[TeX-buffer].
Used by \\[TeX-print].")
(defvar TeX-show-queue-command "lpq"
  "*Command string used by \\[TeX-show-print-queue] to show the print queue
that \\[TeX-print] put your job on.")
(defvar TeX-default-mode 'plain-TeX-mode
  "*Mode to enter for a new file when it can't be determined whether
the file is plain TeX or LaTeX or what.")

(defvar TeX-command nil
  "The command to run TeX on a file.  The name of the file will be appended
to this string, separated by a space.")
(defvar TeX-trailer nil
  "String appended after the end of a region sent to TeX by \\[TeX-region].")
(defvar TeX-start-of-header nil
  "String used by \\[TeX-region] to delimit the start of the file's header.")
(defvar TeX-end-of-header nil
  "String used by \\[TeX-region] to delimit the end of the file's header.")
(defvar TeX-zap-file nil
  "Temporary file name used for text being sent as input to TeX.
Should be a simple file name with no extension or directory specification.")

(defvar TeX-mode-syntax-table nil
  "Syntax table used while in TeX mode.")

(defun TeX-define-common-keys (keymap)
  "Define the keys that we want defined both in TeX-mode
and in the TeX-shell."
  (define-key keymap "\C-c\C-k" 'TeX-kill-job)
  (define-key keymap "\C-c\C-l" 'TeX-recenter-output-buffer)
  (define-key keymap "\C-c\C-q" 'TeX-show-print-queue)
  (define-key keymap "\C-c\C-p" 'TeX-print)
  )

(defvar TeX-mode-map nil "Keymap for TeX mode")

(if TeX-mode-map 
    nil
  (setq TeX-mode-map (make-sparse-keymap))
  (TeX-define-common-keys TeX-mode-map)
  (define-key TeX-mode-map "\"" 'TeX-insert-quote)
  (define-key TeX-mode-map "\n" 'TeX-terminate-paragraph)
  (define-key TeX-mode-map "\e}" 'up-list)
  (define-key TeX-mode-map "\e{" 'TeX-insert-braces)
  (define-key TeX-mode-map "\C-c\C-r" 'TeX-region)
  (define-key TeX-mode-map "\C-c\C-b" 'TeX-buffer)
  (define-key TeX-mode-map "\C-c\C-f" 'TeX-close-LaTeX-block)
  )

(defvar TeX-shell-map nil
  "Keymap for the TeX shell.  A shell-mode-map with a few additions")

;(fset 'TeX-mode 'tex-mode) 		;in loaddefs.

;;; This would be a lot simpler if we just used a regexp search,
;;; but then it would be too slow.
(defun tex-mode ()
  "Major mode for editing files of input for TeX or LaTeX.
Trys to intuit whether this file is for plain TeX or LaTeX and
calls plain-tex-mode or latex-mode.  If it cannot be determined
\(e.g., there are no commands in the file), the value of
TeX-default-mode is used."
  (interactive)
  (let (mode slash comment)
    (save-excursion
      (goto-char (point-min))
      (while (and (setq slash (search-forward "\\" nil t))
		  (setq comment (let ((search-end (point)))
				  (save-excursion
				    (beginning-of-line)
				    (search-forward "%" search-end t))))))
      (if (and slash (not comment))
	  (setq mode (if (looking-at "documentstyle")
			 'latex-mode
		       'plain-tex-mode))))
    (if mode (funcall mode)
      (funcall TeX-default-mode))))

(fset 'plain-TeX-mode 'plain-tex-mode)
(fset 'LaTeX-mode 'latex-mode)

(defun plain-tex-mode ()
  "Major mode for editing files of input for plain TeX.
Makes $ and } display the characters they match.
Makes \" insert `` when it seems to be the beginning of a quotation,
and '' when it appears to be the end; it inserts \" only after a \\.

Use \\[TeX-region] to run TeX on the current region, plus a \"header\"
copied from the top of the file (containing macro definitions, etc.),
running TeX in a separate window.  \\[TeX-buffer] does the whole buffer.
\\[TeX-print] prints the .dvi file made by either of these.

Use \\[validate-TeX-buffer] to check buffer for paragraphs containing
mismatched $'s or braces.

Special commands:
\\{TeX-mode-map}

Mode variables:
TeX-directory
	Directory in which to create temporary files for TeX jobs
	run by \\[TeX-region] or \\[TeX-buffer].
TeX-dvi-print-command
	Command string used by \\[TeX-print] to print a .dvi file.
TeX-show-queue-command
	Command string used by \\[TeX-show-print-queue] to show the print
	queue that \\[TeX-print] put your job on.

Entering plain-TeX mode calls the value of text-mode-hook,
then the value of TeX-mode-hook, and then the value
of plain-TeX-mode-hook."
  (interactive)
  (TeX-common-initialization)
  (setq mode-name "TeX")
  (setq major-mode 'plain-TeX-mode)
  (setq TeX-command "tex")
  (setq TeX-start-of-header "%**start of header")
  (setq TeX-end-of-header "%**end of header")
  (setq TeX-trailer "\\bye\n")
  (run-hooks 'text-mode-hook 'TeX-mode-hook 'plain-TeX-mode-hook))

(defun latex-mode ()
  "Major mode for editing files of input for LaTeX.
Makes $ and } display the characters they match.
Makes \" insert `` when it seems to be the beginning of a quotation,
and '' when it appears to be the end; it inserts \" only after a \\.

Use \\[TeX-region] to run LaTeX on the current region, plus the preamble
copied from the top of the file (containing \\documentstyle, etc.),
running LaTeX under a special subshell.  \\[TeX-buffer] does the whole buffer.
\\[TeX-print] prints the .dvi file made by either of these.

Use \\[validate-TeX-buffer] to check buffer for paragraphs containing
mismatched $'s or braces.

Special commands:
\\{TeX-mode-map}

Mode variables:
TeX-directory
	Directory in which to create temporary files for TeX jobs
	run by \\[TeX-region] or \\[TeX-buffer].
TeX-dvi-print-command
	Command string used by \\[TeX-print] to print a .dvi file.
TeX-show-queue-command
	Command string used by \\[TeX-show-print-queue] to show the print
	queue that \\[TeX-print] put your job on.

Entering LaTeX mode calls the value of text-mode-hook,
then the value of TeX-mode-hook, and then the value
of LaTeX-mode-hook."
  (interactive)
  (TeX-common-initialization)
  (setq mode-name "LaTeX")
  (setq major-mode 'LaTeX-mode)
  (setq TeX-command "latex")
  (setq TeX-start-of-header "\\documentstyle")
  (setq TeX-end-of-header "\\begin{document}")
  (setq TeX-trailer "\\end{document}\n")
  (run-hooks 'text-mode-hook 'TeX-mode-hook 'LaTeX-mode-hook))

(defun TeX-common-initialization ()
  (kill-all-local-variables)
  (use-local-map TeX-mode-map)
  (setq local-abbrev-table text-mode-abbrev-table)
  (if (null TeX-mode-syntax-table)
      (progn
	(setq TeX-mode-syntax-table (make-syntax-table))
	(set-syntax-table TeX-mode-syntax-table)
	(modify-syntax-entry ?\\ ".")
	(modify-syntax-entry ?\f ">")
	(modify-syntax-entry ?\n ">")
	(modify-syntax-entry ?$ "$$")
	(modify-syntax-entry ?% "<")
	(modify-syntax-entry ?\" ".")
	(modify-syntax-entry ?& ".")
	(modify-syntax-entry ?_ ".")
	(modify-syntax-entry ?@ "_")
	(modify-syntax-entry ?~ " ")
	(modify-syntax-entry ?' "w"))
    (set-syntax-table TeX-mode-syntax-table))
  (make-local-variable 'paragraph-start)
  (setq paragraph-start "^[ \t]*$\\|^[\f\\\\]")
  (make-local-variable 'paragraph-separate)
  (setq paragraph-separate paragraph-start)
  (make-local-variable 'comment-start)
  (setq comment-start "%")
  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip "[^\\]\\(\\(\\\\\\\\\\)*\\)%+ *")
  (make-local-variable 'comment-indent-hook)
  (setq comment-indent-hook 'TeX-comment-indent)
  (make-local-variable 'TeX-command)
  (make-local-variable 'TeX-start-of-header)
  (make-local-variable 'TeX-end-of-header)
  (make-local-variable 'TeX-trailer))

(defun TeX-comment-indent ()
  (if (looking-at "%%%")
      (current-column)
    (skip-chars-backward " \t")
    (max (if (bolp) 0 (1+ (current-column)))
	 comment-column)))

(defun TeX-insert-quote (arg)
  "Insert ``, '' or \" according to preceding character.
With prefix argument, always insert \" characters."
  (interactive "P")
  (if arg
      (let ((count (prefix-numeric-value arg)))
	(if (listp arg)
	    (self-insert-command 1)	;C-u always inserts just one
	  (self-insert-command count)))
    (insert
     (cond
      ((or (bobp)
	   (save-excursion
	     (forward-char -1)
	     (looking-at "[ \t\n]\\|\\s(")))
       "``")
      ((= (preceding-char) ?\\)
       ?\")
      (t "''")))))

(defun validate-TeX-buffer ()
  "Check current buffer for paragraphs containing mismatched $'s.
As each such paragraph is found, a mark is pushed at its beginning,
and the location is displayed for a few seconds."
  (interactive)
  (let ((opoint (point)))
    (goto-char (point-max))
    ;; Does not use save-excursion
    ;; because we do not want to save the mark.
    (unwind-protect
	(while (and (not (input-pending-p)) (not (bobp)))
	  (let ((end (point)))
	    (search-backward "\n\n" nil 'move)
	    (or (TeX-validate-paragraph (point) end)
		(progn
		  (push-mark (point))
		  (message "Mismatch found in pararaph starting here")
		  (sit-for 4)))))
      (goto-char opoint))))

(defun TeX-validate-paragraph (start end)
  (condition-case ()
      (save-excursion
	(save-restriction
	  (narrow-to-region start end)
	  (goto-char start)
	  (forward-sexp (- end start))
	  t))
    (error nil)))

(defun TeX-terminate-paragraph (inhibit-validation)
  "Insert two newlines, breaking a paragraph for TeX.
Check for mismatched braces/$'s in paragraph being terminated.
A prefix arg inhibits the checking."
  (interactive "P")
  (or inhibit-validation
      (TeX-validate-paragraph
       (save-excursion
	 (search-backward "\n\n" nil 'move)
	 (point))
       (point))
      (message "Paragraph being closed appears to contain a mismatch"))
  (insert "\n\n"))

(defun TeX-insert-braces ()
  "Make a pair of braces and be poised to type inside of them."
  (interactive)
  (insert ?\{)
  (save-excursion
    (insert ?})))

;;; Like TeX-insert-braces, but for LaTeX.
(defun TeX-close-LaTeX-block ()
  "Creates an \\end{...} to match \\begin{...} on the current line and
puts point on the blank line between them."
  (interactive "*")
  (let ((fail-point (point)))
    (end-of-line)
    (if (re-search-backward "\\\\begin{\\([^}\n]*\\)}"
			    (save-excursion (beginning-of-line) (point)) t)
	(let ((text (buffer-substring (match-beginning 1) (match-end 1)))
	      (indentation (current-column)))
	  (end-of-line)
	  (delete-horizontal-space)
	  (insert "\n\n")
	  (indent-to indentation)
	  (insert "\\end{" text "}")
	  (forward-line -1))
      (goto-char fail-point)
      (ding))))

;;; Invoking TeX in an inferior shell.

;;; The utility functions:

(defun TeX-make-process-buffer ()
  (require 'comint)
  (save-excursion
    (set-buffer (get-buffer-create "*TeX-shell*"))
    (comint-mode)
    (setq TeX-shell-map (full-copy-sparse-keymap comint-mode-map))
    (TeX-define-common-keys TeX-shell-map)
    (use-local-map TeX-shell-map)))

(defun set-buffer-directory (buffer directory)
  "Set BUFFER's default directory to be DIRECTORY."
  (setq directory (file-name-as-directory (expand-file-name directory)))
  (if (not (file-directory-p directory))
      (error "%s is not a directory" directory)
    (save-excursion
      (set-buffer buffer)
      (setq default-directory directory))))

;;; The commands:

;;; It's a kludge that we have to create a special buffer just 
;;; to write out the TeX-trailer.  It would nice if there were a
;;; function like write-region that would write literal strings.

;;; Changed a (forward-line -1) that was causing an extra
;;; line to be included in the header to a (beginning-of-line). Olin 1/89

(defun TeX-region (beg end)
  "Run TeX on the current region.  A temporary file (TeX-zap-file) is
written in directory TeX-directory, and TeX is run in that directory.
If the buffer has a header, it is written to the temporary file before
the region itself.  The buffer's header is all lines between the
strings defined by TeX-start-of-header and TeX-end-of-header
inclusive.  The header must start in the first 100 lines.  The value
of TeX-trailer is appended to the temporary file after the region."
  (interactive "r")
  (or TeX-zap-file (setq TeX-zap-file (make-temp-name "#tz")))
  (let* ((temp-buffer (get-buffer-create " TeX-Output-Buffer"))
	 (zap-directory
	  (file-name-as-directory (expand-file-name TeX-directory)))
	 (tex-out-file (concat TeX-zap-file ".tex")) ; Looks nicer
	 (full-tex-out-file (expand-file-name tex-out-file zap-directory)))
    (save-excursion
      (save-restriction
	(widen)
	(goto-char (point-min))
	(forward-line 100)
	(let ((search-end (point))
	      (hbeg (point-min)) (hend (point-min)))
	  (goto-char (point-min))
	  ;; Initialize the temp file with either the header or nothing
	  (if (search-forward TeX-start-of-header search-end t)
	      (progn
		(beginning-of-line)
		(setq hbeg (point))	;mark beginning of header
		(if (search-forward TeX-end-of-header nil t)
		    (progn (forward-line 1)
			   (setq hend (point)))	;mark end of header
		  (setq hbeg (point-min))))) ;no header
	  (write-region (min hbeg beg) hend full-tex-out-file nil nil)
	  (write-region (max beg hend) end full-tex-out-file t nil))
	(let ((local-tex-trailer TeX-trailer))
	  (set-buffer temp-buffer)
	  (erase-buffer)
	  ;; make sure trailer isn't hidden by a comment
	  (insert-string "\n")
	  (if local-tex-trailer (insert-string local-tex-trailer))
	  (write-region (point-min) (point-max) full-tex-out-file t nil))))
    (TeX-process-file zap-directory tex-out-file)))

;;; Altered so that TeX is run directly on the file, if the buffer
;;; is visiting a file and unmodified. Olin 1/89

(defun TeX-buffer ()
  "Run TeX on current buffer.  
If buffer is a file buffer, and modified, first offers to save buffer.
Afterwards, if buffer is a file buffer and unmodified, runs TeX directly on
file.  Otherwise, calls TeX-region on whole buffer."
  (interactive)
  (let ((fname (buffer-file-name)))
    ;; Offer to save buffer if a modified file buffer.
    (if (and fname
	     (buffer-modified-p)
	     (y-or-n-p (format "Save buffer %s first? " (buffer-name))))
	(save-buffer))
    (if (and fname (not (buffer-modified-p)))
	;; If unmodified file buffer, then tex it directly.
	(TeX-process-file (file-name-directory fname)
			  (file-name-nondirectory fname)) ; looks nicer
	;; otherwise, just Tex-region the whole buffer.
	(TeX-region (point-min) (point-max)))))

;; New auxiliary function.
(defun TeX-process-file (dir fname)
  "Fires up a TeX process, working directory DIR, on file FNAME."
  (if (get-buffer "*TeX-shell*")
      (let ((old-proc (get-buffer-process "*TeX-shell*")))
	(if (and old-proc (memq (process-status old-proc) '(run stop)))
	    (message "Killing old TeX process %s..." (process-name old-proc))))
      (TeX-make-process-buffer))
  (set-buffer-directory "*TeX-shell*" dir)
  (TeX-recenter-output-buffer 0)
  (save-excursion
    (let ((command TeX-command)) ; TeX-command is buffer local.
      (set-buffer "*TeX-shell*")
      (goto-char (point-max))
      (insert (format "\n--- Running process: %s %s\n--- working directory %s\n"
		      command fname dir))))
  (comint-exec "*TeX-shell*" "TeX-shell" TeX-command nil (list fname))
  (setq TeX-last-processed-file (expand-file-name fname dir)))

(defun TeX-kill-job ()
  "Kill the currently running TeX job."
  (interactive)
  (kill-process "TeX-shell" t)) ; no mercy. no coredump.

(defun TeX-recenter-output-buffer (linenum)
  "Redisplay buffer of TeX job output so that most recent output can be seen.
The last line of the buffer is displayed on
line LINE of the window, or centered if LINE is nil."
  (interactive "P")
  (let ((tex-shell (get-buffer "*TeX-shell*"))
	(old-buffer (current-buffer)))
    (if (null tex-shell)
	(message "No TeX output buffer")
      (pop-to-buffer tex-shell)
      (bury-buffer tex-shell) ; WHAT IS THE POINT OF THIS LINE??? Olin
      (goto-char (point-max))
      (recenter (if linenum
		    (prefix-numeric-value linenum)
		  (/ (window-height) 2)))
      (pop-to-buffer old-buffer)
      )))

(defun TeX-print ()
  "Print the .dvi file made by \\[TeX-region] or \\[TeX-buffer].
Runs the shell command defined by TeX-dvi-print-command."
  (interactive)
  (if (stringp TeX-last-processed-file)
      (let ((dir (file-name-directory TeX-last-processed-file))
	    (fname (file-name-nondirectory TeX-last-processed-file)))
	(if (string-match "\\(^.*\\.\\)[^.]*$" fname) ; Strip off extension
	    (let* ((noext (substring fname 0 (match-end 1))) ; foo.tex ==> foo.
		   (cmd (format TeX-dvi-print-command
				(concat "\"" dir noext "dvi\""))))
	      (message cmd)
	      (shell-command cmd))
	    (error "Can't figure out name of dvi file")))
      (error "No last file processed.")))
	
(defun TeX-show-print-queue ()
  "Show the print queue that \\[TeX-print] put your job on.
Runs the shell command defined by TeX-show-queue-command."
  (interactive)
  (shell-command TeX-show-queue-command))
!E!O!F!
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/post3.shar   to unpack this archive.
#
echo extracting tea.el...
cat >tea.el <<'!E!O!F!'
;;; tea.el -- Teach emacs about T.
;;; Copyright Olin Shivers (1988)
;;; Please imagine a long, tedious, legalistic 5-page gnu-style copyright
;;; notice appearing here to the effect that you may use this code any
;;; way you like, as long as you don't charge money for it, remove this
;;; notice, or hold me liable for its results.
;;;
;;; 1. Major mode for editing T source: t-mode
;;;    This is just a variant of scheme-mode, tweaked for T.
;;; 2. Major mode for running T in a buffer: run-tea
;;;    This is a customisation of comint-mode.
;;;
;;; Written by Olin Shivers (olin.shivers@cs.cmu.edu). With bits and pieces
;;; lifted from scheme.el, shell.el, clisp.el, newclisp.el, cobol.el, et al..
;;; 8/88
;;; Please send me bug reports, bug fixes, and extensions, so that I can
;;; merge them into the master source.
;;;

;; YOUR .EMACS FILE
;;=============================================================================
;; Some suggestions for your .emacs file.
;;
;; ; If tea.el lives in some non-standard directory, you must tell emacs
;; ; where to get it. This may or may not be necessary.
;; (setq load-path (cons (expand-file-name "~jones/lib/emacs") load-path))
;;
;; ; Autoload run-tea and t-mode from file tea.el
;; (autoload 'run-tea "tea"
;;           "Run an inferior T process."
;;           t)
;;
;; (autoload 't-mode "tea"
;;           "Major mode for editing T source. Just Scheme mode, tuned a bit."
;;           t)
;;
;; ; Files ending in ".t" are T source, so put their buffers in t-mode.
;; (setq auto-mode-alist
;;       (cons '("\\.t$" . t-mode) 
;;	       auto-mode-alist))   
;;
;; ; Define C-c C-t to run my favorite command in inferior T mode:
;; (setq tea-load-hook
;;       '((lambda () (define-key inferior-t-mode-map "\C-c\C-t"
;;                                'favorite-cmd))))

;; ETAGS
;;=============================================================================
;; A suggestion for modifying the etags program so that it knows about T.
;; You should modify the few lines that allow etags to conclude that
;; files that end with ".t" are lisp or scheme source code.  
;; Find a line that looks like
;; /* .scm or .sm or .scheme implies scheme source code */
;; and add a 
;;	     !strcmp (cp + 1, "t") ||
;; suffix check to the following clauses that check filename suffixes.
;; This is already done for some versions of etags. Have a look, or try it 
;; & see.

(setq scheme-mit-dialect nil) ; Give me a break.
(require 'scheme)
(require 'cmushell)


;;; T mode stuff
;;;============================================================================

;;; Note: T mode alters the scheme-mode syntax table and indentation
;;; hooks slightly. If you were using scheme-mode and t-mode simultaneously
;;; this might be a problem, except that the alterations are fairly 
;;; innocuous.

;; This adds [] and {} as matching delimiters. So emacs will treat #[Char 0]
;; or #{Procedure 1 ADD} as an s-exp with a quote sign in front.
(modify-syntax-entry ?[ "(]" scheme-mode-syntax-table)
(modify-syntax-entry ?] ")[" scheme-mode-syntax-table)
(modify-syntax-entry ?{ "(}" scheme-mode-syntax-table)
(modify-syntax-entry ?} "){" scheme-mode-syntax-table)

;; Modify scheme-indent-hook for T.
(put 'labels 'scheme-indent-hook 1)
(put 'block 'scheme-indent-hook 0)
(put 'block0 'scheme-indent-hook 0)
(put 'object 'scheme-indent-hook 1)
(put 'lset 'scheme-indent-hook 1)
(put 'xcase 'scheme-indent-hook 1)
(put 'select 'scheme-indent-hook 1)
(put 'xselect 'scheme-indent-hook 1)
(put 'iterate 'scheme-indent-hook 2)
(put 'cond 'scheme-indent-hook 0)
(put 'xcond 'scheme-indent-hook 0)
(put 'catch 'scheme-indent-hook 1)
(put 'bind 'scheme-indent-hook 1)
(put 'define-operation 'scheme-indent-hook 1)
(put 'operation 'scheme-indent-hook 1)
(put 'object 'scheme-indent-hook 1)
(put 'join 'scheme-indent-hook 0)
(put 'destructure 'scheme-indent-hook 1)
(put 'destructure* 'scheme-indent-hook 1)
(put 'define-integrable 'scheme-indent-hook 1)
(put 'define-constant 'scheme-indent-hook 1)
(put 'define-syntax 'scheme-indent-hook 1)
(put 'let-syntax 'scheme-indent-hook 1)
(put 'define-local-syntax 'scheme-indent-hook 1)
(put 'macro-expander 'scheme-indent-hook 1)
(put 'with-open-streams 'scheme-indent-hook 1)
(put 'with-open-ports 'scheme-indent-hook 1)
(put 'with-input-from-string 'scheme-indent-hook 1)
(put 'with-output-to-string 'scheme-indent-hook 1)
(put 'with-output-width-string 'scheme-indent-hook 1)
(put 'receive 'scheme-indent-hook 1)
(put 'receive-values 'scheme-indent-hook 1)

(defvar t-mode-hook nil
 "*Hook for customising T mode")

(defvar t-mode-map (full-copy-sparse-keymap scheme-mode-map))

(defun t-mode ()
  "Major mode for editing T code. 
This is Scheme mode, slightly tuned for T. Editing commands are similar
to those of Lisp mode.

In addition, if an inferior T process is running, some additional
commands will be defined, for evaluating expressions and controlling
the interpreter, and the state of the process will be displayed in the
modeline of all T buffers.  The names of commands that interact
with the T process start with \"tea-\".  For more information
see the documentation for inferior-t-mode.

Commands:
Delete converts tabs to spaces as it moves back.
Blank lines separate paragraphs.  Semicolons start comments.
\\{t-mode-map}
Customisation: Entry to this mode runs the hooks on t-mode-hook"
  (interactive)
  (kill-all-local-variables)
  (use-local-map t-mode-map)
  (scheme-mode-variables)
  (setq major-mode 't-mode)
  (setq mode-name "T")
  (run-hooks 't-mode-hook))


;;; INFERIOR T MODE STUFF
;;;============================================================================

(defvar inferior-t-mode-map nil)
(cond ((not inferior-t-mode-map)
       (setq inferior-t-mode-map (full-copy-sparse-keymap comint-mode-map))
       (scheme-mode-commands inferior-t-mode-map)
       (define-key inferior-t-mode-map "\C-cl"    'tea-load-file)
       (define-key inferior-t-mode-map "\C-ck"    'tea-compile-file) ;"kompile"
       ))

;; Install the process communication commands in the scheme-mode keymap.
(define-key t-mode-map "\M-\C-x"  'tea-send-definition) ; gnu convention
(define-key t-mode-map "\C-ce"    'tea-send-definition)
(define-key t-mode-map "\C-c\C-e" 'tea-send-definition-and-go)
(define-key t-mode-map "\C-cr"    'tea-send-region)
(define-key t-mode-map "\C-c\C-r" 'tea-send-region-and-go)
(define-key t-mode-map "\C-cc"    'tea-compile-definition)
(define-key t-mode-map "\C-c\C-c" 'tea-compile-definition-and-go)
(define-key t-mode-map "\C-cz"    'switch-to-tea)
(define-key t-mode-map "\C-cl"    'tea-load-file)
(define-key t-mode-map "\C-ck"    'tea-compile-file)

(defvar inferior-t-mode-hook nil
  "*Hook for customising inferior-T mode")

(defun inferior-t-mode ()
  "Major mode for interacting with an inferior T process.

The following commands are available:
\\{inferior-t-mode-map}

A T process can be fired up with M-x run-tea.

Customisation: Entry to this mode runs the hooks on comint-mode-hook and
inferior-t-mode-hook (in that order).

You can send text to the inferior T process from other buffers containing
T source.  
    switch-to-tea switches the current buffer to the T process buffer.
    tea-send-definition sends the current definition to the T process.
    tea-compile-definition compiles the current definition.
    tea-send-region sends the current region to the T process.
    tea-compile-region compiles the current region.

    tea-send-definition-and-go, tea-compile-definition-and-go,
        tea-send-region-and-go, and tea-compile-region-and-go
        switch to the T process buffer after sending their text.

Commands:
Return after the end of the process' output sends the text from the 
    end of process to point.
Return before the end of the process' output copies the sexp ending at point
    to the end of the process' output, and sends it.
Delete converts tabs to spaces as it moves back.
Tab indents for T; with argument, shifts rest
    of expression rigidly with the current line.
C-M-q does Tab on each line starting within following expression.
Paragraphs are separated only by blank lines.  Semicolons start comments.
If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it."
  (interactive)
  (comint-mode)
  (setq comint-prompt-regexp "^>+ *") ; Customise in inferior-t-mode-hook
  (scheme-mode-variables)

  (setq major-mode 'inferior-t-mode)
  (setq mode-name "Inferior T")
  (setq mode-line-process '(": %s"))
  (use-local-map inferior-t-mode-map)
  (setq comint-input-filter 'tea-input-filter)
  (setq comint-input-sentinel 'ignore)
  (setq comint-get-old-input 'tea-get-old-input)
  (run-hooks 'inferior-t-mode-hook))

(defun tea-input-filter (str)
  "Don't save anything matching tea-filter-regexp"
  (not (string-match tea-filter-regexp str)))

(defvar tea-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'"
  "*Input matching this regexp are not saved on the history list.
Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.")

(defun tea-get-old-input ()
  "Snarf the sexp ending at point"
  (save-excursion
    (let ((end (point)))
      (backward-sexp)
      (buffer-substring (point) end))))

(defun tea-args-to-list (string)
  (let ((where (string-match "[ \t]" string)))
    (cond ((null where) (list string))
	  ((not (= where 0))
	   (cons (substring string 0 where)
		 (tea-args-to-list (substring string (+ 1 where)
					      (length string)))))
	  (t (let ((pos (string-match "[^ \t]" string)))
	       (if (null pos)
		   nil
		 (tea-args-to-list (substring string pos
					      (length string)))))))))

(defvar tea-program-name "t"
  "*Program invoked by the tea and run-tea commands")

(defun tea (arg)
  "Like run-tea, except prompts for a command line."
  (interactive "sExtra arguments to tea: ")
  (switch-to-buffer
   (apply 'make-comint (append (list "tea" tea-program-name nil)
			       (tea-args-to-list arg)
			       '("-emacs"))))
  (inferior-t-mode))

(defun run-tea (arg)
  "Run an inferior T process, input and output via buffer *tea*.
With argument, it asks for a command line.  Take the program name from the
variable tea-program-name.  Runs the hooks from inferior-t-mode-hook (after
the comint-mode-hook is run).
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
  (interactive "P")
  (if arg (call-interactively 'tea)
      (tea "")))

(defun tea-send-region (start end)
  "Send the current region to the inferior T process"
  (interactive "r")
  (send-region "tea" start end)
  (send-string "tea" "\n"))

(defun tea-send-definition ()
  "Send the current definition to the inferior T process."
  (interactive)
  (save-excursion
   (end-of-defun)
   (let ((end (point)))
     (beginning-of-defun)
     (tea-send-region (point) end))))

(defun tea-compile-region (start end)
  "Compile the current region in the inferior T process.
\(A BLOCK is wrapped around the region: (BLOCK <region>)"
  (interactive "r")
  (send-string "tea" "(orbit '(block ")
  (send-region "tea" start end)
  (send-string "tea" "))\n"))

(defun tea-compile-definition ()
  "Compile the current definition in the inferior T process."
  (interactive)
  (save-excursion
   (end-of-defun)
   (let ((end (point)))
     (beginning-of-defun)
     (tea-compile-region (point) end))))

(defun switch-to-tea (eob-p)
  "Switch to the *tea* buffer.
With argument, positions cursor at end of buffer."
  (interactive "P")
  (pop-to-buffer "*tea*")
  (cond (eob-p
	 (push-mark)
	 (goto-char (point-max)))))

(defun tea-send-region-and-go (start end)
  "Send the current region to the inferior T process,
and switch to the process buffer."
  (interactive "r")
  (tea-send-region start end)
  (switch-to-tea t))

(defun tea-send-definition-and-go ()
  "Send the current definition to the inferior T process, 
and switch to the process buffer."
  (interactive)
  (tea-send-definition)
  (switch-to-tea t))

(defun tea-compile-region-and-go (start end)
  "Compile the current region in the inferior T process, 
and switch to process buffer."
  (interactive "r")
  (tea-compile-region start end)
  (switch-to-tea t))

(defun tea-compile-definition-and-go ()
  "Compile the current definition in the inferior T process,
and switch to process buffer."
  (interactive)
  (tea-compile-definition)
  (switch-to-tea t))

(defvar tea-source-modes '(t-mode)
  "*Used to determine if a buffer contains T source code.
If it's loaded into a buffer that is in one of these major modes, it's
considered a T source file by tea-load-file and tea-compile-file.
Used by these commands to determine defaults.")

(defvar tea-prev-l/c-dir/file nil
  "Caches the (directory . file) pair used in the last tea-load-file or
tea-compile-file command. Used for determining the default in the next one.")

(defun tea-load-file (file-name)
  "Load a T file into the inferior T process."
  (interactive (comint-get-source "Load T file: " tea-prev-l/c-dir/file
				  tea-source-modes t)) ; T because LOAD needs
                                                       ; an exact name.
  (comint-check-source file-name) ; Check to see if buffer needs saved.
  (setq tea-prev-l/c-dir/file (cons (file-name-directory    file-name)
				    (file-name-nondirectory file-name)))
  (send-string "tea" (concat "(load \""
			     file-name
			     "\"\)\n"))
  (switch-to-tea t))

(defun tea-compile-file (file-name)
  "Compile a T file in the inferior T process."
  (interactive (comint-get-source "Compile T file: " tea-prev-l/c-dir/file
				  tea-source-modes
				  nil)) ; NIL because COMPILE doesn't
                                        ; need an exact name.
  (comint-check-source file-name) ; Check to see if buffer needs saved.
  (setq tea-prev-l/c-dir/file (cons (file-name-directory    file-name)
				    (file-name-nondirectory file-name)))
  (send-string "tea" (concat "(compile-file \""
			     file-name
			     "\"\)\n"))
  (switch-to-tea t))

;;; Do the user's customisation...

(defvar tea-load-hook nil
  "This hook is run when tea is loaded in.
This is a good place to put keybindings.")
	
(run-hooks 'tea-load-hook)
!E!O!F!
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/post3.shar   to unpack this archive.
#
echo extracting tea2.el...
cat >tea2.el <<'!E!O!F!'
;;; -*-Emacs-Lisp-*- Tea mode
;;; Copyright Olin Shivers (1988)
;;; Please imagine a long, tedious, legalistic 5-page gnu-style copyright
;;; notice appearing here to the effect that you may use this code any
;;; way you like, as long as you don't charge money for it, remove this
;;; notice, or hold me liable for its results.

;;; Written by Olin Shivers (shivers@cs.cmu.edu) 10/88.
;;; Please send me bug reports, bug fixes, and extensions, so that I can
;;; merge them into the master source.

;;; This file tunes scheme and inferior-scheme modes for T.
;;; This is a fairly simple operation, and consists of:
;;; 1. Adding T-MODE to the list of modes that are recognised by 
;;;    scheme-load-file and scheme-compile-file as marking source files.
;;; 2. Changing the scheme-compile-exp-command string to
;;;    "(orbit '%s)"
;;; 3. Tweaking the scheme syntax table just a bit.
;;; 5. Specifying indentation information for a few T specific special forms,
;;;    and turning off the scheme-mit-dialect flag.
;;; 3. Having commands TEA and RUN-TEA that are just like SCHEME and
;;;    RUN-SCHEME except that (a) that the name of the program to run isn't
;;;    taken from the variable SCHEME-PROGRAM-NAME, it's taken from
;;;    TEA-PROGRAM-NAME, and (b) the mode hook isn't INFERIOR-SCHEME-MODE-HOOK
;;;    but INFERIOR-T-MODE-HOOK.
;;; 4. Having a mode T-MODE, that is exactly equal to SCHEME-MODE, except
;;;    that the hooks from T-MODE-HOOK are run instead of SCHEME-MODE-HOOK.
 
;;; This is the minimal-delta approach to a T mode: we just mutate Scheme
;;; mode. Note, for example, that there is no "inferior-t mode."
;;; This means you can run exactly *one* scheme and/or T
;;; process at a time; you *can't* run a scheme process *and* a T process.
;;; I don't think this is a problem.

;;; This stuff uses the scheme-in-a-buffer stuff defined in cmuscheme.el and
;;; comint.el; the commands provided are extensively documented in the
;;; source text for these files.

;; YOUR .EMACS FILE
;;=============================================================================
;; Some suggestions for your .emacs file.
;;
;; ; If tea.el lives in some non-standard directory, you must tell emacs
;; ; where to get it. This may or may not be necessary.
;; (setq load-path (cons (expand-file-name "~jones/lib/emacs") load-path))
;;
;; ; Autoload run-tea and t-mode from file tea.el
;; (autoload 'run-tea "tea"
;;           "Run an inferior T process."
;;           t)
;;
;; (autoload 't-mode "tea"
;;           "Major mode for editing T source. Just Scheme mode, tuned a bit."
;;           t)
;;
;; ; Files ending in ".t" are T source, so put their buffers in t-mode.
;; (setq auto-mode-alist
;;       (cons '("\\.t$" . t-mode) 
;;	       auto-mode-alist))   
;;
;; ; Define C-c C-t to run my favorite command in inferior Scheme mode:
;; (setq tea-load-hook
;;       '((lambda () (define-key inferior-scheme-mode-map "\C-c\C-t"
;;                                'favorite-cmd))))

;; ETAGS
;;=============================================================================
;; A suggestion for modifying the etags program so that it knows about T.
;; You should modify the few lines that allow etags to conclude that
;; files that end with ".t" are lisp or scheme source code.  
;; Find a line that looks like
;; /* .scm or .sm or .scheme implies scheme source code */
;; and add a 
;;	     !strcmp (cp + 1, "t") ||
;; suffix check to the following clauses that check filename suffixes.
;; This is already done for some versions of etags. Have a look, or try it 
;; & see.

(require 'cmuscheme)	; For inferior-scheme mode
(provide 'tea)

; File buffers in t-mode contain scheme source
(if (not (memq 't-mode scheme-source-modes))  
    (setq scheme-source-modes (cons 't-mode scheme-source-modes)))

(setq scheme-compile-exp-command "(orbit '%s)")

;; This adds [] and {} as matching delimiters. So emacs will treat #[Char 0]
;; or #{Procedure 1 ADD} as an s-exp with a quote sign in front.
(modify-syntax-entry ?[ "(]" scheme-mode-syntax-table)
(modify-syntax-entry ?] ")[" scheme-mode-syntax-table)
(modify-syntax-entry ?{ "(}" scheme-mode-syntax-table)
(modify-syntax-entry ?} "){" scheme-mode-syntax-table)

;; Modify indenting for T.
(setq scheme-mit-dialect nil) ; Give me a break.
(put 'labels 'scheme-indent-hook 1)
(put 'block 'scheme-indent-hook 0)
(put 'block0 'scheme-indent-hook 0)
(put 'object 'scheme-indent-hook 1)
(put 'lset 'scheme-indent-hook 1)
(put 'xcase 'scheme-indent-hook 1)
(put 'select 'scheme-indent-hook 1)
(put 'xselect 'scheme-indent-hook 1)
(put 'iterate 'scheme-indent-hook 2)
(put 'cond 'scheme-indent-hook 0)
(put 'xcond 'scheme-indent-hook 0)
(put 'catch 'scheme-indent-hook 1)
(put 'bind 'scheme-indent-hook 1)
(put 'define-operation 'scheme-indent-hook 1)
(put 'operation 'scheme-indent-hook 1)
(put 'object 'scheme-indent-hook 1)
(put 'join 'scheme-indent-hook 0)
(put 'destructure 'scheme-indent-hook 1)
(put 'destructure* 'scheme-indent-hook 1)
(put 'define-integrable 'scheme-indent-hook 1)
(put 'define-constant 'scheme-indent-hook 1)
(put 'define-syntax 'scheme-indent-hook 1)
(put 'let-syntax 'scheme-indent-hook 1)
(put 'define-local-syntax 'scheme-indent-hook 1)
(put 'macro-expander 'scheme-indent-hook 1)
(put 'with-open-streams 'scheme-indent-hook 1)
(put 'with-open-ports 'scheme-indent-hook 1)
(put 'with-input-from-string 'scheme-indent-hook 1)
(put 'with-output-to-string 'scheme-indent-hook 1)
(put 'with-output-width-string 'scheme-indent-hook 1)
(put 'receive 'scheme-indent-hook 1)
(put 'receive-values 'scheme-indent-hook 1)

(defvar tea-program-name "t"
  "*Name of the T program to run when RUN-TEA is called.")

(defun run-tea (arg)
  "Run an inferior T process, input and output via buffer *scheme*.
With argument, it asks for a command line.  Take the program name from the
variable tea-program-name. Runs the hooks from inferior-t-hook (after
comint-mode-hook is run).
\(Type \\[describe-mode] in the process buffer for a list of commands.)"
  (interactive "P")
  (if arg (call-interactively 'tea)
      (tea "")))

(defun tea (arg)
  "Like run-tea, except prompts for a command line."
  (interactive "sExtra arguments to tea: ")
  (let ((scheme-program-name tea-program-name)	   ; Lexical scope loses!
	(inferior-scheme-mode-hook (function (lambda (); Dynamic binding rules!
				     (run-hooks 'inferior-t-mode-hook)))))
    (scheme arg)))


(defvar inferior-t-mode-hook nil
  "*Hook for customising inferior-scheme mode when invoked via run-tea or tea")


(defun t-mode ()
  "Major mode for editing T code. Basically, it's just scheme mode, except 
for the entry hook.
Commands:
Delete converts tabs to spaces as it moves back.
Blank lines separate paragraphs.  Semicolons start comments.
\\{scheme-mode-map}
Entry to this mode runs the hooks on t-mode-hook.
If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it."
  (interactive)
  (kill-all-local-variables)
  (use-local-map scheme-mode-map)
  (setq major-mode 't-mode)
  (setq mode-name "T")
  (scheme-mode-variables)
  (run-hooks t-mode-hook))

(defvar t-mode-hook nil
  "*Hook for customising T mode.")

;;; Do the user's customisation...
(defvar tea-load-hook nil
  "This hook is run when tea is loaded in.
This is a good place to put keybindings.")
	
(run-hooks 'tea-load-hook)
!E!O!F!
--