Update copyright year to 2014 by running admin/update-copyright.
[bpt/emacs.git] / lisp / eshell / esh-io.el
CommitLineData
ae5e4c48 1;;; esh-io.el --- I/O management -*- lexical-binding:t -*-
affbf647 2
ba318903 3;; Copyright (C) 1999-2014 Free Software Foundation, Inc.
affbf647 4
7de5b421
GM
5;; Author: John Wiegley <johnw@gnu.org>
6
affbf647
GM
7;; This file is part of GNU Emacs.
8
4ee57b2a 9;; GNU Emacs is free software: you can redistribute it and/or modify
affbf647 10;; it under the terms of the GNU General Public License as published by
4ee57b2a
GM
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
affbf647
GM
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
4ee57b2a 20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
affbf647 21
affbf647
GM
22;;; Commentary:
23
24;; At the moment, only output redirection is supported in Eshell. To
25;; use input redirection, the following syntax will work, assuming
26;; that the command after the pipe is always an external command:
27;;
28;; cat <file> | <command>
29;;
30;; Otherwise, output redirection and piping are provided in a manner
31;; consistent with most shells. Therefore, only unique features are
32;; mentioned here.
33;;
34;;;_* Insertion
35;;
36;; To insert at the location of point in a buffer, use '>>>':
37;;
38;; echo alpha >>> #<buffer *scratch*>;
39;;
40;;;_* Pseudo-devices
41;;
42;; A few pseudo-devices are provided, since Emacs cannot write
43;; directly to a UNIX device file:
44;;
45;; echo alpha > /dev/null ; the bit bucket
46;; echo alpha > /dev/kill ; set the kill ring
47;; echo alpha >> /dev/clip ; append to the clipboard
48;;
49;;;_* Multiple output targets
50;;
51;; Eshell can write to multiple output targets, including pipes.
52;; Example:
53;;
54;; (+ 1 2) > a > b > c ; prints number to all three files
55;; (+ 1 2) > a | wc ; prints to 'a', and pipes to 'wc'
56
8c7309fe
GM
57;;; Code:
58
5153ac72
GM
59(provide 'esh-io)
60
f87b1284
GM
61(require 'esh-arg)
62(require 'esh-util)
63
fc17acd1 64(eval-when-compile
f87b1284 65 (require 'cl-lib))
5153ac72
GM
66
67(defgroup eshell-io nil
68 "Eshell's I/O management code provides a scheme for treating many
69different kinds of objects -- symbols, files, buffers, etc. -- as
70though they were files."
71 :tag "I/O management"
72 :group 'eshell)
73
affbf647
GM
74;;; User Variables:
75
d783d303 76(defcustom eshell-io-load-hook nil
ec60da52 77 "A hook that gets run when `eshell-io' is loaded."
d783d303 78 :version "24.1" ; removed eshell-io-initialize
affbf647
GM
79 :type 'hook
80 :group 'eshell-io)
81
82(defcustom eshell-number-of-handles 3
ec60da52 83 "The number of file handles that eshell supports.
affbf647
GM
84Currently this is standard input, output and error. But even all of
85these Emacs does not currently support with asynchronous processes
86\(which is what eshell uses so that you can continue doing work in
87other buffers) ."
88 :type 'integer
89 :group 'eshell-io)
90
91(defcustom eshell-output-handle 1
ec60da52 92 "The index of the standard output handle."
affbf647
GM
93 :type 'integer
94 :group 'eshell-io)
95
96(defcustom eshell-error-handle 2
ec60da52 97 "The index of the standard error handle."
affbf647
GM
98 :type 'integer
99 :group 'eshell-io)
100
101(defcustom eshell-buffer-shorthand nil
ec60da52 102 "If non-nil, a symbol name can be used for a buffer in redirection.
affbf647
GM
103If nil, redirecting to a buffer requires buffer name syntax. If this
104variable is set, redirection directly to Lisp symbols will be
105impossible.
106
107Example:
108
109 echo hello > '*scratch* ; works if `eshell-buffer-shorthand' is t
110 echo hello > #<buffer *scratch*> ; always works"
111 :type 'boolean
112 :group 'eshell-io)
113
114(defcustom eshell-print-queue-size 5
ec60da52 115 "The size of the print queue, for doing buffered printing.
affbf647
GM
116This is basically a speed enhancement, to avoid blocking the Lisp code
117from executing while Emacs is redisplaying."
118 :type 'integer
119 :group 'eshell-io)
120
49a053fc
GM
121(defvar x-select-enable-clipboard) ; term/common-win
122
affbf647
GM
123(defcustom eshell-virtual-targets
124 '(("/dev/eshell" eshell-interactive-print nil)
125 ("/dev/kill" (lambda (mode)
126 (if (eq mode 'overwrite)
127 (kill-new ""))
128 'eshell-kill-append) t)
129 ("/dev/clip" (lambda (mode)
130 (if (eq mode 'overwrite)
131 (let ((x-select-enable-clipboard t))
132 (kill-new "")))
133 'eshell-clipboard-append) t))
ec60da52 134 "Map virtual devices name to Emacs Lisp functions.
affbf647
GM
135If the user specifies any of the filenames above as a redirection
136target, the function in the second element will be called.
137
138If the third element is non-nil, the redirection mode is passed as an
139argument (which is the symbol `overwrite', `append' or `insert'), and
140the function is expected to return another function -- which is the
141output function. Otherwise, the second element itself is the output
142function.
143
ca7aae91
JW
144The output function is then called repeatedly with single strings,
145which represents successive pieces of the output of the command, until nil
affbf647
GM
146is passed, meaning EOF.
147
148NOTE: /dev/null is handled specially as a virtual target, and should
149not be added to this variable."
150 :type '(repeat
151 (list (string :tag "Target")
152 function
153 (choice (const :tag "Func returns output-func" t)
154 (const :tag "Func is output-func" nil))))
155 :group 'eshell-io)
156
157(put 'eshell-virtual-targets 'risky-local-variable t)
158
159;;; Internal Variables:
160
161(defvar eshell-current-handles nil)
162
163(defvar eshell-last-command-status 0
164 "The exit code from the last command. 0 if successful.")
165
166(defvar eshell-last-command-result nil
167 "The result of the last command. Not related to success.")
168
169(defvar eshell-output-file-buffer nil
170 "If non-nil, the current buffer is a file output buffer.")
171
172(defvar eshell-print-count)
173(defvar eshell-current-redirections)
174
175;;; Functions:
176
177(defun eshell-io-initialize ()
178 "Initialize the I/O subsystem code."
affbf647
GM
179 (add-hook 'eshell-parse-argument-hook
180 'eshell-parse-redirection nil t)
181 (make-local-variable 'eshell-current-redirections)
affbf647
GM
182 (add-hook 'eshell-pre-rewrite-command-hook
183 'eshell-strip-redirections nil t)
e8b66a6a
SM
184 (add-function :filter-return (local 'eshell-post-rewrite-command-function)
185 #'eshell--apply-redirections))
affbf647
GM
186
187(defun eshell-parse-redirection ()
188 "Parse an output redirection, such as '2>'."
189 (if (and (not eshell-current-quoted)
190 (looking-at "\\([0-9]\\)?\\(<\\|>+\\)&?\\([0-9]\\)?\\s-*"))
191 (if eshell-current-argument
192 (eshell-finish-arg)
193 (let ((sh (match-string 1))
194 (oper (match-string 2))
195; (th (match-string 3))
196 )
197 (if (string= oper "<")
198 (error "Eshell does not support input redirection"))
199 (eshell-finish-arg
200 (prog1
201 (list 'eshell-set-output-handle
6b0e3e4d 202 (or (and sh (string-to-number sh)) 1)
affbf647
GM
203 (list 'quote
204 (aref [overwrite append insert]
205 (1- (length oper)))))
206 (goto-char (match-end 0))))))))
207
208(defun eshell-strip-redirections (terms)
209 "Rewrite any output redirections in TERMS."
210 (setq eshell-current-redirections (list t))
211 (let ((tl terms)
212 (tt (cdr terms)))
213 (while tt
214 (if (not (and (consp (car tt))
215 (eq (caar tt) 'eshell-set-output-handle)))
216 (setq tt (cdr tt)
217 tl (cdr tl))
218 (unless (cdr tt)
219 (error "Missing redirection target"))
220 (nconc eshell-current-redirections
221 (list (list 'ignore
222 (append (car tt) (list (cadr tt))))))
223 (setcdr tl (cddr tt))
224 (setq tt (cddr tt))))
225 (setq eshell-current-redirections
226 (cdr eshell-current-redirections))))
227
e8b66a6a 228(defun eshell--apply-redirections (cmd)
affbf647
GM
229 "Apply any redirection which were specified for COMMAND."
230 (if eshell-current-redirections
e8b66a6a
SM
231 `(progn
232 ,@eshell-current-redirections
233 ,cmd)
234 cmd))
affbf647
GM
235
236(defun eshell-create-handles
e8b66a6a 237 (stdout output-mode &optional stderr error-mode)
affbf647
GM
238 "Create a new set of file handles for a command.
239The default location for standard output and standard error will go to
e8b66a6a 240STDOUT and STDERR, respectively.
ca7aae91
JW
241OUTPUT-MODE and ERROR-MODE are either `overwrite', `append' or `insert';
242a nil value of mode defaults to `insert'."
affbf647 243 (let ((handles (make-vector eshell-number-of-handles nil))
e8b66a6a
SM
244 (output-target (eshell-get-target stdout output-mode))
245 (error-target (eshell-get-target stderr error-mode)))
affbf647 246 (aset handles eshell-output-handle (cons output-target 1))
e8b66a6a
SM
247 (aset handles eshell-error-handle
248 (cons (if stderr error-target output-target) 1))
affbf647
GM
249 handles))
250
251(defun eshell-protect-handles (handles)
252 "Protect the handles in HANDLES from a being closed."
253 (let ((idx 0))
254 (while (< idx eshell-number-of-handles)
255 (if (aref handles idx)
256 (setcdr (aref handles idx)
257 (1+ (cdr (aref handles idx)))))
258 (setq idx (1+ idx))))
259 handles)
260
261(defun eshell-close-target (target status)
262 "Close an output TARGET, passing STATUS as the result.
263STATUS should be non-nil on successful termination of the output."
264 (cond
265 ((symbolp target) nil)
266
267 ;; If we were redirecting to a file, save the file and close the
268 ;; buffer.
269 ((markerp target)
270 (let ((buf (marker-buffer target)))
271 (when buf ; somebody's already killed it!
272 (save-current-buffer
273 (set-buffer buf)
274 (when eshell-output-file-buffer
275 (save-buffer)
276 (when (eq eshell-output-file-buffer t)
277 (or status (set-buffer-modified-p nil))
278 (kill-buffer buf)))))))
279
280 ;; If we're redirecting to a process (via a pipe, or process
281 ;; redirection), send it EOF so that it knows we're finished.
ca7aae91 282 ((eshell-processp target)
affbf647
GM
283 (if (eq (process-status target) 'run)
284 (process-send-eof target)))
285
286 ;; A plain function redirection needs no additional arguments
287 ;; passed.
288 ((functionp target)
289 (funcall target status))
290
291 ;; But a more complicated function redirection (which can only
292 ;; happen with aliases at the moment) has arguments that need to be
293 ;; passed along with it.
294 ((consp target)
295 (apply (car target) status (cdr target)))))
296
297(defun eshell-close-handles (exit-code &optional result handles)
298 "Close all of the current handles, taking refcounts into account.
299EXIT-CODE is the process exit code; mainly, it is zero, if the command
300completed successfully. RESULT is the quoted value of the last
301command. If nil, then the meta variables for keeping track of the
302last execution result should not be changed."
303 (let ((idx 0))
a464a6c7 304 (cl-assert (or (not result) (eq (car result) 'quote)))
affbf647
GM
305 (setq eshell-last-command-status exit-code
306 eshell-last-command-result (cadr result))
307 (while (< idx eshell-number-of-handles)
308 (let ((handles (or handles eshell-current-handles)))
309 (when (aref handles idx)
310 (setcdr (aref handles idx)
311 (1- (cdr (aref handles idx))))
312 (when (= (cdr (aref handles idx)) 0)
313 (let ((target (car (aref handles idx))))
314 (if (not (listp target))
315 (eshell-close-target target (= exit-code 0))
316 (while target
317 (eshell-close-target (car target) (= exit-code 0))
318 (setq target (cdr target)))))
319 (setcar (aref handles idx) nil))))
320 (setq idx (1+ idx)))
321 nil))
322
323(defun eshell-kill-append (string)
324 "Call `kill-append' with STRING, if it is indeed a string."
325 (if (stringp string)
326 (kill-append string nil)))
327
328(defun eshell-clipboard-append (string)
329 "Call `kill-append' with STRING, if it is indeed a string."
330 (if (stringp string)
331 (let ((x-select-enable-clipboard t))
332 (kill-append string nil))))
333
334(defun eshell-get-target (target &optional mode)
335 "Convert TARGET, which is a raw argument, into a valid output target.
ca7aae91
JW
336MODE is either `overwrite', `append' or `insert'; if it is omitted or nil,
337it defaults to `insert'."
affbf647
GM
338 (setq mode (or mode 'insert))
339 (cond
340 ((stringp target)
341 (let ((redir (assoc target eshell-virtual-targets)))
e1703ba9
JW
342 (if redir
343 (if (nth 2 redir)
344 (funcall (nth 1 redir) mode)
345 (nth 1 redir))
346 (let* ((exists (get-file-buffer target))
347 (buf (find-file-noselect target t)))
348 (with-current-buffer buf
16b0b347 349 (if buffer-file-read-only
e1703ba9 350 (error "Cannot write to read-only file `%s'" target))
16b0b347 351 (setq buffer-read-only nil)
e1703ba9
JW
352 (set (make-local-variable 'eshell-output-file-buffer)
353 (if (eq exists buf) 0 t))
354 (cond ((eq mode 'overwrite)
355 (erase-buffer))
356 ((eq mode 'append)
357 (goto-char (point-max))))
358 (point-marker))))))
359
affbf647
GM
360 ((or (bufferp target)
361 (and (boundp 'eshell-buffer-shorthand)
362 (symbol-value 'eshell-buffer-shorthand)
9c041409
JW
363 (symbolp target)
364 (not (memq target '(t nil)))))
affbf647
GM
365 (let ((buf (if (bufferp target)
366 target
367 (get-buffer-create
368 (symbol-name target)))))
369 (with-current-buffer buf
370 (cond ((eq mode 'overwrite)
371 (erase-buffer))
372 ((eq mode 'append)
373 (goto-char (point-max))))
374 (point-marker))))
e1703ba9
JW
375
376 ((functionp target) nil)
377
affbf647
GM
378 ((symbolp target)
379 (if (eq mode 'overwrite)
380 (set target nil))
381 target)
e1703ba9 382
ca7aae91 383 ((or (eshell-processp target)
affbf647
GM
384 (markerp target))
385 target)
e1703ba9 386
affbf647 387 (t
5b423d48 388 (error "Invalid redirection target: %s"
affbf647
GM
389 (eshell-stringify target)))))
390
1a32899d 391(defvar grep-null-device)
affbf647
GM
392
393(defun eshell-set-output-handle (index mode &optional target)
394 "Set handle INDEX, using MODE, to point to TARGET."
395 (when target
396 (if (and (stringp target)
397 (or (cond
398 ((boundp 'null-device)
399 (string= target null-device))
400 ((boundp 'grep-null-device)
401 (string= target grep-null-device))
402 (t nil))
403 (string= target "/dev/null")))
404 (aset eshell-current-handles index nil)
405 (let ((where (eshell-get-target target mode))
406 (current (car (aref eshell-current-handles index))))
407 (if (and (listp current)
408 (not (member where current)))
409 (setq current (append current (list where)))
2c762cee 410 (setq current (list where)))
affbf647
GM
411 (if (not (aref eshell-current-handles index))
412 (aset eshell-current-handles index (cons nil 1)))
413 (setcar (aref eshell-current-handles index) current)))))
414
415(defun eshell-interactive-output-p ()
416 "Return non-nil if current handles are bound for interactive display."
417 (and (eq (car (aref eshell-current-handles
418 eshell-output-handle)) t)
419 (eq (car (aref eshell-current-handles
420 eshell-error-handle)) t)))
421
422(defvar eshell-print-queue nil)
423(defvar eshell-print-queue-count -1)
424
5153ac72
GM
425(defsubst eshell-print (object)
426 "Output OBJECT to the standard output handle."
427 (eshell-output-object object eshell-output-handle))
428
affbf647
GM
429(defun eshell-flush (&optional reset-p)
430 "Flush out any lines that have been queued for printing.
431Must be called before printing begins with -1 as its argument, and
432after all printing is over with no argument."
433 (ignore
434 (if reset-p
435 (setq eshell-print-queue nil
436 eshell-print-queue-count reset-p)
437 (if eshell-print-queue
438 (eshell-print eshell-print-queue))
439 (eshell-flush 0))))
440
441(defun eshell-init-print-buffer ()
442 "Initialize the buffered printing queue."
443 (eshell-flush -1))
444
445(defun eshell-buffered-print (&rest strings)
446 "A buffered print -- *for strings only*."
447 (if (< eshell-print-queue-count 0)
448 (progn
449 (eshell-print (apply 'concat strings))
450 (setq eshell-print-queue-count 0))
451 (if (= eshell-print-queue-count eshell-print-queue-size)
452 (eshell-flush))
453 (setq eshell-print-queue
454 (concat eshell-print-queue (apply 'concat strings))
455 eshell-print-queue-count (1+ eshell-print-queue-count))))
456
affbf647 457(defsubst eshell-error (object)
ca7aae91 458 "Output OBJECT to the standard error handle."
affbf647
GM
459 (eshell-output-object object eshell-error-handle))
460
461(defsubst eshell-errorn (object)
ca7aae91 462 "Output OBJECT followed by a newline to the standard error handle."
affbf647
GM
463 (eshell-error object)
464 (eshell-error "\n"))
465
466(defsubst eshell-printn (object)
ca7aae91 467 "Output OBJECT followed by a newline to the standard output handle."
affbf647
GM
468 (eshell-print object)
469 (eshell-print "\n"))
470
f87b1284
GM
471(autoload 'eshell-output-filter "esh-mode")
472
affbf647
GM
473(defun eshell-output-object-to-target (object target)
474 "Insert OBJECT into TARGET.
475Returns what was actually sent, or nil if nothing was sent."
476 (cond
477 ((functionp target)
478 (funcall target object))
479
480 ((symbolp target)
481 (if (eq target t) ; means "print to display"
482 (eshell-output-filter nil (eshell-stringify object))
483 (if (not (symbol-value target))
484 (set target object)
485 (setq object (eshell-stringify object))
486 (if (not (stringp (symbol-value target)))
487 (set target (eshell-stringify
488 (symbol-value target))))
489 (set target (concat (symbol-value target) object)))))
490
491 ((markerp target)
492 (if (buffer-live-p (marker-buffer target))
493 (with-current-buffer (marker-buffer target)
494 (let ((moving (= (point) target)))
495 (save-excursion
496 (goto-char target)
e1703ba9
JW
497 (unless (stringp object)
498 (setq object (eshell-stringify object)))
affbf647
GM
499 (insert-and-inherit object)
500 (set-marker target (point-marker)))
501 (if moving
502 (goto-char target))))))
503
ca7aae91 504 ((eshell-processp target)
affbf647 505 (when (eq (process-status target) 'run)
e1703ba9
JW
506 (unless (stringp object)
507 (setq object (eshell-stringify object)))
affbf647
GM
508 (process-send-string target object)))
509
510 ((consp target)
511 (apply (car target) object (cdr target))))
512 object)
513
514(defun eshell-output-object (object &optional handle-index handles)
515 "Insert OBJECT, using HANDLE-INDEX specifically)."
516 (let ((target (car (aref (or handles eshell-current-handles)
517 (or handle-index eshell-output-handle)))))
518 (if (and target (not (listp target)))
519 (eshell-output-object-to-target object target)
520 (while target
521 (eshell-output-object-to-target object (car target))
522 (setq target (cdr target))))))
523
affbf647 524;;; esh-io.el ends here