mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-27 15:43:18 +02:00
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:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user