track initial line number of {'', (', and `[[' compound commands; don't have readline check for signals while it's handling a signal; fix for malformed printf time format specifier

This commit is contained in:
Chet Ramey
2024-09-04 17:26:15 -04:00
parent 2610d40b32
commit 5576c26da8
10 changed files with 176 additions and 83 deletions
+53
View File
@@ -10110,3 +10110,56 @@ parse.y
token about to be returned; don't worry about trying to set it after
the fact
Report from Collin Funk <collin.funk1@gmail.com>
8/28
----
parse.y
- CHECK_FOR_RESERVED_WORD: change word_top to keep track of the line
number where a compound command begins; call set_word_top on the
current token about to be returned
- read_token_word: call set_word_top if special_case_tokens returns
something; currently used for `{'
- set_word_top: add `{' to the list of compound commands we track
- grammar: every time we parse a group command or equivalent for the
`for' and `select' compound commands, decrement word_top after we
see the `}'
- parse_dparen: set arith_lineno from word_lineno[word_top] since we
now set it for FOR when we read it
8/29
----
parse.y
- read_token: don't turn on PST_SUBSHELL after reading a left paren
if the last token was a WORD, since that's a function definition,
not a subshell
- read_token,parse_dparen: don't turn on PST_SUBSHELL if we run into
a left paren while parsing a conditional command (PST_CONDCMD is
set in parser_state)
- set_word_top: add `(' to the tokens saved in word_lineno
- grammar: decrement word_top in the subshell_command production
- read_token,parse_dparen: call set_word_top where we add PST_SUBSHELL
to parser_state
8/30
----
parse.y
- set_word_top: add COND_START to the list of tokens that set
word_lineno[word_top]
- grammar: decrement word_top after COND_END
- rename: word_lineno -> compoundcmd_lineno, word_top -> compoundcmd_top,
set_word_top -> set_compoundcmd_top
lib/readline/signals.c
- _rl_release_sigint: don't check for signals if we're already
handling a received signal
Report from Tycho Kirchner <tychokirchner@mail.de> and
Eduardo A. Bustamante López <dualbus@gmail.com>
8/31
----
builtins/printf.def
- printf_builtin: when parsing a time specification format, don't
increment the format pointer to check for 'T' unless it points
to a closing right paren, rejecting the format if it's not a `)'
when the loop breaks
Report from Andrey Kovalev <i.not.student@yandex.ru>
+2 -2
View File
@@ -597,8 +597,8 @@ printf_builtin (WORD_LIST *list)
break;
*t++ = *fmt++;
}
*t = '\0';
if (*++fmt != 'T')
*t = '\0'; /*(*/
if (*fmt != ')' || *++fmt != 'T')
{
builtin_warning (_("`%c': invalid time format specification"), *fmt);
fmt = start;
-1
View File
@@ -643,7 +643,6 @@ Set to a value denoting Readline's current editing mode. A value of
means that vi mode is active.
@end deftypevar
@node Readline Convenience Functions
@section Readline Convenience Functions
+3 -2
View File
@@ -643,7 +643,8 @@ attempts word completion. The default is @samp{off}.
@vindex force-meta-prefix
If set to @samp{on}, Readline modifies its behavior when binding key
sequences containing @kbd{\M-} or @code{Meta-}
(@pxref{Key Bindings}) by converting a key sequence of the form
(see @code{Key Bindings} in @ref{Readline Init File Syntax})
by converting a key sequence of the form
@kbd{\M-}@var{C} or @code{Meta-}@var{C} to the two-character sequence
@kbd{ESC}@var{C} (adding the meta prefix).
If @code{force-meta-prefix} is set to @samp{off} (the default),
@@ -964,7 +965,7 @@ control prefix
@item @kbd{\M-}
adding the meta prefix or converting the following character to a meta
character, as described above under @code{force-meta-prefix}
(@pxref{Variable Settings}).
(see @code{Variable Settings} in @ref{Readline Init File Syntax}).
@item @kbd{\e}
an escape character
@item @kbd{\\}
+2 -1
View File
@@ -671,7 +671,8 @@ _rl_release_sigint (void)
return;
sigint_blocked = 0;
RL_CHECK_SIGNALS ();
if (RL_ISSTATE (RL_STATE_SIGHANDLER) == 0)
RL_CHECK_SIGNALS ();
}
/* Cause SIGWINCH to not be delivered until the corresponding call to
+111 -72
View File
@@ -185,7 +185,7 @@ static void free_string_list (void);
static char *read_a_line (int);
static int set_word_top (int);
static int set_compoundcmd_top (int);
static int reserved_word_acceptable (int);
static int yylex (void);
@@ -341,12 +341,20 @@ static int token_before_that;
/* The token read prior to token_before_that. */
static int two_tokens_ago;
/* The line number in a script where the word in a `case WORD', `select WORD'
or `for WORD' begins. This is a nested command maximum, since the array
index is decremented after a case, select, or for command is parsed. */
/* Someday compoundcmd_lineno will be an array of these structs. */
struct tokeninfo {
int tok;
int lineno;
};
/* The line number in a script where a compound command begins. The
compound commands for which this is tracked are the ones listed in
set_compoundcmd_top().
This is a nested command maximum, since the array index is decremented
after a compound command is parsed. */
#define MAX_COMPOUND_NEST 256
static int word_lineno[MAX_COMPOUND_NEST+1];
static int word_top = -1;
static int compoundcmd_lineno[MAX_COMPOUND_NEST+1];
static int compoundcmd_top = -1;
/* If non-zero, it is the token that we want read_token to return
regardless of what text is (or isn't) present to be read. This
@@ -857,12 +865,12 @@ shell_command: for_command
| WHILE compound_list DO compound_list DONE
{
$$ = make_while_command ($2, $4);
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| UNTIL compound_list DO compound_list DONE
{
$$ = make_until_command ($2, $4);
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| select_command
{ $$ = $1; }
@@ -882,43 +890,47 @@ shell_command: for_command
for_command: FOR WORD newline_list DO compound_list DONE
{
$$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| FOR WORD newline_list '{' compound_list '}'
{
$$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* FOR */
}
| FOR WORD ';' newline_list DO compound_list DONE
{
$$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| FOR WORD ';' newline_list '{' compound_list '}'
{
$$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* FOR */
}
| FOR WORD newline_list IN word_list list_terminator newline_list DO compound_list DONE
{
$$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| FOR WORD newline_list IN word_list list_terminator newline_list '{' compound_list '}'
{
$$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* FOR */
}
| FOR WORD newline_list IN list_terminator newline_list DO compound_list DONE
{
$$ = make_for_command ($2, (WORD_LIST *)NULL, $8, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_for_command ($2, (WORD_LIST *)NULL, $8, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| FOR WORD newline_list IN list_terminator newline_list '{' compound_list '}'
{
$$ = make_for_command ($2, (WORD_LIST *)NULL, $8, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_for_command ($2, (WORD_LIST *)NULL, $8, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* FOR */
}
;
@@ -926,84 +938,90 @@ arith_for_command: FOR ARITH_FOR_EXPRS list_terminator newline_list DO compound_
{
$$ = make_arith_for_command ($2, $6, arith_for_lineno);
if ($$ == 0) YYERROR;
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| FOR ARITH_FOR_EXPRS list_terminator newline_list '{' compound_list '}'
{
$$ = make_arith_for_command ($2, $6, arith_for_lineno);
if ($$ == 0) YYERROR;
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* FOR */
}
| FOR ARITH_FOR_EXPRS DO compound_list DONE
{
$$ = make_arith_for_command ($2, $4, arith_for_lineno);
if ($$ == 0) YYERROR;
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| FOR ARITH_FOR_EXPRS '{' compound_list '}'
{
$$ = make_arith_for_command ($2, $4, arith_for_lineno);
if ($$ == 0) YYERROR;
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* FOR */
}
;
select_command: SELECT WORD newline_list DO compound_list DONE
{
$$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| SELECT WORD newline_list '{' compound_list '}'
{
$$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* SELECT */
}
| SELECT WORD ';' newline_list DO compound_list DONE
{
$$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| SELECT WORD ';' newline_list '{' compound_list '}'
{
$$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* SELECT */
}
| SELECT WORD newline_list IN word_list list_terminator newline_list DO compound_list DONE
{
$$ = make_select_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_select_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| SELECT WORD newline_list IN word_list list_terminator newline_list '{' compound_list '}'
{
$$ = make_select_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_select_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* SELECT */
}
| SELECT WORD newline_list IN list_terminator newline_list DO compound_list DONE
{
$$ = make_select_command ($2, (WORD_LIST *)NULL, $8, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_select_command ($2, (WORD_LIST *)NULL, $8, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| SELECT WORD newline_list IN list_terminator newline_list '{' compound_list '}'
{
$$ = make_select_command ($2, (WORD_LIST *)NULL, $8, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_select_command ($2, (WORD_LIST *)NULL, $8, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
if (compoundcmd_top >= 0) compoundcmd_top--; /* SELECT */
}
;
case_command: CASE WORD newline_list IN newline_list ESAC
{
$$ = make_case_command ($2, (PATTERN_LIST *)NULL, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_case_command ($2, (PATTERN_LIST *)NULL, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| CASE WORD newline_list IN case_clause_sequence newline_list ESAC
{
$$ = make_case_command ($2, $5, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_case_command ($2, $5, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| CASE WORD newline_list IN case_clause ESAC
{
$$ = make_case_command ($2, $5, word_lineno[word_top]);
if (word_top >= 0) word_top--;
$$ = make_case_command ($2, $5, compoundcmd_lineno[compoundcmd_top]);
if (compoundcmd_top >= 0) compoundcmd_top--;
}
;
@@ -1054,6 +1072,7 @@ subshell: '(' compound_list ')'
{
$$ = make_subshell_command ($2);
$$->flags |= CMD_WANT_SUBSHELL;
if (compoundcmd_top >= 0) compoundcmd_top--; /* RPAREN */
}
;
@@ -1133,23 +1152,26 @@ coproc: COPROC shell_command
if_command: IF compound_list THEN compound_list FI
{
$$ = make_if_command ($2, $4, (COMMAND *)NULL);
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| IF compound_list THEN compound_list ELSE compound_list FI
{
$$ = make_if_command ($2, $4, $6);
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--;
}
| IF compound_list THEN compound_list elif_clause FI
{
$$ = make_if_command ($2, $4, $5);
if (word_top >= 0) word_top--;
if (compoundcmd_top >= 0) compoundcmd_top--;
}
;
group_command: '{' compound_list '}'
{ $$ = make_group_command ($2); }
{
$$ = make_group_command ($2);
if (compoundcmd_top >= 0) compoundcmd_top--; /* RBRACE */
}
;
arith_command: ARITH_CMD
@@ -1157,7 +1179,10 @@ arith_command: ARITH_CMD
;
cond_command: COND_START COND_CMD COND_END
{ $$ = $2; }
{
$$ = $2;
if (compoundcmd_top >= 0) compoundcmd_top--; /* COND_END */
}
;
elif_clause: ELIF compound_list THEN compound_list
@@ -3137,7 +3162,7 @@ static int open_brace_count;
else if (word_token_alist[i].token == '}' && open_brace_count) \
open_brace_count--; \
\
set_word_top (word_token_alist[i].token); \
set_compoundcmd_top (word_token_alist[i].token); \
\
if (posixly_correct) \
parser_state &= ~PST_ALEXPNEXT; \
@@ -3711,11 +3736,16 @@ read_token (int command)
}
/* case pattern lists may be preceded by an optional left paren. If
we're not trying to parse a case pattern list, the left paren
indicates a subshell. */
/* XXX - check for last_read_token != WORD before setting PST_SUBSHELL? */
if MBTEST(character == '(' && (parser_state & PST_CASEPAT) == 0) /* ) */
parser_state |= PST_SUBSHELL;
we're not trying to parse a case pattern list or a conditional
command (which may use parens for grouping), and the last token
wasn't a WORD (indicating a function definition), the left paren
introduces a subshell. */
if MBTEST(character == '(' && (parser_state & (PST_CASEPAT|PST_CONDCMD)) == 0 && /* ) */
last_read_token != WORD)
{
set_compoundcmd_top (character);
parser_state |= PST_SUBSHELL;
}
/*(*/
else if MBTEST((parser_state & PST_CASEPAT) && character == ')')
parser_state &= ~PST_CASEPAT;
@@ -4840,7 +4870,7 @@ parse_dparen (int c)
#if defined (ARITH_FOR_COMMAND)
if (last_read_token == FOR)
{
arith_for_lineno = line_number;
arith_for_lineno = compoundcmd_top[compoundcmd_lineno];
cmdtyp = parse_arith_cmd (&wval, 0);
if (cmdtyp == 1)
{
@@ -4872,8 +4902,11 @@ parse_dparen (int c)
{
push_string (wval, 0, (alias_t *)NULL);
pushed_string_list->flags = PSH_DPAREN;
if ((parser_state & PST_CASEPAT) == 0)
parser_state |= PST_SUBSHELL;
if ((parser_state & (PST_CASEPAT|PST_CONDCMD)) == 0)
{
set_compoundcmd_top (c);
parser_state |= PST_SUBSHELL;
}
return (c);
}
else /* ERROR */
@@ -5667,7 +5700,10 @@ got_token:
/* Check for special case tokens. */
result = (last_shell_getc_is_singlebyte) ? special_case_tokens (token) : -1;
if (result >= 0)
return result; /* don't need to set word_top in any of these cases */
{
set_compoundcmd_top (result); /* need to set compoundcmd_top if it returns `{' */ /*}*/
return result;
}
#if defined (ALIAS)
/* Posix.2 does not allow reserved words to be aliased, so check for all
@@ -5791,7 +5827,7 @@ got_token:
}
static inline int
set_word_top (int t)
set_compoundcmd_top (int t)
{
switch (t)
{
@@ -5801,14 +5837,17 @@ set_word_top (int t)
case IF:
case WHILE:
case UNTIL:
if (word_top < MAX_COMPOUND_NEST)
word_top++;
word_lineno[word_top] = line_number;
case '{': /*}*/
case '(': /*)*/
case COND_START:
if (compoundcmd_top < MAX_COMPOUND_NEST)
compoundcmd_top++;
compoundcmd_lineno[compoundcmd_top] = line_number;
break;
default:
break;
}
return word_top;
return compoundcmd_top;
}
/* Return 1 if TOKSYM is a token that after being read would allow
@@ -6782,8 +6821,8 @@ report_syntax_error (const char *message)
{
if (EOF_Reached && shell_eof_token && current_token != shell_eof_token)
parser_error (line_number, _("unexpected EOF while looking for matching `%c'"), shell_eof_token);
else if (EOF_Reached && word_top >= 0)
parser_error (line_number, _("syntax error: unexpected end of file from command on line %d"), word_lineno[word_top]);
else if (EOF_Reached && compoundcmd_top >= 0)
parser_error (line_number, _("syntax error: unexpected end of file from command on line %d"), compoundcmd_lineno[compoundcmd_top]);
else
{
msg = EOF_Reached ? _("syntax error: unexpected end of file") : _("syntax error");
+2 -2
View File
@@ -154,9 +154,9 @@ ok 6
ok 7
ok 8
bash: -c: line 1: unexpected token `EOF', expected `)'
bash: -c: line 2: syntax error: unexpected end of file
bash: -c: line 2: syntax error: unexpected end of file from command on line 1
bash: -c: line 1: unexpected EOF while looking for `]]'
bash: -c: line 2: syntax error: unexpected end of file
bash: -c: line 2: syntax error: unexpected end of file from command on line 1
bash: -c: line 1: syntax error in conditional expression: unexpected token `]'
bash: -c: line 1: syntax error near `]'
bash: -c: line 1: `[[ ( -t X ) ]'
+1 -1
View File
@@ -240,7 +240,7 @@ after non-special builtin: 1
./errors7.sub: line 27: x: readonly variable
./errors7.sub: line 29: x: readonly variable
./errors7.sub: line 32: v: readonly variable
./errors8.sub: eval: line 7: syntax error: unexpected end of file
./errors8.sub: eval: line 7: syntax error: unexpected end of file from command on line 6
ok 1
./errors8.sub: line 8: v: readonly variable
ok 2
+1 -1
View File
@@ -1,7 +1,7 @@
exportfunc ok 1
exportfunc ok 2
./exportfunc.tests: line 37: cve7169-bad: No such file or directory
./exportfunc.tests: eval: line 44: syntax error: unexpected end of file
./exportfunc.tests: eval: line 44: syntax error: unexpected end of file from command on line 42
./exportfunc.tests: line 43: cve7169-bad2: No such file or directory
./exportfunc1.sub: line 14: maximum here-document count exceeded
./exportfunc.tests: line 72: HELLO_WORLD: No such file or directory
+1 -1
View File
@@ -95,7 +95,7 @@ hello
\END
end hello<NL>\END
./heredoc3.sub: line 98: warning: here-document at line 96 delimited by end-of-file (wanted `EOF')
./heredoc3.sub: line 99: syntax error: unexpected end of file
./heredoc3.sub: line 99: syntax error: unexpected end of file from command on line 96
heredoc1
EOF
Ok:0