From e1f183b388bb9c6e8d13b0154f82a08ed30522b0 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Wed, 26 Jun 2024 20:21:34 -0700 Subject: open-process: new ?fdno option for selecting stream fd. If, for instance ?2 is specified in the mode string argument of open-process and related functions, this means that the file descriptor 2 of the process will be used as the data source (or sink) for the stream that is returned by the function. With this feature we can easily read the standard error of a process while leaving its standard output unredirected. * stream.c (do_parse_mode): Parse the ? mode option. (open_subprocess): Check for the presence of the alternative file descriptor in the stdio_mode structure, and and use it isntead of STDIN_FILENO or STDOUT_FILENO. * stream.h (struct stdio_mode): New member, streamfd. (stdio_mode_init_blank, stdio_mode_init_r, stdio_mode_init_rpb, stdio_mode_init_blank, stdio_mode_init_r, stdio_mode_init_rpb): Update initializer macros to cover the new member, setting it to the default value -1 (not specified). * txr.1: Documented. --- stream.c | 24 ++++++++++++++++++++---- stream.h | 7 ++++--- txr.1 | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/stream.c b/stream.c index 953dc76a..7114a123 100644 --- a/stream.c +++ b/stream.c @@ -1584,6 +1584,20 @@ static struct stdio_mode do_parse_mode(val mode_str, struct stdio_mode m_dfl, nredir++; break; } + case '?': + { + wchar_t *past; + long val = wcstol(ms + 1, &past, 10); + + if (past == ms + 1 || val < 0 || val >= INT_MAX) { + m.malformed = 1; + return m; + } + + m.streamfd = val; + ms = past - 1; + break; + } case 'z': m.gzip = 1; if (isdigit(convert(unsigned char, ms[1]))) { @@ -4526,6 +4540,8 @@ static val open_subprocess(val name, val mode_str, val args, val fun) struct save_fds sfds; val ret = nil; int fds_flags = (input ? FDS_IN : FDS_OUT) | FDS_ERR; + int streamfd_in = m.streamfd != -1 ? m.streamfd : STDIN_FILENO; + int streamfd_out = m.streamfd != -1 ? m.streamfd : STDOUT_FILENO; args = default_null_arg(args); fun = default_null_arg(fun); @@ -4579,13 +4595,13 @@ static val open_subprocess(val name, val mode_str, val args, val fun) fds_clobber(&sfds, fds_flags); if (input) { - dup2(fd[1], STDOUT_FILENO); - if (fd[1] != STDOUT_FILENO) /* You never know */ + dup2(fd[1], streamfd_out); + if (fd[1] != streamfd_out) /* You never know */ close(fd[1]); close(fd[0]); } else { - dup2(fd[0], STDIN_FILENO); - if (fd[0] != STDIN_FILENO) /* You never know */ + dup2(fd[0], streamfd_in); + if (fd[0] != streamfd_in) /* You never know */ close(fd[0]); close(fd[1]); } diff --git a/stream.h b/stream.h index d4300154..9b3adf1f 100644 --- a/stream.h +++ b/stream.h @@ -121,11 +121,12 @@ struct stdio_mode { unsigned tmpfile : 1; int buforder : 5; int redir[STDIO_MODE_NREDIRS][2]; + int streamfd; }; -#define stdio_mode_init_blank { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } } } -#define stdio_mode_init_r { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } } } -#define stdio_mode_init_rpb { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } } } +#define stdio_mode_init_blank { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } }, -1 } +#define stdio_mode_init_r { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } }, -1 } +#define stdio_mode_init_rpb { 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, { { 0 } }, -1 } #define std_input (deref(lookup_var_l(nil, stdin_s))) #define std_output (deref(lookup_var_l(nil, stdout_s))) diff --git a/txr.1 b/txr.1 index ec0a09b9..f20e0963 100644 --- a/txr.1 +++ b/txr.1 @@ -62988,7 +62988,7 @@ Note that it permits no whitespace characters: .mets < mode := { < selector [ + ] | + } .mets < selector := { r | w | a | m | T } .mets < options := { b | x | l | u | i | n | < digit | -.mets \ \ \ \ \ \ \ \ \ \ \ \ \ \ <> z[ digit ] | < redirection } +.mets \ \ \ \ \ \ \ \ \ \ \ \ \ \ <> z[ digit ] | < redirection | >> ? fdno } .mets < digit := { 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 } .onom @@ -63160,6 +63160,17 @@ in mode strings that are passed to the function; the syntax performs I/O redirections in the child process created by that function, and is described in that function's documentation. +.meIP >> ? fdno +Like +.metn redirection , +this option refers to syntax which only has an effect in +mode strings that are passed to the +.code open-process +function. The syntax selects an alternative file descriptor +to connect to the returned stream. This is described in the +documentation for +.code open-process +function. .RE .IP The @@ -66218,6 +66229,31 @@ output of the process. In that process, standard error is redirected to standard output, standard input is connected to the null device, and descriptor 27 is redirected to descriptor 31. +The +.meta mode-string +argument of +.code open-process +also supports a special +.mono +.meti >> ? fdno +.onom +syntax. This syntax specifies an alternative file descriptor in +the process to which the returned stream should be connected. +By default, when the process is opened for writing, its standard +output descriptpr 1 is used, and when it is opened for reading, +its standard input descriptor 0 is used. This option overrides the +choice of descriptor. The +.meta fdno +portion of the syntax must be a sequence of decimal digits, immediately +following the +.code ? +character. For example, the mode string +.str ?2 +specifies that the process is to be open for input, such that +the input stream captures the standard error output of +that process. In this situation, the standard output will not be +captured; it remains unredirected. + The .code open-subprocess function is a variant of -- cgit v1.2.3