]> git.eshelyaron.com Git - emacs.git/commitdiff
Add 'eshell-duplicate-handles' to return a copy of file handles
authorJim Porter <jporterbugs@gmail.com>
Tue, 20 Dec 2022 06:21:10 +0000 (22:21 -0800)
committerJim Porter <jporterbugs@gmail.com>
Thu, 22 Dec 2022 19:35:12 +0000 (11:35 -0800)
* lisp/eshell/esh-io.el (eshell-create-handles): Support creating with
multiple targets for stdout and/or stderr.  Make the targets for a
handle always be a list, and store whether the targets are the default
in a separate 'default' field.
(eshell-protect-handles, eshell-close-handles)
(eshell-copy-output-handle, eshell-interactive-output-p)
(eshell-output-object): Update for changes in 'eshell-create-handles'.
(eshell-duplicate-handles, eshell-get-targets): New functions.

* lisp/eshell/esh-cmd.el (eshell-copy-handles): Rename and alias to...
(eshell-with-copied-handles): ... this function, and use
'eshell-duplicate-handles'.
(eshell-execute-pipeline): Use 'eshell-duplicate-handles'.

lisp/eshell/esh-cmd.el
lisp/eshell/esh-io.el

index 1fb849911203bf70d8bce75bb1c10aa776c5ffa9..03388236b06855f85d60f1a04a9f4cbb98a21687 100644 (file)
@@ -788,16 +788,15 @@ this grossness will be made to disappear by using `call/cc'..."
 (defvar eshell-output-handle)           ;Defined in esh-io.el.
 (defvar eshell-error-handle)            ;Defined in esh-io.el.
 
-(defmacro eshell-copy-handles (object)
+(defmacro eshell-with-copied-handles (object)
   "Duplicate current I/O handles, so OBJECT works with its own copy."
   `(let ((eshell-current-handles
-         (eshell-create-handles
-          (car (aref eshell-current-handles
-                     eshell-output-handle)) nil
-          (car (aref eshell-current-handles
-                     eshell-error-handle)) nil)))
+          (eshell-duplicate-handles eshell-current-handles)))
      ,object))
 
+(define-obsolete-function-alias 'eshell-copy-handles
+  #'eshell-with-copied-handles "30.1")
+
 (defmacro eshell-protect (object)
   "Protect I/O handles, so they aren't get closed after eval'ing OBJECT."
   `(progn
@@ -808,7 +807,7 @@ this grossness will be made to disappear by using `call/cc'..."
   "Execute the commands in PIPELINE, connecting each to one another.
 This macro calls itself recursively, with NOTFIRST non-nil."
   (when (setq pipeline (cadr pipeline))
-    `(eshell-copy-handles
+    `(eshell-with-copied-handles
       (progn
        ,(when (cdr pipeline)
           `(let ((nextproc
@@ -880,11 +879,8 @@ This is used on systems where async subprocesses are not supported."
      (progn
        ,(if (fboundp 'make-process)
            `(eshell-do-pipelines ,pipeline)
-         `(let ((tail-handles (eshell-create-handles
-                               (car (aref eshell-current-handles
-                                          ,eshell-output-handle)) nil
-                               (car (aref eshell-current-handles
-                                          ,eshell-error-handle)) nil)))
+          `(let ((tail-handles (eshell-duplicate-handles
+                                eshell-current-handles)))
             (eshell-do-pipelines-synchronously ,pipeline)))
        (eshell-process-identity (cons (symbol-value headproc)
                                       (symbol-value tailproc))))))
index 4620565f857f9f767828868030e1b99d4990c78b..58084db28a8f2691e654d17fe751b898dd105906 100644 (file)
@@ -291,25 +291,42 @@ describing the mode, e.g. for using with `eshell-get-target'.")
 (defun eshell-create-handles
   (stdout output-mode &optional stderr error-mode)
   "Create a new set of file handles for a command.
-The default location for standard output and standard error will go to
-STDOUT and STDERR, respectively.
-OUTPUT-MODE and ERROR-MODE are either `overwrite', `append' or `insert';
-a nil value of mode defaults to `insert'."
+The default target for standard output and standard error will
+go to STDOUT and STDERR, respectively.  OUTPUT-MODE and
+ERROR-MODE are either `overwrite', `append' or `insert'; a nil
+value of mode defaults to `insert'.
+
+The result is a vector of file handles.  Each handle is of the form:
+
+  (TARGETS DEFAULT REF-COUNT)
+
+TARGETS is a list of destinations for output.  DEFAULT is non-nil
+if handle has its initial default value (always t after calling
+this function).  REF-COUNT is the number of references to this
+handle (initially 1); see `eshell-protect-handles' and
+`eshell-close-handles'."
   (let* ((handles (make-vector eshell-number-of-handles nil))
-         (output-target (eshell-get-target stdout output-mode))
+         (output-target (eshell-get-targets stdout output-mode))
          (error-target (if stderr
-                           (eshell-get-target stderr error-mode)
+                           (eshell-get-targets stderr error-mode)
                          output-target)))
-    (aset handles eshell-output-handle (cons output-target 1))
-    (aset handles eshell-error-handle (cons error-target 1))
+    (aset handles eshell-output-handle (list output-target t 1))
+    (aset handles eshell-error-handle (list error-target t 1))
     handles))
 
+(defun eshell-duplicate-handles (handles)
+  "Create a duplicate of the file handles in HANDLES.
+This will copy the targets of each handle in HANDLES, setting the
+DEFAULT field to t (see `eshell-create-handles')."
+  (eshell-create-handles
+   (car (aref handles eshell-output-handle)) nil
+   (car (aref handles eshell-error-handle)) nil))
+
 (defun eshell-protect-handles (handles)
   "Protect the handles in HANDLES from a being closed."
   (dotimes (idx eshell-number-of-handles)
-    (when (aref handles idx)
-      (setcdr (aref handles idx)
-              (1+ (cdr (aref handles idx))))))
+    (when-let ((handle (aref handles idx)))
+      (setcar (nthcdr 2 handle) (1+ (nth 2 handle)))))
   handles)
 
 (defun eshell-close-handles (&optional exit-code result handles)
@@ -330,8 +347,8 @@ the value already set in `eshell-last-command-result'."
   (let ((handles (or handles eshell-current-handles)))
     (dotimes (idx eshell-number-of-handles)
       (when-let ((handle (aref handles idx)))
-        (setcdr handle (1- (cdr handle)))
-       (when (= (cdr handle) 0)
+        (setcar (nthcdr 2 handle) (1- (nth 2 handle)))
+        (when (= (nth 2 handle) 0)
           (dolist (target (ensure-list (car (aref handles idx))))
             (eshell-close-target target (= eshell-last-command-status 0)))
           (setcar handle nil))))))
@@ -344,15 +361,17 @@ If HANDLES is nil, use `eshell-current-handles'."
       (if (and (stringp target)
                (string= target (null-device)))
           (aset handles index nil)
-        (let ((where (eshell-get-target target mode))
-              (current (car (aref handles index))))
-          (if (listp current)
+        (let* ((where (eshell-get-target target mode))
+               (handle (or (aref handles index)
+                           (aset handles index (list nil nil 1))))
+               (current (car handle))
+               (defaultp (cadr handle)))
+          (if (not defaultp)
               (unless (member where current)
                 (setq current (append current (list where))))
             (setq current (list where)))
-          (if (not (aref handles index))
-              (aset handles index (cons nil 1)))
-          (setcar (aref handles index) current))))))
+          (setcar handle current)
+          (setcar (cdr handle) nil))))))
 
 (defun eshell-copy-output-handle (index index-to-copy &optional handles)
   "Copy the handle INDEX-TO-COPY to INDEX for the current HANDLES.
@@ -482,6 +501,13 @@ it defaults to `insert'."
     (error "Invalid redirection target: %s"
           (eshell-stringify target)))))
 
+(defun eshell-get-targets (targets &optional mode)
+  "Convert TARGETS into valid output targets.
+TARGETS can be a single raw target or a list thereof.  MODE is either
+`overwrite', `append' or `insert'; if it is omitted or nil, it
+defaults to `insert'."
+  (mapcar (lambda (i) (eshell-get-target i mode)) (ensure-list targets)))
+
 (defun eshell-interactive-output-p (&optional index handles)
   "Return non-nil if the specified handle is bound for interactive display.
 HANDLES is the set of handles to check; if nil, use
@@ -493,9 +519,9 @@ INDEX is the handle index to check.  If nil, check
   (let ((handles (or handles eshell-current-handles))
         (index (or index eshell-output-handle)))
     (if (eq index 'all)
-        (and (eq (car (aref handles eshell-output-handle)) t)
-             (eq (car (aref handles eshell-error-handle)) t))
-      (eq (car (aref handles index)) t))))
+        (and (equal (car (aref handles eshell-output-handle)) '(t))
+             (equal (car (aref handles eshell-error-handle)) '(t)))
+      (equal (car (aref handles index)) '(t)))))
 
 (defvar eshell-print-queue nil)
 (defvar eshell-print-queue-count -1)
@@ -602,15 +628,10 @@ Returns what was actually sent, or nil if nothing was sent."
 If HANDLE-INDEX is nil, output to `eshell-output-handle'.
 HANDLES is the set of file handles to use; if nil, use
 `eshell-current-handles'."
-  (let ((target (car (aref (or handles eshell-current-handles)
-                          (or handle-index eshell-output-handle)))))
-    (if (listp target)
-        (while target
-         (eshell-output-object-to-target object (car target))
-         (setq target (cdr target)))
-      (eshell-output-object-to-target object target)
-      ;; Explicitly return nil to match the list case above.
-      nil)))
+  (let ((targets (car (aref (or handles eshell-current-handles)
+                            (or handle-index eshell-output-handle)))))
+    (dolist (target targets)
+      (eshell-output-object-to-target object target))))
 
 (provide 'esh-io)
 ;;; esh-io.el ends here