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

This commit is contained in:
Chet Ramey
2025-02-12 11:18:16 -05:00
parent 3cfc255efe
commit c3ca11424d
13 changed files with 403 additions and 44 deletions
+56
View File
@@ -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 <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>
+1
View File
@@ -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
+83 -16
View File
@@ -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);
}
+8 -3
View File
@@ -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
+1 -1
View File
@@ -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)
+3 -3
View File
@@ -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
+41 -5
View File
@@ -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.
+3 -3
View File
@@ -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
+7 -3
View File
@@ -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
+156
View File
@@ -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 <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
};
+1 -1
View File
@@ -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)
+6 -1
View File
@@ -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);
}
+37 -8
View File
@@ -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)
{