pmailhdr.el: File removed.
[bpt/emacs.git] / lisp / eshell / esh-proc.el
CommitLineData
60370d40 1;;; esh-proc.el --- process management
affbf647 2
f2e3589a 3;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004,
ae940284 4;; 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
affbf647 5
7de5b421
GM
6;; Author: John Wiegley <johnw@gnu.org>
7
affbf647
GM
8;; This file is part of GNU Emacs.
9
4ee57b2a 10;; GNU Emacs is free software: you can redistribute it and/or modify
affbf647 11;; it under the terms of the GNU General Public License as published by
4ee57b2a
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
affbf647
GM
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
4ee57b2a 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
affbf647 22
4e6cc05c
GM
23;;; Commentary:
24
affbf647
GM
25(provide 'esh-proc)
26
4e6cc05c
GM
27(eval-when-compile
28 (require 'eshell)
29 (require 'esh-util))
affbf647
GM
30
31(defgroup eshell-proc nil
32 "When Eshell invokes external commands, it always does so
33asynchronously, so that Emacs isn't tied up waiting for the process to
34finish."
35 :tag "Process management"
36 :group 'eshell)
37
affbf647
GM
38;;; User Variables:
39
40(defcustom eshell-proc-load-hook '(eshell-proc-initialize)
41 "*A hook that gets run when `eshell-proc' is loaded."
42 :type 'hook
43 :group 'eshell-proc)
44
45(defcustom eshell-process-wait-seconds 0
46 "*The number of seconds to delay waiting for a synchronous process."
47 :type 'integer
48 :group 'eshell-proc)
49
50(defcustom eshell-process-wait-milliseconds 50
51 "*The number of milliseconds to delay waiting for a synchronous process."
52 :type 'integer
53 :group 'eshell-proc)
54
55(defcustom eshell-done-messages-in-minibuffer t
56 "*If non-nil, subjob \"Done\" messages will display in minibuffer."
57 :type 'boolean
58 :group 'eshell-proc)
59
60(defcustom eshell-delete-exited-processes t
61 "*If nil, process entries will stick around until `jobs' is run.
62This variable sets the buffer-local value of `delete-exited-processes'
63in Eshell buffers.
64
65This variable causes Eshell to mimic the behavior of bash when set to
66nil. It allows the user to view the exit status of a completed subjob
67\(process) at their leisure, because the process entry remains in
68memory until the user examines it using \\[list-processes].
69
70Otherwise, if `eshell-done-messages-in-minibuffer' is nil, and this
71variable is set to t, the only indication the user will have that a
72subjob is done is that it will no longer appear in the
73\\[list-processes\\] display.
74
75Note that Eshell will have to be restarted for a change in this
76variable's value to take effect."
77 :type 'boolean
78 :group 'eshell-proc)
79
80(defcustom eshell-reset-signals
81 "^\\(interrupt\\|killed\\|quit\\|stopped\\)"
82 "*If a termination signal matches this regexp, the terminal will be reset."
83 :type 'regexp
84 :group 'eshell-proc)
85
86(defcustom eshell-exec-hook nil
87 "*Called each time a process is exec'd by `eshell-gather-process-output'.
88It is passed one argument, which is the process that was just started.
89It is useful for things that must be done each time a process is
90executed in a eshell mode buffer (e.g., `process-kill-without-query').
91In contrast, `eshell-mode-hook' is only executed once when the buffer
92is created."
93 :type 'hook
94 :group 'eshell-proc)
95
96(defcustom eshell-kill-hook '(eshell-reset-after-proc)
97 "*Called when a process run by `eshell-gather-process-output' has ended.
98It is passed two arguments: the process that was just ended, and the
99termination status (as a string). Note that the first argument may be
100nil, in which case the user attempted to send a signal, but there was
101no relevant process. This can be used for displaying help
102information, for example."
103 :type 'hook
104 :group 'eshell-proc)
105
106;;; Internal Variables:
107
108(defvar eshell-current-subjob-p nil)
109
110(defvar eshell-process-list nil
111 "A list of the current status of subprocesses.")
112
113;;; Functions:
114
115(defun eshell-proc-initialize ()
116 "Initialize the process handling code."
117 (make-local-variable 'eshell-process-list)
118 (define-key eshell-command-map [(meta ?i)] 'eshell-insert-process)
119 (define-key eshell-command-map [(control ?c)] 'eshell-interrupt-process)
120 (define-key eshell-command-map [(control ?k)] 'eshell-kill-process)
121 (define-key eshell-command-map [(control ?d)] 'eshell-send-eof-to-process)
36e81327 122; (define-key eshell-command-map [(control ?q)] 'eshell-continue-process)
affbf647 123 (define-key eshell-command-map [(control ?s)] 'list-processes)
36e81327 124; (define-key eshell-command-map [(control ?z)] 'eshell-stop-process)
affbf647
GM
125 (define-key eshell-command-map [(control ?\\)] 'eshell-quit-process))
126
127(defun eshell-reset-after-proc (proc status)
128 "Reset the command input location after a process terminates.
129The signals which will cause this to happen are matched by
130`eshell-reset-signals'."
ca7aae91
JW
131 (if (and (stringp status)
132 (string-match eshell-reset-signals status))
affbf647
GM
133 (eshell-reset)))
134
135(defun eshell-wait-for-process (&rest procs)
136 "Wait until PROC has successfully completed."
137 (while procs
138 (let ((proc (car procs)))
ca7aae91 139 (when (eshell-processp proc)
affbf647
GM
140 ;; NYI: If the process gets stopped here, that's bad.
141 (while (assq proc eshell-process-list)
142 (if (input-pending-p)
143 (discard-input))
144 (sit-for eshell-process-wait-seconds
145 eshell-process-wait-milliseconds))))
146 (setq procs (cdr procs))))
147
148(defalias 'eshell/wait 'eshell-wait-for-process)
149
150(defun eshell/jobs (&rest args)
151 "List processes, if there are any."
ca7aae91
JW
152 (and (fboundp 'process-list)
153 (process-list)
affbf647
GM
154 (list-processes)))
155
156(defun eshell/kill (&rest args)
157 "Kill processes, buffers, symbol or files."
158 (let ((ptr args)
159 (signum 'SIGINT))
160 (while ptr
ca7aae91 161 (if (or (eshell-processp (car ptr))
affbf647
GM
162 (and (stringp (car ptr))
163 (string-match "^[A-Za-z/][A-Za-z0-9<>/]+$"
164 (car ptr))))
165 ;; What about when $lisp-variable is possible here?
166 ;; It could very well name a process.
167 (setcar ptr (get-process (car ptr))))
168 (setq ptr (cdr ptr)))
169 (while args
ca7aae91 170 (let ((id (if (eshell-processp (car args))
affbf647
GM
171 (process-id (car args))
172 (car args))))
173 (when id
174 (cond
175 ((null id)
176 (error "kill: bad signal spec"))
177 ((and (numberp id) (= id 0))
178 (error "kill: bad signal spec `%d'" id))
179 ((and (stringp id)
180 (string-match "^-?[0-9]+$" id))
181 (setq signum (abs (string-to-number id))))
182 ((stringp id)
183 (let (case-fold-search)
be6e5004 184 (if (string-match "^-\\([A-Z]+[12]?\\)$" id)
affbf647
GM
185 (setq signum
186 (intern (concat "SIG" (match-string 1 id))))
187 (error "kill: bad signal spec `%s'" id))))
188 ((< id 0)
189 (setq signum (abs id)))
190 (t
191 (signal-process id signum)))))
192 (setq args (cdr args)))
193 nil))
194
195(defun eshell-read-process-name (prompt)
196 "Read the name of a process from the minibuffer, using completion.
197The prompt will be set to PROMPT."
198 (completing-read prompt
199 (mapcar
200 (function
201 (lambda (proc)
202 (cons (process-name proc) t)))
203 (process-list)) nil t))
204
205(defun eshell-insert-process (process)
206 "Insert the name of PROCESS into the current buffer at point."
207 (interactive
208 (list (get-process
209 (eshell-read-process-name "Name of process: "))))
210 (insert-and-inherit "#<process " (process-name process) ">"))
211
212(defsubst eshell-record-process-object (object)
213 "Record OBJECT as now running."
ca7aae91 214 (if (and (eshell-processp object)
affbf647
GM
215 eshell-current-subjob-p)
216 (eshell-interactive-print
217 (format "[%s] %d\n" (process-name object) (process-id object))))
218 (setq eshell-process-list
219 (cons (list object eshell-current-handles
220 eshell-current-subjob-p nil nil)
221 eshell-process-list)))
222
223(defun eshell-remove-process-entry (entry)
224 "Record the process ENTRY as fully completed."
ca7aae91 225 (if (and (eshell-processp (car entry))
affbf647
GM
226 (nth 2 entry)
227 eshell-done-messages-in-minibuffer)
b2a9524a
DG
228 (message "[%s]+ Done %s" (process-name (car entry))
229 (process-command (car entry))))
affbf647
GM
230 (setq eshell-process-list
231 (delq entry eshell-process-list)))
232
ca7aae91
JW
233(defvar eshell-scratch-buffer " *eshell-scratch*"
234 "Scratch buffer for holding Eshell's input/output.")
235(defvar eshell-last-sync-output-start nil
236 "A marker that tracks the beginning of output of the last subprocess.
237Used only on systems which do not support async subprocesses.")
238
1e262c45
GM
239(defvar eshell-needs-pipe '("bc")
240 "List of commands which need `process-connection-type' to be nil.
241Currently only affects commands in pipelines, and not those at
242the front. If an element contains a directory part it must match
243the full name of a command, otherwise just the nondirectory part must match.")
244
245(defun eshell-needs-pipe-p (command)
246 "Return non-nil if COMMAND needs `process-connection-type' to be nil.
247See `eshell-needs-pipe'."
248 (and eshell-in-pipeline-p
249 (not (eq eshell-in-pipeline-p 'first))
250 ;; FIXME should this return non-nil for anything that is
251 ;; neither 'first nor 'last? See bug#1388 discussion.
252 (catch 'found
253 (dolist (exe eshell-needs-pipe)
254 (if (string-equal exe (if (string-match "/" exe)
255 command
256 (file-name-nondirectory command)))
257 (throw 'found t))))))
258
affbf647
GM
259(defun eshell-gather-process-output (command args)
260 "Gather the output from COMMAND + ARGS."
261 (unless (and (file-executable-p command)
262 (file-regular-p command))
263 (error "%s: not an executable file" command))
264 (let* ((delete-exited-processes
265 (if eshell-current-subjob-p
266 eshell-delete-exited-processes
267 delete-exited-processes))
268 (process-environment (eshell-environment-variables))
ca7aae91
JW
269 proc decoding encoding changed)
270 (cond
271 ((fboundp 'start-process)
272 (setq proc
1e262c45
GM
273 (let ((process-connection-type
274 (unless (eshell-needs-pipe-p command)
275 process-connection-type)))
276 (apply 'start-process
277 (file-name-nondirectory command) nil
278 ;; `start-process' can't deal with relative filenames.
279 (append (list (expand-file-name command)) args))))
ca7aae91
JW
280 (eshell-record-process-object proc)
281 (set-process-buffer proc (current-buffer))
282 (if (eshell-interactive-output-p)
283 (set-process-filter proc 'eshell-output-filter)
284 (set-process-filter proc 'eshell-insertion-filter))
285 (set-process-sentinel proc 'eshell-sentinel)
286 (run-hook-with-args 'eshell-exec-hook proc)
287 (when (fboundp 'process-coding-system)
288 (let ((coding-systems (process-coding-system proc)))
289 (setq decoding (car coding-systems)
290 encoding (cdr coding-systems)))
291 ;; If start-process decided to use some coding system for
292 ;; decoding data sent from the process and the coding system
293 ;; doesn't specify EOL conversion, we had better convert CRLF
294 ;; to LF.
295 (if (vectorp (coding-system-eol-type decoding))
296 (setq decoding (coding-system-change-eol-conversion decoding 'dos)
297 changed t))
298 ;; Even if start-process left the coding system for encoding
299 ;; data sent from the process undecided, we had better use the
300 ;; same one as what we use for decoding. But, we should
301 ;; suppress EOL conversion.
302 (if (and decoding (not encoding))
303 (setq encoding (coding-system-change-eol-conversion decoding 'unix)
304 changed t))
305 (if changed
306 (set-process-coding-system proc decoding encoding))))
307 (t
308 ;; No async subprocesses...
309 (let ((oldbuf (current-buffer))
310 (interact-p (eshell-interactive-output-p))
311 lbeg lend line proc-buf exit-status)
312 (and (not (markerp eshell-last-sync-output-start))
313 (setq eshell-last-sync-output-start (point-marker)))
314 (setq proc-buf
315 (set-buffer (get-buffer-create eshell-scratch-buffer)))
316 (erase-buffer)
317 (set-buffer oldbuf)
318 (run-hook-with-args 'eshell-exec-hook command)
319 (setq exit-status
320 (apply 'call-process-region
321 (append (list eshell-last-sync-output-start (point)
322 command t
323 eshell-scratch-buffer nil)
324 args)))
325 ;; When in a pipeline, record the place where the output of
326 ;; this process will begin.
327 (and eshell-in-pipeline-p
328 (set-marker eshell-last-sync-output-start (point)))
329 ;; Simulate the effect of the process filter.
330 (when (numberp exit-status)
331 (set-buffer proc-buf)
332 (goto-char (point-min))
333 (setq lbeg (point))
334 (while (eq 0 (forward-line 1))
335 (setq lend (point)
336 line (buffer-substring-no-properties lbeg lend))
337 (set-buffer oldbuf)
338 (if interact-p
339 (eshell-output-filter nil line)
340 (eshell-output-object line))
341 (setq lbeg lend)
342 (set-buffer proc-buf))
343 (set-buffer oldbuf))
344 (eshell-update-markers eshell-last-output-end)
345 ;; Simulate the effect of eshell-sentinel.
346 (eshell-close-handles (if (numberp exit-status) exit-status -1))
347 (run-hook-with-args 'eshell-kill-hook command exit-status)
348 (or eshell-in-pipeline-p
349 (setq eshell-last-sync-output-start nil))
350 (if (not (numberp exit-status))
351 (error "%s: external command failed: %s" command exit-status))
352 (setq proc t))))
affbf647
GM
353 proc))
354
355(defun eshell-insertion-filter (proc string)
356 "Insert a string into the eshell buffer, or a process/file/buffer.
357PROC is the process for which we're inserting output. STRING is the
358output."
359 (when (buffer-live-p (process-buffer proc))
dafac6f3
GM
360 (with-current-buffer (process-buffer proc)
361 (let ((entry (assq proc eshell-process-list)))
362 (when entry
363 (setcar (nthcdr 3 entry)
364 (concat (nth 3 entry) string))
365 (unless (nth 4 entry) ; already being handled?
366 (while (nth 3 entry)
367 (let ((data (nth 3 entry)))
368 (setcar (nthcdr 3 entry) nil)
369 (setcar (nthcdr 4 entry) t)
370 (eshell-output-object data nil (cadr entry))
371 (setcar (nthcdr 4 entry) nil)))))))))
affbf647
GM
372
373(defun eshell-sentinel (proc string)
374 "Generic sentinel for command processes. Reports only signals.
375PROC is the process that's exiting. STRING is the exit message."
376 (when (buffer-live-p (process-buffer proc))
dafac6f3
GM
377 (with-current-buffer (process-buffer proc)
378 (unwind-protect
379 (let* ((entry (assq proc eshell-process-list)))
380; (if (not entry)
381; (error "Sentinel called for unowned process `%s'"
382; (process-name proc))
383 (when entry
384 (unwind-protect
385 (progn
386 (unless (string= string "run")
387 (unless (string-match "^\\(finished\\|exited\\)" string)
388 (eshell-insertion-filter proc string))
389 (eshell-close-handles (process-exit-status proc) 'nil
390 (cadr entry))))
391 (eshell-remove-process-entry entry))))
392 (run-hook-with-args 'eshell-kill-hook proc string)))))
affbf647
GM
393
394(defun eshell-process-interact (func &optional all query)
395 "Interact with a process, using PROMPT if more than one, via FUNC.
396If ALL is non-nil, background processes will be interacted with as well.
397If QUERY is non-nil, query the user with QUERY before calling FUNC."
398 (let (defunct result)
399 (eshell-for entry eshell-process-list
400 (if (and (memq (process-status (car entry))
401 '(run stop open closed))
402 (or all
403 (not (nth 2 entry)))
404 (or (not query)
405 (y-or-n-p (format query (process-name (car entry))))))
406 (setq result (funcall func (car entry))))
407 (unless (memq (process-status (car entry))
408 '(run stop open closed))
409 (setq defunct (cons entry defunct))))
410 ;; clean up the process list; this can get dirty if an error
411 ;; occurred that brought the user into the debugger, and then they
412 ;; quit, so that the sentinel was never called.
413 (eshell-for d defunct
414 (eshell-remove-process-entry d))
415 result))
416
417(defcustom eshell-kill-process-wait-time 5
418 "*Seconds to wait between sending termination signals to a subprocess."
419 :type 'integer
420 :group 'eshell-proc)
421
422(defcustom eshell-kill-process-signals '(SIGINT SIGQUIT SIGKILL)
423 "*Signals used to kill processes when an Eshell buffer exits.
424Eshell calls each of these signals in order when an Eshell buffer is
425killed; if the process is still alive afterwards, Eshell waits a
426number of seconds defined by `eshell-kill-process-wait-time', and
427tries the next signal in the list."
428 :type '(repeat symbol)
429 :group 'eshell-proc)
430
431(defcustom eshell-kill-processes-on-exit nil
432 "*If non-nil, kill active processes when exiting an Eshell buffer.
433Emacs will only kill processes owned by that Eshell buffer.
434
435If nil, ownership of background and foreground processes reverts to
436Emacs itself, and will die only if the user exits Emacs, calls
437`kill-process', or terminates the processes externally.
438
439If `ask', Emacs prompts the user before killing any processes.
440
441If `every', it prompts once for every process.
442
443If t, it kills all buffer-owned processes without asking.
444
445Processes are first sent SIGHUP, then SIGINT, then SIGQUIT, then
446SIGKILL. The variable `eshell-kill-process-wait-time' specifies how
447long to delay between signals."
448 :type '(choice (const :tag "Kill all, don't ask" t)
449 (const :tag "Ask before killing" ask)
450 (const :tag "Ask for each process" every)
451 (const :tag "Don't kill subprocesses" nil))
452 :group 'eshell-proc)
453
454(defun eshell-round-robin-kill (&optional query)
455 "Kill current process by trying various signals in sequence.
456See the variable `eshell-kill-processes-on-exit'."
457 (let ((sigs eshell-kill-process-signals))
458 (while sigs
459 (eshell-process-interact
460 (function
461 (lambda (proc)
462 (signal-process (process-id proc) (car sigs)))) t query)
463 (setq query nil)
464 (if (not eshell-process-list)
465 (setq sigs nil)
466 (sleep-for eshell-kill-process-wait-time)
467 (setq sigs (cdr sigs))))))
468
469(defun eshell-query-kill-processes ()
470 "Kill processes belonging to the current Eshell buffer, possibly w/ query."
471 (when (and eshell-kill-processes-on-exit
472 eshell-process-list)
473 (save-window-excursion
474 (list-processes)
475 (if (or (not (eq eshell-kill-processes-on-exit 'ask))
476 (y-or-n-p (format "Kill processes owned by `%s'? "
477 (buffer-name))))
478 (eshell-round-robin-kill
479 (if (eq eshell-kill-processes-on-exit 'every)
480 "Kill Eshell child process `%s'? ")))
481 (let ((buf (get-buffer "*Process List*")))
482 (if (and buf (buffer-live-p buf))
483 (kill-buffer buf)))
484 (message nil))))
485
486(custom-add-option 'eshell-exit-hook 'eshell-query-kill-processes)
487
488(defun eshell-interrupt-process ()
489 "Interrupt a process."
490 (interactive)
491 (unless (eshell-process-interact 'interrupt-process)
492 (run-hook-with-args 'eshell-kill-hook nil "interrupt")))
493
494(defun eshell-kill-process ()
495 "Kill a process."
496 (interactive)
497 (unless (eshell-process-interact 'kill-process)
498 (run-hook-with-args 'eshell-kill-hook nil "killed")))
499
500(defun eshell-quit-process ()
501 "Send quit signal to process."
502 (interactive)
503 (unless (eshell-process-interact 'quit-process)
504 (run-hook-with-args 'eshell-kill-hook nil "quit")))
505
36e81327
JW
506;(defun eshell-stop-process ()
507; "Send STOP signal to process."
508; (interactive)
509; (unless (eshell-process-interact 'stop-process)
510; (run-hook-with-args 'eshell-kill-hook nil "stopped")))
511
512;(defun eshell-continue-process ()
513; "Send CONTINUE signal to process."
514; (interactive)
515; (unless (eshell-process-interact 'continue-process)
516; ;; jww (1999-09-17): this signal is not dealt with yet. For
517; ;; example, `eshell-reset' will be called, and so will
518; ;; `eshell-resume-eval'.
519; (run-hook-with-args 'eshell-kill-hook nil "continue")))
affbf647
GM
520
521(defun eshell-send-eof-to-process ()
522 "Send EOF to process."
523 (interactive)
524 (eshell-send-input nil nil t)
525 (eshell-process-interact 'process-send-eof))
526
527;;; Code:
528
cbee283d 529;; arch-tag: ac477a3e-ee4d-4b44-8ec6-212010e607bb
affbf647 530;;; esh-proc.el ends here