fix up parser flags command substitution parsing inherits; using temp files for here-documents is now a compatibility mode option

This commit is contained in:
Chet Ramey
2022-04-27 16:16:59 -04:00
parent 98300c9405
commit d0cd67ee13
12 changed files with 141 additions and 72 deletions
+20
View File
@@ -3571,3 +3571,23 @@ redir.c
earlier, use tempfiles for all here-documents and here-strings. From
a bug-bash discussion started by Sam Liddicott <sam@liddicott.com>
4/26
----
parse.y
- parse_comsub: non-interactive shells exit on a syntax error while
parsing the command substitution
- parse_comsub: unset additional PARSER_STATE flags before calling
yyparse(). Inspired by https://bugs.gentoo.org/837203; unsetting
PST_COMPASSIGN is the fix for that bug
- parse_string_to_word_list: use save_parser_state/restore_parser_state
instead of saving pieces of the shell state in individual variables
- parse_compound_assignment: use save_parser_state/restore_parser_state
instead of saving pieces of the shell state in individual variables
- parse_compound_assignment: unset additional PARSER_STATE flags before
calling read_token(); set esacs_needed_count and expecting_in_token
to 0 like in parse_comsub() since read_token can use them
4/27
----
lib/sh/strvis.c
- strivs: changes to handle being compiled without multibyte support
+1
View File
@@ -1013,6 +1013,7 @@ tests/comsub-posix1.sub f
tests/comsub-posix2.sub f
tests/comsub-posix3.sub f
tests/comsub-posix5.sub f
tests/comsub-posix6.sub f
tests/cond.tests f
tests/cond.right f
tests/cond-regexp1.sub f
+1 -1
View File
@@ -121,7 +121,7 @@ void wcdequote_pathname PARAMS((wchar_t *));
static void wdequote_pathname PARAMS((char *));
static void dequote_pathname PARAMS((char *));
#else
# define dequote_pathname udequote_pathname
# define dequote_pathname(p) udequote_pathname(p)
#endif
static int glob_testdir PARAMS((char *, int));
static char **glob_dir_to_array PARAMS((char *, char **, int));
+6
View File
@@ -69,7 +69,11 @@ sh_charvis (s, sindp, slen, ret, rindp)
ri = *rindp;
c = s[*sindp];
#if defined (HANDLE_MULTIBYTE)
send = (locale_mb_cur_max > 1) ? s + slen : 0;
#else
send = 0;
#endif
if (SAFECHAR (c))
{
@@ -88,10 +92,12 @@ sh_charvis (s, sindp, slen, ret, rindp)
ret[ri++] = UNCTRL (c);
si++;
}
#if defined (HANDLE_MULTIBYTE)
else if (locale_utf8locale && (c & 0x80))
COPY_CHAR_I (ret, ri, s, send, si);
else if (locale_mb_cur_max > 1 && is_basic (c) == 0)
COPY_CHAR_I (ret, ri, s, send, si);
#endif
else if (META_CHAR (c))
{
ret[ri++] = 'M';
+45 -51
View File
@@ -4063,7 +4063,12 @@ parse_comsub (qc, open, close, lenp, flags)
pushed_string_list = (STRING_SAVER *)NULL;
/* State flags we don't want to persist into command substitutions. */
parser_state &= ~(PST_REGEXP|PST_EXTPAT|PST_CONDCMD|PST_CONDEXPR);
parser_state &= ~(PST_REGEXP|PST_EXTPAT|PST_CONDCMD|PST_CONDEXPR|PST_COMPASSIGN);
/* Could do PST_CASESTMT too, but that also affects history. Setting
expecting_in_token below should take care of the parsing requirements.
Unsetting PST_REDIRLIST isn't strictly necessary because of how we set
token_to_read below, but we do it anyway. */
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;
@@ -4102,7 +4107,14 @@ parse_comsub (qc, open, close, lenp, flags)
else if (r != 0)
{
/* parser_error (start_lineno, _("could not parse command substitution")); */
jump_to_top_level (DISCARD);
/* Non-interactive shells exit on parse error in a command substitution. */
if (last_command_exit_value == 0)
last_command_exit_value = EXECUTION_FAILURE;
set_exit_status (last_command_exit_value);
if (interactive_shell == 0)
jump_to_top_level (FORCE_EOF); /* This is like reader_loop() */
else
jump_to_top_level (DISCARD);
}
if (current_token != shell_eof_token)
@@ -6285,31 +6297,26 @@ parse_string_to_word_list (s, flags, whom)
const char *whom;
{
WORD_LIST *wl;
int tok, orig_current_token, orig_line_number, orig_input_terminator;
int orig_line_count, orig_parser_state;
int old_echo_input, old_expand_aliases, ea;
#if defined (HISTORY)
int old_remember_on_history, old_history_expansion_inhibited;
#endif
int tok, orig_current_token, orig_line_number;
int orig_parser_state;
sh_parser_state_t ps;
int ea;
#if defined (HISTORY)
old_remember_on_history = remember_on_history;
# if defined (BANG_HISTORY)
old_history_expansion_inhibited = history_expansion_inhibited;
# endif
bash_history_disable ();
#endif
orig_line_number = line_number;
orig_line_count = current_command_line_count;
orig_input_terminator = shell_input_line_terminator;
old_echo_input = echo_input_at_read;
old_expand_aliases = expand_aliases;
save_parser_state (&ps);
push_stream (1);
if (ea = expanding_alias ())
parser_save_alias ();
last_read_token = WORD; /* WORD to allow reserved words here */
/* WORD to avoid parsing reserved words as themselves and just parse them as
WORDs. */
last_read_token = WORD;
current_command_line_count = 0;
echo_input_at_read = expand_aliases = 0;
@@ -6318,9 +6325,11 @@ parse_string_to_word_list (s, flags, whom)
if (flags & 1)
{
orig_parser_state = parser_state;
parser_state |= PST_COMPASSIGN|PST_REPARSE;
orig_parser_state = parser_state; /* XXX - not needed? */
/* State flags we don't want to persist into compound assignments. */
parser_state &= ~PST_NOEXPAND; /* parse_comsub sentinel */
/* State flags we want to set for this run through the tokenizer. */
parser_state |= PST_COMPASSIGN|PST_REPARSE;
}
while ((tok = read_token (READ)) != yacc_EOF)
@@ -6350,21 +6359,10 @@ parse_string_to_word_list (s, flags, whom)
if (ea)
parser_restore_alias ();
#if defined (HISTORY)
remember_on_history = old_remember_on_history;
# if defined (BANG_HISTORY)
history_expansion_inhibited = old_history_expansion_inhibited;
# endif /* BANG_HISTORY */
#endif /* HISTORY */
echo_input_at_read = old_echo_input;
expand_aliases = old_expand_aliases;
current_command_line_count = orig_line_count;
shell_input_line_terminator = orig_input_terminator;
restore_parser_state (&ps);
if (flags & 1)
parser_state = orig_parser_state;
parser_state = orig_parser_state; /* XXX - not needed? */
if (wl == &parse_string_error)
{
@@ -6383,29 +6381,31 @@ parse_compound_assignment (retlenp)
int *retlenp;
{
WORD_LIST *wl, *rl;
int tok, orig_line_number, orig_last_token, assignok;
size_t orig_token_size;
int orig_parser_state;
char *saved_token, *ret;
int tok, orig_line_number, assignok;
sh_parser_state_t ps;
char *ret;
saved_token = token;
orig_token_size = token_buffer_size;
orig_line_number = line_number;
orig_last_token = last_read_token;
orig_parser_state = parser_state;
save_parser_state (&ps);
last_read_token = WORD; /* WORD to allow reserved words here */
/* WORD to avoid parsing reserved words as themselves and just parse them as
WORDs. Plus it means we won't be in a command position and so alias
expansion won't happen. */
last_read_token = WORD;
token = (char *)NULL;
token_buffer_size = 0;
wl = (WORD_LIST *)NULL; /* ( */
assignok = parser_state&PST_ASSIGNOK; /* XXX */
wl = (WORD_LIST *)NULL; /* ( */
orig_parser_state = parser_state;
parser_state &= ~PST_NOEXPAND;
/* State flags we don't want to persist into compound assignments. */
parser_state &= ~(PST_NOEXPAND|PST_CONDCMD|PST_CONDEXPR|PST_REGEXP|PST_EXTPAT);
/* State flags we want to set for this run through the tokenizer. */
parser_state |= PST_COMPASSIGN;
esacs_needed_count = expecting_in_token = 0;
while ((tok = read_token (READ)) != ')')
{
if (tok == '\n') /* Allow newlines in compound assignments */
@@ -6429,11 +6429,7 @@ parse_compound_assignment (retlenp)
wl = make_word_list (yylval.word, wl);
}
FREE (token);
token = saved_token;
token_buffer_size = orig_token_size;
parser_state = orig_parser_state;
restore_parser_state (&ps);
if (wl == &parse_string_error)
{
@@ -6445,8 +6441,6 @@ parse_compound_assignment (retlenp)
jump_to_top_level (DISCARD);
}
last_read_token = orig_last_token; /* XXX - was WORD? */
if (wl)
{
rl = REVERSE_LIST (wl, WORD_LIST *);
+13 -7
View File
@@ -61,7 +61,6 @@ here-doc terminated with a parenthesis
line terminated with a backslash
./comsub-posix1.sub: line 1: syntax error near unexpected token `)'
./comsub-posix1.sub: line 1: `echo $( if x; then echo foo )'
after
swap32_posix is a function
swap32_posix ()
{
@@ -77,16 +76,23 @@ swap32_posix ()
));
done
}
./comsub-posix5.sub: line 37: syntax error near unexpected token `done'
./comsub-posix5.sub: line 37: `: $(case x in x) ;; x) done esac)'
./comsub-posix5.sub: line 38: syntax error near unexpected token `done'
./comsub-posix5.sub: line 38: `: $(case x in x) ;; x) done ;; esac)'
./comsub-posix5.sub: line 39: syntax error near unexpected token `esac'
./comsub-posix5.sub: line 39: `: $(case x in x) (esac) esac)'
bash: -c: line 1: syntax error near unexpected token `done'
bash: -c: line 1: `: $(case x in x) ;; x) done esac)'
bash: -c: line 1: syntax error near unexpected token `done'
bash: -c: line 1: `: $(case x in x) ;; x) done ;; esac)'
bash: -c: line 1: syntax error near unexpected token `esac'
bash: -c: line 1: `: $(case x in x) (esac) esac)'
bash: -c: line 1: syntax error near unexpected token `in'
bash: -c: line 1: `: $(case x in esac|in) foo;; esac)'
bash: -c: line 1: syntax error near unexpected token `done'
bash: -c: line 1: `: $(case x in x) ;; x) done)'
case: -c: line 3: syntax error near unexpected token `esac'
case: -c: line 3: `$( esac ; bar=foo ; echo "$bar")) echo bad 2;;'
ok 2
inside outside
ok 3
syntax-error: -c: line 2: syntax error near unexpected token `done'
syntax-error: -c: line 2: `: $(case x in x) ;; x) done ;; esac)'
yes
+1 -2
View File
@@ -234,13 +234,12 @@ echo $(
)
${THIS_SH} ./comsub-posix1.sub
${THIS_SH} ./comsub-posix2.sub
${THIS_SH} ./comsub-posix3.sub
#${THIS_SH} ./comsub-posix4.sub
${THIS_SH} ./comsub-posix5.sub
${THIS_SH} ./comsub-posix6.sub
# produced a parse error through bash-4.0-beta2
: $(echo foo)"
+1 -1
View File
@@ -1,3 +1,3 @@
echo $( if x; then echo foo )
echo after
echo should not see this
+3 -3
View File
@@ -34,9 +34,9 @@
: $(case x in x) (echo esac) esac)
# these errors should be caught sooner
: $(case x in x) ;; x) done esac)
: $(case x in x) ;; x) done ;; esac)
: $(case x in x) (esac) esac)
${THIS_SH} -c ': $(case x in x) ;; x) done esac)' bash
${THIS_SH} -c ': $(case x in x) ;; x) done ;; esac)' bash
${THIS_SH} -c ': $(case x in x) (esac) esac)' bash
# these are not errors
: $(case x in x) ;; x) eval done ;; esac)
+43
View File
@@ -0,0 +1,43 @@
: ${THIS_SH:=./bash}
# comsub should not inherit PST_COMPASSIGN
C=($(echo "${A[@]}" | \
(while read -d ' ' i; do
C=(${C/ ${i%% *} / })
done
echo ${C[@]})))
# comsub should not inherit PST_CASEPAT
${THIS_SH} -c '
case foo in
$( esac ; bar=foo ; echo "$bar")) echo bad 2;;
*) echo ok 2;;
esac
echo we should not see this' case
# comsub should not inherit PST_SUBSHELL
${THIS_SH} -c '( case foo in
( $(echo foo | cat )) echo ok 2;;
*) echo bad 2;;
esac
echo $( echo inside ) outside )' subshell
# comsub should not inherit PST_REDIRLIST
${THIS_SH} -c '
{fd}</dev/null {fd2}<$(foo=/dev/null ; echo $foo) exec
case $fd2 in
[0-9]*) echo ok 3 ;;
*) echo bad 3 ;;
esac' redirlist
# comsub should exit on syntax error while parsing
${THIS_SH} -c '
: $(case x in x) ;; x) done ;; esac)
echo after syntax error' syntax-error
+4 -4
View File
@@ -54,10 +54,10 @@ umask: usage: umask [-p] [-S] [mode]
./errors.tests: line 177: declare: VAR: readonly variable
./errors.tests: line 179: declare: unset: not found
./errors.tests: line 182: VAR: readonly variable
./errors.tests: line 185: syntax error near unexpected token `)'
./errors.tests: line 185: `: $( for z in 1 2 3; do )'
./errors.tests: line 186: syntax error near unexpected token `done'
./errors.tests: line 186: `: $( for z in 1 2 3; done )'
comsub: -c: line 1: syntax error near unexpected token `)'
comsub: -c: line 1: `: $( for z in 1 2 3; do )'
comsub: -c: line 1: syntax error near unexpected token `done'
comsub: -c: line 1: `: $( for z in 1 2 3; done )'
./errors.tests: line 189: cd: HOME not set
./errors.tests: line 190: cd: /tmp/xyz.bash: No such file or directory
./errors.tests: line 192: cd: OLDPWD not set
+3 -3
View File
@@ -181,9 +181,9 @@ declare -p unset
# iteration variable in a for statement being readonly
for VAR in 1 2 3 ; do echo $VAR; done
# parser errors
: $( for z in 1 2 3; do )
: $( for z in 1 2 3; done )
# parser errors; caught early so we have to run them in subshells
${THIS_SH} -c ': $( for z in 1 2 3; do )' comsub
${THIS_SH} -c ': $( for z in 1 2 3; done )' comsub
# various `cd' errors
( unset HOME ; cd )