From: Chet Ramey Date: Wed, 30 Apr 2025 20:19:08 +0000 (-0400) Subject: check for delimiter_depth being 0 before popping off the stack (reset_parser resets... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=15df5993542463ba9798e4ea5e488dfddf83c276;p=thirdparty%2Fbash.git check for delimiter_depth being 0 before popping off the stack (reset_parser resets it); make EOFs while reading here-doc delimiters sticky; when reading arith command, push back a character after the first closing paren that isn't a second close paren to fix backslash issue --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 2f3f1e13..a96c3dc4 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -11151,3 +11151,38 @@ builtins/read.def only push the delimiter back if we read that character ourselves (i > 1). Report from Greg Wooledge + + 4/23 + ---- +parse.y + - pop_delimiter: only decrement delimiter_depth if it's > 0, since + reset_parser() may have set it to 0 after the matching call to + push_delimiter + Report from Grisha Levit based on a report + from Александр Ушаков + + 4/25 + ---- +parse.y + - read_a_line: if the shell is interactive, and not reading from a + string, check whether a previous call to shell_getc has set + EOF_Reached and return EOF in this case, after resetting the + current token to '\n'. This makes EOFs that are not the first + character on the line `sticky' instead of just token delimiters. + From https://savannah.gnu.org/bugs/?67045 + - history_delimiting_chars: if it looks like we just finished a + subshell, and the line we're adding begins with an operator that + can't follow a semicolon, return a newline + From https://savannah.gnu.org/patch/?10517 + + 4/28 + ---- +parse.y + - parse_arith_command: if the character after the first right paren + isn't a right paren, making the construct a nested subshell, push + that character back and return the subshell command as the current + token string, which we push onto the pushed string list. Reading + one character more can cause synchronization problems with backslash + newline, among other things. + From https://savannah.gnu.org/patch/?10517 + diff --git a/parse.y b/parse.y index dd190319..4e64464a 100644 --- a/parse.y +++ b/parse.y @@ -2214,7 +2214,15 @@ read_a_line (int remove_quoted_newline) QUIT; /* If we're reading the here-document from an alias, use shell_getc */ - c = heredoc_string ? shell_getc (0) : yy_getc (); + if (interactive && EOF_Reached && heredoc_string == 0) + { + c = EOF; + EOF_Reached = 0; + if (current_token == yacc_EOF) + current_token = '\n'; /* reset state */ + } + else + c = heredoc_string ? shell_getc (0) : yy_getc (); /* Ignore null bytes in input. */ if (c == 0) @@ -2451,7 +2459,10 @@ static struct dstack temp_dstack = { (char *)NULL, 0, 0 }; } \ while (0) -#define pop_delimiter(ds) ds.delimiter_depth-- +/* The parsing or expansion code may have called reset_parser() between the + time push_delimiter was called and this call to pop_delimiter, which resets + delimiter_depth to 0, so we check. */ +#define pop_delimiter(ds) do { if (ds.delimiter_depth > 0) ds.delimiter_depth--; } while (0) /* Return the next shell input character. This always reads characters from shell_input_line; when that line is exhausted, it is time to @@ -4983,11 +4994,12 @@ parse_arith_cmd (char **ep, int adddq) } else /* nested subshell */ { + shell_ungetc (c); + tokstr[0] = '('; strncpy (tokstr + 1, ttok, ttoklen - 1); tokstr[ttoklen] = ')'; - tokstr[ttoklen+1] = c; - tokstr[ttoklen+2] = '\0'; + tokstr[ttoklen+1] = '\0'; } *ep = tokstr; @@ -5990,6 +6002,10 @@ static const int no_semi_successors[] = { 0 }; +static const int no_semi_predecessors[] = { +'&', '|', ';', 0 +}; + /* If we are not within a delimited expression, try to be smart about which separators can be semi-colons and which must be newlines. Returns the string that should be added into the @@ -5999,6 +6015,7 @@ char * history_delimiting_chars (const char *line) { static int last_was_heredoc = 0; /* was the last entry the start of a here document? */ + const char *lp; register int i; if ((parser_state & PST_HEREDOC) == 0) @@ -6045,6 +6062,9 @@ history_delimiting_chars (const char *line) if (parser_state & PST_COMPASSIGN) return (" "); + for (lp = line; *lp && shellblank(*lp); lp++) + ; + /* First, handle some special cases. */ /*(*/ /* If we just read `()', assume it's a function definition, and don't @@ -6061,7 +6081,15 @@ history_delimiting_chars (const char *line) else if (parser_state & PST_CASESTMT) /* case statement pattern */ return " "; else - return "; "; /* (...) subshell */ + { + /* (...) subshell. Make sure this line doesn't start with an + operator that cannot be preceded by a semicolon. If it can't + (basically the command terminators), return a newline. */ + for (i = 0; no_semi_predecessors[i]; i++) + if (*lp == no_semi_predecessors[i]) + return "\n"; + return "; "; + } } else if (token_before_that == WORD && two_tokens_ago == FUNCTION) return " "; /* function def using `function name' without `()' */ diff --git a/tests/read.right b/tests/read.right index 5c658163..277a02b6 100644 --- a/tests/read.right +++ b/tests/read.right @@ -52,11 +52,6 @@ a = abc <$'spring\375'> - -<$'spring\375'> -<$'\277summer'> -<$'\277'> - timeout 1: ok unset or null 1 timeout 2: ok diff --git a/tests/read1.sub b/tests/read1.sub index 916e22cf..d5fb8b85 100644 --- a/tests/read1.sub +++ b/tests/read1.sub @@ -44,14 +44,16 @@ printf '%b\0' winter spring 'summer\0200apple\0200banana\0200cherry' automn | printf '%b\200' winter 'spring\0375' summer automn | while IFS= read -rd $'\200' season; do LC_ALL=C printf "<%q>\n" "$season"; done -: ${TMPDIR:=/tmp} -INFILE=$TMPDIR/read-in-$$ -printf '%b\243' winter 'spring\0375' '\0277summer' '\0277' automn > $INFILE - -LANG=zh_HK.big5hkscs -while IFS= read -rd $'\243' season; do - LC_ALL=C printf "<%q>\n" "$season" -done < $INFILE +# this test is encoding-dependent, and varies from system to system +#: ${TMPDIR:=/tmp} +#INFILE=$TMPDIR/read-in-$$ +#printf '%b\243' winter 'spring\0375' '\0277summer' '\0277' automn > $INFILE +# +#LANG=zh_HK.big5hkscs +#while IFS= read -rd $'\243' season; do +# LC_ALL=C printf "<%q>\n" "$season" +#done < $INFILE +# +#rm -f $INFILE -rm -f $INFILE exit 0