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
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
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));
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))
{
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';
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;
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)
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;
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)
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)
{
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 */
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)
{
jump_to_top_level (DISCARD);
}
- last_read_token = orig_last_token; /* XXX - was WORD? */
-
if (wl)
{
rl = REVERSE_LIST (wl, WORD_LIST *);
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 ()
{
));
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
)
${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)"
echo $( if x; then echo foo )
-echo after
+echo should not see this
: $(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)
--- /dev/null
+: ${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
./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
# 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 )