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 <saturns_rings@protonmail.com>
+
+ 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 <martin@kurahaupo.gen.nz>
+
+ 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 <kre@munnari.oz.au>
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
#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:
/* 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 **);
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;
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;
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++;
/* 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)
#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])
{
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;
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
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)
{
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;
DECLARE_MBSTATE;
level = quoted = pass_next = 0;
+ btype = BRACE_NONE;
#if defined (CSH_BRACE_COMPAT)
commas = 1;
#else
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)
}
*indx = i;
+ if (typep)
+ *typep = btype;
return (c);
}
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;
}
CHECK_HELPOPT (list);
- any_succeeded = listing = saw_signal = 0;
+ any_succeeded = any_failed = listing = saw_signal = 0;
sig = SIGTERM;
sigspec = "TERM";
sh_invalidsig (sigspec);
else
kill_error (pid, errno);
+ any_failed++;
CONTINUE_OR_FAIL;
}
else
sh_invalidsig (sigspec);
else
kill_error (pid, errno);
+ any_failed++;
CONTINUE_OR_FAIL;
}
else
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
(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)
.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]
.B while
or
.B until
-keyword,
+reserved word,
part of the test following the
.B if
or
.B while
or
.B until
-keyword,
+reserved word,
part of the test in an
.I if
statement, part of a command executed in a
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
$ echo $var
DEFAULT
$ var=
+$ : $@{var=DEFAULT@}
+$ echo $var
+
+$ var=
+$ : $@{var:=DEFAULT@}
+$ echo $var
+DEFAULT
+$ unset var
$ : $@{var:=DEFAULT@}
$ echo $var
DEFAULT
$ 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}@}
$ 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
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{||},
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{||},
@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
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
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.
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
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
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)
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
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
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include "bashtypes.h"
+#include "posixstat.h"
+#include <errno.h>
+#include <stdio.h>
+#include "bashansi.h"
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#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
+};
{
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)
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);
}
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);
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;
dispose_redirects (new_redirect);
}
+ fdactive = 0;
+
switch (ri)
{
case r_output_direction:
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
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)
{
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);
}
/* 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)
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);
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)
{