mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-29 16:39:53 +02:00
subshells should not inherit FIFOs; initial framework for nofork (foreground) command substitutions
This commit is contained in:
@@ -6257,3 +6257,48 @@ eval.c
|
||||
setting it to 0 for the expansion so it doesn't affect things like
|
||||
command substitution.
|
||||
From a report by Grisha Levit <grishalevit@gmail.com>
|
||||
|
||||
parse.y
|
||||
- parse_comsub: handle ${Ccommand; }, where C == space, tab, newline,
|
||||
or '|'
|
||||
- read_token_word: understand ${Ccommand; } and call parse_comsub to
|
||||
parse the contents
|
||||
- parse_matched_pair: understand ${Ccommand; } inside another paired
|
||||
construct
|
||||
- new `funsub' production, like comsub but for ${Ccommand;} nofork
|
||||
command substitution
|
||||
|
||||
execute_cmd.c
|
||||
- execute_simple_command: if we fork because of pipes in or out, clear
|
||||
the fifo list, since subshell aren't supposed to inherit fifos
|
||||
|
||||
5/8
|
||||
---
|
||||
parser.h
|
||||
- PST_FUNSUBST: new parser state, parsing the command in ${Ccommand;}.
|
||||
Primarily used to have read_token_word return `}' as a separate
|
||||
token even if it's not delimited like a reserved word
|
||||
|
||||
subst.h
|
||||
- SX_FUNSUB: new flag, tell xparse_dolparen we are parsing the command
|
||||
in a ${Ccommand;} nofork command substitution
|
||||
|
||||
parse.y
|
||||
- xparse_dolparen: handle parsing ${Ccommand;} nofork command
|
||||
substitution like parse_comsub
|
||||
- read_token: if we're parsing a foreground command substitution,
|
||||
treat a word that begins with an unquoted `}' as a `}' token to
|
||||
terminate the ${Ccommand;} construct without requiring that the
|
||||
close brace be delimited like a reserved word
|
||||
- no_semi_successors: add DOLBRACE
|
||||
- reserved_word_acceptable: add DOLBRACE
|
||||
|
||||
subst.c
|
||||
- extract_function_subst: like extract_command_subst, but for nofork
|
||||
command substitutions; calls xparse_dolparen with the SX_FUNSUB
|
||||
flag to differentiate
|
||||
- string_extract_double_quoted,extract_delimited_string,
|
||||
extract_heredoc_dolbrace_string,extract_dollar_brace_string,
|
||||
param_expand: call extract_function_subst as appropriate
|
||||
- function_substitute: like command_substitute, but for nofork command
|
||||
substitutions. Just a stub for now
|
||||
|
||||
+2
-1
@@ -811,7 +811,8 @@ $(srcdir)/configure: $(srcdir)/configure.ac $(srcdir)/aclocal.m4 $(srcdir)/confi
|
||||
reconfig: force
|
||||
sh $(srcdir)/configure -C
|
||||
|
||||
loadables:
|
||||
# force loadables to wait until the shell is built
|
||||
loadables: .made
|
||||
cd $(LOADABLES_DIR) && $(MAKE) $(MFLAGS) all
|
||||
|
||||
#newversion: mkversion
|
||||
|
||||
+1
-1
@@ -9539,7 +9539,7 @@ in the same way as \fBecho \-e\fP.
|
||||
.B %q
|
||||
causes \fBprintf\fP to output the corresponding
|
||||
\fIargument\fP in a format that can be reused as shell input.
|
||||
\fB%q\fP and \fB%Q\fP use the \fB$''\fP quoting style if any characters
|
||||
\fB%q\fP and \fB%Q\fP use the \fB$\(aq\(aq\fP quoting style if any characters
|
||||
in the argument string require it, and backslash quoting otherwise.
|
||||
If the format string uses the \fIprintf\fP alternate form, these two
|
||||
formats quote the argument string using single quotes.
|
||||
|
||||
+2
-2
@@ -475,8 +475,8 @@ when in double quotes (@pxref{Shell Parameter Expansion}).
|
||||
@subsubsection ANSI-C Quoting
|
||||
@cindex quoting, ANSI
|
||||
|
||||
Character sequences of the form $'@var{string}' are treated as a special
|
||||
kind of single quotes.
|
||||
Character sequences of the form @code{$'@var{string}'} are treated as
|
||||
a special kind of single quotes.
|
||||
The sequence expands to @var{string}, with backslash-escaped characters
|
||||
in @var{string} replaced as specified by the ANSI C standard.
|
||||
Backslash escape sequences, if present, are decoded as follows:
|
||||
|
||||
+30
-1
@@ -4430,6 +4430,10 @@ execute_simple_command (SIMPLE_COM *simple_command, int pipe_in, int pipe_out, i
|
||||
coproc_closeall ();
|
||||
#endif
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
clear_fifo_list (); /* subshells don't inherit fifos */
|
||||
#endif
|
||||
|
||||
last_asynchronous_pid = old_last_async_pid;
|
||||
|
||||
if (async)
|
||||
@@ -4767,13 +4771,38 @@ execute_from_filesystem:
|
||||
/* The old code did not test already_forked and only did this if
|
||||
subshell_environment&SUBSHELL_COMSUB != 0 (comsubs and procsubs). Other
|
||||
uses of the no-fork optimization left FIFOs in $TMPDIR */
|
||||
if (already_forked == 0 && (cmdflags & CMD_NO_FORK) && fifos_pending() > 0)
|
||||
if (already_forked == 0 && (cmdflags & CMD_NO_FORK) && fifos_pending () > 0)
|
||||
cmdflags &= ~CMD_NO_FORK;
|
||||
|
||||
if (dofork && already_forked && (subshell_environment & SUBSHELL_PIPE) &&
|
||||
(cmdflags & CMD_NO_FORK) && fifos_pending () > 0)
|
||||
#if 0
|
||||
cmdflags &= ~CMD_NO_FORK;
|
||||
#else
|
||||
; /* can't turn off nofork here, too many processes have the FIFOs open */
|
||||
#endif
|
||||
#endif
|
||||
result = execute_disk_command (words, simple_command->redirects, command_line,
|
||||
pipe_in, pipe_out, async, fds_to_close,
|
||||
cmdflags);
|
||||
|
||||
#if 0
|
||||
/* If we forked but still have to fork again to run the disk command, we
|
||||
did so because we created FIFOs. We can't just execve the command in case
|
||||
it dies of a signal without a chance to clean up the FIFOs, so we fork
|
||||
again, then make sure we wait for the child from execute_disk_command(),
|
||||
unlink the FIFOs we created, and exit ourselves. */
|
||||
if (dofork && already_forked && (cmdflags & CMD_NO_FORK) == 0)
|
||||
{
|
||||
result = wait_for (last_made_pid, 0);
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
if (fifos_pending ())
|
||||
unlink_fifo_list ();
|
||||
#endif
|
||||
sh_exit (result & 0xFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
return_result:
|
||||
bind_lastarg (lastarg);
|
||||
FREE (command_line);
|
||||
|
||||
@@ -394,7 +394,7 @@ static FILE *yyerrstream;
|
||||
%type <command> cond_command
|
||||
%type <command> arith_for_command
|
||||
%type <command> coproc
|
||||
%type <command> comsub
|
||||
%type <command> comsub funsub
|
||||
%type <command> function_def function_body if_command elif_clause subshell
|
||||
%type <redirect> redirection redirection_list
|
||||
%type <element> simple_command_element
|
||||
@@ -429,6 +429,14 @@ inputunit: simple_list simple_list_terminator
|
||||
eof_encountered = 0;
|
||||
YYACCEPT;
|
||||
}
|
||||
| funsub
|
||||
{
|
||||
/* This is special; look at the production and how
|
||||
parse_comsub/parse_valsub sets token_to_read */
|
||||
global_command = $1;
|
||||
eof_encountered = 0;
|
||||
YYACCEPT;
|
||||
}
|
||||
| '\n'
|
||||
{
|
||||
/* Case of regular command, but not a very
|
||||
@@ -1043,7 +1051,9 @@ comsub: DOLPAREN compound_list ')'
|
||||
{
|
||||
$$ = (COMMAND *)NULL;
|
||||
}
|
||||
| DOLBRACE compound_list '}'
|
||||
;
|
||||
|
||||
funsub: DOLBRACE compound_list '}'
|
||||
{
|
||||
$$ = $2;
|
||||
}
|
||||
@@ -3983,7 +3993,16 @@ parse_dollar_word:
|
||||
if (ch == '(') /* ) */
|
||||
nestret = parse_comsub (0, '(', ')', &nestlen, (rflags|P_COMMAND) & ~P_DQUOTE);
|
||||
else if (ch == '{') /* } */
|
||||
nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|P_DOLBRACE|rflags);
|
||||
{
|
||||
int npeek;
|
||||
|
||||
npeek = shell_getc (1);
|
||||
shell_ungetc (npeek);
|
||||
if (FUNSUB_CHAR (npeek))
|
||||
nestret = parse_comsub (0, '{', '}', &nestlen, rflags|P_COMMAND);
|
||||
else
|
||||
nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE|P_DOLBRACE|rflags);
|
||||
}
|
||||
else if (ch == '[') /* ] */
|
||||
nestret = parse_matched_pair (0, '[', ']', &nestlen, rflags|P_ARITH);
|
||||
|
||||
@@ -4141,6 +4160,11 @@ dump_pflags (int flags)
|
||||
f &= ~PST_CMDSUBST;
|
||||
fprintf (stderr, "PST_CMDSUBST%s", f ? "|" : "");
|
||||
}
|
||||
if (f & PST_FUNSUBST)
|
||||
{
|
||||
f &= ~PST_FUNSUBST;
|
||||
fprintf (stderr, "PST_FUNSUBST%s", f ? "|" : "");
|
||||
}
|
||||
if (f & PST_CASESTMT)
|
||||
{
|
||||
f &= ~PST_CASESTMT;
|
||||
@@ -4248,7 +4272,7 @@ static char *
|
||||
parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
|
||||
{
|
||||
int peekc, r;
|
||||
int start_lineno, local_extglob, was_extpat;
|
||||
int start_lineno, dolbrace_spec, local_extglob, was_extpat;
|
||||
char *ret, *tcmd;
|
||||
size_t retlen;
|
||||
sh_parser_state_t ps;
|
||||
@@ -4261,9 +4285,20 @@ parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
|
||||
{
|
||||
peekc = shell_getc (1);
|
||||
shell_ungetc (peekc);
|
||||
if (peekc == '(') /*)*/
|
||||
if (peekc == '(') /* ) */
|
||||
return (parse_matched_pair (qc, open, close, lenp, P_ARITH));
|
||||
}
|
||||
else if (open == '{') /* } */
|
||||
{
|
||||
peekc = shell_getc (1);
|
||||
if (FUNSUB_CHAR (peekc))
|
||||
dolbrace_spec = peekc;
|
||||
else
|
||||
{
|
||||
shell_ungetc (peekc);
|
||||
return parse_matched_pair (qc, open, close, lenp, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/*itrace("parse_comsub: qc = `%c' open = %c close = %c", qc, open, close);*/
|
||||
|
||||
@@ -4283,6 +4318,8 @@ parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
|
||||
parser_state &= ~(PST_CASEPAT|PST_ALEXPNEXT|PST_SUBSHELL|PST_REDIRLIST);
|
||||
/* State flags we want to set for this run through the parser. */
|
||||
parser_state |= PST_CMDSUBST|PST_EOFTOKEN|PST_NOEXPAND;
|
||||
if (open == '{') /* } */
|
||||
parser_state |= PST_FUNSUBST;
|
||||
|
||||
/* leave pushed_string_list alone, since we might need to consume characters
|
||||
from it to satisfy this command substitution (in some perverse case). */
|
||||
@@ -4312,7 +4349,7 @@ parse_comsub (int qc, int open, int close, size_t *lenp, int flags)
|
||||
#endif
|
||||
|
||||
current_token = '\n'; /* XXX */
|
||||
token_to_read = DOLPAREN; /* let's trick the parser */
|
||||
token_to_read = (open == '(') ? DOLPAREN : DOLBRACE; /* let's trick the parser ) */
|
||||
|
||||
r = yyparse ();
|
||||
|
||||
@@ -4381,17 +4418,33 @@ INTERNAL_DEBUG(("current_token (%d) != shell_eof_token (%c)", current_token, she
|
||||
|
||||
tcmd = print_comsub (parsed_command); /* returns static memory */
|
||||
retlen = strlen (tcmd);
|
||||
if (tcmd[0] == '(') /* ) need a space to prevent arithmetic expansion */
|
||||
retlen++;
|
||||
ret = xmalloc (retlen + 2);
|
||||
if (tcmd[0] == '(') /* ) */
|
||||
if (open == '(') /* ) */
|
||||
{
|
||||
ret[0] = ' ';
|
||||
strcpy (ret + 1, tcmd);
|
||||
if (tcmd[0] == '(') /* ) need a space to prevent arithmetic expansion */
|
||||
retlen++;
|
||||
ret = xmalloc (retlen + 2);
|
||||
if (tcmd[0] == '(') /* ) */
|
||||
{
|
||||
ret[0] = ' ';
|
||||
strcpy (ret + 1, tcmd);
|
||||
}
|
||||
else
|
||||
strcpy (ret, tcmd);
|
||||
}
|
||||
else
|
||||
strcpy (ret, tcmd);
|
||||
ret[retlen++] = ')';
|
||||
else /* open == '{' } */
|
||||
{
|
||||
int lastc;
|
||||
|
||||
lastc = tcmd[retlen - 1];
|
||||
retlen++;
|
||||
ret = xmalloc (retlen + 4);
|
||||
ret[0] = (dolbrace_spec == '|') ? '|' : ' ';
|
||||
strcpy (ret + 1, tcmd); /* ( */
|
||||
if (lastc != '\n' && lastc != ';' && lastc != '&' && lastc != ')')
|
||||
ret[retlen++] = ';';
|
||||
ret[retlen++] = ' ';
|
||||
}
|
||||
ret[retlen++] = close;
|
||||
ret[retlen] = '\0';
|
||||
|
||||
dispose_command (parsed_command);
|
||||
@@ -4412,7 +4465,7 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
|
||||
{
|
||||
sh_parser_state_t ps;
|
||||
sh_input_line_state_t ls;
|
||||
int orig_ind, nc, sflags, start_lineno, local_extglob;
|
||||
int orig_ind, nc, sflags, start_lineno, local_extglob, funsub, closer;
|
||||
char *ret, *ep, *ostring;
|
||||
|
||||
/*debug_parser(1);*/
|
||||
@@ -4432,6 +4485,8 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
|
||||
|
||||
/*itrace("xparse_dolparen: size = %d shell_input_line = `%s' string=`%s'", shell_input_line_size, shell_input_line, string);*/
|
||||
|
||||
funsub = flags & SX_FUNSUB;
|
||||
|
||||
sflags = SEVAL_NONINT|SEVAL_NOHIST|SEVAL_NOFREE;
|
||||
if (flags & SX_NOLONGJMP)
|
||||
sflags |= SEVAL_NOLONGJMP;
|
||||
@@ -4443,10 +4498,12 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
|
||||
pushed_string_list = (STRING_SAVER *)NULL;
|
||||
#endif
|
||||
/*(*/
|
||||
parser_state |= PST_CMDSUBST|PST_EOFTOKEN; /* allow instant ')' */ /*(*/
|
||||
shell_eof_token = ')';
|
||||
parser_state |= PST_CMDSUBST|PST_EOFTOKEN; /* allow instant ')' */ /*{(*/
|
||||
closer = shell_eof_token = funsub ? '}' : ')';
|
||||
if (flags & SX_COMPLETE)
|
||||
parser_state |= PST_NOERROR;
|
||||
if (funsub)
|
||||
parser_state |= PST_FUNSUBST;
|
||||
|
||||
/* Don't expand aliases on this pass at all. Either parse_comsub() does it
|
||||
at parse time, in which case this string already has aliases expanded,
|
||||
@@ -4458,7 +4515,10 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
|
||||
local_extglob = extended_glob;
|
||||
#endif
|
||||
|
||||
token_to_read = DOLPAREN; /* let's trick the parser */
|
||||
if (funsub && FUNSUB_CHAR (*string))
|
||||
string++;
|
||||
|
||||
token_to_read = funsub ? DOLBRACE : DOLPAREN; /* let's trick the parser */
|
||||
|
||||
nc = parse_string (string, "command substitution", sflags, (COMMAND **)NULL, &ep);
|
||||
|
||||
@@ -4485,7 +4545,7 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
|
||||
{
|
||||
clear_shell_input_line (); /* XXX */
|
||||
if (bash_input.type != st_string) /* paranoia */
|
||||
parser_state &= ~(PST_CMDSUBST|PST_EOFTOKEN);
|
||||
parser_state &= ~(PST_CMDSUBST|PST_EOFTOKEN|PST_FUNSUBST);
|
||||
if ((flags & SX_NOLONGJMP) == 0)
|
||||
jump_to_top_level (-nc); /* XXX */
|
||||
}
|
||||
@@ -4495,7 +4555,7 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
|
||||
and return it. If flags & 1 (SX_NOALLOC) we can return NULL. */
|
||||
|
||||
/*(*/
|
||||
if (ep[-1] != ')')
|
||||
if (ep[-1] != closer)
|
||||
{
|
||||
#if 0
|
||||
if (ep[-1] != '\n')
|
||||
@@ -4516,7 +4576,7 @@ xparse_dolparen (const char *base, char *string, int *indp, int flags)
|
||||
itrace("xparse_dolparen:%d: *indp (%d) < orig_ind (%d), orig_string = `%s'", line_number, *indp, orig_ind, ostring);
|
||||
#endif
|
||||
|
||||
if (base[*indp] != ')' && (flags & SX_NOLONGJMP) == 0)
|
||||
if (base[*indp] != closer && (flags & SX_NOLONGJMP) == 0)
|
||||
{
|
||||
/*(*/
|
||||
if ((flags & SX_NOERROR) == 0)
|
||||
@@ -5096,6 +5156,24 @@ read_token_word (int character)
|
||||
}
|
||||
}
|
||||
|
||||
/* Just an awful special case. We want '}' to delimit the foreground
|
||||
command substitution construct ${Ccommand; } but since it's a word
|
||||
expansion, we need to join it with any potential following
|
||||
characters. We fake things out here and treat a word beginning with
|
||||
a close brace as the '}' reserved word, treat it as a separate
|
||||
token, terminate the command substitution, and go on reading
|
||||
characters into the same upper-layer token. */
|
||||
if ((parser_state & PST_FUNSUBST) && token_index == 0 && quoted == 0 &&
|
||||
reserved_word_acceptable (last_read_token) &&
|
||||
MBTEST(character == '}'))
|
||||
{
|
||||
RESIZE_MALLOCED_BUFFER (token, token_index, 2,
|
||||
token_buffer_size, TOKEN_DEFAULT_GROW_SIZE);
|
||||
token[token_index++] = character;
|
||||
all_digit_token = dollar_present = 0;
|
||||
goto got_token;
|
||||
}
|
||||
|
||||
/* Parse a matched pair of quote characters. */
|
||||
if MBTEST(shellquote (character))
|
||||
{
|
||||
@@ -5180,7 +5258,19 @@ read_token_word (int character)
|
||||
((peek_char == '{' || peek_char == '[') && character == '$')) /* ) ] } */
|
||||
{
|
||||
if (peek_char == '{') /* } */
|
||||
ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE|P_DOLBRACE);
|
||||
{
|
||||
int npeek;
|
||||
npeek = shell_getc (1);
|
||||
shell_ungetc (npeek);
|
||||
if (FUNSUB_CHAR (npeek))
|
||||
{
|
||||
push_delimiter (dstack, peek_char);
|
||||
ttok = parse_comsub (cd, '{', '}', &ttoklen, P_COMMAND);
|
||||
pop_delimiter (dstack);
|
||||
}
|
||||
else
|
||||
ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE|P_DOLBRACE);
|
||||
}
|
||||
else if (peek_char == '(') /* ) */
|
||||
{
|
||||
/* XXX - push and pop the `(' as a delimiter for use by
|
||||
@@ -5587,6 +5677,7 @@ reserved_word_acceptable (int toksym)
|
||||
case WHILE:
|
||||
case 0:
|
||||
case DOLPAREN:
|
||||
case DOLBRACE:
|
||||
return 1;
|
||||
default:
|
||||
#if defined (COPROCESS_SUPPORT)
|
||||
@@ -5655,7 +5746,7 @@ reset_readline_prompt (void)
|
||||
static const int no_semi_successors[] = {
|
||||
'\n', '{', '(', ')', ';', '&', '|',
|
||||
CASE, DO, ELSE, IF, SEMI_SEMI, SEMI_AND, SEMI_SEMI_AND, THEN, UNTIL,
|
||||
WHILE, AND_AND, OR_OR, IN, DOLPAREN,
|
||||
WHILE, AND_AND, OR_OR, IN, DOLPAREN, DOLBRACE,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#define PST_NOERROR 0x800000 /* don't print error messages in yyerror */
|
||||
#define PST_STRING 0x1000000 /* parsing a string to a command or word list */
|
||||
#define PST_CMDBLTIN 0x2000000 /* last token was the `command' builtin */
|
||||
#define PST_FUNSUBST 0x4000000 /* parsing a foreground command substitution */
|
||||
|
||||
/* Definition of the delimiter stack. Needed by parse.y and bashhist.c. */
|
||||
struct dstack {
|
||||
@@ -75,6 +76,10 @@ struct dstack {
|
||||
#define DOLBRACE_QUOTE 0x40 /* single quote is special in double quotes */
|
||||
#define DOLBRACE_QUOTE2 0x80 /* single quote is semi-special in double quotes */
|
||||
|
||||
/* characters that can appear following ${ to introduce a function or value
|
||||
substitution (this is mksh terminology and needs to be changed). */
|
||||
#define FUNSUB_CHAR(n) ((n) == ' ' || (n) == '\t' || (n) == '\n' || (n) == '|')
|
||||
|
||||
/* variable declarations from parse.y */
|
||||
extern struct dstack dstack;
|
||||
|
||||
|
||||
@@ -934,6 +934,8 @@ add_one_character:
|
||||
si = i + 2;
|
||||
if (string[i + 1] == LPAREN)
|
||||
ret = extract_command_subst (string, &si, (flags & SX_COMPLETE));
|
||||
else if (string[i + 1] == LBRACE && FUNSUB_CHAR (string[si]))
|
||||
ret = extract_function_subst (string, &si, Q_DOUBLE_QUOTES, (flags & SX_COMPLETE));
|
||||
else
|
||||
ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, 0);
|
||||
|
||||
@@ -1036,6 +1038,8 @@ skip_double_quoted (const char *string, size_t slen, int sind, int flags)
|
||||
si = i + 2;
|
||||
if (string[i + 1] == LPAREN)
|
||||
ret = extract_command_subst (string, &si, SX_NOALLOC|(flags&SX_COMPLETE));
|
||||
else if (string[i + 1] == LBRACE && FUNSUB_CHAR (string[si]))
|
||||
ret = extract_function_subst (string, &si, Q_DOUBLE_QUOTES, (flags & SX_COMPLETE));
|
||||
else
|
||||
ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC);
|
||||
|
||||
@@ -1256,6 +1260,25 @@ extract_command_subst (const char *string, int *sindex, int xflags)
|
||||
}
|
||||
}
|
||||
|
||||
/* Take a ${Ccommand} where C is a character that introduces a function
|
||||
substitution and extract the string. */
|
||||
char *
|
||||
extract_function_subst (const char *string, int *sindex, int quoted, int xflags)
|
||||
{
|
||||
char *ret;
|
||||
char *xstr;
|
||||
|
||||
if (string[*sindex] == LBRACE || (xflags & SX_COMPLETE))
|
||||
return (extract_dollar_brace_string (string, sindex, quoted, xflags|SX_COMMAND));
|
||||
else
|
||||
{
|
||||
xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0);
|
||||
xstr = (char *)string + *sindex;
|
||||
ret = xparse_dolparen (string, xstr, sindex, xflags|SX_FUNSUB);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the $[ construct in STRING, and return a new string. (])
|
||||
Start extracting at (SINDEX) as if we had just seen "$[".
|
||||
Make (SINDEX) get the position of the matching "]". */
|
||||
@@ -1392,6 +1415,16 @@ extract_delimited_string (const char *string, int *sindex, char *opener, char *a
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Process alternate form of nested command substitution. */
|
||||
if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LBRACE && FUNSUB_CHAR (string[i+2]))
|
||||
{
|
||||
si = i + 2;
|
||||
t = extract_function_subst (string, &si, 0, flags|SX_NOALLOC);
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
i = si + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Process a nested OPENER. */
|
||||
if (STREQN (string + i, opener, len_opener))
|
||||
{
|
||||
@@ -1641,16 +1674,22 @@ extract_heredoc_dolbrace_string (const char *string, int *sindex, int quoted, in
|
||||
|
||||
/* Pass the contents of new-style command substitutions and
|
||||
arithmetic substitutions through verbatim. */
|
||||
if (string[i] == '$' && string[i+1] == LPAREN)
|
||||
if (string[i] == '$' && (string[i+1] == LPAREN || (string[i+1] == LBRACE && FUNSUB_CHAR (string[i+2]))))
|
||||
{
|
||||
int open;
|
||||
|
||||
si = i + 2;
|
||||
t = extract_command_subst (string, &si, flags);
|
||||
open = string[i+1];
|
||||
if (open == LPAREN)
|
||||
t = extract_command_subst (string, &si, flags);
|
||||
else
|
||||
t = extract_function_subst (string, &si, quoted, flags);
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
|
||||
tlen = si - i - 2;
|
||||
RESIZE_MALLOCED_BUFFER (result, result_index, tlen + 4, result_size, 64);
|
||||
result[result_index++] = c;
|
||||
result[result_index++] = LPAREN;
|
||||
result[result_index++] = open;
|
||||
strncpy (result + result_index, t, tlen);
|
||||
result_index += tlen;
|
||||
result[result_index++] = string[si];
|
||||
@@ -1854,6 +1893,19 @@ extract_dollar_brace_string (const char *string, int *sindex, int quoted, int fl
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Pass the contents of foreground command substitutions (funsub/valsub)
|
||||
through verbatim. */
|
||||
if (string[i] == '$' && string[i+1] == LBRACE && FUNSUB_CHAR (string[i+2]))
|
||||
{
|
||||
si = i + 2;
|
||||
t = extract_function_subst (string, &si, quoted, flags|SX_NOALLOC);
|
||||
|
||||
CHECK_STRING_OVERRUN (i, si, slen, c);
|
||||
|
||||
i = si + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
/* Technically this should only work at the start of a word */
|
||||
if ((string[i] == '<' || string[i] == '>') && string[i+1] == LPAREN)
|
||||
@@ -2084,7 +2136,7 @@ skip_matched_pair (const char *string, int start, int open, int close, int flags
|
||||
if (string[si] == '\0')
|
||||
CQ_RETURN(si);
|
||||
|
||||
/* XXX - extract_command_subst here? */
|
||||
/* XXX - extract_command_subst/extract_function_subst here? */
|
||||
if (string[i+1] == LPAREN)
|
||||
temp = extract_delimited_string (string, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */
|
||||
else
|
||||
@@ -6693,6 +6745,52 @@ read_comsub (int fd, int quoted, int flags, int *rflag)
|
||||
return istring;
|
||||
}
|
||||
|
||||
WORD_DESC *
|
||||
function_substitute (char *string, int quoted, int flags)
|
||||
{
|
||||
pid_t old_pipeline_pgrp;
|
||||
char *istring, *s;
|
||||
int fd;
|
||||
int result, fildes[2], function_value, pflags, rc, tflag, fork_flags;
|
||||
int valsub;
|
||||
WORD_DESC *ret;
|
||||
sigset_t set, oset;
|
||||
|
||||
istring = (char *)NULL;
|
||||
|
||||
/* In the case of no command to run, just return NULL. */
|
||||
for (s = string; s && *s && (shellblank (*s) || *s == '\n'); s++)
|
||||
;
|
||||
if (s == 0 || *s == 0)
|
||||
return ((WORD_DESC *)NULL);
|
||||
|
||||
if (valsub = (*string == '|'))
|
||||
string++;
|
||||
|
||||
/* Flags to pass to parse_and_execute() */
|
||||
pflags = (interactive && sourcelevel == 0) ? SEVAL_RESETLINE : 0;
|
||||
|
||||
#if defined (JOB_CONTROL)
|
||||
old_pipeline_pgrp = pipeline_pgrp;
|
||||
/* Don't reset the pipeline pgrp if we're already a subshell in a pipeline or
|
||||
we've already forked to run a disk command (and are expanding redirections,
|
||||
for example). */
|
||||
if ((subshell_environment & (SUBSHELL_FORK|SUBSHELL_PIPE)) == 0)
|
||||
pipeline_pgrp = shell_pgrp;
|
||||
save_pipeline (1);
|
||||
stop_making_children ();
|
||||
#endif /* JOB_CONTROL */
|
||||
|
||||
last_command_exit_value = EXECUTION_FAILURE;
|
||||
report_error ("bad substitution: %s", string);
|
||||
|
||||
pipeline_pgrp = old_pipeline_pgrp;
|
||||
restore_pipeline (1);
|
||||
|
||||
/* exp_jump_to_top_level (DISCARD); */
|
||||
return ((WORD_DESC *)NULL);
|
||||
}
|
||||
|
||||
/* Perform command substitution on STRING. This returns a WORD_DESC * with the
|
||||
contained string possibly quoted. */
|
||||
WORD_DESC *
|
||||
@@ -10277,6 +10375,30 @@ param_expand (char *string, int *sindex, int quoted,
|
||||
break;
|
||||
|
||||
case LBRACE:
|
||||
/* Foreground command substitution. */
|
||||
if (FUNSUB_CHAR (string[zindex + 1]))
|
||||
{
|
||||
/* We have to extract the contents of this command substitution. */
|
||||
t_index = zindex + 1;
|
||||
temp = extract_function_subst (string, &t_index, quoted, (pflags&PF_COMPLETE) ? SX_COMPLETE : 0);
|
||||
zindex = t_index;
|
||||
|
||||
/* This is basically the same as the comsub code. */
|
||||
if (pflags & PF_NOCOMSUB)
|
||||
/* we need zindex+1 because string[zindex] == RPAREN */
|
||||
temp1 = substring (string, *sindex, zindex+1);
|
||||
else
|
||||
{
|
||||
tdesc = function_substitute (temp, quoted, pflags&PF_ASSIGNRHS);
|
||||
temp1 = tdesc ? tdesc->word : (char *)NULL;
|
||||
if (tdesc)
|
||||
dispose_word_desc (tdesc);
|
||||
}
|
||||
FREE (temp);
|
||||
temp = temp1;
|
||||
break;
|
||||
}
|
||||
|
||||
tdesc = parameter_brace_expand (string, &zindex, quoted, pflags,
|
||||
quoted_dollar_at_p,
|
||||
contains_dollar_at);
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
#define SX_NOCTLESC 0x0010 /* don't honor CTLESC quoting */
|
||||
#define SX_NOESCCTLNUL 0x0020 /* don't let CTLESC quote CTLNUL */
|
||||
#define SX_NOLONGJMP 0x0040 /* don't longjmp on fatal error */
|
||||
#define SX_ARITHSUB 0x0080 /* extracting $(( ... )) (currently unused) */
|
||||
#define SX_FUNSUB 0x0080 /* extracting ${ command; }; passed to xparse_dolparen */
|
||||
#define SX_POSIXEXP 0x0100 /* extracting new Posix pattern removal expansions in extract_dollar_brace_string */
|
||||
#define SX_WORD 0x0200 /* extracting word in ${param op word} */
|
||||
#define SX_COMPLETE 0x0400 /* extracting word for completion */
|
||||
@@ -86,6 +86,9 @@ extern void unquote_bang (char *);
|
||||
XFLAGS is additional flags to pass to other extraction functions, */
|
||||
extern char *extract_command_subst (const char *, int *, int);
|
||||
|
||||
/* Placeholder */
|
||||
extern char *extract_function_subst (const char *, int *, int, int);
|
||||
|
||||
/* Extract the $[ construct in STRING, and return a new string.
|
||||
Start extracting at (SINDEX) as if we had just seen "$[".
|
||||
Make (SINDEX) get the position just after the matching "]". */
|
||||
@@ -274,6 +277,7 @@ extern WORD_LIST *expand_words_no_vars (WORD_LIST *);
|
||||
extern WORD_LIST *expand_words_shellexp (WORD_LIST *);
|
||||
|
||||
extern WORD_DESC *command_substitute (char *, int, int);
|
||||
extern WORD_DESC *function_substitute (char *, int, int);
|
||||
extern char *pat_subst (char *, char *, char *, int);
|
||||
|
||||
#if defined (PROCESS_SUBSTITUTION)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
FROM alpine:3.17
|
||||
RUN apk add --no-cache bison coreutils gcc libc-dev make
|
||||
ncurses-dev libncurses5-dev libncursesw5-dev
|
||||
WORKDIR /tmp/bash
|
||||
COPY . .
|
||||
RUN ./configure --enable-readline --with-curses
|
||||
RUN make
|
||||
RUN make tests
|
||||
@@ -78,6 +78,8 @@ env -i BASH_FUNC_x%%='() { _; } >_[${ $() }] { id; }' ${THIS_SH} -c : 2>/dev/nul
|
||||
env BASH_FUNC_x%%=$'() { _;}>_[$($())]\n{ echo vuln;}' ${THIS_SH} -c : 2>/dev/null
|
||||
eval 'x() { _;}>_[$($())] { echo vuln;}' 2>/dev/null
|
||||
|
||||
echo this will fail now that '${ ' has syntactic meaning
|
||||
|
||||
eval 'foo() { _; } >_[${ $() }] ;{ echo eval ok; }'
|
||||
|
||||
# other tests fixed in bash43-030 concerning function name transformation
|
||||
|
||||
Reference in New Issue
Block a user