[comp.emacs] CMU process modes, release 2.01

shivers@a.gp.cs.cmu.edu (Olin Shivers) (09/13/90)

This message is broken into 5 parts so notesfiles sites can handle it:
1. The 2.01 release notes (this msg)
2. The source for comint.el
3. A shar file for cmushell.el and tea.el
4. A shar file for cmulisp.el and cmuscheme.el
5. A shar file for background.el and cmutex.el
-------------------------------------------------------------------------------
I am posting release 2.01 of the CMU process modes. These are a suite of
gnu-emacs packages for running various command interpreters in buffers. They
are all built on top of a single base mode (comint mode) that provides a
consistent set of base functionality and keybindings.

These modes have the following advantages:
- 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.
- 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 comint source has a section giving hints on converting older
shell.el-based modes to use comint. I am told that the FSF will be
using comint in the next release of gnumacs.


The current set of modes are:
    background.el   	- run process in background, a la csh.
    cmulisp.el	    	- simple lisp-in-a-buffer mode.
    cmuscheme.el    	- Scheme-in-a-buffer.
    cmushell.el	    	- shell-in-a-buffer.
    cmutex.el	    	- for TeX and LaTeX editing. Process support for
    	    	    	  running tex and latex, and printing and previewing
    	    	    	  .dvi files.
    comint.el	    	- base mode. Used by everyone else.
    tea.el  	    	- T-in-a-buffer

These packages can be used singly or together. They are careful not to
interfere with other modes. Where possible, I have tried to make them
compatible with other similar modes, in terms of keybindings, functionality,
and variable names. Users familiar with Hemlock or the LispM's Zemacs will
find the commands in cmulisp are bound to similar keys. Cmushell mode pays
attention to the variables that customise shell.el. And so forth.


Also separately available are:
    ilisp.el	    	- Powerful tightly-integrated lisp-in-a-buffer mode.
    comint-ipc.el       - Facility for piggy-backing emacs/subprocess
    	    	    	  communications on top of the user's pipe or pty
    	    	    	  communications. Unavoidably somewhat fragile, but
    	    	    	  nontheless useful. Used by ilisp.el.
These files should be released imminently by Chris McConnell.


CHANGES FROM RELEASE 2.00
===============================================================================
You can now anonymously ftp the source files from CMU:
- Ftp to a.gp.cs.cmu.edu (128.2.242.7) or any other machine that mounts
  the AFS file tree.
- cd /afs/cs.cmu.edu/user/shivers/lib/emacs
  Don't try to cd to or ls any of the intermediate directories;
  go straight to the .../emacs subdirectory. The CMU security mechanisms
  will block you from accessing any of the other directories while you
  are doing anonymous accesses.
- ls *.el
- get the files you want.
- Release notes are in the notes subdirectory.

Of course, anyone with AFS access at CMU, Dartmouth, Michigan, MIT, Mt. Xinu
Transarc, et al. can just cd to the directory and copy what they want.

2.00->2.01 CHANGES:
A few small bugs have been fixed in version 2.00 of the comint modes.
For further details, see the changelogs in the source files.
Release 2.01 has the following fixes/improvements over 2.00:

- The directory tracking machinery in cmushell now expands environment 
variables in path names. This doesn't always work right, since the
variables are evaluated in *emacs*' process environment, not the shell's.
However, enough people wanted this feature that I installed it in spite
of its obvious fragility.

- Background.el had a bug that sometimes fails to correctly set the current
working directory for the process to the current buffer's w.d. when
starting up a job. This has been fixed. 

- Emacs has two separate mechanisms for maintaining the process environment
(**environ in C programs). You choose one at compile time. If your emacs
is compiled with the default mechanism (which uses the process-environment
global var), then you have no problems. If your emacs was compiled with
#define MAINTAIN_ENVIRONMENT, then the comint-exec function would break.
This has been fixed in rel 2.01. The bug was reported by several people.
Dan Pierson provided some of the new code that repairs it.

Some people patched around this bug in rel 2.00 by setq'ing the
process-environment variable to nil. Don't do this in 2.01. It will 
screw up emacs' process environment mechanisms.

- Lisp and Scheme modes consider "." to separate symbols. E.g., "foo.bar" is
not recognised by emacs as one symbol; forward-sexp on it will only skip
past the "foo". Similarly, forward-sexp on "3.1415" will only skip past the
"3". This will mess up certain commands that deal with sexps.  For
instance, lisp-show-variable-documentation will not get the default choice
right.  lisp-eval-last-sexp will try to evaluate "bar" instead of
"foo.bar".  This is really a problem with the syntax table in Scheme and
Lisp modes, not with the CMU process modes. You could fix it by defining
"." to have the symbol syntax code in the relevant syntax tables:
    (modify-syntax-entry ?. "_" lisp-mode-syntax-table)
This will, however, cause emacs to consider dotted pairs like (a . b)
to be 3 element lists. A more sophisticated fix is up to the FSF folks.
Doug Roberts pointed this problem out.

- Cmushell documentation mentions variables of the form explicit-csh-args.
The code actually used variables of the form explicit-csh-arguments.  The
doc has been changed to match the code in rel 2.01 for compatibility with
shell.el. This is a long-standing documentation bug that goes back to the
original shell.el. I suspect very few people use this feature, so most people
won't notice the fix. Stephen Anderson reported the problem.

- Several people have written extensions for cmutex.el. To help people
use these extensions and still be able to autoload cmutex.el (it's pretty
big), I've added a load hook to rel 2.01 cmutex.el: cmutex-load-hook.  The
last thing cmutex does when it loads in is run the load hook.  This is just
the right thing for loading in extensions when cmutex loads:
    ;; For your .emacs -- when cmutex.el loads, myhacks.el will load, too.
    (setq cmutex-load-hook '((lambda () (load-library "myhacks"))))
All of the CMU process modes except background.el have load hooks now.

- The interaction between comint-previous-input and
comint-previous-similar-input has been improved.

- Some of the keybindings for the environment commands in cmutex.el
have been moved around for greater consistency. sk@thp.Uni-Koeln
reported this problem.

   	-Olin


MAJOR CHANGES FROM RELEASE ONE
===============================================================================
- An extremely hairy Lisp/Scheme interface by Chris Mconnell (ccm@cs.cmu.edu).
  This package, ilisp.el, allows emacs to query the running process for useful
  state. Example commands are:
    lisp-arglist    display the arguments taken by the current function.
    lisp-describe   call the Common Lisp DESCRIBE function on the symbol
    	    	    the cursor is over, and put the results in a window.
    lisp-macroexpand macroexpand the current form and display the results.
    lisp-find-source Edit the file that defines the function the point is on.
    edit-next-caller Edit the next place this function is called.

   Ilisp.el allows tight integration with the lisp subprocess by
   sending messages to the subprocess "behind-the-scenes" over the
   user's connection. It does not assume a multithreaded lisp (as in
   Franz Inc.'s Allegro Common Lisp). (However, it could certainly be
   ported to take advantage of a multithreaded lisp, providing greater
   communications robustness without altering the interface functionality
   at all. Anyone interested in doing so should contact Chris.)

   Ilisp can be customised for most major dialects of Lisp and Scheme. This
   package is so massive it is being released in a separate post by Chris.
   I've included a dumb, simple inferior-lisp package in this release, as well
   (cmulisp.el).

- A new package, background.el that allows you to run background processes in 
  buffers. This was adapted for comint.el from Joe Keane's background.el.

- A very hairy TeX and LaTeX package. This includes buffer validation,
  command entry commands, process interfaces to TeX, LaTeX, dvi printers and
  previewers. Pieces of this have been contributed by many people,
  notably Steve Anderson, Stephen Gildea, Dorab Patel, and Ashwin Ram.

- Lots of tweaks to the basic comint package, many contributed by other
  people.
  + A new input history command comint-previous-similar-input (M-s) taken
    from some LispM interface. This is *quite* useful.
  + The other input history commands have been improved.
  + Filename completion has been improved.
  + By popular demand, return now jumps to the end of line before 
    submitting input (this is settable, but defaults to the eol behavior).
  + New hooks for mode hackers.
  + comint-exec no longer dependent on etc/env program.
  + Process communication via pipes supported better.
  + Simple process-query facility available.
  + New procedures for sending input to processes that avoid hanging
    on large text blocks.

- Tweaks to the other packages:
  + Improved directory tracking in cmushell.el. It now handles
    all forms of pushd and popd. New commands dirtrack-toggle and
    dirs toggle the directory tracking machinery off and on, and
    resync the mode's directory stack with the shell's.
  + cmushell and cmulisp modes now reside in separate files
    cmushell.el and cmulisp.el. Not only is this a good idea, 
    it's apparently the way it'll be rel 19. N.B.: CMULISP USERS
    MUST CHANGE THEIR .EMACS AUTOLOAD'S ACCORDINGLY.
  + better support for multiple processes in cmulisp, cmuscheme 
    and tea modes.
  + cmulisp has some simple query commands, for function and
    variable documentation, describing symbols, and getting
    function arglists. This is a simple facility; Chris McConnell's
    ilisp package is much more featureful. It was based on some code
    contributed by Doug Roberts.
  + cmulisp, cmuscheme, and tea now have a command (c-x c-e) to eval the
    last sexp. The keybinding is compatible with emacs lisp and Hemlock.
  + Filename defaults in Scheme and Lisp modes will use the string
    the cursor is over, if it's an existing filename. This is a
    debatable decision. Feedback appreciated.

GENERAL NOTES
===============================================================================
Directory tracking:
The new directory tracking code will handle
    pushd [+n | <dir>]
    popd [+n]
    cd [<dir>]
The emacs command M-x dirs will stuff a "dirs" at the process, wait for
a line of output, parse it up, and reset the buffer's directory
stack accordingly. The emacs command M-x dirtrack-toggle will toggle
the directory tracking machinery on and off. This is useful, for example,
when you are running ftp, and don't want ftp's "cd" command to throw
emacs off.

The new code was based on source contributed by Vince Broman, Mike Coffin,
Jeff Peck, and Barry Warsaw. Its structure should allow zealous shell hackers
to extend it pretty easily.

The commands that directory tracking looks for are customisable.
If you define aliases for cd or pushd or popd, you can set the right
emacs variables and win.

Note that in general, complex directory tracking is a losing game.
You can't win. If you have a programmable shell, an excellent hack
is to program the directory-changing commands to type code sequences
at the terminal, and write a process filter to watch for these sequences.
A sequence like "ESC D <length> <directory>" where <directory> is <length>
characters long would do fine. (Using a <length> parameter instead of
string delimeters prevents emacs from gobbling up unbounded amounts of shell
output in the event the terminating delimiter gets lost.)


Multiple process buffers:
Many people suggest ways to run multiple shell buffers. This does not require
extra features. Suppose you want two shell buffers.  Simply make a shell
buffer with M-x cmushell, and then rename it with M-x rename-buffer. Make the
next shell with M-x cmushell, and now you've got two. Use M-x cmushell to get
to the newest one, and C-x b <bufname> to get to the other.

Multiple Lisp and Scheme buffers work similarly. Commands that send text
from a source buffer to the process buffer will always send to the
buffer with the "official" name unless you explicitly override this
(see variable lisp/scheme/tea-buffer). I don't see any real good ways to
extend this. You could associate each source buffer with some process
buffer, but this gets messy. I've tried to keep it simple. If you find
yourself switching between multiple process buffers a lot, consider using
ilisp.el. Or write a little code to soup up cmulisp.el.


Documentation:
There is no manual or info node for any of this stuff. I am not going to
write one. Feel free to write one if you like, and submit it to me or the FSF.
It would be a good thing.

The .el files are copiously documented, however. If you want to use any of
these modes, I strongly suggest that you read the source files.  Really. Do
it. They contain many useful sections of comments. Skip anything you don't
understand; read the easy parts.  You may find some useful commands you didn't
know about.  The top of each file contains a clearly-marked, friendly
"suggestions for your .emacs file" section that will show you exactly how to
get set up using the package. Also, pay attention to the DEFVAR'd variables.
These are typically the means by which you can customise the package for your
local environment.

The usual on-line documentation on modes, variables and commands
is also available via c-h m, c-h v, and c-h d.


===============================================================================
This code is available under the terms of the Gnu Copyright agreement: you
can do whatever you like with it as long as you don't charge for it or hold
me responsible for anything it does.  Please send any comments, improvements,
bug reports, fixes or suggestions to olin.shivers@cs.cmu.edu.

I would like to thank all the people who took the time to send me the code
that went into this second release.
    	-Olin
    	Sept 12, 1990

shivers@a.gp.cs.cmu.edu (Olin Shivers) (09/13/90)

This message is broken into 5 parts so notesfiles sites can handle it:
1. The 2.01 release notes
2. The source for comint.el (this msg)
3. A shar file for cmushell.el and tea.el
4. A shar file for cmulisp.el and cmuscheme.el
5. A shar file for background.el and cmutex.el
-------------------------------------------------------------------------------
;;; -*-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.

;;; The changelog is at the end of this file.

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

;;; This hopefully generalises shell mode, lisp mode, tea mode, soar mode,...
;;; 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:
;;; - cmushell.el defines a shell-in-a-buffer mode.
;;; - cmulisp.el defines a simple lisp-in-a-buffer 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 a scheme-in-a-buffer 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.
;;; - cmutex.el defines tex and latex modes that invoke tex, latex, bibtex,
;;;   previewers, and printers from within emacs.
;;; - background.el allows csh-like job control inside emacs.
;;; It is pretty easy to make new derived modes for other processes.

;;; 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.

;;; 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)
(defconst comint-version "2.01")


;;; 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
;;; m-s     comint-previous-similar-input   Previous similar input
;;; 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.
;;;     comint-input-send	- function
;;;     comint-eol-on-send	- boolean

(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-input-sender (function comint-simple-send)
  "Function to actually send to PROCESS the STRING submitted by user.
Usually this is just 'comint-simple-send, but if your mode needs to 
massage the input string, this is your hook. This is called from
the user command comint-send-input. comint-simple-send just sends
the string plus a newline.")

(defvar comint-eol-on-send 'T
  "If non-nil, then jump to the end of the line before sending input to process.
See COMINT-SEND-INPUT")

(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.
Setting mode variable comint-eol-on-send means jump to the end of the line
before submitting new input.

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, comint-input-sender 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.

If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it.

\\{comint-mode-map}

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))
	(old-ptyp comint-ptyp)) ; preserve across local var kill. gross.
    (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-local-variable 'comint-prompt-regexp) ; Don't set; default
    (make-local-variable 'input-ring-size)      ; ...to global val.
    (make-local-variable 'input-ring)
    (make-local-variable 'input-ring-index)
    (setq input-ring-index 0)
    (make-local-variable 'comint-get-old-input)
    (make-local-variable 'comint-input-sentinel)
    (make-local-variable 'comint-input-filter)  
    (make-local-variable 'comint-input-sender)
    (make-local-variable 'comint-eol-on-send)
    (make-local-variable 'comint-ptyp)
    (setq comint-ptyp old-ptyp)
    (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)))))

;;; The old-ptyp stuff above is because we have to preserve the value of
;;; comint-ptyp across calls to comint-mode, in spite of the
;;; kill-all-local-variables that it does. Blech. Hopefully, this will all
;;; go away when a later release fixes the signalling bug.

(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 "\es" 'comint-previous-similar-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))))
	   (save-excursion
	     (set-buffer buffer)
	     (comint-mode)) ; Install local vars, mode, keymap, ...
	   (comint-exec buffer name program startfile switches)))
    buffer))

(defvar comint-ptyp t
  "True if communications via pty; false if by pipe. Buffer local.
This is to work around a bug in emacs process signalling.")

(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 (comint-exec-1 name buffer command switches)))
      (make-local-variable 'comint-ptyp)
      (setq comint-ptyp process-connection-type) ; T if pty, NIL if pipe.
      ;; Jump to the end, and set the process mark.
      (goto-char (point-max))
      (set-marker (process-mark proc) (point)))
      ;; 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))
	     (comint-send-string proc startfile)))
    buffer))

;;; This auxiliary function cranks up the process for comint-exec in
;;; the appropriate environment. It is twice as long as it should be
;;; because emacs has two distinct mechanisms for manipulating the
;;; process environment, selected at compile time with the
;;; MAINTAIN-ENVIRONMENT #define. In one case, process-environment
;;; is bound; in the other it isn't.

(defun comint-exec-1 (name buffer command switches)
  (if (boundp 'process-environment) ; Not a completely reliable test.
      (let ((process-environment
	     (comint-update-env process-environment
				(list (format "TERMCAP=emacs:co#%d:tc=unknown"
					      (screen-width))
				      "TERM=emacs"
				      "EMACS=t"))))
	(apply 'start-process name buffer command switches))

      (let ((tcapv (getenv "TERMCAP"))
	    (termv (getenv "TERM"))
	    (emv   (getenv "EMACS")))
	(unwind-protect
	     (progn (setenv "TERMCAP" (format "emacs:co#%d:tc=unknown"
					      (screen-width)))
		    (setenv "TERM" "emacs")
		    (setenv "EMACS" "t")
		    (apply 'start-process name buffer command switches))
	  (setenv "TERMCAP" tcapv)
	  (setenv "TERM"    termv)
	  (setenv "EMACS"   emv)))))
	     


;; This is just (append new old-env) that compresses out shadowed entries.
;; It's also pretty ugly, mostly due to elisp's horrible iteration structures.
(defun comint-update-env (old-env new)
  (let ((ans (reverse new))
	(vars (mapcar (function (lambda (vv)
			(and (string-match "^[^=]*=" vv)
			     (substring vv 0 (match-end 0)))))
		      new)))
    (while old-env
      (let* ((vv (car old-env)) ; vv is var=value
	     (var (and (string-match "^[^=]*=" vv)
		       (substring vv 0 (match-end 0)))))
	(setq old-env (cdr old-env))
	(cond ((not (and var (comint-mem var vars)))
	       (if var (setq var (cons var vars)))
	       (setq ans (cons vv ans))))))
    (nreverse ans)))

;;; This should be in emacs, but it isn't.
(defun comint-mem (item list &optional elt=)
  "Test to see if ITEM is equal to an item in LIST.
Option comparison function ELT= defaults to equal."
  (let ((elt= (or elt= (function equal)))
	(done nil))
    (while (and list (not done))
      (if (funcall elt= item (car list))
	  (setq done list)
	  (setq list (cdr list))))
    done))


;;; 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)
  "Number of elts in the 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)))
		 ((eq last-command 'comint-previous-similar-input)
		  (delete-region 
		   (process-mark (get-buffer-process (current-buffer)))
		   (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)))))
	 
(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* ((last-command last-command) ; preserve around r-f-m
		      (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
  (if (not (eq last-command 'comint-previous-input))
      (setq input-ring-index -1))
  (let ((str (regexp-quote str))
        (len (ring-length input-ring))
	(n (+ input-ring-index 1)))
    (while (and (< n len) (not (string-match str (ring-ref input-ring n))))
      (setq n (+ n 1)))
    (cond ((< n len)
	   (comint-previous-input (- n input-ring-index)))
	  (t (if (eq last-command 'comint-previous-input) 
		 (setq this-command 'comint-previous-input))
	     (message "Not found.")
	     (ding)))))


;;; 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"))))

;;;
;;; Similar input -- contributed by ccm and highly winning.
;;;
;;; Reenter input, removing back to the last insert point if it exists. 
;;;
(defvar comint-last-similar-string "" 
  "The string last used in a similar string search.")
(defun comint-previous-similar-input (arg)
  "Reenters the last input that matches the string typed so far.  If repeated 
successively older inputs are reentered.  If arg is 1, it will go back
in the history, if -1 it will go forward."
  (interactive "p")
  (if (not (comint-after-pmark-p))
      (error "Not after process mark"))
  (if (not (eq last-command 'comint-previous-similar-input))
      (setq input-ring-index -1
	    comint-last-similar-string 
	    (buffer-substring 
	     (process-mark (get-buffer-process (current-buffer)))
	     (point))))
  (let* ((size (length comint-last-similar-string))
	 (len (ring-length input-ring))
	 (n (+ input-ring-index arg))
	 entry)
    (while (and (< n len) 
		(or (< (length (setq entry (ring-ref input-ring n))) size)
		    (not (equal comint-last-similar-string 
				(substring entry 0 size)))))
      (setq n (+ n arg)))
    (cond ((< n len)
	   (setq input-ring-index n)
	   (if (eq last-command 'comint-previous-similar-input)
	       (delete-region (mark) (point)) ; repeat
	       (push-mark (point)))	      ; 1st time
	   (insert (substring entry size)))
	  (t (message "Not found.") (ding) (sit-for 1)))
    (message "%d" (1+ input-ring-index))))


(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 non-nil when called on the input.

If variable comint-eol-on-send is non-nil, then point is moved to the end of
line before sending 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)
			  (progn (if comint-eol-on-send (end-of-line))
				 (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)
	  (funcall comint-input-sender proc input)
	  (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-simple-send (proc string)
  "Default function for sending to PROC input STRING.
This just sends STRING plus a newline. To override this,
set the hook COMINT-INPUT-SENDER."
  (comint-send-string proc string)
  (comint-send-string proc "\n"))

(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")
	(comint-send-string proc
			    (if (stringp str) str
				(comint-read-noecho "Enter non-echoed text")))
	(comint-send-string proc "\n"))))


;;; Low-level process communication

(defvar comint-input-chunk-size 512
  "*Long inputs send to comint processes are broken up into chunks of this size.
If your process is choking on big inputs, try lowering the value.")

(defun comint-send-string (proc str)
  "Send PROCESS the contents of STRING as input.
This is equivalent to process-send-string, except that long input strings
are broken up into chunks of size comint-input-chunk-size. Processes
are given a chance to output between chunks. This can help prevent processes
from hanging when you send them long inputs on some OS's."
  (let* ((len (length str))
	 (i (min len comint-input-chunk-size)))
    (process-send-string proc (substring str 0 i))
    (while (< i len)
      (let ((next-i (+ i comint-input-chunk-size)))
	(accept-process-output)
	(process-send-string proc (substring str i (min len next-i)))
	(setq i next-i)))))

(defun comint-send-region (proc start end)
  "Sends to PROC the region delimited by START and END.
This is a replacement for process-send-region that tries to keep
your process from hanging on long inputs. See comint-send-string."
  (comint-send-string proc (buffer-substring start end)))


;;; 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 comint-ptyp))

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

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

(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 comint-ptyp))

(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 comint-ptyp))

(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 process 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 while the cursor is inside a string, *and*
;;; the string is an existing filename, *and* the filename is not a directory,
;;; then the string is taken as default. This allows you to just position
;;; your cursor over a string that's a filename and have it taken as default.
;;;
;;; If the command is given in a file buffer whose major mode 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-mode) t))

;;; This is pretty stupid about strings. It decides we're in a string
;;; if there's a quote on both sides of point on the current line.
(defun comint-extract-string ()
  "Returns string around point that starts the current line or nil." 
  (save-excursion
    (let* ((point (point))
	   (bol (progn (beginning-of-line) (point)))
	   (eol (progn (end-of-line) (point)))
	   (start (progn (goto-char point) 
			 (and (search-backward "\"" bol t) 
			      (1+ (point)))))
	   (end (progn (goto-char point)
		       (and (search-forward "\"" eol t)
			    (1- (point))))))
      (and start end
	   (buffer-substring start end)))))

(defun comint-get-source (prompt prev-dir/file source-modes mustmatch-p)
  (let* ((def (comint-source-default prev-dir/file source-modes))
         (stringfile (comint-extract-string))
	 (sfile-p (and stringfile
		       (file-exists-p stringfile)
		       (not (file-directory-p stringfile))))
	 (defdir  (if sfile-p (file-name-directory stringfile)
                      (car def)))
	 (deffile (if sfile-p (file-name-nondirectory stringfile)
                      (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)))))

;;; I am somewhat divided on this string-default feature. It seems
;;; to violate the principle-of-least-astonishment, in that it makes
;;; the default harder to predict, so you actually have to look and see
;;; what the default really is before choosing it. This can trip you up.
;;; On the other hand, it can be useful, I guess. I would appreciate feedback
;;; on this.
;;;     -Olin


;;; Simple process query facility.
;;; ===========================================================================
;;; This function is for commands that want to send a query to the process
;;; and show the response to the user. For example, a command to get the
;;; arglist for a Common Lisp function might send a "(arglist 'foo)" query
;;; to an inferior Common Lisp process.
;;; 
;;; This simple facility just sends strings to the inferior process and pops
;;; up a window for the process buffer so you can see what the process
;;; responds with.  We don't do anything fancy like try to intercept what the
;;; process responds with and put it in a pop-up window or on the message
;;; line. We just display the buffer. Low tech. Simple. Works good.

;;; Send to the inferior process PROC the string STR. Pop-up but do not select
;;; a window for the inferior process so that its response can be seen.
(defun comint-proc-query (proc str)
  (let* ((proc-buf (process-buffer proc))
	 (proc-mark (process-mark proc)))
    (display-buffer proc-buf)
    (set-buffer proc-buf) ; but it's not the selected *window*
    (let ((proc-win (get-buffer-window proc-buf))
	  (proc-pt (marker-position proc-mark)))
      (comint-send-string proc str) ; send the query
      (accept-process-output proc)  ; wait for some output
      ;; Try to position the proc window so you can see the answer.
      ;; This is bogus code. If you delete the (sit-for 0), it breaks.
      ;; I don't know why. Wizards invited to improve it.
      (if (not (pos-visible-in-window-p proc-pt proc-win))
	  (let ((opoint (window-point proc-win)))
	    (set-window-point proc-win proc-mark) (sit-for 0)
	    (if (not (pos-visible-in-window-p opoint proc-win))
		(push-mark opoint)
		(set-window-point proc-win opoint)))))))


;;; 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 an 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 the filename at point with an expanded, canonicalised, and
completed replacement.
\"Expanded\" means environment variables (e.g., $HOME) and ~'s are
replaced with the corresponding directories.  \"Canonicalised\" means ..
and \. are removed, and the filename is made absolute instead of relative.
See functions expand-file-name and substitute-in-file-name. See also
comint-dynamic-complete."
  (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 (expand-file-name (concat pathdir completion)))))))


(defun comint-dynamic-complete ()
  "Dynamically complete the filename at point.
This function is similar to comint-replace-by-expanded-filename, except
that it won't change parts of the filename already entered in the buffer; 
it just adds completion characters to the end of the filename."
  (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.
	   (goto-char (match-end 0))
	   (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
	   (let ((conf (current-window-configuration)))
	     (with-output-to-temp-buffer "*Help*"
	       (display-completion-list completions))
	     (sit-for 0)
	     (message "Hit space to flush.")
	     (let ((ch (read-char)))
	       (if (= ch ?\ )
		   (set-window-configuration conf)
		   (setq unread-command-char ch))))))))

; 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)

;;; 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 its 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)

;;; Change log:
;;; 9/12/89 
;;;  - Souped up the filename expansion procedures.
;;;    Doc strings are much clearer and more detailed.
;;;    Fixed a bug where doing a filename completion when the point
;;;    was in the middle of the filename instead of at the end would lose.
;;;
;;; 2/17/90 
;;;  - Souped up the command history stuff so that text inserted
;;;    by comint-previous-input-matching is removed by following
;;;    command history recalls. comint-next/previous-input-matching
;;;    is now much more smoothly integrated w/the command history stuff.
;;;  - Added comint-eol-on-send flag and comint-input-sender hook.
;;;    Comint-input-sender based on code contributed by Jeff Peck
;;;    (peck@sun.com).
;;;
;;; 3/13/90 ccm@cmu.cs.edu
;;;  - Added comint-previous-similar-input for looking up similar inputs.
;;;  - Added comint-send-and-get-output to allow snarfing input from
;;;    buffer. 
;;;  - Added the ability to pick up a source file by positioning over
;;;    a string in comint-get-source.
;;;  - Added add-hook to make it a little easier for the user to use
;;;    multiple hooks.
;;;  
;;; 5/22/90 shivers
;;; - Moved Chris' multiplexed ipc stuff to comint-ipc.el.
;;; - Altered Chris' comint-get-source string feature. The string
;;;   is only offered as a default if it names an existing file.
;;; - Changed comint-exec to directly crank up the process, instead
;;;   of calling the env program. This made background.el happy.
;;; - Added new buffer-local var comint-ptyp. The problem is that
;;;   the signalling functions don't work as advertised. If you are
;;;   communicating via pipes, the CURRENT-GROUP arg is supposed to
;;;   be ignored, but, unfortunately it seems to be the case that you
;;;   must pass a NIL for this arg in the pipe case. COMINT-PTYP
;;;   is a flag that tells whether the process is communicating
;;;   via pipes or a pty. The comint signalling functions use it
;;;   to determine the necessary CURRENT-GROUP arg value. The bug
;;;   has been reported to the Gnu folks.
;;; - comint-dynamic-complete flushes the help window if you hit space
;;;   after you execute it.
;;; - Added functions comint-send-string, comint-send-region and var 
;;;   comint-input-chunk-size.  comint-send-string tries to prevent processes
;;;   from hanging when you send them long strings by breaking them into
;;;   chunks and allowing process output between chunks. I got the idea from
;;;   Eero Simoncelli's Common Lisp package. Note that using
;;;   comint-send-string means that the process buffer's contents can change
;;;   during a call!  If you depend on process output only happening between
;;;   toplevel commands, this could be a problem. In such a case, use
;;;   process-send-string instead. If this is a problem for people, I'd like
;;;   to hear about it.
;;; - Added comint-proc-query as a simple mechanism for commands that
;;;   want to query an inferior process and display its response. For a
;;;   typical use, see lisp-show-arglist in cmulisp.el.
;;; - Added constant comint-version, which is now "2.01".
;;;
;;; 6/14/90 shivers
;;; - Had comint-update-env defined twice. Removed extra copy. Also
;;;   renamed mem to be comint-mem, for modularity. The duplication
;;;   was reported by Michael Meissner.
;;; 6/16/90 shivers
;;; - Emacs has two different mechanisms for maintaining the process
;;;   environment, determined at compile time by the MAINTAIN-ENVIRONMENT
;;;   #define. One uses the process-environment global variable, and
;;;   one uses a getenv/setenv interface. comint-exec assumed the
;;;   process-environment interface; it has been generalised (with
;;;   comint-exec-1) to handle both cases. Pretty bogus. We could,
;;;   of course, skip all this and just use the etc/env program to
;;;   handle the environment tweaking, but that obscures process
;;;   queries that other modules (like background.el) depend on. etc/env
;;;   is also fairly bogus. This bug, and some of the fix code was
;;;   reported by Dan Pierson.
;;;
;;; 9/5/90 shivers
;;; - Changed make-variable-buffer-local's to make-local-variable's.
;;;   This leaves non-comint-mode buffers alone. Stephane Payrard
;;;   reported the sloppy useage.
;;; - You can now go from comint-previous-similar-input to
;;;   comint-previous-input with no problem.

shivers@a.gp.cs.cmu.edu (Olin Shivers) (09/13/90)

This message is broken into 5 parts so notesfiles sites can handle it:
1. The 2.01 release notes
2. The source for comint.el
3. A shar file for cmushell.el and tea.el (this msg)
4. A shar file for cmulisp.el and cmuscheme.el
5. A shar file for background.el and cmutex.el
-------------------------------------------------------------------------------
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/shar1   to unpack this archive.
#
echo extracting cmushell.el...
cat >cmushell.el <<'!E!O!F!'
;;; -*-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.

;;; The changelog is at the end of file.

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

;;; This file defines a a shell-in-a-buffer package (cmushell mode) built on
;;; top of comint mode.  Cmushell mode is similar to, and intended to replace,
;;; its counterpart in the standard gnu emacs release. This replacement is
;;; more featureful, robust, and uniform than the released version.

;;; Since this mode is built on top of the general command-interpreter-in-
;;; a-buffer mode (comint mode), it shares a common base functionality, 
;;; and a common set of bindings, with all modes derived from comint mode.
;;; This makes these modes easier to use.

;;; 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 cmushell mode, see the comments below.

;;; Needs fixin:
;;; When sending text from a source file to a subprocess, the process-mark can 
;;; move off the window, so you can lose sight of the process interactions.
;;; Maybe I should ensure the process mark is in the window when I send
;;; text to the process? Switch selectable?

(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 from file cmushell.el
;; (autoload 'cmushell "cmushell"
;;           "Run an inferior shell process."
;;           t)
;;
;; ; Define C-c C-t to run my favorite command in cmushell 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 all comint-derived 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.

;;; 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
;;; 	    dirs    			    Resync the buffer's dir stack.
;;; 	    dirtrack-toggle                 Turn dir tracking on/off.
;;;
;;; 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-prompt-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., 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.

;;; Customisation and Buffer Variables
;;; ===========================================================================
;;; 

;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-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.")

;;; All the above vars aren't prefixed "cmushell-" to make them
;;; backwards compatible w/shell.el and old .emacs files.

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

(defvar cmushell-dirstack-query "dirs"
  "Command used by shell-resync-dirlist to query shell.")

(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")


;;; Basic Procedures
;;; ===========================================================================
;;;

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

If you accidentally suspend your process, use \\[comint-continue-subjob]
to continue it.

cd, pushd and popd commands given to the shell are watched by Emacs to keep
this buffer's default directory the same as the shell's working directory.
M-x dirs queries the shell and resyncs Emacs' idea of what the current 
    directory stack is.
M-x dirtrack-toggle turns directory tracking on and off.

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

Variables shell-cd-regexp, shell-pushd-regexp and shell-popd-regexp are used
to match their respective commands."
  (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 'cmushell-dirstack)
  (setq cmushell-dirstack nil)
  (make-local-variable 'cmushell-dirtrackp)
  (setq cmushell-dirtrackp t)
  (setq comint-input-sentinel 'cmushell-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
;;;     CMUSHELL-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 only spots the first command in a command sequence. E.g., it will
;;;    miss the cd in "ls; cd foo"
;;; 3. 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. Shell aliases mess it up. File sourcing
;;; messes it up. You run other processes under the shell; these each have
;;; separate working directories, and some have commands for manipulating
;;; their w.d.'s (e.g., the lcd command in ftp). Some of these programs have
;;; commands that do *not* effect the current w.d. at all, but look like they
;;; do (e.g., the cd command in ftp).  In shells that allow you job
;;; control, you can switch between jobs, all having different w.d.'s. So
;;; simply saying %3 can shift your w.d..
;;;
;;; The solution is to relax, not stress out about it, and settle for
;;; a hack that works pretty well in typical circumstances. Remember
;;; that a half-assed solution is more in keeping with the spirit of Unix, 
;;; anyway. Blech.
;;;
;;; One good hack not implemented here for users of programmable shells
;;; is to program up the shell w.d. manipulation commands to output
;;; a coded command sequence to the tty. Something like
;;;     ESC | <cwd> |
;;; where <cwd> is the new current working directory. Then trash the
;;; directory tracking machinery currently used in this package, and
;;; replace it with a process filter that watches for and strips out
;;; these messages.

;;; 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.
(defun cmushell-front-match (regexp str start)
  (eq start (string-match regexp str start)))

(defun cmushell-directory-tracker (str)
  "Tracks cd, pushd and popd commands issued to the shell.
This function is called on each input passed to the shell.
It watches for cd, pushd and popd commands and sets the buffer's
default directory to track these commands.

You may toggle this tracking on and off with M-x dirtrack-toggle.
If emacs gets confused, you can resync with the shell with M-x dirs.

See variables shell-cd-regexp, shell-pushd-regexp, and shell-popd-regexp.
Environment variables are expanded, see function substitute-in-file-name."
  (condition-case err
    (cond (cmushell-dirtrackp
	   (string-match "^\\s *" str) ; skip whitespace
	   (let ((bos (match-end 0))
		 (x nil))
	     (cond ((setq x (cmushell-match-cmd-w/optional-arg shell-popd-regexp
							       str bos))
		    (cmushell-process-popd (substitute-in-file-name x)))
		   ((setq x (cmushell-match-cmd-w/optional-arg shell-pushd-regexp
							       str bos))
		    (cmushell-process-pushd (substitute-in-file-name x)))
		   ((setq x (cmushell-match-cmd-w/optional-arg shell-cd-regexp
							       str bos))
		    (cmushell-process-cd (substitute-in-file-name x)))))))
    (error (message (car (cdr err))))))


;;; Try to match regexp CMD to string, anchored at position START.
;;; CMD may be followed by a single argument. If a match, then return
;;; the argument, if there is one, or the empty string if not. If
;;; no match, return nil.

(defun cmushell-match-cmd-w/optional-arg (cmd str start)
  (and (cmushell-front-match cmd str start)
       (let ((eoc (match-end 0))) ; end of command
	 (cond ((cmushell-front-match "\\s *\\(\;\\|$\\)" str eoc)
		"")			; no arg
	       ((cmushell-front-match "\\s +\\([^ \t\;]+\\)\\s *\\(\;\\|$\\)"
				      str eoc)
		(substring str (match-beginning 1) (match-end 1))) ; arg
	       (t nil))))) ; something else.
;;; The first regexp is [optional whitespace, (";" or the end of string)].
;;; The second regexp is [whitespace, (an arg), optional whitespace,
;;;     (";" or end of string)].


;;; popd [+n]
(defun cmushell-process-popd (arg)
  (let ((num (if (zerop (length arg)) 0 ; no arg means +0
		 (cmushell-extract-num arg))))
    (if (and num (< num (length cmushell-dirstack)))
	(if (= num 0) ; condition-case because the CD could lose.
	    (condition-case nil (progn (cd (car cmushell-dirstack))
				       (setq cmushell-dirstack
					     (cdr cmushell-dirstack))
				       (cmushell-dirstack-message))
	      (error (message "Couldn't cd.")))
	    (let* ((ds (cons nil cmushell-dirstack))
		   (cell (nthcdr (- num 1) ds)))
	      (rplacd cell (cdr (cdr cell)))
	      (setq cmushell-dirstack (cdr ds))
	      (cmushell-dirstack-message)))
	(message "Bad popd."))))


;;; cd [dir]
(defun cmushell-process-cd (arg)
  (condition-case nil (progn (cd (if (zerop (length arg)) (getenv "HOME")
				     arg))
			     (cmushell-dirstack-message))
	   (error (message "Couldn't cd."))))


;;; pushd [+n | dir]
(defun cmushell-process-pushd (arg)
  (if (zerop (length arg))
      ;; no arg -- swap pwd and car of shell stack
      (condition-case nil (if cmushell-dirstack
			      (let ((old default-directory))
				(cd (car cmushell-dirstack))
				(setq cmushell-dirstack
				      (cons old (cdr cmushell-dirstack)))
				(cmushell-dirstack-message))
			      (message "Directory stack empty."))
	(message "Couldn't cd."))

      (let ((num (cmushell-extract-num arg)))
	(if num				; pushd +n
	    (if (> num (length cmushell-dirstack))
		(message "Directory stack not that deep.")
		(let* ((ds (cons default-directory cmushell-dirstack))
		       (dslen (length ds))
		       (front (nthcdr num ds))
		       (back (reverse (nthcdr (- dslen num) (reverse ds))))
		       (new-ds (append front back)))
		  (condition-case nil
		      (progn (cd (car new-ds))
			     (setq cmushell-dirstack (cdr new-ds))
			     (cmushell-dirstack-message))
		    (error (message "Couldn't cd.")))))
	       
	    ;; pushd <dir>
	    (let ((old-wd default-directory))
	      (condition-case nil
		  (progn (cd arg)
			 (setq cmushell-dirstack
			       (cons old-wd cmushell-dirstack))
			 (cmushell-dirstack-message))
		(error (message "Couldn't cd."))))))))

;; If STR is of the form +n, for n>0, return n. Otherwise, nil.
(defun cmushell-extract-num (str)
  (and (string-match "^\\+[1-9][0-9]*$" str)
       (string-to-int str)))


(defun cmushell-dirtrack-toggle ()
  "Turn directory tracking on and off in a cmushell buffer."
  (interactive)
  (setq cmushell-dirtrackp (not cmushell-dirtrackp))
  (message "directory tracking %s."
	   (if cmushell-dirtrackp "ON" "OFF")))

;;; For your typing convenience:
(fset 'dirtrack-toggle 'cmushell-dirtrack-toggle)


(defun cmushell-resync-dirs ()
  "Resync the buffer's idea of the current directory stack.
This command queries the shell with the command bound to 
cmushell-dirstack-query (default \"dirs\"), reads the next
line output and parses it to form the new directory stack.
DON'T issue this command unless the buffer is at a shell prompt.
Also, note that if some other subprocess decides to do output
immediately after the query, its output will be taken as the
new directory stack -- you lose. If this happens, just do the
command again."
  (interactive)
  (let* ((proc (get-buffer-process (current-buffer)))
	 (pmark (process-mark proc)))
    (goto-char pmark)
    (insert cmushell-dirstack-query) (insert "\n")
    (sit-for 0) ; force redisplay
    (comint-send-string proc cmushell-dirstack-query) 
    (comint-send-string proc "\n")
    (set-marker pmark (point))
    (let ((pt (point))) ; wait for 1 line
      ;; This extra newline prevents the user's pending input from spoofing us.
      (insert "\n") (backward-char 1)
      (while (not (looking-at ".+\n"))
	(accept-process-output proc)
	(goto-char pt)))
    (goto-char pmark) (delete-char 1) ; remove the extra newline
    ;; That's the dirlist. grab it & parse it.
    (let* ((dl (buffer-substring (match-beginning 0) (- (match-end 0) 1)))
	   (dl-len (length dl))
	   (ds '())			; new dir stack
	   (i 0))
      (while (< i dl-len)
	;; regexp = optional whitespace, (non-whitespace), optional whitespace
	(string-match "\\s *\\(\\S +\\)\\s *" dl i) ; pick off next dir
	(setq ds (cons (substring dl (match-beginning 1) (match-end 1))
		       ds))
	(setq i (match-end 0)))
      (let ((ds (reverse ds)))
	(condition-case nil
	    (progn (cd (car ds))
		   (setq cmushell-dirstack (cdr ds))
		   (cmushell-dirstack-message))
	  (error (message "Couldn't cd.")))))))

;;; For your typing convenience:
(fset 'dirs 'cmushell-resync-dirs)


;;; Show the current dirstack on the message line.
;;; Pretty up dirs a bit by changing "/usr/jqr/foo" to "~/foo".
;;; (This isn't necessary if the dirlisting is generated with a simple "dirs".)
;;; All the commands that mung the buffer's dirstack finish by calling
;;; this guy.
(defun cmushell-dirstack-message ()
  (let ((msg "")
	(ds (cons default-directory cmushell-dirstack)))
    (while ds
      (let ((dir (car ds)))
	(if (string-match (format "^%s\\(/\\|$\\)" (getenv "HOME")) dir)
	    (setq dir (concat "~/" (substring dir (match-end 0)))))
	(if (string-equal dir "~/") (setq dir "~"))
	(setq msg (concat msg dir " "))
	(setq ds (cdr ds))))
    (message msg)))



;;; 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)

;;; Change Log
;;; ===========================================================================
;;; Olin 8/88
;;; Created.
;;;
;;; Olin 5/26/90
;;; - Split cmulisp and cmushell modes into separate files. 
;;;   Not only is this a good idea, it's apparently the way it'll be rel 19.
;;; - Souped up the directory tracking; it now can handle pushd, pushd +n, 
;;;   and popd +n.
;;; - Added cmushell-dirtrack-toggle command to toggle the directory
;;;   tracking that cmushell tries to do. This is useful, for example,
;;;   when you are running ftp -- it prevents the ftp "cd" command from
;;;   spoofing the tracking machinery. This command is also named 
;;;   dirtrack-toggle, so you need only type M-x dirtrack to run it.
;;; - Added cmushell-resync-dirs command. This queries the shell
;;;   for the current directory stack, and resets the buffer's stack
;;;   accordingly. This command is also named dirs, so you need only type
;;;   M-x dirs to run it.
;;; - Bits of the new directory tracking code were adapted from source
;;;   contributed by Vince Broman, Jeff Peck, and Barry Warsaw.
;;; - See also the improvements made to comint.el at the same time.
;;; - Renamed several variables. Mostly this comprised changing "shell"
;;;   to "cmushell" in the names. The only variables that are not prefixed
;;;   with "cmushell-" are the ones that are common with shell.el:
;;;       explicit-shell-file-name shell-prompt-pattern explicit-csh-args 
;;;       and shell-cd/popd/pushd-regexp
;;;   The variables and functions that were changed to have "cmushell-" 
;;;   prefixes are:
;;;       shell-directory-stack (v), shell-directory-tracker (f)
;;;   This should not affect users, only elisp hackers. Hopefully
;;;   one day shell.el will just go away, and we can drop all this
;;;   "cmushell" bullshit.
;;; - Upgraded process sends to use comint-send-string instead of
;;;   process-send-string.
;;;
;;; Olin 6/14/90
;;; - If your shell is named <shellname>, and a variable named
;;;   explicit-<shellname>-args exists, cmushell is supposed
;;;   to use its value as the arglist to the shell invocation.
;;;   E.g., if you define explicit-csh-args to be 
;;;   ("-ifx"), then when cmushell cranks up a csh, it execs it
;;;   as "csh -ifx". This is what is documented. What has actually
;;;   been the case is that the variable checked is
;;;   explicit-<shellname>-arguments, not explicit-<shellname>-args.
;;;   The documentation has been changed to conform to the code (for
;;;   backwards compatibility with shell.el). This bug is inherited from
;;;   the same bug in shell.el.
;;;   This bug reported by Stephen Anderson.
;;;
;;; Olin 9/5/90
;;; - Arguments to cd, popd, and pushd now have their env vars expanded
;;;   out by the tracking machinery. So if you say "cd $SRCDIR/funs", the
;;;   $SRCDIR var will be replaced by its value *in emacs' process
;;;   environment*. If this is different from the shell's binding of the
;;;   variable, you lose.  Several users needed this feature, fragile
;;;   though it may be.  The fix was contributed by sk@thp.Uni-Koeln.DE.
!E!O!F!
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/shar1   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.
;;;
;;; Change log at end of file.


;; 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 2)
(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 "\M-\C-x"  'tea-send-definition)
       (define-key inferior-t-mode-map "\C-x\C-e" 'tea-send-last-sexp)
       (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-x\C-e" 'tea-send-last-sexp)  ; 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.
For information on running multiple processes in multiple buffers, see
documentation for variable tea-buffer.

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))))

;;; This will break if you have an argument with whitespace, as in
;;; string = "-ab +c -x 'you lose'".
(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 run-tea command")

;;; Obsolete
(defun tea (&rest foo) (message "Use run-tea"))

(defun run-tea (cmd)
  "Run an inferior T process, input and output via buffer *tea*.
If there is a process already running in *tea*, just switch to that buffer.
With argument, allows you to edit the command line (default is value
of 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 (list (if current-prefix-arg
			 (read-string "Run T: " tea-program-name)
			 tea-program-name)))
  (if (not (comint-check-proc "*tea*"))
      (let ((cmdlist (tea-args-to-list cmd)))
	(set-buffer (apply 'make-comint "tea" (car cmdlist)
			   nil (cdr cmdlist)))
	(inferior-t-mode)))
  (setq tea-buffer "*tea*")
  (switch-to-buffer "*tea*"))

(defun tea-send-region (start end)
  "Send the current region to the inferior T process"
  (interactive "r")
  (comint-send-region (tea-proc) start end)
  (comint-send-string (tea-proc) "\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-send-last-sexp ()
  "Send the previous sexp to the inferior T process."
  (interactive)
  (tea-send-region (save-excursion (backward-sexp) (point)) (point)))

(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")
  (comint-send-string (tea-proc) "(orbit '(block ")
  (comint-send-region (tea-proc) start end)
  (comint-send-string (tea-proc) "))\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 T process buffer.
With argument, positions cursor at end of buffer."
  (interactive "P")
  (if (get-buffer tea-buffer)
      (pop-to-buffer tea-buffer)
      (error "No current process buffer. See variable tea-buffer."))
  (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)))
  (comint-send-string (tea-proc) (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)))
  (comint-send-string (tea-proc) (concat "(compile-file \""
					 file-name
					 "\"\)\n"))
  (switch-to-tea t))


;;; This helps when you run more than one T process at a time.

;;; If we're in some inferior T buffer, return its process,
;;; even if the buffer's been renamed. If we're elsewhere, assume
;;; the standard process named "tea".
(defun tea-proc ()
  (let ((proc (get-buffer-process (current-buffer))))
    (if (and proc (eq major-mode 'inferior-t-mode)) proc
	(get-process "tea"))))

(defvar tea-buffer nil "*The current T process buffer.

MULTIPLE PROCESS SUPPORT
===========================================================================
Tea.el supports, in a fairly simple fashion, running multiple T
processes. To run multiple T processes, you start the first up with
\\[run-tea]. It will be in a buffer named *tea*. Rename this buffer
with \\[rename-buffer]. You may now start up a new process with another
\\[run-tea]. It will be in a new buffer, named *tea*. You can
switch between the different process buffers with \\[switch-to-buffer].

Commands that send text from source buffers to T processes --
like tea-send-definition or tea-compile-region -- have to choose a
process to send to, when you have more than one T process around. This
is determined by the global variable tea-buffer. Suppose you
have three inferior T's running:
    Buffer	Process
    foo		tea
    bar		tea<2>
    *tea*       tea<3>
If you do a \\[tea-send-definition-and-go] command on some T source code,
what process do you send it to?

- If you're in a process buffer (foo, bar, or *tea*), 
  you send it to that process.
- If you're in some other buffer (e.g., a source file), you
  send it to the process attached to buffer tea-buffer.
This process selection is performed by function tea-proc.

Whenever \\[run-tea] fires up a new process, it resets tea-buffer
to be the new process's buffer. If you only run one process, this will
do the right thing. If you run multiple processes, you can change
tea-buffer to another process buffer with \\[set-variable].

More sophisticated approaches are, of course, possible. If you find youself
needing to switch back and forth between multiple processes frequently,
you may wish to consider ilisp.el, a larger, more sophisticated package
for running inferior Lisp and Scheme processes. The approach taken here is
for a minimal, simple implementation. Feel free to extend it.")

(defun tea-proc ()
  "Returns the current T process. See variable tea-buffer."
  (let ((proc (get-buffer-process (if (eq major-mode 'inferior-t-mode)
				      (current-buffer)
				      tea-buffer))))
    (or proc
	(error "No current process. See variable tea-buffer"))))


;;; 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)

;;; CHANGE LOG
;;; ===========================================================================
;;; 8/88 Olin
;;; Created.
;;;
;;; 2/15/89 Olin
;;; Removed -emacs flag from process invocation. It's only useful for
;;; cscheme, and makes cscheme assume it's running under xscheme.el,
;;; which messes things up royally. A bug.
;;;
;;; 5/22/90 Olin
;;; Upgraded to use comint-send-string and comint-send-region.
;;; - run-tea now offers to let you edit the command line if
;;;   you invoke it with a prefix-arg. M-x tea is redundant, and
;;;   has been removed.
;;; - Explicit references to process "tea" have been replaced with
;;;   (tea-proc). This allows better handling of multiple process bufs.
;;; - Added tea-send-last-sexp, bound to C-x C-e. A gnu convention.
;;; - Have not added process query facility a la cmulisp.el's lisp-show-arglist
;;;   and friends, but interested hackers might find a useful application
;;;   of this facility.
!E!O!F!

shivers@a.gp.cs.cmu.edu (Olin Shivers) (09/13/90)

This message is broken into 5 parts so notesfiles sites can handle it:
1. The 2.01 release notes
2. The source for comint.el
3. A shar file for cmushell.el and tea.el
4. A shar file for cmulisp.el and cmuscheme.el (this msg)
5. A shar file for background.el and cmutex.el
-------------------------------------------------------------------------------
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/shar2   to unpack this archive.
#
echo extracting cmulisp.el...
cat >cmulisp.el <<'!E!O!F!'
;;; -*-Emacs-Lisp-*- cmulisp.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 replaces the standard inferior-lisp mode.
;;; Hacked from tea.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.
;;;
;;; Change log at end of file.

;;; This file defines a a lisp-in-a-buffer package (cmulisp mode) built on top
;;; of comint mode.  Cmulisp mode is similar to, and intended to replace, its
;;; counterpart in the standard gnu emacs release. This replacements is more
;;; featureful, robust, and uniform than the released version.  The key
;;; bindings are also more compatible with the bindings of Hemlock and Zwei
;;; (the Lisp Machine emacs).

;;; Since this mode is built on top of the general command-interpreter-in-
;;; a-buffer mode (comint mode), it shares a common base functionality, 
;;; and a common set of bindings, with all modes derived from comint mode.
;;; This makes these modes easier to use.

;;; For documentation on the functionality provided by comint mode, and
;;; the hooks available for customising it, see the file comint.el.
;;; For further information on cmulisp mode, 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 'cmulisp)

;; YOUR .EMACS FILE
;;=============================================================================
;; Some suggestions for your .emacs file.
;;
;; ; If cmulisp 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 cmulisp from file cmulisp.el
;; (autoload 'cmulisp "cmulisp"
;;           "Run an inferior Lisp process."
;;           t)
;;
;; ; Define C-c C-t to run my favorite command in cmulisp mode:
;; (setq cmulisp-load-hook
;;       '((lambda () 
;;           (define-key cmulisp-mode-map "\C-c\C-t" 'favorite-cmd))))


;;; Brief Command Documentation:
;;;============================================================================
;;; Comint Mode Commands: (common to cmulisp and all comint-derived 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-x c-e lisp-eval-last-sexp     Send the previous sexp 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.)
;;; c-c c-d lisp-describe-sym	    Query Lisp for a symbol's description.
;;; c-c a   lisp-show-arglist	    Query Lisp for function's arglist.
;;; c-c f   lisp-show-function-documentation Query Lisp for a function's doc.
;;; c-c v   lisp-show-variable-documentation Query Lisp for a variable's doc.

;;; 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.

;;; Read the rest of this file for more information.

(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-x\C-e" 'lisp-eval-last-sexp)
       (define-key cmulisp-mode-map "\C-cl"    'lisp-load-file)
       (define-key cmulisp-mode-map "\C-ck"    'lisp-compile-file)
       (define-key cmulisp-mode-map "\C-ca"    'lisp-show-arglist)
       (define-key cmulisp-mode-map "\C-c\C-d" 'lisp-describe-sym)
       (define-key cmulisp-mode-map "\C-cf"    'lisp-show-function-documentation)
       (define-key cmulisp-mode-map "\C-cv"    'lisp-show-variable-documentation)))

;;; 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-x\C-e" 'lisp-eval-last-sexp) ; 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
(define-key lisp-mode-map "\C-ca"    'lisp-show-arglist)
(define-key lisp-mode-map "\C-c\C-d" 'lisp-describe-sym)
(define-key lisp-mode-map "\C-cf"    'lisp-show-function-documentation)
(define-key lisp-mode-map "\C-cv"    'lisp-show-variable-documentation)


(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.

For information on running multiple processes in multiple buffers, see
documentation for variable cmulisp-buffer.

\\{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)))
  (setq cmulisp-buffer "*cmulisp*")
  (switch-to-buffer "*cmulisp*"))

(defun lisp-eval-region (start end)
  "Send the current region to the inferior Lisp process."
  (interactive "r")
  (comint-send-region (cmulisp-proc) start end)
  (comint-send-string (cmulisp-proc) "\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))))

(defun lisp-eval-last-sexp ()
  "Send the previous sexp to the inferior Lisp process."
  (interactive)
  (lisp-eval-region (save-excursion (backward-sexp) (point)) (point)))

;;; CommonLisp COMPILE sux. 
(defun lisp-compile-region (start end)
  "Compile the current region in the inferior Lisp process."
  (interactive "r")
  (comint-send-string (cmulisp-proc)
    (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")
  (if (get-buffer cmulisp-buffer)
      (pop-to-buffer cmulisp-buffer)
      (error "No current process buffer. See variable cmulisp-buffer."))
  (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)))
  (comint-send-string (cmulisp-proc)
		      (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)))
  (comint-send-string (cmulisp-proc) (concat "(compile-file \""
					     file-name
					     "\"\)\n"))
  (switch-to-lisp t))



;;; Documentation functions: function doc, var doc, arglist, and
;;; describe symbol.
;;; ===========================================================================

;;; Command strings
;;; ===============

(defvar lisp-function-doc-command
  "(let ((fn '%s))
     (format t \"Documentation for ~a:~&~a\"
	     fn (documentation fn 'function))
     (values))\n"
  "Command to query inferior Lisp for a function's documentation.")

(defvar lisp-var-doc-command
  "(let ((v '%s))
     (format t \"Documentation for ~a:~&~a\"
	     v (documentation v 'variable))
     (values))\n"
  "Command to query inferior Lisp for a variable's documentation.")

(defvar lisp-arglist-command
  "(let ((fn '%s))
     (format t \"Arglist for ~a: ~a\" fn (arglist fn))
     (values))\n"
  "Command to query inferior Lisp for a function's arglist.")

(defvar lisp-describe-sym-command
  "(describe '%s)\n"
  "Command to query inferior Lisp for a variable's documentation.")


;;; Ancillary functions
;;; ===================

;;; Reads a string from the user.
(defun lisp-symprompt (prompt default)
  (list (let* ((prompt (if default
			   (format "%s (default %s): " prompt default)
			   (concat prompt ": ")))
	       (ans (read-string prompt)))
	  (if (zerop (length ans)) default ans))))


;;; Adapted from function-called-at-point in help.el.
(defun lisp-fn-called-at-pt ()
  "Returns the name of the function called in the current call.
Nil if it can't find one."
  (condition-case nil
      (save-excursion
	(narrow-to-region (max (point-min) (- (point) 1000)) (point-max))
	(backward-up-list 1)
	(forward-char 1)
	(let ((obj (read (current-buffer))))
	  (and (symbolp obj) obj)))
    (error nil)))


;;; Adapted from variable-at-point in help.el.
(defun lisp-var-at-pt ()
  (condition-case ()
      (save-excursion
	(forward-sexp -1)
	(skip-chars-forward "'")
	(let ((obj (read (current-buffer))))
	  (and (symbolp obj) obj)))
    (error nil)))


;;; Documentation functions: fn and var doc, arglist, and symbol describe.
;;; ======================================================================

(defun lisp-show-function-documentation (fn)
  "Send a command to the inferior Lisp to give documentation for function FN.
See variable lisp-function-doc-command."
  (interactive (lisp-symprompt "Function doc" (lisp-fn-called-at-pt)))
  (comint-proc-query (cmulisp-proc) (format lisp-function-doc-command fn)))

(defun lisp-show-variable-documentation (var)
  "Send a command to the inferior Lisp to give documentation for function FN.
See variable lisp-var-doc-command."
  (interactive (lisp-symprompt "Variable doc" (lisp-var-at-pt)))
  (comint-proc-query (cmulisp-proc) (format lisp-var-doc-command var)))

(defun lisp-show-arglist (fn)
  "Sends an query to the inferior Lisp for the arglist for function FN.
See variable lisp-arglist-command."
  (interactive (lisp-symprompt "Arglist" (lisp-fn-called-at-pt)))
  (comint-proc-query (cmulisp-proc) (format lisp-arglist-command fn)))

(defun lisp-describe-sym (sym)
  "Send a command to the inferior Lisp to describe symbol SYM.
See variable lisp-describe-sym-command."
  (interactive (lisp-symprompt "Describe" (lisp-var-at-pt)))
  (comint-proc-query (cmulisp-proc) (format lisp-describe-sym-command sym)))


(defvar cmulisp-buffer nil "*The current cmulisp process buffer.

MULTIPLE PROCESS SUPPORT
===========================================================================
Cmulisp.el supports, in a fairly simple fashion, running multiple Lisp
processes. To run multiple Lisp processes, you start the first up with
\\[cmulisp]. It will be in a buffer named *cmulisp*. Rename this buffer
with \\[rename-buffer]. You may now start up a new process with another
\\[cmulisp]. It will be in a new buffer, named *cmulisp*. You can
switch between the different process buffers with \\[switch-to-buffer].

Commands that send text from source buffers to Lisp processes --
like lisp-eval-defun or lisp-show-arglist -- have to choose a process
to send to, when you have more than one Lisp process around. This
is determined by the global variable cmulisp-buffer. Suppose you
have three inferior lisps running:
    Buffer	Process
    foo		cmulisp
    bar		cmulisp<2>
    *cmulisp*   cmulisp<3>
If you do a \\[lisp-eval-defun-and-go] command on some Lisp source code, 
what process do you send it to?

- If you're in a process buffer (foo, bar, or *cmulisp*), 
  you send it to that process.
- If you're in some other buffer (e.g., a source file), you
  send it to the process attached to buffer cmulisp-buffer.
This process selection is performed by function cmulisp-proc.

Whenever \\[cmulisp] fires up a new process, it resets cmulisp-buffer
to be the new process's buffer. If you only run one process, this will
do the right thing. If you run multiple processes, you can change
cmulisp-buffer to another process buffer with \\[set-variable].

More sophisticated approaches are, of course, possible. If you find youself
needing to switch back and forth between multiple processes frequently,
you may wish to consider ilisp.el, a larger, more sophisticated package
for running inferior Lisp processes. The approach taken here is for a
minimal, simple implementation. Feel free to extend it.")

(defun cmulisp-proc ()
  "Returns the current cmulisp process. See variable cmulisp-buffer."
  (let ((proc (get-buffer-process (if (eq major-mode 'inferior-lisp-mode)
				      (current-buffer)
				      cmulisp-buffer))))
    (or proc
	(error "No current process. See variable cmulisp-buffer"))))


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

;;; CHANGE LOG
;;; ===========================================================================
;;; 5/24/90 Olin
;;; - Split cmulisp and cmushell modes into separate files. 
;;;   Not only is this a good idea, it's apparently the way it'll be rel 19.
;;; - Upgraded process sends to use comint-send-string instead of
;;;   process-send-string.
;;; - Explicit references to process "cmulisp" have been replaced with
;;;   (cmulisp-proc). This allows better handling of multiple process bufs.
;;; - Added process query and var/function/symbol documentation
;;;   commands. Based on code written by Douglas Roberts.
;;; - Added lisp-eval-last-sexp, bound to C-x C-e.
!E!O!F!
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/shar2   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.
;;;
;;; The changelog is at the end of this file.
;;;
;;; NOTE: MIT Cscheme, when invoked with the -emacs flag, has a special user
;;; interface that communicates process state back to the superior emacs by
;;; outputting special control sequences. The gnumacs package, xscheme.el, has
;;; lots and lots of special purpose code to read these control sequences, and
;;; so is very tightly integrated with the cscheme process. The cscheme
;;; interrupt handler and debugger read single character commands in cbreak
;;; mode; when this happens, xscheme.el switches to special keymaps that bind
;;; the single letter command keys to emacs functions that directly send the
;;; character to the scheme process.  Cmuscheme mode does *not* provide this
;;; functionality. If you are a cscheme user, you may prefer to use the
;;; xscheme.el/cscheme -emacs interaction.
;;; 
;;; Here's a summary of the pros and cons, as I see them.
;;; xscheme: Tightly integrated with inferior cscheme process!  A few commands
;;;	     not in cmuscheme. But. Integration is a bit of a hack.  Input
;;;	     history only keeps the immediately prior input. Bizarre
;;;	     keybindings.
;;; 
;;; cmuscheme: Not tightly integrated with inferior cscheme process.  But.
;;;            Carefully integrated functionality with the entire suite of
;;;            comint-derived CMU process modes. Keybindings reminiscent of
;;;            Zwei and Hemlock. Good input history. A few commands not in
;;;            xscheme.
;;;  
;;; It's a tradeoff. Pay your money; take your choice. If you use a Scheme
;;; that isn't Cscheme, of course, there isn't a choice. Xscheme.el is *very*
;;; Cscheme-specific; you must use cmuscheme.el.  Interested parties are
;;; invited to port xscheme functionality on top of comint mode...

;; 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. Three fixes:
;;; - Put the autoload on your scheme mode hook and in your .emacs toplevel:
;;;   (setq scheme-mode-hook
;;;         '((lambda () (autoload 'run-scheme "cmuscheme"
;;;                                "Run an inferior Scheme" t))))
;;;   (autoload 'run-scheme "cmuscheme" "Run an inferior Scheme" t)
;;;   Now when scheme.el autoloads, it will restore the run-scheme autoload.
;;; - 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 inferior-scheme-mode-map "\M-\C-x" ;gnu convention
	           'scheme-send-definition)
       (define-key inferior-scheme-mode-map "\C-x\C-e" 'scheme-send-last-sexp)
       (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-x\C-e" 'scheme-send-last-sexp);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.
For information on running multiple processes in multiple buffers, see
documentation for variable scheme-buffer.

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 run-scheme command")

;;; Obsolete
(defun scheme (&rest foo)
  "Use run-scheme"
  (interactive)
  (message "Use run-scheme")
  (ding))

(defun run-scheme (cmd)
  "Run an inferior Scheme process, input and output via buffer *scheme*.
If there is a process already running in *scheme*, just switch to that buffer.
With argument, allows you to edit the command line (default is value
of 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 (list (if current-prefix-arg
			 (read-string "Run Scheme: " scheme-program-name)
			 scheme-program-name)))
  (if (not (comint-check-proc "*scheme*"))
      (let ((cmdlist (scheme-args-to-list cmd)))
	(set-buffer (apply 'make-comint "scheme" (car cmdlist)
			   nil (cdr cmdlist)))
	(inferior-scheme-mode)))
  (setq scheme-buffer "*scheme*")
  (switch-to-buffer "*scheme*"))


(defun scheme-send-region (start end)
  "Send the current region to the inferior Scheme process."
  (interactive "r")
  (comint-send-region (scheme-proc) start end)
  (comint-send-string (scheme-proc) "\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))))

(defun scheme-send-last-sexp ()
  "Send the previous sexp to the inferior Scheme process."
  (interactive)
  (scheme-send-region (save-excursion (backward-sexp) (point)) (point)))

(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")
  (comint-send-string (scheme-proc) (format scheme-compile-exp-command
					    (format "(begin %s)"
						    (buffer-substring start end))))
  (comint-send-string (scheme-proc) "\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 process buffer.
With argument, positions cursor at end of buffer."
  (interactive "P")
  (if (get-buffer scheme-buffer)
      (pop-to-buffer scheme-buffer)
      (error "No current process buffer. See variable scheme-buffer."))
  (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)))
  (comint-send-string (scheme-proc) (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)))
  (comint-send-string (scheme-proc) (concat "(compile-file \""
					    file-name
					    "\"\)\n"))
  (switch-to-scheme t))


(defvar scheme-buffer nil "*The current scheme process buffer.

MULTIPLE PROCESS SUPPORT
===========================================================================
Cmuscheme.el supports, in a fairly simple fashion, running multiple Scheme
processes. To run multiple Scheme processes, you start the first up with
\\[run-scheme]. It will be in a buffer named *scheme*. Rename this buffer
with \\[rename-buffer]. You may now start up a new process with another
\\[run-scheme]. It will be in a new buffer, named *scheme*. You can
switch between the different process buffers with \\[switch-to-buffer].

Commands that send text from source buffers to Scheme processes --
like scheme-send-definition or scheme-compile-region -- have to choose a
process to send to, when you have more than one Scheme process around. This
is determined by the global variable scheme-buffer. Suppose you
have three inferior Schemes running:
    Buffer	Process
    foo		scheme
    bar		scheme<2>
    *scheme*    scheme<3>
If you do a \\[scheme-send-definition-and-go] command on some Scheme source
code, what process do you send it to?

- If you're in a process buffer (foo, bar, or *scheme*), 
  you send it to that process.
- If you're in some other buffer (e.g., a source file), you
  send it to the process attached to buffer scheme-buffer.
This process selection is performed by function scheme-proc.

Whenever \\[run-scheme] fires up a new process, it resets scheme-buffer
to be the new process's buffer. If you only run one process, this will
do the right thing. If you run multiple processes, you can change
scheme-buffer to another process buffer with \\[set-variable].

More sophisticated approaches are, of course, possible. If you find youself
needing to switch back and forth between multiple processes frequently,
you may wish to consider ilisp.el, a larger, more sophisticated package
for running inferior Lisp and Scheme processes. The approach taken here is
for a minimal, simple implementation. Feel free to extend it.")

(defun scheme-proc ()
  "Returns the current scheme process. See variable scheme-buffer."
  (let ((proc (get-buffer-process (if (eq major-mode 'inferior-scheme-mode)
				      (current-buffer)
				      scheme-buffer))))
    (or proc
	(error "No current process. See variable scheme-buffer"))))


;;; 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)


;;; CHANGE LOG
;;; ===========================================================================
;;; 8/88 Olin
;;; Created. 
;;;
;;; 2/15/89 Olin
;;; Removed -emacs flag from process invocation. It's only useful for
;;; cscheme, and makes cscheme assume it's running under xscheme.el,
;;; which messes things up royally. A bug.
;;;
;;; 5/22/90 Olin
;;; - Upgraded to use comint-send-string and comint-send-region.
;;; - run-scheme now offers to let you edit the command line if
;;;   you invoke it with a prefix-arg. M-x scheme is redundant, and
;;;   has been removed.
;;; - Explicit references to process "scheme" have been replaced with
;;;   (scheme-proc). This allows better handling of multiple process bufs.
;;; - Added scheme-send-last-sexp, bound to C-x C-e. A gnu convention.
;;; - Have not added process query facility a la cmulisp.el's lisp-show-arglist
;;;   and friends, but interested hackers might find a useful application
;;;   of this facility.
!E!O!F!

shivers@a.gp.cs.cmu.edu (Olin Shivers) (09/13/90)

This message is broken into 5 parts so notesfiles sites can handle it:
1. The 2.01 release notes
2. The source for comint.el
3. A shar file for cmushell.el and tea.el
4. A shar file for cmulisp.el and cmuscheme.el
5. A shar file for background.el and cmutex.el (this msg)
-------------------------------------------------------------------------------
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/shar3   to unpack this archive.
#
echo extracting background.el...
cat >background.el <<'!E!O!F!'
;; Fun with background jobs.
;; Copyright (C) 1988 Joe Keane <jk3k+@andrew.cmu.edu>
;; Refer to the GNU Emacs General Public License for copyright info.

;; - Adapted to use comint and cleaned up somewhat. Olin Shivers 5/90
;; - Background failed to set the process buffer's working directory
;;   in some cases. Fixed. Olin 6/14/90

(provide 'background)
(require 'comint)

;; user variables
(defvar background-show t
  "*If non-nil, background jobs' buffers are shown when they're started.")
(defvar background-select nil
  "*If non-nil, background jobs' buffers are selected when they're started.")

(defun background (command)
  "Run COMMAND in the background like csh.  
A message is displayed when the job starts and finishes.  The buffer is in
comint mode, so you can send input and signals to the job.  The process object
is returned if anyone cares.  See also comint-mode and the variables
background-show and background-select."
  (interactive "s%% ")
  (let ((job-number 1)
	(job-name "%1")
	(dir default-directory))
    (while (process-status job-name)
      (setq job-name (concat "%" (setq job-number (1+ job-number)))))
    (if background-select (pop-to-buffer job-name)
      (if background-show (with-output-to-temp-buffer job-name)) ; cute
      (set-buffer (get-buffer-create job-name)))
    (erase-buffer)
    (setq default-directory
	  (if (string-match "^cd[\t ]+\\([^\t ;]+\\)[\t ]*;[\t ]*" command)
	      (file-name-as-directory
	       (expand-file-name
		(substring command (match-beginning 1) (match-end 1))))
	      dir))
    (insert "% " command ?\n)
    (let ((proc (get-buffer-process
		 (comint-exec job-name job-name shell-file-name
			      nil (list "-c" command)))))
      (comint-mode)
      ;; COND because the proc may have died before the G-B-P is called.
      (cond (proc (set-process-sentinel proc 'background-sentinel)
		  (message "[%d] %d" job-number (process-id proc))))
      (setq mode-name "Background")
      proc)))

(defun background-sentinel (process msg)
  "Called when a background job changes state."
  (let ((msg (cond ((string= msg "finished\n") "Done")
		   ((string-match "^exited" msg)
		    (concat "Exit " (substring msg 28 -1)))
		   ((zerop (length msg)) "Continuing")
		   (t (concat (upcase (substring msg 0 1))
			      (substring msg 1 -1))))))
    (message "[%s] %s %s" (substring (process-name process) 1)
	                  msg
			  (nth 2 (process-command process)))
    (if (null (buffer-name (process-buffer process)))
	(set-process-buffer process nil) ; WHY? Olin.
	(if (memq (process-status process) '(signal exit))
	    (save-excursion
	      (set-buffer (process-buffer process))
	      (let ((at-end (eobp)))
		(save-excursion
		  (goto-char (point-max))
		  (insert ?\n msg ? 
			  (substring (current-time-string) 11 19) ?\n))
		(if at-end (goto-char (point-max))))
	      (set-buffer-modified-p nil))))))
!E!O!F!
#
# type    sh /afs/cs.cmu.edu/user/shivers/lib/emacs/shar3   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 mode 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)
;
; ;; Tell ispell to run latex source through delatex before spell checking:
; (setq latex-mode-hook '((lambda () (setq ispell-filter-hook "delatex")
;                                    (setq ispell-filter-hook-args nil))))
;
; ;; Arrange for tex-view-file to use dvi2tty when I'm displaying on a
; ;; terminal.
; (if (not (eq window-system 'x))
;     (setq tex-view-dvi-file-command "dvi2tty %s | cat -s"))
;
; ;; 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))
;
; ;; Arrange to load in your extensions to cmutex when cmutex loads in:
; (setq cmutex-load-hook '((lambda () (load-library "mytex"))))


;; See notes below if you intend to use the tex-region command.
;;
;; CHANGE LOG:
;; ============================================================================
;; 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
;;
;; Modified by S. Anderson <anderson@sapir.cog.jhu.edu> to add 
;; extended LaTeX support (including contributions from Ashwin Ram 
;; and others), BibTeX and dvi-file previewer invocations
;; 21 Nov. 1989
;;
;; Anderson, Patel and Ram's stuff merged in to master source at CMU. Small
;; things cleaned up. Shivers 2/90.
;; New features:
;; - LaTeX mode is default, not TeX mode.
;; - Stunning array of commands to insert and close LaTeX commands.
;;   So many commands, the bindings are now politically incorrect.
;; - Commands to invoke dvi previewer and bibtex.
;; - Separate TeX and LaTeX keymaps.
;; - Smarter indent-for-comment command.
;; - TeX-print with a numerical arg allows you to edit the print command.
;;   Handy for specifying a non-default printer, page count, etc.
;; - Smarter validate-region and close-block commands.
;; - Added help menu on ? to latex-insert-begin. Shuffled keybindings
;;   on latex keymap and env codes on latex-insert-begin. See code
;;   for rationale.
;; - Munged the validation commands, but they still need work.
;;   Also swapped around pieces of the names to make for consistent
;;   naming scheme: validate-TeX/latex-region/buffer/paragraph.
;; - Added stuff to allow customisation of the print command.
;; This package is now so featureful it makes my head hurt.
;;
;; Gildea worked it over again 3/90
;; - Cleaned up the doc strings
;; - Deleted TeX-indent-for-comment and changed comment-start-skip.
;;   In 18.51 indent-for-comment changed so TeX mode could use it directly.
;; - Changed paragraph-start to make lines starting with '%' separate
;;   paragraphs.  (This is from 18.54 tex-mode.el.)
;; - Tweaked quote handling.  The characters that " inserts are now variables
;;   so they can be changed for editing other languages. "
;; - Fixed an error in TeX-define-common-keys.
;;
;; Shivers worked it over again 5/90
;; - Lower-cased occurences of TeX and LaTeX in command and variable names.
;;   This was a problem.
;; - Changed tex-directory default to "./" since that's what everyone uses.
;;   This makes tex-region work when the source contains \include and \input's.
;; - zap file name is just "zap", not "#zap".
;;
;; Shivers 6/90
;; Added cmutex-load-hook so people can do stuff like load in extra packages
;; along with cmutex.
;;
;; Shivers 9/90
;; - tex-dvi-print-command-suffix is no longer buffer-local.
;;   Several of these other buffer-local vars (tex-command, -header, -trailer,
;;   -start-of-header, and -end-of-header) shouldn't be buffer-local either.
;;   They should be "mode" local: one setting for latex, and one for tex.
;;   Client functions then pick depending on the buffer's mode. Bogus.
;;   I don't have time to do this. You start by defining a chooser
;;   function: (la/tex-value tex-command latex-command) returns
;;   tex-command if called in a tex-mode buffer, and latex-command if
;;   called in a latex-mode buffer.
;; - Removed the latex-example command. There is no \begin{example} latex env.
;;   Who put that in?
;; - Moved latex-footnote from C-c C-f to C-c F to obey the general rule that
;;   latex commands with short/long names are bound to lower/upper case 
;;   letters. The pair of commands here is figure/footnote bound to f/F.
;;   Similarly, verse/verbatim are bound to v/V. The inconsistency with 
;;   the rule was pointed out by sk@thp.Uni-Koeln.DE.

;; 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.

;; Feel free to do yourself:
;;  buffer-local stupidity. See the Shivers 9/90 changelog note.
;; 
;;  Ed Reingold says to add a slitex mode, for compatibility w/latex package.
;;  But the standard tex-mode.el doesn't have a slitex mode. Where is
;;  this alleged slitex mode?
;;
;;  The moby keybindings are still kind of a mess.
;;
;;  The good features of the Ram and Reingold latex-close-block code
;;  should be combined.
;;
;;  Validation needs work!
;;
;;  Make TAB indent correctly for TeX code.  Then we can make linefeed
;;  do something more useful.
;;
;;  The code for finding matching $ needs to be fixed.


(provide 'tex-mode)

;;; HOOKS AND SWITCHES
;;; ===========================================================================
;;; Here's all the variables the user can set to customise the mode.

(defvar tex-directory "./"
  "*Directory in which to run TeX subjob.  Temporary files are created here.")

(defvar tex-dvi-print-command "dvipr %s"
  "*Shell command used by \\[tex-print] to print a .dvi file.")

(defvar tex-dvi-print-command-suffix ""
  "*Additional arguments or command name modification for command string 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.
Should show the queue that \\[tex-print] put your job on.")

(defvar tex-bibtex-command "bibtex %s"
  "*Command string used by \\[bibtex-buffer] to run BiBtex on a LaTeX'ed file.")

(defvar tex-view-dvi-file-command (if (eq window-system 'x) "texx %s" "texsun %s")
  "*Command to invoke previewer on dvi file.")

(defvar tex-default-mode 'latex-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-open-quote "``"
  "String inserted by typing \\[tex-insert-quote] to open a quotation.")

(defvar tex-close-quote "''"
  "String inserted by typing \\[tex-insert-quote] to close a quotation.")

(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.
This is a buffer local variable, defaults to "tex" in plain-tex-mode buffers,
and "latex" in latex-mode buffers. If you want to alter it, do the change
in the plain-tex-mode-hook or the latex-mode-hook.")

(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.")


;;; STARTUP STUFF: MODES & MAPS
;;; ===========================================================================
;;; Here's the code that sets up the modes: initialises the keymaps, buffer
;;; local variables, and so forth.

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

(defvar tex-mode-map nil "Keymap for TeX mode.")
(defvar latex-mode-map nil "Keymap for LaTeX mode.")

(defun tex-define-common-keys (keymap)
  "Define the keys that we want defined in all TeX-related modes.
These modes are tex-mode, latex-mode, and 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)
  (define-key keymap "\C-cP" 'tex-view-file))

(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-cb" 'bibtex-buffer)
  )

;;; Shuffled keybindings so that
;;; - latex-insert-begin codes are consistent with keybindings to
;;;   equivalent commands. This must be so, or you will *never* remember
;;;   all those bindings!
;;; - When binding t/T to table/tabular or q/Q to quote/quotation,
;;;   consistently bound the small letter to the smaller (shorter)
;;;   command. A simple rule to help you remember.
;;; - latex-close-block is on C-c space. Easy to type.
;;; Olin 2/90

(if latex-mode-map
    nil
  (setq latex-mode-map (make-sparse-keymap))
  (tex-define-common-keys latex-mode-map)
  (define-key latex-mode-map "\C-j" 'tex-terminate-paragraph)
  (define-key latex-mode-map "\e{" 'tex-insert-braces)
  (define-key latex-mode-map "\e}" 'up-list)
  (define-key latex-mode-map "\"" 'tex-insert-quote)
  (define-key latex-mode-map "\C-ca" 'latex-array)
  (define-key latex-mode-map "\C-cA" 'latex-abstract)
  (define-key latex-mode-map "\C-c\C-b" 'tex-buffer)
  (define-key latex-mode-map "\C-cb" 'bibtex-buffer)
  (define-key latex-mode-map "\C-c\C-c" 'latex-cite)
  (define-key latex-mode-map "\C-cc" 'latex-center)
  (define-key latex-mode-map "\C-cC" 'latex-chapter)
  (define-key latex-mode-map "\C-c\C-d" 'latex-document)
  (define-key latex-mode-map "\C-cd" 'latex-description)
  (define-key latex-mode-map "\C-c\C-e" 'latex-equation)
  (define-key latex-mode-map "\C-ce" 'latex-enumerate)
  (define-key latex-mode-map "\C-cf" 'latex-figure)
  (define-key latex-mode-map "\C-cF" 'latex-footnote)
  (define-key latex-mode-map "\C-c\C-i" 'latex-item)    ; ^C-TAB
  (define-key latex-mode-map "\C-ci" 'latex-insert-begin)
  (define-key latex-mode-map "\C-cI" 'latex-itemize)
;;;                                                     ; ^C^K = kill job
  (define-key latex-mode-map "\C-cl"  'latex-label)     ; ^C^L = recenter output buffer
  (define-key latex-mode-map "\C-c\C-m" 'latex-make-environment)
  (define-key latex-mode-map "\C-cm" 'latex-minipage)
  (define-key latex-mode-map "\C-cN" 'latex-eqnarray)
  (define-key latex-mode-map "\C-cp" 'latex-picture)    ; ^C^P = tex-print
;;;							; ^CP = tex-view-file
  (define-key latex-mode-map "\C-cq" 'latex-quote)      ; ^C^Q = show print queue
  (define-key latex-mode-map "\C-cQ" 'latex-quotation)
  (define-key latex-mode-map "\C-c\C-r" 'tex-region)
  (define-key latex-mode-map "\C-cr" 'latex-ref)
; What is this? Olin 2/90
; (define-key latex-mode-map "\C-cR" 'refer-find-entry) ; call refer.el package for bib lookup
  (define-key latex-mode-map "\C-c\C-s" 'latex-section)
  (define-key latex-mode-map "\C-cS" 'latex-sloppypar)
  (define-key latex-mode-map "\C-c\C-t" 'latex-titlepage)
  (define-key latex-mode-map "\C-ct" 'latex-table)
  (define-key latex-mode-map "\C-cT" 'latex-tabular)
  (define-key latex-mode-map "\C-c\C-v" 'latex-verb)
  (define-key latex-mode-map "\C-cv" 'latex-verse)
  (define-key latex-mode-map "\C-cV" 'latex-verbatim)
  (define-key latex-mode-map "\C-c " 'latex-close-block) ;easy to type
  (define-key latex-mode-map "\C-c$" 'latex-mathmode)
  )

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

;;; 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.
Tries to determine (by looking at the beginning of the file) 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))))

(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-dvi-print-command-suffix
        Additional argument(s) or modifier to tex-dvi-print-command
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 tex-mode-map)
  (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-latex-buffer] to check buffer for unmatched latex constructs.

Special commands:
\\{latex-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-dvi-print-command-suffix
        Additional argument(s) or other modification for tex-dvi-print-command
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 latex-mode-map)
  (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 (keymap)
  (kill-all-local-variables)
  (use-local-map keymap)
  (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))


;;; COMMENT INDENTING AND QUOTES
;;; ===========================================================================

(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 the appropriate quote marks for TeX.
Inserts the value of tex-open-quote (normally ``) or tex-close-quote
(normally '') depending on the context.  With prefix argument, always
inserts \" characters."
  (interactive "P")
  (if arg
      (self-insert-command (prefix-numeric-value arg))
    (insert
     (cond ((or (bobp)
		(save-excursion
		  (forward-char -1)
		  (looking-at "\\s(\\|\\s \\|\\s>")))
	    tex-open-quote)
	   ((= (preceding-char) ?\\)
	    ?\")
	   (t
	    tex-close-quote)))))


;;; VALIDATION FUNCTIONS
;;; ===========================================================================
;;; There are two kinds of validation function: TeX and LaTeX.
;;; TeX validation means checking for balanced {}, $ $, and $$ $$ pairs.
;;; LaTeX validation also means checking for balanced environment delimiters,
;;; such as \begin{center} ... \end{center}.
;;; It is probably a mistake to insist on LaTeX validating on paragraph
;;; boundaries, because LaTeX environments can span multiple environments.
;;;
;;; Validation is a mess. See my notes at the end of the file. Olin


;;; Added code to really position cursor at the beginning of the offending
;;; paragraph, not 2 lines above it. Olin 2/90

(defun validate-tex-buffer ()
  "Check current buffer for paragraphs containing mismatched $'s and {}'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))
		 (found-para (search-backward "\n\n" nil 'move)))
	    (or (validate-tex-paragraph (point) end)
		(progn
		  (if found-para (forward-char 2))
		  (push-mark (point))
		  (message "Mismatch found in pararaph starting here")
		  (sit-for 4)
		  (if found-para (backward-char 2))))))
      (goto-char opoint))))

(defun validate-tex-region (start end)
  "Check for mismatched braces or $'s in region.
Returns t if no mismatches.  Returns nil and moves point to suspect
area if a mismatch is found."
 (interactive "r")
  (let ((failure-point (save-excursion
			 (condition-case ()
			     (save-restriction
			       (narrow-to-region start end)
			       (goto-char start)
			       (while (< (point) (point-max))
				 (forward-sexp))
			       nil)
			   (error (point))))))
    (if failure-point
	(progn
	  (goto-char failure-point)
	  (re-search-forward "[\n\t ]*") ; Skip to start of bad sexp.
	  nil)
      t)))

;; AR's additions backed out. They're good for LaTeX, but screw TeX up.
;; But see validate-latex-region below. Olin. 2/90 

(defun validate-tex-paragraph (start end)
  (condition-case ()
      (save-excursion
	(save-restriction
	  (narrow-to-region start end)
;         (goto-char end)       ;; Added, AR 2/22/88.
;         (latex-close-block t) ;; Added, AR 2/22/88.
	  (goto-char start)
	  (while (< (point) (point-max))
	    (forward-sexp))
	  t))
    (error nil)))

;;; Changed to only insert a single newline. Hit it twice. This
;;; double-newline stuff is amazingly irritating when you tend to
;;; do it a lot accidentally. People who are used to using linefeed
;;; to terminate lines of Lisp code do this a lot. Olin 2/90

(defun tex-terminate-paragraph (inhibit-validation)
  "Insert a newline and check for mismatched braces/$'s in previous paragraph.
A prefix arg inhibits the checking."
  (interactive "P")
  (or inhibit-validation
      (save-excursion
	(validate-tex-region
	 (save-excursion
	   (search-backward "\n\n" nil 'move)
	   (point))
	 (point)))
      (message "Paragraph being closed appears to contain a mismatch"))
  (insert "\n"))


;;; LaTeX validation functions.
;;; ===========================================================================
;;; Both functions only report the first tex-validation problem they find,
;;; possibly missing others. This can be losing.

(defun validate-latex-region (start end)
  "TeX validate the region, and also check for balanced \\begin \\end pairs."
  (interactive "r")
  (cond ((not (validate-tex-region start end)) ; validate for TeX syntax
	 (message "Unbalanced identifier.")
	 (sit-for 2)))
  (save-restriction
    (let ((opoint (point)))
      (narrow-to-region start end)
      (goto-char end)			; Added, AR 2/22/88.
      (latex-close-block t)		; Added, AR 2/22/88.
      (goto-char opoint))))

;;; Note that this uses a global TeX validate, not a per-paragraph one.
(defun validate-latex-buffer ()
 "TeX validate the entire buffer, and check for balanced \\begin \\end pairs."
 (interactive)
 (validate-latex-region (point-min) (point-max))
 (message "Buffer validated."))


;;; SOURCE ENTRY COMMANDS
;;; ===========================================================================
;;; (Mostly courtesy Dorab Patel)

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

;;;
;;; Most of the environments below are based on versions by Dorab Patel
;;; at UCLA, further adapted by Steve Anderson <anderson@sapir.cog.jhu.edu>
;;;
;;; These functions make an entire LaTeX environment and leave the cursor
;;; poised to type at the appropriate place within it:

(defun latex-mathmode ()
  "Make a pair of dollar-signs and be poised to type inside of them."
  (interactive)
  (insert ?\$)
  (save-excursion
    (insert ?\$)))

(defun latex-make-environment (name)
  "Make a latex NAME environment."
  (interactive "sEnvironment: ")
  (insert "\\begin\{" name "}\n")
  (save-excursion (insert "\n\\end\{" name "}")))

(defun latex-abstract () "Make an \\abstract environment."
  (interactive) (latex-make-environment "abstract"))

(defun latex-array (arg)
  "Make a \\array env; with argument, include \[pos\] slot."
  (interactive "P")
  (insert "\\begin\{array\}")
  (if arg (progn (insert "\[")
		 (save-excursion
		   (insert "\]\{\}\n\n\\end\{array\}")))
      (progn (insert ?{)
	     (save-excursion (insert "\}\n\n\\end\{array\}")))))

(defun latex-center () "Make a \\center environment."
  (interactive) (latex-make-environment "center"))

(defun latex-chapter (&optional arg)
  "Insert a \\chapter or (with argument) \\chapter* command."
  (interactive "P")
  (insert (concat "\\chapter" (if arg "*") "\{"))
  (save-excursion (insert?})))

(defun latex-cite (arg)
  "Insert a \\cite command.
With one \\[universal-argument], insert a \\shortcite command
With two \\[universal-argument]'s, insert a \\nocite command"
  (interactive "p")
  (insert (cond ((eq arg 1) "\\cite\{")
		((eq arg 4) "\\shortcite\{")
		((eq arg 16) "\\nocite\{")
		(t (error "Bad arg to latex-cite"))))
  (save-excursion (insert ?})))

(defun latex-description () "Make a \\description environment."
  (interactive) (latex-make-environment "description"))

(defun latex-document (&optional arg)
  "Set up a LaTeX document; with argument, just make a \\document environment."
  (interactive "P")
  (if arg (latex-make-environment "document")
      (progn
	(insert "\\documentstyle\[\]\{")
	(save-excursion
	  (insert-string "\}\n\n\\begin\{document\}\n\n\\end\{document\}")))))

(defun latex-enumerate () "Make an \\enumerate environment."
  (interactive) (latex-make-environment "enumerate"))

(defun latex-eqnarray (&optional arg)
  "Make a \\eqnarray or (with argument) \\eqnarray* environment."
  (interactive "P")
  (latex-make-environment (concat "eqnarray"
				  (if arg "*"))))

(defun latex-equation () "Make an \\equation environment."
  (interactive) (latex-make-environment "equation"))

(defun latex-figure (&optional arg)
  "Make a \\figure environment; with arg, include optional \[loc\] slot."
  (interactive "P")
  (insert "\\begin\{figure\}")
  (if arg (progn (insert ?[)
		 (save-excursion
		   (insert "\]\n\n\\end\{figure\}")))
      (progn (insert "\n")
	     (save-excursion (insert "\n\\end\{figure\}")))))

(defun latex-footnote ()
    "Make a LaTeX footnote."
  (interactive)
  (insert "\\footnote\{")
  (save-excursion (insert ?})))

(defun latex-item ()
  "Insert an \\item command."
  (interactive)
  (insert "\\item "))

(defun latex-itemize () "Make an \\itemize environment."
  (interactive) (latex-make-environment "itemize"))

(defun latex-label ()
  "Insert a \\label command."
  (interactive)
  (insert "\\label\{")
  (save-excursion (insert ?})))

(defun latex-minipage (&optional arg)
  "Make a \\minipage environment; with argument, include \[pos\] slot."
  (interactive "P") 
  (insert "\\begin\{minipage\}")
  (if arg (progn (insert "\[")
		 (save-excursion
		   (insert "\]\{\}\n\n\\end\{minipage\}")))
      (progn (insert ?{)
	     (save-excursion (insert "\}\n\n\\end\{minipage\}")))))

(defun latex-picture () "Make a \\picture environment."
  (interactive) (latex-make-environment "picture"))

(defun latex-quotation () "Make a \\quotation environment."
  (interactive) (latex-make-environment "quotation"))

(defun latex-quote () "Make a \\quote environment."
  (interactive) (latex-make-environment "quote"))

(defun latex-ref ()
  "Insert a \\ref command."
  (interactive)
  (insert "\\ref\{")
  (save-excursion (insert ?})))
  
(defun latex-section (arg)
  "Insert a \\section command or a variation of it.
With no argument, insert a \\section command.
With one \\[universal-argument], insert a \\subsection command.
With two \\[universal-argument]'s, insert a \\subsubsection command.
With three \\[universal-argument]'s, insert a \\paragraph command.
With four \\[universal-argument]'s, insert a \\subparagraph command."
  (interactive "p")
  (insert (cond ((eq arg 1) "\\section\{")
		((eq arg 4) "\\subsection\{")
		((eq arg 16) "\\subsubsection\{")
		((eq arg 64) "\\paragraph\{")
		((eq arg 256) "\\subparagraph\{")
		(t (error "Bad arg to latex-section"))))
  (save-excursion (insert ?})))

(defun latex-sloppypar () "Make a \\sloppypar environment."
  (interactive) (latex-make-environment "sloppypar"))

(defun latex-table (&optional arg)
  "Make a \\table environment; with arg, include \[loc\] slot."
  (interactive "P")
  (insert "\\begin\{table\}")
  (if arg (progn (insert "\[" )
		 (save-excursion
		   (insert "\]\n\n\\end\{table\}")))
      (progn (insert "\n")
	     (save-excursion (insert "\n\\end\{table\}")))))

(defun latex-tabular (&optional arg)
  "Make a \\tabular environment; with arg, include optional \[pos\] slot."
  (interactive "P")
  (insert "\\begin\{tabular\}")
  (if arg (progn (insert "\[")
		 (save-excursion
		   (insert "\]\{\}\n\n\\end\{tabular\}")))
      (progn (insert ?{)
	     (save-excursion (insert "\}\n\n\\end\{tabular\}")))))

(defun latex-titlepage () "Make a \\titlepage environment."
  (interactive) (latex-make-environment "titlepage"))

(defun latex-verb (&optional arg)
  "Insert a \\verb or (with argument) \\verb* command."
  (interactive "P")
  (insert "\\verb" (if arg "*" "")))

(defun latex-verbatim (&optional arg)
  "Make a \\verbatim or \\verbatim* environment."
  (interactive "P")
  (latex-make-environment (concat "verbatim" (if arg "*"))))

(defun latex-verse () "Make a \\verse environment."
  (interactive)
  (latex-make-environment "verse"))

;; The following function (based on an idea originally due to
;; Niall Mansfield <mansfiel%scr.slb.com@relay.cs.net>), as
;; modified by Steve Anderson <anderson@sapir.cog.jhu.edu>
;; inserts just the \begin{environment} statement, leaving it to 
;; be closed by the function latex-close-block below
;;
;; The command codes have been made consistent with the default 
;; latex-mode keybindings. You also now get a helpful 1 line menu if you get
;; confused and type ? at the prompt. Whew. Olin 2/90.

(defun latex-insert-begin (environment)
  "Define a LaTeX environment.
You are prompted for environment single character replies insert an
appropriate \\begin\{environment\} as follows:

a  array          q  quote            
A  abstract       Q  quotation        
c  center         S  sloppypar        
d  description    t  table            
e  enumerate      T  tabular          
f  figure         V  verbatim         
I  itemize        ?  <show short menu>
L  list           
                  
Any other reply just yields \\begin\{\}."
; (interactive "cEnvironment: ")
  (interactive (let* ((cursor-in-echo-area t)
		      (result (progn (message "Environment (? for help):")
				     (read-char))))
		 (while (eq result ??)
		   (message
 "arr Abs cent desc enum fig Item Lis quot Quota Sloppy tabl Tabu Verbat")
		   (setq result (read-char)))
		 (list result)))
  (let ((environment-string
	 (cond
	   ((equal environment ?a)"array\}\{")
	   ((equal environment ?A)"abstract")
	   ((equal environment ?c)"center")
	   ((equal environment ?C)"center")
	   ((equal environment ?d)"description")
	   ((equal environment ?D)"description")
	   ((equal environment ?e)"enumerate")
	   ((equal environment ?f)"figure")
	   ((equal environment ?F)"figure")
	   ((equal environment ?i)"itemize")
	   ((equal environment ?I)"itemize")
	   ((equal environment ?l)"list")
	   ((equal environment ?L)"list")
	   ((equal environment ?q)"quote")
	   ((equal environment ?Q)"quotation")
	   ((equal environment ?s)"sloppypar")
	   ((equal environment ?S)"sloppypar")
	   ((equal environment ?t)"table")
	   ((equal environment ?T)"tabular\}\{")
	   ((equal environment ?v)"verbatim")
	   ((equal environment ?V)"verbatim")
	   (t ""))))
    (insert "\\begin{" 
	    environment-string 
	    "}\n")
    (if (not (or (memq (downcase environment) '(?c ?d ?e ?f ?i ?l ?q ?s ?v))
		 (memq environment '(?A ?t))))
	; ?a or ?T or unknown
	(backward-char 2))))

;; Replacement for latex-close-block in lisp/tex-mode.el.
;; Ashwin Ram, 2/22/88.
(defun latex-close-block (&optional validate)
   "Inserts an \\end{...} to match corresponding \\begin{...}.
If optional argument VALIDATE is t, doesn't insert anything, only validates."
   (interactive "*")
   (let ((fail-point (point))
         (nesting '())
         (done nil))         
      (end-of-line)
      (while (not done)
         (if (re-search-backward "\\\\\\(begin\\|end\\){\\([^}\n]*\\)}" (point-min) t)
             (let ((which (buffer-substring (match-beginning 1) (match-end 1)))
                   (text (buffer-substring (match-beginning 2) (match-end 2))))
                (if (equal which "end")
                    (setq nesting (cons text nesting))
                    (if (null nesting)
                        (let ((indentation (current-column)))
                           (goto-char fail-point)
                           (if validate
                               (error "\\begin{%s} is never ended" text)
                               (progn
                                  (beginning-of-line)
                                  (if (not (looking-at "^[ \t]*\n"))
                                      (progn (end-of-line) (insert "\n")))
                                  (indent-to indentation)
                                  (insert "\\end{" text "}\n")))
                           (setq done t))
                        (if (equal text (car nesting))
                            (setq nesting (cdr nesting))
                            (error "\\begin{%s} ended by \\end{%s}" text (car nesting))))))
             (progn
                (goto-char fail-point)
                (if (not validate) (message "No unmatched \\begin{...} to end here"))
                (setq done t))))))

;;; Courtesy Ed Reingold 6/17/90
;;; This doesn't insist that a \begin{figure} match up with a \end{figure} --
;;; it just looks at \begin's and \end's. This could be fixed, and then
;;; integrated with the clients of the Ram code above, but I don't have time
;;; to do it. -Olin
;(defun tex-last-unended-begin ()
;  "Leave point at the beginning of the last \\begin{...} that is unended."
;  (while (and (re-search-backward "\\(\\\\begin{\\)\\|\\(\\\\end{\\)")
;              (looking-at "\\\\end{"))
;    (tex-last-unended-begin)))
;
;(defun tex-close-latex-block ()
;  "Creates an \\end{...} to match the last unclosed \\begin{...}."
;  (interactive "*")
;  (let ((text)
;        (indentation)
;        (new-line-needed (bolp)))
;    (save-excursion
;      (condition-case ERR
;          (tex-last-unended-begin)
;        (error (error "Couldn't find unended \\begin")))
;      (setq indentation (current-column))
;      (re-search-forward "\\\\begin{\\([^}\n]*\\)}")
;      (setq text (buffer-substring (match-beginning 1) (match-end 1))))
;    (indent-to indentation)
;    (insert "\\end{" text "}")
;    (if new-line-needed (insert ?\n))))


;;; PROCESS STUFF: TEXING, BIBTEXING, PRINTING, VIEWING & DISPLAYING QUEUE
;;; ===========================================================================

;;; 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, via a temporary file.
The temporary file is tex-zap-file and it 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.

Process is run in a comint buffer (see comint mode). Additional
commands available are:
	tex-kill-job		tex-recenter-output-buffer
	tex-print		tex-show-print-queue
	tex-view-file"
  (interactive "r")
  (or tex-zap-file (setq tex-zap-file (make-temp-name "zap")))
  (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 (arg)
  "Print the .dvi file made by \\[tex-region] or \\[tex-buffer].
Runs the shell command defined by tex-dvi-print-command.
With argument, allows the user to edit the command string first.
This is useful for inserting special flags, e.g., to print a range of
pages, route output to non-default printer, print landscape, etc..
See variables tex-dvi-print-command and tex-dvi-print-command-suffix.
Non-nil tex-after-print-hook is called after a successful completion."
  (interactive "P")
  (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 (tex-optionally-mung-command
			 (format tex-dvi-print-command
				 (concat tex-dvi-print-command-suffix
					 "\"" dir noext "dvi\"")))))
	      (shell-command cmd)
	      (run-hooks 'tex-after-print-hook))
	    (error "Can't figure out name of dvi file")))
      (error "No last file processed.")))

(defun bibtex-buffer ()
  "Run BibTeX on the .aux file made by \\[tex-region] or \\[tex-buffer].
Runs the shell command defined by tex-bibtex-command."
  (interactive)
  (require 'background)
  (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-bibtex-command                   ;;my copy of
				(concat "\"" dir noext "\"" "\n")))) ;;bibtex requires 
	      (message cmd)		                             ;;extra linefeed
	      (background cmd))
	    (error "Can't figure out name of aux file")))
      (error "No last file processed.")))

(defun tex-view-file (&optional arg)
  "Display the .dvi file made by \\[tex-region] or \\[tex-buffer] in a window.
Runs the shell command defined by tex-view-dvi-file-command in the background.
With argument, allows the user to edit the command string first."
  (interactive "P")
  (require 'background)
  (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 (tex-optionally-mung-command
			 (format tex-view-dvi-file-command
				 (concat "\"" dir noext "\"")))))
	      (background cmd))
	    (error "Can't figure out name of dvi file")))
      (error "No last file processed.")))
	
(defun tex-show-print-queue (arg)
  "Show the print queue that \\[tex-print] put your job on.
Runs the shell command defined by tex-show-queue-command.
With argument, allows the user to edit the command string first."
  (interactive "P")
  (require 'background)
  (background (tex-optionally-mung-command tex-show-queue-command)))

;;; If the user gave a prefix arg to the interactive command, give him
;;; a chance to edit CMD before returning it.
(defun tex-optionally-mung-command (cmd &optional prompt)
  (if current-prefix-arg (read-string (or prompt ": ") cmd)
      (message cmd)
      cmd))

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

(defvar cmutex-load-hook nil
  "This hook is run when cmutex is loaded in.
This is a good place to put keybindings. It is also the right place to
load in packages that extend cmutex.")
	
(run-hooks 'cmutex-load-hook)



;;;Notes on validation
;;;============================================================================
;;; Validation is a mess. There are several interacting problems.
;;; - Validation is errorful in both directions: it can miss real problems,
;;;   and report non-problems. For example, TeX validating $\left( X \right.$
;;;   will decide that the open paren is unbalanced, and there isn't much
;;;   you can do to fix this. So validation error reports must be treated
;;;   as suggestions. Further, you shouldn't stop at the first error report,
;;;   because it may be bogus, and you might miss out on reporting a real
;;;   error further in.
;;;
;;; - Validating at paragraph boundaries is debatable. It is a useful
;;;   heuristic, but some TeX delimiters and many LaTeX environments
;;;   span multiple paragraphs. Validating regions a paragraph at a time
;;;   in such cases is very irritating, because each non-error is bogusly
;;;   reported twice: at the open delimiter and at the close delimiter.
;;;   Validating globally, on the other hand, can miss two errors that
;;;   balance each other out -- this is probably very rare, though.
;;;
;;;   Per-paragraph validation has the advantage that you can reset
;;;   after finding a possible error to the next paragraph and keep on
;;;   looking for further errors. Global validation uses forward-sexp,
;;;   and it would be a lot of hair to use it to find more than a
;;;   single (potential) error. What to do? You lose.
;;;
;;; - Some validation functions signal errors by returning nil, some
;;;   by calling ERROR. The interprocedural communications of these
;;;   guys is a mess.
;;;
;;; - TeX support is very important for Emacs. But this issue is not
;;;   trivial. It needs a careful working out by someone reasonably
;;;   competent who will take the time and trouble to do a good
;;;   solution.
;;;       -Olin 2/90
!E!O!F!