diff lisp/ilisp/comint-ipc.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children b82b59fe008d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/ilisp/comint-ipc.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,918 @@
+;;; -*-Emacs-Lisp-*-
+;;;
+;;;
+;;;%Header
+;;;
+;;; Rcs_Info: comint-ipc.el,v 1.20 1993/09/03 02:05:07 ivan Rel $
+;;;
+;;; IPC extensions for comint
+;;; Copyright (C) 1990 Chris McConnell, ccm@cs.cmu.edu.
+;;;
+;;; Send mail to ilisp@lehman.com if you have problems.
+;;;
+;;; Send mail to ilisp-request@lehman.com if you want to be on the
+;;; ilisp mailing list.
+
+;;; 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.
+
+;;; This file contains extensions to multiplex the single channel of
+;;; an inferior process between multiple purposes.  It provides both
+;;; synchronous and asynchronous sends with error handling.  
+
+;;; USAGE: Load this file and call comint-setup-ipc in a comint
+;;; buffer.  This is not a standalone application.  For an example of
+;;; it being used see ilisp.el.
+
+;;; CUSTOMIZATION: See the parameters and hooks below.  
+
+;;; INTERFACE.  See the function documentation and code for more information.
+;;;
+;;; PROCESS INPUT: comint-send, comint-send-code, comint-default-send,
+;;; comint-sync, comint-abort-sends
+;;;
+;;; PROCESS OUTPUT: comint-display-output, comint-display-error-output
+
+
+;;;%Parameters
+(defvar comint-log nil
+  "If T, then record all process input and output in a buffer called
+process name.")
+
+(defvar comint-send-newline t 
+  "If T then add a newline to string in comint-default-send.")
+
+(defvar comint-always-scroll nil
+  "If T then process output will always be visible in first window on buffer.")
+
+(defvar comint-fix-error nil
+  "String to send to send to the command interpreter to fix errors.")
+
+(defvar comint-continue nil
+  "String to send to continue an interrupted job.")
+
+(defvar comint-interrupt-regexp nil
+  "Regular expression for the start of an interrupt in process output.")
+
+(defvar comint-error-regexp nil
+  "Regular expression for setting comint-errorp if found in process output.")
+
+(defvar comint-output-buffer " *Output*"
+  "Name of the output buffer.")
+
+(defvar comint-error-buffer " *Error Output*" 
+  "Name of the error output buffer.")
+
+(defvar comint-show-status t
+  "Set to nil to inhibit status redisplay.")
+
+;;;%%Hooks
+(defvar comint-output-filter (function identity)
+  "Given the complete OUTPUT of a send, return the result of the send.")
+
+(defvar comint-interrupt-start 'comint-interrupt-start
+  "Return the start in OUTPUT of the text printed by
+comint-interrupt-subjob in the inferior process.")
+
+(defvar comint-handler 'comint-error-popup
+  "Default handler for sends.  When a send completes, the handler is
+called with error-p, wait-p, message, output and prompt.")
+
+(defvar comint-update-status 'comint-update-status
+  "Function to update the STATUS of the inferior process.  It should
+set comint-status to a status string in addition to whatever else it
+does.")
+
+(defvar comint-prompt-status 'comint-prompt-status
+  "Given the previous prompt and the last line output, return 'error
+if an error, T if a prompt and nil otherwise.  If it is a prompt, also
+funcall comint-update-status to set the status.  If old is nil, then
+just return T if last line is a prompt.")
+
+;;;
+(defvar comint-abort-hook nil 
+  "List of hooks to run after sends are aborted.")
+
+;;;%Globals
+(defvar comint-send-queue nil 
+  "List of currently pending IPC send requests.  The first element in
+the queue is where output to the process will be stored.
+A send record is a list of: 
+
+string -- The string sent to the process.
+
+no-insert -- nil to insert output into the process buffer.  If this is
+being done, the results will only contain the very last line.
+
+wait-p -- nil if not waiting, non-nil if waiting.  If it is a string,
+results are inserted in the buffer until a result matches the string
+as a regexp.
+
+status -- A symbol for the process status while the send is running.
+
+message -- A message to be displayed when an asynchronous send is
+popped up by the handler.
+
+handler -- A function that given error-p, wait-p, message, output and
+prompt decides if the user should be notified.  If it is nil or
+returns nil, then no error processing will be done.
+
+running -- nil if a send is waiting, T if it is running, another send
+if interrupting and a string with pending output if the send was
+interrupted.
+
+old-prompt -- The prompt before the send was sent.  If it is nil, then
+errors will not be detected.
+
+line -- The start of the last line in the results.
+
+result -- Cons of the output and the prompt after the send.")
+
+(defvar comint-end-queue nil "Pointer to the end of comint-send-queue.")
+(defvar comint-queue-emptied t 
+  "Set to T each time send queue empties.")
+
+(defvar comint-output nil
+  "Set to the output of the last send.  This is useful when ilisp code
+is put in the send stream.")
+(defvar comint-errorp nil
+  "Set to T if the last send was an error.")
+
+(defvar comint-status " :run" "The current comint status.")
+(defvar comint-original-buffer nil 
+  "The original buffer when there was output to a comint buffer.")
+
+(defvar comint-last-send nil "Last send that was put in queue.")
+
+(defvar comint-aborting nil
+  "Set to T if we are aborting commands.")
+
+;;;%Utils
+;;;
+(defun comint-remove-whitespace (string)
+  "Remove leading and trailing whitespace in STRING."
+  (if string
+      (let* ((start (if (string-match "[^ \t\n]" string)
+			(match-beginning 0)
+			0))
+	     (end start))
+	(while (string-match "[ \t\n]*[^ \t\n]+" string end)
+	  (setq end (match-end 0)))
+	(substring string start end))))
+
+;;;
+(defun comint-log (process string &optional output)
+  "Log to PROCESS, STRING marking as optional OUTPUT."
+  (if comint-log
+      (save-excursion
+	(set-buffer (get-buffer-create (process-name process)))
+	(goto-char (point-max))
+	(if output
+	    (progn
+	      (insert "{") (insert string) (insert "}"))
+	    (insert string)))))
+
+;;;
+(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."
+  (comint-log proc str)
+  (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-sender (process string)
+  "Send to PROCESS STRING with newline if comint-send-newline."
+  (comint-send-string process string)
+  (if comint-send-newline
+      (progn
+	(comint-log process "\n")
+	(process-send-string process "\n"))))
+
+;;;
+(defun comint-interrupt-subjob ()
+  "Interrupt the current subjob."
+  (interactive)
+  (comint-log (get-buffer-process (current-buffer)) "")
+  (interrupt-process nil comint-ptyp))
+
+;;;
+(defun comint-send-variables (send)
+  "Return a pointer to the start of the variables for SEND.  It
+returns \(running old-prompt line \(output . prompt))."
+  (cdr (cdr (cdr (cdr (cdr (cdr send)))))))
+
+;;;
+(defun comint-send-results (send)
+  "Return the results of SEND which are \(output . prompt).  If there is
+an error, the prompt will be a list."
+  (car (cdr (cdr (cdr (comint-send-variables send))))))
+
+;;;
+(defun comint-send-description (send)
+  "Return a description of SEND."
+  (let* ((status (cdr (cdr (cdr send)))))
+    (or (car (cdr status))		;Message
+	(and (stringp (car send)) (car send)) ;String
+	(and (car status) (symbol-name (car status))))))
+  
+;;;
+(defun comint-interrupted ()
+  "Return T if there is an interrupted send."
+  (let ((send comint-send-queue)
+	(done nil))
+    (while (and send (not done))
+      (if (stringp (car (comint-send-variables (car send))))
+	  (setq done t)
+	  (setq send (cdr send))))
+    done))
+      
+
+;;;%Default hooks
+(defun comint-process-sentinel (process status)
+  "Update PROCESS STATUS by funcalling comint-update-status."
+  (setq status (process-status process))
+  (save-excursion
+    (if (buffer-name (process-buffer process))
+	(set-buffer (process-buffer process)))
+    (funcall comint-update-status status)))
+
+;;;
+(defun comint-interrupt-start (output)
+  "Return the start of comint-interrupt-regexp in OUTPUT."
+  (if (and comint-interrupt-regexp 
+	   (string-match comint-interrupt-regexp output))
+      (match-beginning 0)))
+
+;;;
+(defun comint-update-status (status)
+  "Update the process STATUS of the current buffer."
+  (setq comint-status (format " :%s" status))
+  (if comint-show-status
+      (progn
+	(save-excursion (set-buffer (other-buffer)))
+	(sit-for 0))))
+
+;;;
+(defun comint-prompt-status (old line &optional equal)
+  "Called by comint-process filter with OLD and LINE, return 'error if
+LINE is an error, T if it is a prompt as determined by
+comint-prompt-regexp or nil otherwise.  Also set the status
+appropriately by funcalling comint-update-status.  If specified EQUAL
+will be called with old and line and should return T if line is not an
+error.  OLD will be nil for the first prompt."
+  (if (string-match comint-prompt-regexp line)
+      (let ((error (or (if equal
+			   (funcall equal old line)
+			   (or (null old) (string-equal old line)))
+		       'error)))
+	(funcall comint-update-status (if (eq error 'error) error 'ready))
+	error)
+      nil))
+
+;;;
+(defun comint-insert (output)
+  "Insert process OUTPUT into the current buffer."
+  (if output
+      (let* ((buffer (current-buffer))
+	     (process (get-buffer-process buffer))
+	     (mark (process-mark process))
+	     (window (selected-window))
+	     (at-end nil))
+	(if (eq (window-buffer window) buffer)
+	    (setq at-end (= (point) mark))
+	    (setq window (get-buffer-window buffer)))
+	(save-excursion
+	  (goto-char mark)
+	  (insert output)
+	  (set-marker mark (point)))
+	(if window 
+	    (progn
+	      (if (or at-end comint-always-scroll) (goto-char mark))
+	      (if (not (pos-visible-in-window-p (point) window))
+		  (let ((original (selected-window)))
+		    (save-excursion
+		      (select-window window)
+		      (recenter '(center))
+		      (select-window original)))))))))
+
+;;;
+(defun comint-handle-error (output prompt keys &optional delay)
+  "Handle an error by beeping, displaying OUTPUT and then waiting for
+the user to pause.  Once there is pause, PROMPT until one of the
+characters in KEYS is typed.  If optional DELAY is specified, it is
+the number of seconds that the user must pause.  The key found will be
+returned."
+  (save-excursion
+    (setq delay (or delay 1))
+    (beep t)
+    (comint-display-error output)
+    (set-buffer comint-original-buffer)
+    (while (not (sit-for delay nil))
+      (execute-kbd-macro (read-key-sequence nil)))
+    (if (not (get-buffer-window (get-buffer comint-error-buffer)))
+	(comint-display-error output))
+    (let ((cursor-in-echo-area t)
+	  (echo-keystrokes 0)
+	  char)
+      (while (progn (message prompt)
+		    (not (memq (setq char (downcase (read-char))) keys)))
+	(if (= char ? ) 
+	    (ilisp-scroll-output)
+	    (setq quit-flag nil)
+	    (beep)))
+      char)))
+
+;;;
+(defun comint-error-popup (error wait-p message output prompt)
+  "If there is an ERROR pop up a window with MESSAGE and OUTPUT.
+Nothing is done with PROMPT or WAIT-P."
+  (if error
+      (save-excursion
+	(with-output-to-temp-buffer comint-output-buffer
+	  (set-buffer comint-output-buffer)
+	  (if message (insert message))
+	  (insert ?\n)
+	  (insert output)
+	  (beep t))))
+  t)
+
+;;;
+(defun comint-process-filter (process output)
+  "Filter PROCESS OUTPUT.  See comint-send for more information.  The
+first element of the comint-send-queue is the current send entry.  If
+the entry has a nil no-insert flag, insert the results into the
+process buffer.
+
+If the send is an interrupt, comint-interrupt-start is funcalled on
+the output and should return the start of the output of an interrupt.
+
+comint-prompt-status is called with the old prompt and the last line.
+It should return 'error if the last line is an error, T if it is a
+prompt and nil otherwise.  It should also update the process status by
+funcalling comint-update-status.
+
+If there is a send handler, it is called with \(error-p wait-p message
+output prompt) and should determine what sort of notification is
+appropriate and return T if errors should be fixed and NIL otherwise.
+
+If the prompt is an error, then comint-fix-error will be sent to fix
+the error. 
+
+When there is a prompt in the output stream, the next send will be
+dispatched unless the wait flag for the send is a string.  If it is a
+string, then results will be discarded until one matches the string as
+a regexp.
+
+Output to the process should only be done through the functions
+comint-send or comint-default-send, or results will be mixed up."
+  (let* ((inhibit-quit t)
+	 (window (selected-window))
+	 (comint-original-buffer (prog1 (current-buffer)
+				   (set-buffer (process-buffer process))))
+	 (match-data (match-data))
+	 (send (car comint-send-queue))
+	 (no-insert (cdr send))
+	 (wait-p (cdr no-insert))
+	 (messagep (cdr (cdr wait-p)))
+	 (handler (cdr messagep))
+	 (running (cdr handler))
+	 (old-prompt (cdr running))
+	 (line (cdr old-prompt))
+	 (result (car (cdr line)))
+	 (old-result (car result))
+	 (no-insert (car no-insert))
+	 (message (car messagep))
+	 (wait-p (car wait-p))
+	 (sync (stringp wait-p)))
+    (comint-log process output t)
+    ;; Remove leading whitespace
+    (if (and (null old-result)
+	     (save-excursion (goto-char (process-mark process)) (bolp))
+	     (eq (string-match "[ \t]*\n" output) 0))
+	(setq output (substring output (match-end 0))))
+    (rplaca result (concat old-result output))
+    (while (string-match "\n" (car result) (car line))
+      (rplaca line (match-end 0)))
+    (if (not (or sync no-insert))
+	(progn
+	  (comint-insert output)
+	  ;; Throw away output if storing in buffer
+	  (rplaca result (substring (car result) (car line)))
+	  (rplaca line 0)))
+    (if (consp (car running))		;Waiting for interrupt
+	(let ((split (funcall comint-interrupt-start (car result))))
+	  (if split
+	      (let ((interrupted (car running)))
+		;; Store output to previous send
+		(rplaca (comint-send-variables interrupted) 
+			(substring (car result) 0 split))
+		(rplaca result (substring (car result) (car line)))
+		(rplaca line 0)
+		(rplaca running t)))))
+    (if (not (consp (car running)))	;Look for prompt
+	(let* ((last (substring (car result) (car line)))
+	       (is-prompt
+		(funcall comint-prompt-status (car old-prompt) last)))
+	  (if is-prompt
+	      (let* ((output
+		      (if (or no-insert sync)
+			  (funcall comint-output-filter 
+				   (substring (car result) 0 (car line)))))
+		     (handler (car handler))
+		     (error (eq is-prompt 'error)))
+		(setq old-result (car result))
+		(rplaca result output)
+		(rplacd result (if error (list last) last))
+		(setq comint-output (car result)
+		      comint-errorp 
+		      (or error
+			  (and comint-error-regexp
+			       comint-output
+			       (string-match comint-error-regexp
+					     comint-output))))
+		(unwind-protect
+		     (if handler
+			 (setq handler
+			       (funcall handler comint-errorp wait-p
+					message output last)))
+		  (if (and error handler no-insert comint-fix-error)
+		      (setq comint-send-queue 
+			    (cons (list comint-fix-error t nil 'fix
+					"Fixing error" nil
+					nil nil 0 (cons nil nil))
+				  ;; We may have aborted
+				  (or (cdr comint-send-queue)
+				      comint-send-queue))))
+		  (if sync
+		      (let ((match (string-match wait-p old-result)))
+			(if match
+			    (progn
+			      (rplaca
+			       (cdr (cdr (cdr (cdr (car comint-end-queue)))))
+			       "Done")
+			      (if (not no-insert)
+				  (comint-insert 
+				   (concat 
+				    (substring old-result 0 match)
+				    (substring old-result (match-end 0)))))
+			      (rplaca result (substring old-result
+							match (car line)))
+			      (rplaca messagep "Done")
+			      (rplaca running nil)
+			      (comint-dispatch-send process))))
+		      ;; Not waiting
+		      (rplaca messagep "Done")
+		      (rplaca running nil)
+		      (comint-dispatch-send process))))
+	      (rplacd result nil))))
+    (store-match-data match-data)
+    (if (or (get-buffer-window comint-original-buffer)
+	    (eq (window-buffer (minibuffer-window)) comint-original-buffer))
+	(set-buffer comint-original-buffer))))
+
+;;;
+(defun comint-dispatch-send (process)
+  "Dispatch the next send in PROCESS comint-send-queue, popping the
+current send if done."
+  (let* ((send (car comint-send-queue))
+	 (results (comint-send-results send))
+	 (prompt (cdr results)))
+    ;; Never pop the last record
+    (cond ((and (null comint-send-queue) ; Catch a bug.
+		(null comint-end-queue)))
+
+	  ((eq comint-send-queue comint-end-queue)
+	   (let ((init (car send))
+		 (running (comint-send-variables send)))
+	     (setq comint-queue-emptied t)
+	     ;; Set old prompt to prompt
+	     (if prompt
+		 (rplaca (cdr (comint-send-variables send)) 
+			 (if (consp prompt) (car prompt) prompt)))
+	     (rplaca send nil)
+	     (if init
+		 (funcall init)
+	       (if (stringp (car running))
+		   ;; Continue if interrupted.  There is no way to
+		   ;; sense if the interrupted command actually
+		   ;; started, so it is possible that a command will
+		   ;; get lost.  
+		   (progn (funcall comint-update-status 
+				   (car (cdr (cdr (cdr send)))))
+			  (comint-sender process comint-continue)
+			  (comint-process-filter process (car running))
+			  (rplaca running t))))))
+	  (t
+	   (if prompt
+	       ;; Pop
+	       (setq comint-send-queue (cdr comint-send-queue)
+		     send (car comint-send-queue))
+	     ;; Set prompt to top-level prompt
+	     (setq prompt (cdr (comint-send-results (car comint-end-queue)))))
+	   (let* ((top-level (eq comint-send-queue comint-end-queue))
+		  (string (car send))
+		  (no-insert (cdr send))
+		  (wait-p (cdr no-insert))
+		  (status (cdr wait-p))
+		  (message (cdr status))
+		  (status (car status))
+		  (no-insert (car no-insert))
+		  (message (car message))
+		  (running (comint-send-variables send)))
+	     (if top-level
+		 (rplaca send nil)
+	       (if (stringp string) (funcall comint-update-status status)))
+	     (if (and message (not no-insert) (not (stringp (car wait-p)))
+		      (not top-level))
+		 ;; Display message on first output
+		 (comint-insert
+		  (concat comment-start comment-start comment-start
+			  message comment-end "\n")))
+	     (if (and string (not (stringp string)))
+		 ;; Elisp code
+		 (progn 
+		   (rplacd (comint-send-results (car comint-send-queue))
+			   (if (consp prompt) (car prompt) prompt))
+		   (funcall string)
+		   (comint-dispatch-send process))
+	       (if (stringp (car running))
+		   ;; Continue interrupted send
+		   (let ((output (car running)))
+		     (if (or top-level (car (comint-send-results send))
+			     (not (string-equal output "")))
+			 ;; Continue old command
+			 (progn
+			   (rplaca running t)
+			   (funcall comint-update-status status)
+			   (comint-sender process comint-continue)
+			   (comint-process-filter process output)
+			   ;; Send queued default sends
+			   (if (and top-level string)
+			       (comint-sender process string)))
+		       ;; Assume we have to restart the command since
+		       ;; there is no output.  There is no way to
+		       ;; sense whether or not the inferior has
+		       ;; started processing the previous send.  This
+		       ;; is a problem only if the original did start
+		       ;; and had side effects.
+		       (rplaca running nil)
+		       (setq comint-send-queue 
+			     (cons (list comint-fix-error t nil 'fix
+					 "Fixing error" nil
+					 nil nil 0 (cons nil nil))
+				   comint-send-queue))
+		       (comint-dispatch-send process)))
+		 (if (not top-level)
+		     ;; New send, set old prompt to the prompt of previous
+		     (rplaca (cdr (comint-send-variables send)) 
+			     (if (consp prompt) (car prompt) prompt)))
+		 (if string
+		     (progn
+		       (rplaca running t)
+		       (comint-sender process string))))))))))
+
+;;;
+(defun comint-interrupt (process send)
+  "Interrupt PROCESS to send SEND if comint-continue is defined and
+the current send is not waiting.  Otherwise, SEND will be the next
+send."
+  (if (and comint-continue (not (car (cdr (cdr (car comint-send-queue))))))
+      (let* ((current (car comint-send-queue))
+	     (interrupt
+	      ;; string no-insert wait-p status message handler
+	      (list nil t nil 'interrupt "Interrupt" nil
+		    ;; running old-prompt line (output . prompt)
+		    current nil 0 (cons nil nil))))
+	(setq comint-send-queue (cons interrupt (cons send comint-send-queue)))
+	(funcall comint-update-status 'interrupt)
+	(comint-interrupt-subjob))
+      (if (eq comint-send-queue comint-end-queue)
+	  (setq comint-send-queue
+		(cons (car comint-send-queue)
+		      (cons send comint-send-queue)))
+	  (rplacd comint-send-queue (cons send (cdr comint-send-queue))))))
+
+;;;%Interface
+(defun comint-setup-ipc (&optional force)
+  "Setup for IPC in the current buffer.  If called interactively,
+force comint-send-queue to be initialized."
+  (interactive "p")
+  (make-local-variable 'comint-send-newline)
+  (make-local-variable 'comint-always-scroll)
+  (make-local-variable 'comint-fix-error)
+  (make-local-variable 'comint-continue)
+  (make-local-variable 'comint-interrupt-regexp)
+  (make-local-variable 'comint-error-regexp)
+  (make-local-variable 'comint-output-filter)
+  (make-local-variable 'comint-interrupt-start)
+  (make-local-variable 'comint-handler)
+  (make-local-variable 'comint-update-status)
+  (make-local-variable 'comint-prompt-status)
+  (make-local-variable 'comint-send-queue)
+  (make-local-variable 'comint-end-queue)
+  (make-local-variable 'comint-queue-emptied)
+  (make-local-variable 'comint-output)
+  (make-local-variable 'comint-errorp)
+  (make-local-variable 'comint-status)
+  (make-local-variable 'comint-aborting)
+  (if (or force (not comint-send-queue))
+      (setq comint-send-queue 
+	    (list (list nil nil nil 'run "Top Level"
+			nil t nil 0 (cons nil nil)))
+	    comint-end-queue comint-send-queue))
+  (let ((process (get-buffer-process (current-buffer))))
+    (set-process-filter process 'comint-process-filter)
+    (set-process-sentinel process 'comint-process-sentinel))
+  (setq mode-line-process 'comint-status))
+
+;;;%%Input
+(defun comint-send (process string 
+			    &optional 
+			    no-insert
+			    wait
+			    status 
+			    message
+			    handler
+			    after)
+  "Do a send to PROCESS of STRING.  Optionally specify NO-INSERT,
+WAIT, STATUS, MESSAGE, HANDLER and AFTER.  Without optional arguments,
+this is just like process-send-string.  If STRING is not a string,
+then it is assumed to be an elisp function and will be called when
+encountered in the send queue.  The send will be the next one if WAIT,
+after the last send if AFTER, otherwise it will be put at the end of
+the queue. If WAIT is non-NIL or on the first send to a busy inferior,
+the inferior will be interrupted if possible, see comint-interrupt for
+more information.  Once the send is sent, the process status will be
+STATUS or 'run.  Output of the send will be inserted into the process
+buffer unless NO-INSERT.  This function returns a list of \(result .
+prompt).  If WAIT is a string, output will be inserted until one
+matches the string as a regexp.  If WAIT is T, then PROMPT will have
+the prompt when finished and RESULT will have the output.  If PROMPT
+is a list, then there was an error.  If WAIT is not T, then the list
+returned will change when the send has been sent and is finished.  If
+HANDLER is nil it will be set to comint-handler.  If it is T, errors
+will be ignored.  When a send is finished, it calls handler with
+\(error-p WAIT MESSAGE output prompt) which decides what to do with
+the output.
+
+VARIABLES:
+
+comint-always-scroll will cause all process output to be visible.
+
+comint-fix-error is the string used to fix errors.
+
+comint-continue is the string used to continue after an interrupt.
+
+comint-interrupt-regexp is the default regexp to use in finding the
+start of the interrupt text.  
+
+comint-error-regexp will set comint-errorp if found in the process output.  
+
+FUNCTIONS:  Each of the functions in these variables is called with
+the buffer set to the appropriate process buffer and
+comint-original-buffer bound to the buffer current when the process
+filter was called.  
+
+comint-update-status is a function \(status) that is called each time
+the process status changes.
+
+comint-prompt-status is called with the old prompt and the last line.
+It should return 'error if the last line is an error, T if it is a
+prompt and nil otherwise.  It should also update the process status by
+funcalling comint-update-status.
+
+comint-output-filter is a function \(output) for sends with NO-INSERT.
+It should return the output string.
+
+comint-interrupt-start is a function \(output) that returns the start
+of the interrupt text in output using comint-interrupt-regexp to find it."
+  (save-excursion
+    (set-buffer (process-buffer process))
+    (let* ((inhibit-quit t)
+	   (send (list string 
+		       no-insert
+		       wait
+		       (or status 'run)
+		       message 
+		       (if (eq handler t) nil (or handler comint-handler))
+		       ;; running, old-prompt, line
+		       nil nil 0
+		       ;; (output . prompt)
+		       (cons nil nil)))
+	   (pointer (comint-send-results send))
+	   (top-level (eq comint-send-queue comint-end-queue))
+	   (end (car comint-end-queue))
+	   (current (car comint-send-queue))
+	   (prompt (cdr (comint-send-results current)))
+	   (ok nil))
+      (setq comint-aborting nil)
+      (if (and top-level (or (stringp wait) prompt))
+	  (progn
+	    (setq comint-send-queue (cons send comint-send-queue))
+	    (comint-dispatch-send process))
+	  (if (or (and wait (not after) (not prompt)) top-level)
+	      (comint-interrupt process send)
+	      (let ((looking t) 
+		    (next comint-send-queue))
+		(if after
+		    (while (and looking next)
+		      (if (eq (car next) comint-last-send)
+			  (progn
+			    (rplacd next (cons send (cdr next)))
+			    (setq looking nil)))
+		      (setq next (cdr next))))
+		(if looking
+		    (progn
+		      (rplaca comint-end-queue send)
+		      (setq comint-end-queue
+			    (rplacd comint-end-queue (cons end nil))))))))
+      (setq comint-last-send send)
+      (unwind-protect
+	   (let ((inhibit-quit nil))
+	     (if (eq wait t)
+		 (while (not (cdr pointer))
+		   (accept-process-output)
+		   (sit-for 0)))
+	     (setq ok pointer))
+	(if (not ok)
+	    (if (eq send (car comint-send-queue))
+		(let ((interrupt 
+		       ;; string no-insert wait status message handler
+		       (list nil t nil 'interrupt "Interrupt" nil
+			     ;; running old-prompt line (output . prompt)
+			     send (car (cdr (comint-send-variables send)))
+			     nil (cons nil nil)))) 
+		  (setq comint-send-queue
+			(cons interrupt (cdr comint-send-queue)))
+		  (comint-interrupt-subjob))
+		(setq comint-send-queue (delq send comint-send-queue))))))))
+
+;;;
+(defun comint-send-code (process code)
+  "Execute after the previous send in PROCESS queue CODE. You do not
+want to execute synchronous sends in the code or it will lock up. " 
+  (comint-send process code nil nil nil nil nil t))
+
+;;;
+(defun comint-default-send (process string)
+  "Send to PROCESS top-level, STRING."  
+  (save-excursion
+    (set-buffer (process-buffer process))
+    (let* ((top (car comint-end-queue))
+	   (old (car top)))
+      (rplaca (cdr (cdr (cdr (cdr (car comint-end-queue))))) string)
+      (if (eq comint-send-queue comint-end-queue)
+	  (progn (funcall comint-update-status 'run)
+		 (rplaca (comint-send-variables (car comint-send-queue)) t)
+		 (rplacd (comint-send-results (car comint-send-queue)) nil)
+		 (comint-sender process string))
+	  (rplaca top
+		  (if old
+		      (concat old (if comint-send-newline "\n") string)
+		      string))))))
+
+;;;
+(defun comint-sync (process start start-regexp end end-regexp)
+  "Synchronize with PROCESS output stream.  START will be sent with
+each prompt received until START-REGEXP shows up in the stream.  Then
+END will be sent and all output will be discarded until END-REGEXP
+shows up in the output stream."
+  (comint-send 
+   process
+   start
+   nil start-regexp 'sync "Start sync" 
+   (function (lambda (error-p wait message output prompt)
+     (if (not (string-match wait output))
+	 (comint-sender 
+	  (get-buffer-process (current-buffer))
+	  (car (car comint-send-queue))))
+     nil)))
+  (comint-send
+   process
+   end
+   t end-regexp 'sync "End sync"
+   (function (lambda (&rest args) nil))))
+
+;;;
+(defun comint-abort-sends (&optional process)
+  "Abort all of the pending sends for optional PROCESS and show their
+messages in *Aborted Commands*."
+  (interactive)
+  (save-excursion
+    (setq process (or process (get-buffer-process (current-buffer))))
+    (set-buffer (process-buffer process))
+    (setq comint-aborting t)
+    (if (not (eq comint-send-queue comint-end-queue))
+	(let* ((inhibit-quit t)
+	       (send (car comint-send-queue))
+	       (vars (comint-send-variables send))
+	       (pointer comint-send-queue)
+	       (new nil)
+	       (interrupt (and (car vars) 
+			       (not (cdr (comint-send-results send))))))
+	  (if interrupt
+	      (progn			;Sent, but no prompt 
+		(if (consp (car vars))
+		    (progn (setq new (list send))
+			   (rplaca (cdr (cdr (cdr (cdr (cdr send)))))
+				   (function (lambda (&rest args) t))))
+		    (setq new
+			  (list
+			   (list nil t nil 'interrupt "Interrupt"
+				 (function (lambda (&rest args) t))
+				 send (car (cdr (comint-send-variables send)))
+				 nil (cons nil nil))))
+		    (comint-interrupt-subjob)))) ;Already interrupting
+	  (save-excursion
+	    (set-buffer (get-buffer-create "*Aborted Commands*"))
+	    (delete-region (point-min) (point-max)))
+	  (while (not (eq pointer comint-end-queue))
+	    (let ((send (car pointer)))
+	      (if (car (cdr (cdr (cdr (cdr send))))) ;Message
+		  (save-excursion
+		    (set-buffer "*Aborted Commands*")
+		    (insert (comint-send-description send))
+		    (insert "\n\n")))
+	      (if (and comint-fix-error
+		       (stringp (car (comint-send-variables send))))
+		  ;; Interrupted 
+		  (setq new (cons 
+			     (list comint-fix-error t nil 'fix
+				   "Fixing error" nil
+				   nil nil 0 (cons nil nil))
+			     new)))
+	      (setq pointer (cdr pointer))))
+	  (bury-buffer "*Aborted Commands*")
+	  (rplaca (car comint-end-queue) nil)
+	  (setq comint-send-queue 
+		(reverse (cons (car comint-end-queue) new))
+		comint-end-queue 
+		(let ((pointer comint-send-queue))
+		  (while (cdr pointer)
+		    (setq pointer (cdr pointer)))
+		  pointer))
+ 	  (run-hooks 'comint-abort-hook)
+	  (if (not interrupt) (comint-dispatch-send process))))))
+
+;;;
+(defun comint-current-send (showp)
+  "Show the message of the current send in the minibuffer."
+  (interactive "P")
+  (if showp
+      (with-output-to-temp-buffer comint-output-buffer
+	(let ((send comint-send-queue))
+	  (save-excursion
+	    (set-buffer comint-output-buffer)
+	    (insert "Pending commands:\n")
+	    (while send
+	      (let ((message (car (cdr (cdr (cdr (cdr (car send))))))))
+		(if message (insert (concat message "\n"))))
+	      (setq send (cdr send)))))))
+  (message
+   (concat "Command: "
+	   (or (comint-send-description (car comint-send-queue))
+	       "Unknown"))))
+
+
+;;;
+(defun comint-display-output (text &optional buffer)
+  "Put TEXT in optional BUFFER and show it in a small temporary window."
+  (setq buffer (or buffer comint-output-buffer))
+  (with-output-to-temp-buffer buffer
+    (save-excursion
+      (set-buffer buffer)
+      (insert text)
+      (set-buffer-modified-p nil)))
+  text)
+;; Perhaps this should use ilisp-display-output.
+
+;;;
+(defun comint-display-error (text)
+  "Put TEXT in the comint-error-buffer and display it."
+  (comint-display-output text comint-error-buffer))
+
+(provide 'comint-ipc)