From 777b383dd0f61488ba4e43756cf43521f994f906 Mon Sep 17 00:00:00 2001 From: montag451 Date: Wed, 21 Dec 2022 14:21:20 -0800 Subject: Fix Eshell electric slash when used from the root directory of a remote host * lisp/eshell/em-elecslash.el (eshell-electric-forward-slash): Insert the remote prefix as determined by 'file-remote-p'. Copyright-paperwork-exempt: Yes --- lisp/eshell/em-elecslash.el | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'lisp/eshell') diff --git a/lisp/eshell/em-elecslash.el b/lisp/eshell/em-elecslash.el index 091acb9a861..0ce3a4cc963 100644 --- a/lisp/eshell/em-elecslash.el +++ b/lisp/eshell/em-elecslash.el @@ -74,8 +74,9 @@ insertion." (command (save-excursion (eshell-bol) (skip-syntax-forward " ") - (thing-at-point 'sexp)))) - (if (and (file-remote-p default-directory) + (thing-at-point 'sexp))) + (prefix (file-remote-p default-directory))) + (if (and prefix ;; We can't formally parse the input. But if there is ;; one of these operators behind us, then looking at ;; the first command would not be sensible. So be @@ -93,14 +94,9 @@ insertion." (or eshell-prefer-lisp-functions (not (eshell-search-path command)))))))) (let ((map (make-sparse-keymap)) - (start (if tilde-before (1- (point)) (point))) - (localname - (tramp-file-name-localname - (tramp-dissect-file-name default-directory)))) + (start (if tilde-before (1- (point)) (point)))) (when tilde-before (delete-char -1)) - (insert - (substring default-directory 0 - (string-search localname default-directory))) + (insert prefix) (unless tilde-before (insert "/")) ;; Typing a second slash undoes the insertion, for when ;; you really do want to type a local absolute file name. -- cgit v1.2.1 From d6c8d5dbc9fc4786e91b76654058e904c96f0e11 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Tue, 20 Dec 2022 16:20:50 -0800 Subject: When redirecting in Eshell, check for "/dev/null" specifically This is so that users can type "cmd ... > /dev/null" in Eshell no matter what their system's null device is called. (Users can still use their system's null device name when redirecting, too. Eshell doesn't need to do anything special to support that.) This partially reverts 67a8bdb90c9b5865b7f17290c7135b1a5458c36d. See bug#59545. Do not merge to master. * lisp/eshell/esh-io.el (eshell-set-output-handle): Use "/dev/null" literally. --- lisp/eshell/esh-io.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lisp/eshell') diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index 4620565f857..d223be680f9 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -342,7 +342,11 @@ If HANDLES is nil, use `eshell-current-handles'." (when target (let ((handles (or handles eshell-current-handles))) (if (and (stringp target) - (string= target (null-device))) + ;; The literal string "/dev/null" is intentional here. + ;; It just provides compatibility so that users can + ;; redirect to "/dev/null" no matter the actual value + ;; of `null-device'. + (string= target "/dev/null")) (aset handles index nil) (let ((where (eshell-get-target target mode)) (current (car (aref handles index)))) -- cgit v1.2.1 From 05d8310fb5ddb35c2566c2b50ca07e86edf3c670 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Thu, 22 Dec 2022 10:03:09 +0200 Subject: Use the new keyword ':repeat' in repeatable keymaps. * lisp/bindings.el (undo-repeat-map) (buffer-navigation-repeat-map, next-error-repeat-map) (page-navigation-repeat-map): * lisp/comint.el (comint-repeat-map): * lisp/dired.el (dired-jump-map): * lisp/outline.el (outline-navigation-repeat-map) (outline-editing-repeat-map): * lisp/shell.el (shell-repeat-map): * lisp/tab-bar.el (tab-bar-switch-repeat-map) (tab-bar-move-repeat-map): * lisp/window.el (other-window-repeat-map) (resize-window-repeat-map): * lisp/winner.el (winner-repeat-map): * lisp/eshell/em-prompt.el (eshell-prompt-repeat-map): * lisp/eshell/esh-mode.el (eshell-command-repeat-map): Add the keyword ':repeat' to 'defvar-keymap' instead of setting the symbol property 'repeat-map' explicitly. * lisp/keymap.el (defvar-keymap): Check for 'props' that is used in 'defvar-form'. --- lisp/eshell/em-prompt.el | 4 +--- lisp/eshell/esh-mode.el | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'lisp/eshell') diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el index a8744de1dba..abb123bcff2 100644 --- a/lisp/eshell/em-prompt.el +++ b/lisp/eshell/em-prompt.el @@ -102,12 +102,10 @@ arriving, or after." (defvar-keymap eshell-prompt-repeat-map :doc "Keymap to repeat eshell-prompt key sequences. Used in `repeat-mode'." + :repeat t "C-n" #'eshell-next-prompt "C-p" #'eshell-previous-prompt) -(put #'eshell-next-prompt 'repeat-map 'eshell-prompt-repeat-map) -(put #'eshell-previous-prompt 'repeat-map 'eshell-prompt-repeat-map) - ;;; Functions: (define-minor-mode eshell-prompt-mode diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index 4357a0e29a0..b3db0f6af45 100644 --- a/lisp/eshell/esh-mode.el +++ b/lisp/eshell/esh-mode.el @@ -282,12 +282,10 @@ This is used by `eshell-watch-for-password-prompt'." (defvar-keymap eshell-command-repeat-map :doc "Keymap to repeat eshell-command key sequences. Used in `repeat-mode'." + :repeat t "C-f" #'eshell-forward-argument "C-b" #'eshell-backward-argument) -(put #'eshell-forward-argument 'repeat-map 'eshell-command-repeat-map) -(put #'eshell-backward-argument 'repeat-map 'eshell-command-repeat-map) - ;;; User Functions: (defun eshell-kill-buffer-function () -- cgit v1.2.1 From 54087e84df872c9aa30866b880e8ac0b917cbd94 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Mon, 19 Dec 2022 22:21:10 -0800 Subject: Add 'eshell-duplicate-handles' to return a copy of file handles * 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 | 20 +++++------- lisp/eshell/esh-io.el | 83 +++++++++++++++++++++++++++++++------------------- 2 files changed, 60 insertions(+), 43 deletions(-) (limited to 'lisp/eshell') diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 1fb84991120..03388236b06 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -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)))))) diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index 4620565f857..58084db28a8 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -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 -- cgit v1.2.1 From 6defbd65b664b17ad7389a936743debe23d5257e Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Tue, 20 Dec 2022 09:39:07 -0800 Subject: Fix handling of output handles in nested Eshell forms Previously, the output handles in nested forms would be reset to the default, leading to wrong behavior for commands like {echo a; echo b} > file "b" would be written to "file" as expected, but "a" would go to standard output (bug#59545). * lisp/eshell/esh-cmd.el (eshell-parse-command): Use 'eshell-with-copied-handles' for each statement within the whole Eshell command. * test/lisp/eshell/esh-io-tests.el (esh-io-test/redirect-subcommands) (esh-io-test/redirect-subcommands/override) (esh-io-test/redirect-subcommands/interpolated): New tests. * test/lisp/eshell/em-script-tests.el (em-script-test/source-script/redirect) (em-script-test/source-script/redirect/dev-null): New tests. (em-script-test/source-script, em-script-test/source-script/arg-vars) (em-script-test/source-script/all-args-var): Tweak names/docstrings. * test/lisp/eshell/em-extpipe-tests.el (em-extpipe-tests--deftest): Skip over the newly-added 'eshell-with-copied-handles' form when checking the parse results. * test/lisp/eshell/em-tramp-tests.el (em-tramp-test/su-default) (em-tramp-test/su-user, em-tramp-test/su-login) (em-tramp-test/sudo-shell, em-tramp-test/sudo-user-shell) (em-tramp-test/doas-shell, em-tramp-test/doas-user-shell): Update expected command forms. --- lisp/eshell/esh-cmd.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lisp/eshell') diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 03388236b06..79957aeb416 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -418,8 +418,12 @@ hooks should be run before and after the command." (eshell-separate-commands terms "[&;]" nil 'eshell--sep-terms)))) (let ((cmd commands)) (while cmd - (if (cdr cmd) - (setcar cmd `(eshell-commands ,(car cmd)))) + ;; Copy I/O handles so each full statement can manipulate them + ;; if they like. As a small optimization, skip this for the + ;; last top-level one; we won't use these handles again + ;; anyway. + (when (or (not toplevel) (cdr cmd)) + (setcar cmd `(eshell-with-copied-handles ,(car cmd)))) (setq cmd (cdr cmd)))) (if toplevel `(eshell-commands (progn -- cgit v1.2.1 From 17bf6a829ca2fd2920c01e1aee30ab16b9c672eb Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Tue, 20 Dec 2022 13:47:20 -0800 Subject: Simplify handling of /dev/null redirection in Eshell This also fixes an issue where "echo hi > foo > /dev/null" didn't write to the file "foo". (Note that users can still use their system's null device name when redirecting; Eshell doesn't need to do anything special to support that.) * lisp/eshell/esh-io.el (eshell-virtual-targets): Add "/dev/null". (eshell-set-output-handle): Handle 'eshell-null-device'. * test/lisp/eshell/esh-io-tests.el (esh-io-test/redirect-subcommands/dev-null) (esh-io-test/virtual/dev-null, esh-io-test/virtual/dev-null/multiple): New tests. --- lisp/eshell/esh-io.el | 54 +++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'lisp/eshell') diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index 58084db28a8..f2bc87374c1 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -116,16 +116,22 @@ from executing while Emacs is redisplaying." :group 'eshell-io) (defcustom eshell-virtual-targets - '(("/dev/eshell" eshell-interactive-print nil) + '(;; The literal string "/dev/null" is intentional here. It just + ;; provides compatibility so that users can redirect to + ;; "/dev/null" no matter the actual value of `null-device'. + ("/dev/null" (lambda (_mode) (throw 'eshell-null-device t)) t) + ("/dev/eshell" eshell-interactive-print nil) ("/dev/kill" (lambda (mode) - (if (eq mode 'overwrite) - (kill-new "")) - 'eshell-kill-append) t) + (when (eq mode 'overwrite) + (kill-new "")) + #'eshell-kill-append) + t) ("/dev/clip" (lambda (mode) - (if (eq mode 'overwrite) - (let ((select-enable-clipboard t)) - (kill-new ""))) - 'eshell-clipboard-append) t)) + (when (eq mode 'overwrite) + (let ((select-enable-clipboard t)) + (kill-new ""))) + #'eshell-clipboard-append) + t)) "Map virtual devices name to Emacs Lisp functions. If the user specifies any of the filenames above as a redirection target, the function in the second element will be called. @@ -138,10 +144,8 @@ function. The output function is then called repeatedly with single strings, which represents successive pieces of the output of the command, until nil -is passed, meaning EOF. - -NOTE: /dev/null is handled specially as a virtual target, and should -not be added to this variable." +is passed, meaning EOF." + :version "30.1" :type '(repeat (list (string :tag "Target") function @@ -357,21 +361,17 @@ the value already set in `eshell-last-command-result'." "Set handle INDEX for the current HANDLES to point to TARGET using MODE. If HANDLES is nil, use `eshell-current-handles'." (when target - (let ((handles (or handles eshell-current-handles))) - (if (and (stringp target) - (string= target (null-device))) - (aset handles index nil) - (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))) - (setcar handle current) - (setcar (cdr handle) nil)))))) + (let* ((handles (or handles eshell-current-handles)) + (handle (or (aref handles index) + (aset handles index (list nil nil 1)))) + (defaultp (cadr handle)) + (current (unless defaultp (car handle)))) + (catch 'eshell-null-device + (let ((where (eshell-get-target target mode))) + (unless (member where current) + (setq current (append current (list where)))))) + (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. -- cgit v1.2.1 From 073da412a139e317959f56e359ed12de726a0a35 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Sat, 24 Dec 2022 14:31:50 -0800 Subject: Fix reference-counting of Eshell I/O handles This ensures that output targets in Eshell are only closed when Eshell is actually done with them. In particular, this means that "{ echo foo; echo bar } | rev" prints "raboof" as expected (bug#59545). * lisp/eshell/esh-io.el (eshell-create-handles): Structure the handles differently so the targets and their ref-count can be shared. (eshell-duplicate-handles): Reimplement this to share targets between the original and new handle sets. Add STEAL-P argument. (eshell-protect-handles, eshell-copy-output-handle) (eshell-interactive-output-p, eshell-output-object): Account for changes to the handle structure. (eshell-close-handle): New function... (eshell-close-handles, eshell-set-output-handle): ... use it. (eshell-get-targets): Remove. This only existed to make the previous implementation of 'eshell-duplicate-handles' work. * lisp/eshell/esh-cmd.el (eshell-with-copied-handles): New argument STEAL-P. (eshell-do-pipelines): Use STEAL-P for the last item in the pipeline. (eshell-parse-command): Don't copy handles for the last command in the list; explain why we can't use STEAL-P here. (eshell-eval-command): When queuing input, set 'eshell-command-body' and 'eshell-test-body' for the 'if' conditional (see 'eshell-do-eval'). * test/lisp/eshell/esh-io-tests.el (esh-io-test/redirect-pipe): Split into... (esh-io-test/pipeline/default, esh-io-test/pipeline/all): ... these. (esh-io-test/pipeline/subcommands): New test. * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test/for-loop-pipe) (esh-cmd-test/while-loop-pipe, esh-cmd-test/if-statement-pipe) esh-cmd-test/if-else-statement-pipe): New tests. (esh-cmd-test/while-loop): Use 'pop' to simplify the test a bit. * test/lisp/eshell/eshell-test-helpers.el (eshell-test--max-subprocess-time): Rename to... (eshell-test--max-wait-time): ... this. (eshell-wait-for): New function... (eshell-wait-for-subprocess): ... use it. * test/lisp/eshell/eshell-tests.el (eshell-test/queue-input): Fix this test. Previously, it didn't correctly verify that the original command completed. * test/lisp/eshell/em-tramp-tests.el (em-tramp-test/should-replace-command): New macro... (em-tramp-test/su-default, em-tramp-test/su-user) (em-tramp-test/su-login, em-tramp-test/sudo-shell) (em-tramp-test/sudo-user-shell, em-tramp-test/doas-shell) (em-tramp-test/doas-user-shell): ... use it. --- lisp/eshell/esh-cmd.el | 25 ++++++---- lisp/eshell/esh-io.el | 123 +++++++++++++++++++++++++++++-------------------- 2 files changed, 89 insertions(+), 59 deletions(-) (limited to 'lisp/eshell') diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 79957aeb416..39579335cf7 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -419,11 +419,10 @@ hooks should be run before and after the command." (let ((cmd commands)) (while cmd ;; Copy I/O handles so each full statement can manipulate them - ;; if they like. As a small optimization, skip this for the - ;; last top-level one; we won't use these handles again - ;; anyway. - (when (or (not toplevel) (cdr cmd)) - (setcar cmd `(eshell-with-copied-handles ,(car cmd)))) + ;; if they like. Steal the handles for the last command in + ;; the list; we won't use the originals again anyway. + (setcar cmd `(eshell-with-copied-handles + ,(car cmd) ,(not (cdr cmd)))) (setq cmd (cdr cmd)))) (if toplevel `(eshell-commands (progn @@ -792,10 +791,12 @@ 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-with-copied-handles (object) - "Duplicate current I/O handles, so OBJECT works with its own copy." +(defmacro eshell-with-copied-handles (object &optional steal-p) + "Duplicate current I/O handles, so OBJECT works with its own copy. +If STEAL-P is non-nil, these new handles will be stolen from the +current ones (see `eshell-duplicate-handles')." `(let ((eshell-current-handles - (eshell-duplicate-handles eshell-current-handles))) + (eshell-duplicate-handles eshell-current-handles ,steal-p))) ,object)) (define-obsolete-function-alias 'eshell-copy-handles @@ -836,7 +837,9 @@ This macro calls itself recursively, with NOTFIRST non-nil." (let ((proc ,(car pipeline))) (set headproc (or proc (symbol-value headproc))) (set tailproc (or (symbol-value tailproc) proc)) - proc)))))) + proc))) + ;; Steal handles if this is the last item in the pipeline. + ,(null (cdr pipeline))))) (defmacro eshell-do-pipelines-synchronously (pipeline) "Execute the commands in PIPELINE in sequence synchronously. @@ -1024,7 +1027,9 @@ produced by `eshell-parse-command'." ;; We can just stick the new command at the end of the current ;; one, and everything will happen as it should. (setcdr (last (cdr eshell-current-command)) - (list `(let ((here (and (eobp) (point)))) + (list `(let ((here (and (eobp) (point))) + (eshell-command-body '(nil)) + (eshell-test-body '(nil))) ,(and input `(insert-and-inherit ,(concat input "\n"))) (if here diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index f2bc87374c1..90826a312b3 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -302,35 +302,51 @@ 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 . REF-COUNT) DEFAULT) -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'." +TARGETS is a list of destinations for output. REF-COUNT is the +number of references to this handle (initially 1); see +`eshell-protect-handles' and `eshell-close-handles'. DEFAULT is +non-nil if handle has its initial default value (always t after +calling this function)." (let* ((handles (make-vector eshell-number-of-handles nil)) - (output-target (eshell-get-targets stdout output-mode)) - (error-target (if stderr - (eshell-get-targets stderr error-mode) - output-target))) - (aset handles eshell-output-handle (list output-target t 1)) - (aset handles eshell-error-handle (list error-target t 1)) + (output-target + (let ((target (eshell-get-target stdout output-mode))) + (cons (when target (list target)) 1))) + (error-target + (if stderr + (let ((target (eshell-get-target stderr error-mode))) + (cons (when target (list target)) 1)) + (cl-incf (cdr output-target)) + output-target))) + (aset handles eshell-output-handle (list output-target t)) + (aset handles eshell-error-handle (list error-target t)) handles)) -(defun eshell-duplicate-handles (handles) +(defun eshell-duplicate-handles (handles &optional steal-p) "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)) +This uses the targets of each handle in HANDLES, incrementing its +reference count by one (unless STEAL-P is non-nil). These +targets are shared between the original set of handles and the +new one, so the targets are only closed when the reference count +drops to 0 (see `eshell-close-handles'). + +This function also sets the DEFAULT field for each handle to +t (see `eshell-create-handles'). Unlike the targets, this value +is not shared with the original handles." + (let ((dup-handles (make-vector eshell-number-of-handles nil))) + (dotimes (idx eshell-number-of-handles) + (when-let ((handle (aref handles idx))) + (unless steal-p + (cl-incf (cdar handle))) + (aset dup-handles idx (list (car handle) t)))) + dup-handles)) (defun eshell-protect-handles (handles) "Protect the handles in HANDLES from a being closed." (dotimes (idx eshell-number-of-handles) (when-let ((handle (aref handles idx))) - (setcar (nthcdr 2 handle) (1+ (nth 2 handle))))) + (cl-incf (cdar handle)))) handles) (defun eshell-close-handles (&optional exit-code result handles) @@ -348,29 +364,45 @@ the value already set in `eshell-last-command-result'." (when result (cl-assert (eq (car result) 'quote)) (setq eshell-last-command-result (cadr result))) - (let ((handles (or handles eshell-current-handles))) + (let ((handles (or handles eshell-current-handles)) + (succeeded (= eshell-last-command-status 0))) (dotimes (idx eshell-number-of-handles) - (when-let ((handle (aref handles idx))) - (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)))))) + (eshell-close-handle (aref handles idx) succeeded)))) + +(defun eshell-close-handle (handle status) + "Close a single HANDLE, taking refcounts into account. +This will pass STATUS to each target for the handle, which should +be a non-nil value on successful termination." + (when handle + (cl-assert (> (cdar handle) 0) + "Attempted to close a handle with 0 references") + (when (and (> (cdar handle) 0) + (= (cl-decf (cdar handle)) 0)) + (dolist (target (caar handle)) + (eshell-close-target target status)) + (setcar (car handle) nil)))) (defun eshell-set-output-handle (index mode &optional target handles) "Set handle INDEX for the current HANDLES to point to TARGET using MODE. -If HANDLES is nil, use `eshell-current-handles'." +If HANDLES is nil, use `eshell-current-handles'. + +If the handle is currently set to its default value (see +`eshell-create-handles'), this will overwrite the targets with +the new target. Otherwise, it will append the new target to the +current list of targets." (when target (let* ((handles (or handles eshell-current-handles)) (handle (or (aref handles index) - (aset handles index (list nil nil 1)))) - (defaultp (cadr handle)) - (current (unless defaultp (car handle)))) + (aset handles index (list (cons nil 1) nil)))) + (defaultp (cadr handle))) + (when defaultp + (cl-decf (cdar handle)) + (setcar handle (cons nil 1))) (catch 'eshell-null-device - (let ((where (eshell-get-target target mode))) + (let ((current (caar handle)) + (where (eshell-get-target target mode))) (unless (member where current) - (setq current (append current (list where)))))) - (setcar handle current) + (setcar (car handle) (append current (list where)))))) (setcar (cdr handle) nil)))) (defun eshell-copy-output-handle (index index-to-copy &optional handles) @@ -378,10 +410,10 @@ If HANDLES is nil, use `eshell-current-handles'." If HANDLES is nil, use `eshell-current-handles'." (let* ((handles (or handles eshell-current-handles)) (handle-to-copy (car (aref handles index-to-copy)))) - (setcar (aref handles index) - (if (listp handle-to-copy) - (copy-sequence handle-to-copy) - handle-to-copy)))) + (when handle-to-copy + (cl-incf (cdr handle-to-copy))) + (eshell-close-handle (aref handles index) nil) + (setcar (aref handles index) handle-to-copy))) (defun eshell-set-all-output-handles (mode &optional target handles) "Set output and error HANDLES to point to TARGET using MODE. @@ -501,13 +533,6 @@ 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 @@ -519,9 +544,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 (equal (car (aref handles eshell-output-handle)) '(t)) - (equal (car (aref handles eshell-error-handle)) '(t))) - (equal (car (aref handles index)) '(t))))) + (and (equal (caar (aref handles eshell-output-handle)) '(t)) + (equal (caar (aref handles eshell-error-handle)) '(t))) + (equal (caar (aref handles index)) '(t))))) (defvar eshell-print-queue nil) (defvar eshell-print-queue-count -1) @@ -628,8 +653,8 @@ 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 ((targets (car (aref (or handles eshell-current-handles) - (or handle-index eshell-output-handle))))) + (let ((targets (caar (aref (or handles eshell-current-handles) + (or handle-index eshell-output-handle))))) (dolist (target targets) (eshell-output-object-to-target object target)))) -- cgit v1.2.1 From c34230f12aa966df091dd7b3cb2e32ce43ad811d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= Date: Fri, 30 Dec 2022 11:11:14 +0100 Subject: ; remove incorrect quoting of condition names --- lisp/eshell/esh-proc.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lisp/eshell') diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el index 950922ea7f8..c56278aad02 100644 --- a/lisp/eshell/esh-proc.el +++ b/lisp/eshell/esh-proc.el @@ -467,7 +467,7 @@ PROC is the process that's exiting. STRING is the exit message." (if (process-get proc :eshell-busy) (run-at-time 0 nil finish-io) (when data - (ignore-error 'eshell-pipe-broken + (ignore-error eshell-pipe-broken (eshell-output-object data index handles))) (eshell-close-handles -- cgit v1.2.1