From 186be9c581e052f365dc219a5e2e0245f5434348 Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Tue, 19 Jul 2022 21:36:54 -0700 Subject: [PATCH 2/2] Add STREAM argument to 'process-tty-name' * src/process.c (process-tty-name): Add STREAM argument. * lisp/eshell/esh-io.el (eshell-close-target): Only call 'process-send-eof' once if the process's stdin is a pipe. * test/src/process-tests.el (make-process/test-connection-type): Check behavior of 'process-tty-name'. * doc/lispref/processes.texi (Process Information): Document the new argument. * etc/NEWS: Announce this change. --- doc/lispref/processes.texi | 17 +++++++++++------ etc/NEWS | 5 ++++- lisp/eshell/esh-io.el | 27 +++++++++++++++------------ src/process.c | 23 +++++++++++++++++++---- test/src/process-tests.el | 3 +++ 5 files changed, 52 insertions(+), 23 deletions(-) diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi index a7e08054c7..bbca48e6c5 100644 --- a/doc/lispref/processes.texi +++ b/doc/lispref/processes.texi @@ -1243,15 +1243,20 @@ Process Information whether the connection was closed normally or abnormally. @end defun -@defun process-tty-name process +@defun process-tty-name process &optional stream This function returns the terminal name that @var{process} is using for its communication with Emacs---or @code{nil} if it is using pipes instead of a pty (see @code{process-connection-type} in -@ref{Asynchronous Processes}). If @var{process} represents a program -running on a remote host, the terminal name used by that program on -the remote host is provided as process property @code{remote-tty}. If -@var{process} represents a network, serial, or pipe connection, the -value is @code{nil}. +@ref{Asynchronous Processes}). If @var{stream} is one of @code{stdin}, +@code{stdout}, or @code{stderr}, this function returns the terminal +name (or @code{nil}, as above) that @var{process} uses for that stream +specifically. You can use this to determine whether a particular +stream uses a pipe or a pty. + +If @var{process} represents a program running on a remote host, the +terminal name used by that program on the remote host is provided as +process property @code{remote-tty}. If @var{process} represents a +network, serial, or pipe connection, the value is @code{nil}. @end defun @defun process-coding-system process diff --git a/etc/NEWS b/etc/NEWS index dc79f0826a..23777d349e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3198,7 +3198,10 @@ invocation. Such shells are POSIX conformant by default. ** 'make-process' can set connection type independently for input and output. When calling 'make-process', communication via pty can be enabled selectively for just input or output by passing a cons cell for -':connection-type', e.g. '(pipe . pty)'. +':connection-type', e.g. '(pipe . pty)'. When examining a process +later, you can determine whether a particular stream for a process +uses a pty by passing one of 'stdin', 'stdout', or 'stderr' as the +second argument to 'process-tty-name'. +++ ** 'signal-process' now consults the list 'signal-process-functions'. diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el index c035890ddf..68e52a2c9c 100644 --- a/lisp/eshell/esh-io.el +++ b/lisp/eshell/esh-io.el @@ -276,18 +276,21 @@ eshell-close-target ;; If we're redirecting to a process (via a pipe, or process ;; redirection), send it EOF so that it knows we're finished. ((eshell-processp target) - ;; According to POSIX.1-2017, section 11.1.9, sending EOF causes - ;; all bytes waiting to be read to be sent to the process - ;; immediately. Thus, if there are any bytes waiting, we need to - ;; send EOF twice: once to flush the buffer, and a second time to - ;; cause the next read() to return a size of 0, indicating - ;; end-of-file to the reading process. However, some platforms - ;; (e.g. Solaris) actually require sending a *third* EOF. Since - ;; sending extra EOFs while the process is running shouldn't break - ;; anything, we'll just send the maximum we'd ever need. See - ;; bug#56025 for further details. - (let ((i 0)) - (while (and (<= (cl-incf i) 3) + ;; According to POSIX.1-2017, section 11.1.9, when communicating + ;; via terminal, sending EOF causes all bytes waiting to be read + ;; to be sent to the process immediately. Thus, if there are any + ;; bytes waiting, we need to send EOF twice: once to flush the + ;; buffer, and a second time to cause the next read() to return a + ;; size of 0, indicating end-of-file to the reading process. + ;; However, some platforms (e.g. Solaris) actually require sending + ;; a *third* EOF. Since sending extra EOFs while the process is + ;; running are a no-op, we'll just send the maximum we'd ever + ;; need. See bug#56025 for further details. + (let ((i 0) + ;; Only call `process-send-eof' once if communicating via a + ;; pipe (in truth, this just closes the pipe). + (max-attempts (if (process-tty-name target 'stdin) 3 1))) + (while (and (<= (cl-incf i) max-attempts) (eq (process-status target) 'run)) (process-send-eof target)))) diff --git a/src/process.c b/src/process.c index da5e9cb182..adc508156f 100644 --- a/src/process.c +++ b/src/process.c @@ -1243,14 +1243,29 @@ DEFUN ("process-command", Fprocess_command, Sprocess_command, 1, 1, 0, return XPROCESS (process)->command; } -DEFUN ("process-tty-name", Fprocess_tty_name, Sprocess_tty_name, 1, 1, 0, +DEFUN ("process-tty-name", Fprocess_tty_name, Sprocess_tty_name, 1, 2, 0, doc: /* Return the name of the terminal PROCESS uses, or nil if none. This is the terminal that the process itself reads and writes on, -not the name of the pty that Emacs uses to talk with that terminal. */) - (register Lisp_Object process) +not the name of the pty that Emacs uses to talk with that terminal. + +If STREAM is one of `stdin', `stdout', or `stderr', return the name of +the terminal PROCESS uses for that stream. This can be used to detect +whether a particular stream is connected via a pipe or a pty. */) + (register Lisp_Object process, Lisp_Object stream) { CHECK_PROCESS (process); - return XPROCESS (process)->tty_name; + register struct Lisp_Process *p = XPROCESS (process); + + if (NILP (stream)) + return p->tty_name; + else if (EQ (stream, Qstdin)) + return p->pty_in ? p->tty_name : Qnil; + else if (EQ (stream, Qstdout)) + return p->pty_out ? p->tty_name : Qnil; + else if (EQ (stream, Qstderr)) + return p->pty_out && NILP (p->stderrproc) ? p->tty_name : Qnil; + else + signal_error ("Unknown stream", stream); } static void diff --git a/test/src/process-tests.el b/test/src/process-tests.el index 41320672a0..6ba5930ee6 100644 --- a/test/src/process-tests.el +++ b/test/src/process-tests.el @@ -294,6 +294,9 @@ make-process/test-connection-type "if [ -t 2 ]; then echo stderr; fi")) :buffer stdout-buffer args))) + (should (eq (and (process-tty-name proc 'stdin) t) (nth 0 ttys))) + (should (eq (and (process-tty-name proc 'stdout) t) (nth 1 ttys))) + (should (eq (and (process-tty-name proc 'stderr) t) (nth 2 ttys))) (process-test-wait-for-sentinel proc 0) (should (equal (with-current-buffer stdout-buffer (buffer-string)) expected-output)))) -- 2.25.1