Commit | Line | Data |
---|---|---|
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 | |
69 | different kinds of objects -- symbols, files, buffers, etc. -- as | |
70 | though 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 |
84 | Currently this is standard input, output and error. But even all of |
85 | these Emacs does not currently support with asynchronous processes | |
86 | \(which is what eshell uses so that you can continue doing work in | |
87 | other 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 |
103 | If nil, redirecting to a buffer requires buffer name syntax. If this |
104 | variable is set, redirection directly to Lisp symbols will be | |
105 | impossible. | |
106 | ||
107 | Example: | |
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 |
116 | This is basically a speed enhancement, to avoid blocking the Lisp code |
117 | from 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 |
135 | If the user specifies any of the filenames above as a redirection |
136 | target, the function in the second element will be called. | |
137 | ||
138 | If the third element is non-nil, the redirection mode is passed as an | |
139 | argument (which is the symbol `overwrite', `append' or `insert'), and | |
140 | the function is expected to return another function -- which is the | |
141 | output function. Otherwise, the second element itself is the output | |
142 | function. | |
143 | ||
ca7aae91 JW |
144 | The output function is then called repeatedly with single strings, |
145 | which represents successive pieces of the output of the command, until nil | |
affbf647 GM |
146 | is passed, meaning EOF. |
147 | ||
148 | NOTE: /dev/null is handled specially as a virtual target, and should | |
149 | not 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. |
239 | The default location for standard output and standard error will go to | |
e8b66a6a | 240 | STDOUT and STDERR, respectively. |
ca7aae91 JW |
241 | OUTPUT-MODE and ERROR-MODE are either `overwrite', `append' or `insert'; |
242 | a 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. | |
263 | STATUS 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. | |
299 | EXIT-CODE is the process exit code; mainly, it is zero, if the command | |
300 | completed successfully. RESULT is the quoted value of the last | |
301 | command. If nil, then the meta variables for keeping track of the | |
302 | last 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 |
336 | MODE is either `overwrite', `append' or `insert'; if it is omitted or nil, |
337 | it 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. | |
431 | Must be called before printing begins with -1 as its argument, and | |
432 | after 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. | |
475 | Returns 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 |