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

This commit is contained in:
Chet Ramey
2025-04-30 16:19:08 -04:00
parent 7731dc5c4d
commit 15df599354
4 changed files with 79 additions and 19 deletions
+35
View File
@@ -11151,3 +11151,38 @@ builtins/read.def
only push the delimiter back if we read that character ourselves
(i > 1).
Report from Greg Wooledge <greg@wooledge.org>
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 <grishalevit@gmail.com> based on a report
from Александр Ушаков <aushakov@astralinux.ru>
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
+33 -5
View File
@@ -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 `()' */
-5
View File
@@ -52,11 +52,6 @@ a = abc
<$'spring\375'>
<summer>
<automn>
<winter>
<$'spring\375'>
<$'\277summer'>
<$'\277'>
<automn>
timeout 1: ok
unset or null 1
timeout 2: ok
+11 -9
View File
@@ -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
# 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
LANG=zh_HK.big5hkscs
while IFS= read -rd $'\243' season; do
LC_ALL=C printf "<%q>\n" "$season"
done < $INFILE
rm -f $INFILE
exit 0