From c3ca11424d2ae66cafa2f931b008dfb728e209a5 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Wed, 12 Feb 2025 11:18:16 -0500 Subject: [PATCH] fix issue with redirections to bash input file descriptor; new minimal chmod builtin; posix mode change for kill builtin return status; perform additional validation on brace expansion sequence expressions --- CWRU/CWRU.chlog | 56 ++++++++++++ MANIFEST | 1 + braces.c | 99 +++++++++++++++++---- builtins/kill.def | 11 ++- command.h | 2 +- doc/bash.1 | 6 +- doc/bashref.texi | 46 ++++++++-- doc/version.texi | 6 +- examples/loadables/Makefile.in | 10 ++- examples/loadables/chmod.c | 156 +++++++++++++++++++++++++++++++++ execute_cmd.c | 2 +- input.c | 7 +- redir.c | 45 ++++++++-- 13 files changed, 403 insertions(+), 44 deletions(-) create mode 100644 examples/loadables/chmod.c diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 632e93d7..d7386faf 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -10929,3 +10929,59 @@ jobs.c return 257 just as if the child was not found, optionally printing an error message if JWAIT_PERROR is supplied From a discussion with Ian + + 2/8 + --- +redir.c + - do_redirection_internal: keep track of whether the redirector fd + was active with a new variable. Not used yet, should be optimized + out. + - add_undo_fd_redirect: new undo function using r_move_input so we + don't need a dup redir and a separate close redir to undo. Not + used yet + - do_redirection_internal: only call check_bash_input if we're + creating an undo list and not running one + +command.h + - OUTPUT_REDIRECT: add r_output_force + +input.c + - check_bash_input: if we dup the bash input fd to another in + save_bash_input, return that fd. Continue to return -1 on error + and 0 if we don't do anything but sync + +execute_cmd.c + - execute_null_command: force a subshell if any of the redirections + involve the bash input buffered fd. + From a report by Martin D Kealey + + 2/10 + ---- + +examples/loadables/chmod.c + - chmod: new loadable builtin, inspired by zhixu.liu@gmail.com in + https://savannah.gnu.org/patch/?10499 + + 2/11 + ---- +builtins/kill.def + - kill_builtin: in posix mode, return a failure status if any of the + pid/job arguments are not found or sending the signal fails. This + is consistent with + https://pubs.opengroup.org/onlinepubs/9799919799/utilities/kill.html#tag_20_64_14 + + 2/12 + ---- +braces.c + - brace_gobbler: take a new argument in which to return the type of + brace expansion (comma or sequence) we think we've found; change + all callers + - brace_expand: even if we find a BRACE_ARG_SEPARATOR, don't try to + perform expansion if brace_gobbler doesn't think we have a + BRACE_COMMA expansion + - valid_seqterm: new function, performs minimal validation on a + sequence expansion, used to skip over invalid sequence expressions + in brace_expand + - brace_expand: call valid_seqterm if brace_gobbler finds a BRACE_SEQ + expansion + Inspired by report from Robert Elz diff --git a/MANIFEST b/MANIFEST index 76917764..4a1af296 100644 --- a/MANIFEST +++ b/MANIFEST @@ -786,6 +786,7 @@ examples/loadables/fdflags.c f examples/loadables/finfo.c f examples/loadables/fltexpr.c f examples/loadables/cat.c f +examples/loadables/chmod.c f examples/loadables/csv.c f examples/loadables/dsv.c f examples/loadables/kv.c f diff --git a/braces.c b/braces.c index 57c0b109..2221e38d 100644 --- a/braces.c +++ b/braces.c @@ -61,6 +61,11 @@ extern int errno; #define BRACE_SEQ_SPECIFIER ".." +/* What kind of brace expansion do we think we have? brace_gobbler() decides. */ +#define BRACE_COMMA 0x01 +#define BRACE_SEQ 0x02 +#define BRACE_NONE 0x04 + extern int asprintf (char **, const char *, ...) __attribute__((__format__ (printf, 2, 3))); /* Basic idea: @@ -75,9 +80,10 @@ extern int asprintf (char **, const char *, ...) __attribute__((__format__ (prin /* The character which is used to separate arguments. */ static const int brace_arg_separator = ','; -static int brace_gobbler (char *, size_t, int *, int); +static int brace_gobbler (char *, size_t, int *, int *, int); static char **expand_amble (char *, size_t, int); static char **expand_seqterm (char *, size_t); +static int valid_seqterm (char *, size_t); static char **mkseq (intmax_t, intmax_t, intmax_t, int, size_t); static char **array_concat (char **, char **); @@ -97,12 +103,12 @@ dump_result (a) char ** brace_expand (char *text) { - register int start; + int start; size_t tlen; char *preamble, *postamble, *amble; size_t alen; char **tack, **result; - int i, j, c, c1; + int i, j, c, c1, etype; DECLARE_MBSTATE; @@ -110,13 +116,13 @@ brace_expand (char *text) tlen = strlen (text); i = 0; #if defined (CSH_BRACE_COMPAT) - c = brace_gobbler (text, tlen, &i, '{'); /* } */ + c = brace_gobbler (text, tlen, &i, (int *)NULL, '{'); /* } */ #else /* Make sure that when we exit this loop, c == 0 or text[i] begins a valid brace expansion sequence. */ do { - c = brace_gobbler (text, tlen, &i, '{'); /* } */ + c = brace_gobbler (text, tlen, &i, (int *)NULL, '{'); /* } */ if (i >= tlen) break; c1 = c; @@ -125,7 +131,16 @@ brace_expand (char *text) if (c) { start = j = i + 1; /* { */ - c = brace_gobbler (text, tlen, &j, '}'); + c = brace_gobbler (text, tlen, &j, &etype, '}'); +#if 1 + /* One alternative is to perform validity checking on the sequence + terms here. If the sequence expression is invalid, we just skip + over the open brace and go on, leaving other brace expressions in + the candidate sequence expression to be expanded. */ + if (etype == BRACE_SEQ && valid_seqterm (text + start, j - 1) == 0) + c = 0; +#endif + if (c == 0) /* it's not */ { i++; @@ -160,7 +175,7 @@ brace_expand (char *text) /* Find the amble. This is the stuff inside this set of braces. */ start = ++i; - c = brace_gobbler (text, tlen, &i, '}'); + c = brace_gobbler (text, tlen, &i, &etype, '}'); /* What if there isn't a matching close brace? */ if (c == 0) @@ -206,8 +221,8 @@ brace_expand (char *text) #if defined (SHELL) INITIALIZE_MBSTATE; - /* If the amble does not contain an unquoted BRACE_ARG_SEPARATOR, then - just return without doing any expansion. */ + /* If the amble does not contain an unquoted BRACE_ARG_SEPARATOR, and we + think we have a BRACE_COMMA-separated sequence, then do no expansion. */ j = 0; while (amble[j]) { @@ -218,14 +233,19 @@ brace_expand (char *text) continue; } - if (amble[j] == brace_arg_separator) + if (amble[j] == brace_arg_separator && etype == BRACE_COMMA) break; ADVANCE_CHAR (amble, alen, j); } - if (amble[j] == 0) + /* If we think we have a sequence expression, try to expand it. */ + if (amble[j] == 0 && etype == BRACE_SEQ) { + /* The other alternative (see call to valid_seqterm() above) is to + perform the validity checking in expand_seqterm(). If we do this, + and the sequence isn't valid, we just treat the entire candidate + sequence expansion as a single unexpanded string. */ tack = expand_seqterm (amble, alen); if (tack) goto add_tack; @@ -293,7 +313,7 @@ expand_amble (char *text, size_t tlen, int flags) c = 1; while (c) { - c = brace_gobbler (text, tlen, &i, brace_arg_separator); + c = brace_gobbler (text, tlen, &i, (int *)NULL, brace_arg_separator); #if defined (SHELL) tem = substring (text, start, i); #else @@ -455,6 +475,44 @@ mkseq (intmax_t start, intmax_t end, intmax_t incr, int type, size_t width) return (result); } +/* For now. */ +static int +valid_seqterm (char *text, size_t tlen) +{ + char *t, *lhs, *rhs; + int lhs_t, rhs_t; + int c; + + c = text[tlen]; + text[tlen] = '\0'; /* don't be tricked by something later in the string */ + t = strstr (text, BRACE_SEQ_SPECIFIER); + text[tlen] = c; + + if (t == 0) + return 0; /* invalid */ + + lhs = text; + rhs = t + sizeof(BRACE_SEQ_SPECIFIER) - 1; + + /*{*/ + if (lhs[0] == BRACE_SEQ_SPECIFIER[0] || rhs[0] == '}') + return 0; /* invalid */ + + /* Now figure out whether LHS and RHS are integers or letters. Both + sides have to match. Minimal checking here, just enough to throw out the + obvious invalid candidates. */ + lhs_t = (ISDIGIT (lhs[0]) || ((lhs[0] == '+' || lhs[0] == '-') && ISDIGIT (lhs[1]))) ? ST_INT : + (ISALPHA (lhs[0]) && lhs[1] == '.') ? ST_CHAR : ST_BAD; + + rhs_t = (ISDIGIT (rhs[0]) || ((rhs[0] == '+' || rhs[0] == '-') && ISDIGIT (rhs[1]))) ? ST_INT : +/*{*/ (ISALPHA (rhs[0]) && (rhs[1] == '}' || rhs[1] == '.')) ? ST_CHAR : ST_BAD; + + if (lhs_t != rhs_t || lhs_t == ST_BAD || rhs_t == ST_BAD) + return 0; /* invalid */ + + return 1; +} + static char ** expand_seqterm (char *text, size_t tlen) { @@ -577,9 +635,9 @@ expand_seqterm (char *text, size_t tlen) an inner set of braces. */ static int -brace_gobbler (char *text, size_t tlen, int *indx, int satisfy) +brace_gobbler (char *text, size_t tlen, int *indx, int *typep, int satisfy) { - register int i, c, quoted, level, commas, pass_next; + int i, c, quoted, level, commas, pass_next, btype; #if defined (SHELL) size_t si; char *t; @@ -587,6 +645,7 @@ brace_gobbler (char *text, size_t tlen, int *indx, int satisfy) DECLARE_MBSTATE; level = quoted = pass_next = 0; + btype = BRACE_NONE; #if defined (CSH_BRACE_COMPAT) commas = 1; #else @@ -719,10 +778,16 @@ comsub: level--; #if !defined (CSH_BRACE_COMPAT) else if (satisfy == '}' && c == brace_arg_separator && level == 0) - commas++; + { + btype = BRACE_COMMA; + commas++; + } else if (satisfy == '}' && STREQN (text+i, BRACE_SEQ_SPECIFIER, 2) && text[i+2] != satisfy && level == 0) - commas++; + { + btype = BRACE_SEQ; + commas++; + } #endif #if defined (SHELL) @@ -733,6 +798,8 @@ comsub: } *indx = i; + if (typep) + *typep = btype; return (c); } diff --git a/builtins/kill.def b/builtins/kill.def index f09c932a..7b16cf49 100644 --- a/builtins/kill.def +++ b/builtins/kill.def @@ -84,7 +84,7 @@ static void kill_error (pid_t, int); int kill_builtin (WORD_LIST *list) { - int sig, any_succeeded, listing, saw_signal, dflags; + int sig, any_succeeded, any_failed, listing, saw_signal, dflags; char *sigspec, *word; pid_t pid; intmax_t pid_value; @@ -96,7 +96,7 @@ kill_builtin (WORD_LIST *list) } CHECK_HELPOPT (list); - any_succeeded = listing = saw_signal = 0; + any_succeeded = any_failed = listing = saw_signal = 0; sig = SIGTERM; sigspec = "TERM"; @@ -199,6 +199,7 @@ use_sigspec: sh_invalidsig (sigspec); else kill_error (pid, errno); + any_failed++; CONTINUE_OR_FAIL; } else @@ -247,6 +248,7 @@ use_sigspec: sh_invalidsig (sigspec); else kill_error (pid, errno); + any_failed++; CONTINUE_OR_FAIL; } else @@ -262,7 +264,10 @@ use_sigspec: list = list->next; } - return (any_succeeded ? EXECUTION_SUCCESS : EXECUTION_FAILURE); + if (posixly_correct) + return ((any_failed == 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE); + else + return (any_succeeded ? EXECUTION_SUCCESS : EXECUTION_FAILURE); } static void diff --git a/command.h b/command.h index 1c068148..189b00de 100644 --- a/command.h +++ b/command.h @@ -49,7 +49,7 @@ enum r_instruction { (ri == r_output_direction || ri == r_err_and_out) #define OUTPUT_REDIRECT(ri) \ - (ri == r_output_direction || ri == r_input_output || ri == r_err_and_out || ri == r_append_err_and_out) + (ri == r_output_direction || ri == r_input_output || ri == r_err_and_out || ri == r_append_err_and_out || ri == r_output_force) #define INPUT_REDIRECT(ri) \ (ri == r_input_direction || ri == r_inputa_direction || ri == r_input_output) diff --git a/doc/bash.1 b/doc/bash.1 index fd86b835..369dee85 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -1381,7 +1381,7 @@ below). .PP A .I variable -may be assigned to by a statement of the form +is assigned to using a statement of the form .RS .PP \fIname\fP=[\fIvalue\fP] @@ -11348,7 +11348,7 @@ command that fails is part of the command list immediately following a .B while or .B until -keyword, +reserved word, part of the test following the .B if or @@ -12547,7 +12547,7 @@ command is part of the command list immediately following a .B while or .B until -keyword, +reserved word, part of the test in an .I if statement, part of a command executed in a diff --git a/doc/bashref.texi b/doc/bashref.texi index 55c4d3c4..6afc4dc2 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -1769,7 +1769,7 @@ The null string is a valid value. Once a variable is set, it may be unset only by using the @code{unset} builtin command. -A variable may be assigned to by a statement of the form +A variable is assigned to using a statement of the form @example @var{name}=[@var{value}] @end example @@ -2325,6 +2325,14 @@ $ : $@{var=DEFAULT@} $ echo $var DEFAULT $ var= +$ : $@{var=DEFAULT@} +$ echo $var + +$ var= +$ : $@{var:=DEFAULT@} +$ echo $var +DEFAULT +$ unset var $ : $@{var:=DEFAULT@} $ echo $var DEFAULT @@ -2345,6 +2353,16 @@ Otherwise, the value of @var{parameter} is substituted. $ var= $ : $@{var:?var is unset or null@} bash: var: var is unset or null +$ echo $@{var?var is unset@} + +$ unset var +$ : $@{var?var is unset@} +bash: var: var is unset +$ : $@{var:?var is unset or null@} +bash: var: var is unset or null +$ var=123 +$ echo $@{var:?var is unset or null@} +123 @end example @item $@{@var{parameter}:+@var{word}@} @@ -2357,9 +2375,18 @@ The value of @var{parameter} is not used. $ var=123 $ echo $@{var:+var is set and not null@} var is set and not null +$ echo $@{var+var is set@} +var is set $ var= $ echo $@{var:+var is set and not null@} +$ echo $@{var+var is set@} +var is set +$ unset var +$ echo $@{var+var is set@} + +$ echo $@{var:+var is set and not null@} + $ @end example @@ -4751,7 +4778,8 @@ command), a list, or a compound command returns a non-zero exit status, subject to the following conditions. The @code{ERR} trap is not executed if the failed command is part of the -command list immediately following an @code{until} or @code{while} keyword, +command list immediately following an +@code{until} or @code{while} reserved word, part of the test following the @code{if} or @code{elif} reserved words, part of a command executed in a @code{&&} or @code{||} list except the command following the final @code{&&} or @code{||}, @@ -5889,7 +5917,8 @@ a list (@pxref{Lists}), or a compound command (@pxref{Compound Commands}) returns a non-zero status. The shell does not exit if the command that fails is part of the -command list immediately following a @code{while} or @code{until} keyword, +command list immediately following a +@code{while} or @code{until} reserved word, part of the test in an @code{if} statement, part of any command executed in a @code{&&} or @code{||} list except the command following the final @code{&&} or @code{||}, @@ -9279,7 +9308,7 @@ double-quoted string, even if the @code{histexpand} option is enabled. @item When printing shell function definitions (e.g., by @code{type}), Bash does -not print the @code{function} keyword unless necessary. +not print the @code{function} reserved word unless necessary. @item Non-interactive shells exit if a syntax error in an arithmetic expansion @@ -9420,6 +9449,13 @@ separated by spaces, without the @samp{SIG} prefix. The @code{kill} builtin does not accept signal names with a @samp{SIG} prefix. +@item +The @code{kill} builtin returns a failure status if any of the pid or job +arguments are invalid or if sending the specified signal to any of them +fails. +In default mode, @code{kill} returns success if the signal was +successfully sent to any of the specified processes. + @item The @code{printf} builtin uses @code{double} (via @code{strtod}) to convert arguments corresponding to floating point conversion specifiers, instead of @@ -10918,7 +10954,7 @@ Bash implements command aliases and the @code{alias} and @code{unalias} builtins (@pxref{Aliases}). @item -Bash implements the @code{!} keyword to negate the return value of +Bash implements the @code{!} reserved word to negate the return value of a pipeline (@pxref{Pipelines}). This is very useful when an @code{if} statement needs to act only if a test fails. diff --git a/doc/version.texi b/doc/version.texi index 021bd4ff..1abeafa9 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2025 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Wed Jan 8 09:27:44 EST 2025 +@set LASTCHANGE Tue Feb 11 11:04:12 EST 2025 @set EDITION 5.3 @set VERSION 5.3 -@set UPDATED 8 January 2025 -@set UPDATED-MONTH January 2025 +@set UPDATED 11 February 2025 +@set UPDATED-MONTH February 2025 diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in index 2a8d7787..650628b9 100644 --- a/examples/loadables/Makefile.in +++ b/examples/loadables/Makefile.in @@ -103,8 +103,8 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \ ALLPROG = print truefalse sleep finfo logname basename dirname fdflags \ tty pathchk tee head mkdir rmdir mkfifo mktemp printenv id whoami \ uname sync push ln unlink realpath strftime mypid setpgid seq rm \ - accept csv dsv cut stat getconf kv strptime -OTHERPROG = necho hello cat pushd asort fltexpr + accept csv dsv cut stat getconf kv strptime chmod +OTHERPROG = necho hello cat pushd asort fltexpr SUBDIRS = perl @@ -238,6 +238,9 @@ strftime: strftime.o strptime: strptime.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ strptime.o $(SHOBJ_LIBS) +chmod: chmod.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ chmod.o $(SHOBJ_LIBS) + mypid: mypid.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mypid.o $(SHOBJ_LIBS) @@ -319,7 +322,7 @@ OBJS = print.o truefalse.o accept.o sleep.o finfo.o getconf.o logname.o \ basename.o dirname.o tty.o pathchk.o tee.o head.o rmdir.o necho.o \ hello.o cat.o csv.o dsv.o kv.o cut.o printenv.o id.o whoami.o uname.o \ sync.o push.o mkdir.o mktemp.o realpath.o strftime.o setpgid.o stat.o \ - fdflags.o seq.o asort.o strptime.o + fdflags.o seq.o asort.o strptime.o chmod.o ${OBJS}: ${BUILD_DIR}/config.h @@ -340,6 +343,7 @@ rmdir.o: rmdir.c necho.o: necho.c hello.o: hello.c cat.o: cat.c +chmod.o: chmod.c csv.o: csv.c dsv.o: dsv.c kv.o: kv.c diff --git a/examples/loadables/chmod.c b/examples/loadables/chmod.c new file mode 100644 index 00000000..dc7ca73a --- /dev/null +++ b/examples/loadables/chmod.c @@ -0,0 +1,156 @@ +/* chmod - change file mode bits */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of GNU Bash. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see . +*/ + +#include + +#include "bashtypes.h" +#include "posixstat.h" +#include +#include +#include "bashansi.h" +#if defined (HAVE_UNISTD_H) +# include +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" +#include "common.h" + +#if !defined (errno) +extern int errno; +#endif + +#define ISOCTAL(c) ((c) >= '0' && (c) <= '7') + +extern int parse_symbolic_mode (char *, mode_t); + +#define STANDARD_BITS (S_IRWXU | S_IRWXG | S_IRWXO) +#define ALLBITS (STANDARD_BITS | S_ISUID| S_ISGID | S_ISVTX) + +int +chmod_builtin (WORD_LIST *list) +{ + int opt, nmode, lmode, rval; + char *mode; + struct stat st; + WORD_LIST *l; + + reset_internal_getopt (); + mode = (char *)NULL; + while ((opt = internal_getopt(list, "fhvRHLP")) != -1) + switch (opt) + { + CASE_HELPOPT; + default: + return (EX_DISKFALLBACK); + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + mode = list->word->word; + list = list->next; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + nmode = -1; + if (ISOCTAL (*mode)) /* octal number */ + { + nmode = read_octal (mode); + if (nmode < 0) + { + builtin_error ("invalid file mode: %s", mode); + return (EXECUTION_FAILURE); + } + } + else /* test for valid symbolic mode */ + { + /* initial bits are a=rwx; the mode argument modifies them */ + lmode = parse_symbolic_mode (mode, ALLBITS); + if (lmode < 0) + { + builtin_error ("invalid file mode: %s", mode); + return (EXECUTION_FAILURE); + } + } + + for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next) + { + lmode = nmode; + if (stat (l->word->word, &st) < 0) + { + builtin_error ("`%s': cannot stat: %s", l->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + continue; + } + + if (lmode == -1) + { + lmode = parse_symbolic_mode (mode, st.st_mode & ALLBITS); + if (lmode < 0) + { + builtin_error ("`%s': invalid file mode: %s", l->word->word, mode); + rval = EXECUTION_FAILURE; + continue; + } + } + + if (chmod (l->word->word, lmode)) + { + builtin_error ("`%s': cannot change mode: %s", l->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + continue; + } + } + return rval; +} + +char *chmod_doc[] = { + "Change file mode bits.", + "", + "Change file mode bits. Change the mode bits of files named as", + "arguments, in the order specified, as specified by MODE." + "The MODE argument may be an octal number or a symbolic mode like", + "that described in chmod(1). If a symbolic mode is used, the", + "operations are interpreted relative to an initial mode of \"a=rwx\".", + "", + "The return value is 0 unless an error occurs.", + (char *)NULL +}; + +struct builtin chmod_struct = { + "chmod", + chmod_builtin, + BUILTIN_ENABLED, + chmod_doc, + "chmod [-R] mode file [file...]", + 0 +}; diff --git a/execute_cmd.c b/execute_cmd.c index 50df6c10..03931c6c 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -4202,7 +4202,7 @@ execute_null_command (REDIRECT *redirects, int pipe_in, int pipe_out, int async) { forcefork += rd->rflags & REDIR_VARASSIGN; /* Safety */ - forcefork += (rd->redirector.dest == 0 || fd_is_bash_input (rd->redirector.dest)) && (INPUT_REDIRECT (rd->instruction) || TRANSLATE_REDIRECT (rd->instruction) || rd->instruction == r_close_this); + forcefork += (rd->redirector.dest == 0 || fd_is_bash_input (rd->redirector.dest)); } if (forcefork || pipe_in != NO_PIPE || pipe_out != NO_PIPE || async) diff --git a/input.c b/input.c index ad1ebefd..9995b94f 100644 --- a/input.c +++ b/input.c @@ -302,10 +302,15 @@ save_bash_input (int fd, int new_fd) int check_bash_input (int fd) { + int nfd; + if (fd_is_bash_input (fd)) { if (fd > 0) - return ((save_bash_input (fd, -1) == -1) ? -1 : 0); + { + nfd = save_bash_input (fd, -1); /* allocates new fd */ + return (nfd); + } else if (fd == 0) return ((sync_buffered_stream (fd) == -1) ? -1 : 0); } diff --git a/redir.c b/redir.c index 6e950f84..98137492 100644 --- a/redir.c +++ b/redir.c @@ -95,6 +95,7 @@ extern REDIRECT *exec_redirection_undo_list; static void add_exec_redirect (REDIRECT *); static int add_undo_redirect (int, enum r_instruction, int); static int add_undo_close_redirect (int); +static int add_undo_fd_redirect (int, int); static int expandable_redirection_filename (REDIRECT *); static int stdin_redirection (enum r_instruction, int); static int undoablefd (int); @@ -761,7 +762,7 @@ static int do_redirection_internal (REDIRECT *redirect, int flags, char **fnp) { WORD_DESC *redirectee; - int redir_fd, fd, redirector, r, oflags, rflags; + int redir_fd, fd, redirector, r, oflags, rflags, fdactive; intmax_t lfd; char *redirectee_word; enum r_instruction ri; @@ -882,6 +883,8 @@ do_redirection_internal (REDIRECT *redirect, int flags, char **fnp) dispose_redirects (new_redirect); } + fdactive = 0; + switch (ri) { case r_output_direction: @@ -947,14 +950,17 @@ do_redirection_internal (REDIRECT *redirect, int flags, char **fnp) if (fd != redirector && (redirect->rflags & REDIR_VARASSIGN) && varassign_redir_autoclose) r = add_undo_close_redirect (redirector); else if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1)) - r = add_undo_redirect (redirector, ri, -1); + { + fdactive = 1; + r = add_undo_redirect (redirector, ri, -1); + } else r = add_undo_close_redirect (redirector); REDIRECTION_ERROR (r, errno, fd); } /* inhibit call to sync_buffered_stream() for async processes */ - if (redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0) + if ((redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0) && (flags & RX_UNDOABLE)) check_bash_input (redirector); /* Make sure there is no pending output before we change the state @@ -1062,13 +1068,17 @@ do_redirection_internal (REDIRECT *redirect, int flags, char **fnp) if (fd != redirector && (redirect->rflags & REDIR_VARASSIGN) && varassign_redir_autoclose) r = add_undo_close_redirect (redirector); else if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1)) - r = add_undo_redirect (redirector, ri, -1); + { + fdactive = 1; + r = add_undo_redirect (redirector, ri, -1); + } else r = add_undo_close_redirect (redirector); REDIRECTION_ERROR (r, errno, fd); } - check_bash_input (redirector); + if (flags & RX_UNDOABLE) + check_bash_input (redirector); if (redirect->rflags & REDIR_VARASSIGN) { @@ -1120,7 +1130,10 @@ do_redirection_internal (REDIRECT *redirect, int flags, char **fnp) if ((redirect->rflags & REDIR_VARASSIGN) && varassign_redir_autoclose) r = add_undo_close_redirect (redirector); else if (fcntl (redirector, F_GETFD, 0) != -1) - r = add_undo_redirect (redirector, ri, redir_fd); + { + fdactive = 1; + r = add_undo_redirect (redirector, ri, redir_fd); + } else r = add_undo_close_redirect (redirector); REDIRECTION_ERROR (r, errno, -1); @@ -1137,7 +1150,7 @@ do_redirection_internal (REDIRECT *redirect, int flags, char **fnp) } /* inhibit call to sync_buffered_stream() for async processes */ - if (redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0) + if ((redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0) && (flags & RX_UNDOABLE)) check_bash_input (redirector); if (redirect->rflags & REDIR_VARASSIGN) @@ -1221,7 +1234,7 @@ do_redirection_internal (REDIRECT *redirect, int flags, char **fnp) xtrace_fdchk (redirector); /* inhibit call to sync_buffered_stream() for async processes */ - if (redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0) + if ((redirector != 0 || (subshell_environment & SUBSHELL_ASYNC) == 0) && (flags & RX_UNDOABLE)) check_bash_input (redirector); r = close_buffered_fd (redirector); @@ -1354,6 +1367,22 @@ add_undo_close_redirect (int fd) return 0; } +static int +add_undo_fd_redirect (int sfd, int rfd) +{ + REDIRECTEE rd, sd; + REDIRECT *nr; + + sd.dest = sfd; + rd.dest = rfd; + nr = make_redirection (sd, r_move_input, rd, 0); + nr->rflags |= RX_INTERNAL; + nr->next = redirection_undo_list; + redirection_undo_list = nr; + + return 0; +} + static void add_exec_redirect (REDIRECT *dummy_redirect) { -- 2.47.3