hush: make interactive ^C break out of PS2 mode
function old new delta syntax_error_unterm_str - 26 +26 parse_stream 2238 2251 +13 fgetc_interactive 243 249 +6 parse_dollar 824 817 -7 syntax_error_unterm_ch 29 21 -8 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 2/2 up/down: 45/-15) Total: 30 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
efe99b59c6
commit
dc9c10a7b2
86
shell/hush.c
86
shell/hush.c
@ -916,6 +916,9 @@ struct globals {
|
|||||||
char opt_c;
|
char opt_c;
|
||||||
#if ENABLE_HUSH_INTERACTIVE
|
#if ENABLE_HUSH_INTERACTIVE
|
||||||
smallint promptmode; /* 0: PS1, 1: PS2 */
|
smallint promptmode; /* 0: PS1, 1: PS2 */
|
||||||
|
# if ENABLE_FEATURE_EDITING
|
||||||
|
smallint flag_ctrlC; /* when set, suppresses syntax error messages */
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
smallint flag_SIGINT;
|
smallint flag_SIGINT;
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
@ -1425,7 +1428,10 @@ static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
|
|||||||
|
|
||||||
static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
|
static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
|
||||||
{
|
{
|
||||||
bb_error_msg("syntax error: unterminated %s", s);
|
#if ENABLE_FEATURE_EDITING
|
||||||
|
if (!G.flag_ctrlC)
|
||||||
|
#endif
|
||||||
|
bb_error_msg("syntax error: unterminated %s", s);
|
||||||
//? source4.tests fails: in bash, echo ${^} in script does not terminate the script
|
//? source4.tests fails: in bash, echo ${^} in script does not terminate the script
|
||||||
// die_if_script();
|
// die_if_script();
|
||||||
}
|
}
|
||||||
@ -2629,26 +2635,27 @@ static int get_user_input(struct in_str *i)
|
|||||||
# if ENABLE_FEATURE_EDITING
|
# if ENABLE_FEATURE_EDITING
|
||||||
for (;;) {
|
for (;;) {
|
||||||
reinit_unicode_for_hush();
|
reinit_unicode_for_hush();
|
||||||
if (G.flag_SIGINT) {
|
G.flag_SIGINT = 0;
|
||||||
/* There was ^C'ed, make it look prettier: */
|
|
||||||
bb_putchar('\n');
|
|
||||||
G.flag_SIGINT = 0;
|
|
||||||
}
|
|
||||||
/* buglet: SIGINT will not make new prompt to appear _at once_,
|
/* buglet: SIGINT will not make new prompt to appear _at once_,
|
||||||
* only after <Enter>. (^C works immediately) */
|
* only after <Enter>. (^C works immediately) */
|
||||||
r = read_line_input(G.line_input_state, prompt_str,
|
r = read_line_input(G.line_input_state, prompt_str,
|
||||||
G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
|
G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
|
||||||
);
|
);
|
||||||
/* read_line_input intercepts ^C, "convert" it to SIGINT */
|
/* read_line_input intercepts ^C, "convert" it to SIGINT */
|
||||||
if (r == 0)
|
if (r == 0) {
|
||||||
|
G.flag_ctrlC = 1;
|
||||||
raise(SIGINT);
|
raise(SIGINT);
|
||||||
|
}
|
||||||
check_and_run_traps();
|
check_and_run_traps();
|
||||||
if (r != 0 && !G.flag_SIGINT)
|
if (r != 0 && !G.flag_SIGINT)
|
||||||
break;
|
break;
|
||||||
/* ^C or SIGINT: repeat */
|
/* ^C or SIGINT: return EOF */
|
||||||
/* bash prints ^C even on real SIGINT (non-kbd generated) */
|
/* bash prints ^C even on real SIGINT (non-kbd generated) */
|
||||||
write(STDOUT_FILENO, "^C", 2);
|
write(STDOUT_FILENO, "^C\n", 3);
|
||||||
G.last_exitcode = 128 + SIGINT;
|
G.last_exitcode = 128 + SIGINT;
|
||||||
|
i->p = NULL;
|
||||||
|
i->peek_buf[0] = r = EOF;
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
/* EOF/error detected */
|
/* EOF/error detected */
|
||||||
@ -5260,22 +5267,31 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
|
ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
struct pipe *pi;
|
struct pipe *pi;
|
||||||
|
#if ENABLE_FEATURE_EDITING
|
||||||
|
if (G.flag_ctrlC) {
|
||||||
|
/* testcase: interactively entering
|
||||||
|
* 'qwe <cr> ^C
|
||||||
|
* should not leave input in PS2 mode, waiting to close single quote.
|
||||||
|
*/
|
||||||
|
G.flag_ctrlC = 0;
|
||||||
|
goto parse_error;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (heredoc_cnt) {
|
if (heredoc_cnt) {
|
||||||
syntax_error_unterm_str("here document");
|
syntax_error_unterm_str("here document");
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
if (end_trigger == ')') {
|
if (end_trigger == ')') {
|
||||||
syntax_error_unterm_ch('(');
|
syntax_error_unterm_ch('(');
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
if (end_trigger == '}') {
|
if (end_trigger == '}') {
|
||||||
syntax_error_unterm_ch('{');
|
syntax_error_unterm_ch('{');
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (done_word(&ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
o_free_and_set_NULL(&ctx.word);
|
o_free_and_set_NULL(&ctx.word);
|
||||||
done_pipe(&ctx, PIPE_SEQ);
|
done_pipe(&ctx, PIPE_SEQ);
|
||||||
@ -5345,7 +5361,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
while (1) {
|
while (1) {
|
||||||
if (ch == EOF) {
|
if (ch == EOF) {
|
||||||
syntax_error_unterm_ch('\'');
|
syntax_error_unterm_ch('\'');
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
nommu_addchr(&ctx.as_string, ch);
|
nommu_addchr(&ctx.as_string, ch);
|
||||||
if (ch == '\'')
|
if (ch == '\'')
|
||||||
@ -5424,7 +5440,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
/* ch == last eaten whitespace char */
|
/* ch == last eaten whitespace char */
|
||||||
#endif
|
#endif
|
||||||
if (done_word(&ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
if (ch == '\n') {
|
if (ch == '\n') {
|
||||||
/* Is this a case when newline is simply ignored?
|
/* Is this a case when newline is simply ignored?
|
||||||
@ -5467,7 +5483,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
if (heredoc_cnt) {
|
if (heredoc_cnt) {
|
||||||
heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
|
heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
|
||||||
if (heredoc_cnt != 0)
|
if (heredoc_cnt != 0)
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
ctx.is_assignment = MAYBE_ASSIGNMENT;
|
ctx.is_assignment = MAYBE_ASSIGNMENT;
|
||||||
debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
|
debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
|
||||||
@ -5517,7 +5533,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
if (done_word(&ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
done_pipe(&ctx, PIPE_SEQ);
|
done_pipe(&ctx, PIPE_SEQ);
|
||||||
ctx.is_assignment = MAYBE_ASSIGNMENT;
|
ctx.is_assignment = MAYBE_ASSIGNMENT;
|
||||||
@ -5538,7 +5554,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
/* Example: bare "{ }", "()" */
|
/* Example: bare "{ }", "()" */
|
||||||
G.last_exitcode = 2; /* bash compat */
|
G.last_exitcode = 2; /* bash compat */
|
||||||
syntax_error_unexpected_ch(ch);
|
syntax_error_unexpected_ch(ch);
|
||||||
goto parse_error2;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
if (heredoc_cnt_ptr)
|
if (heredoc_cnt_ptr)
|
||||||
*heredoc_cnt_ptr = heredoc_cnt;
|
*heredoc_cnt_ptr = heredoc_cnt;
|
||||||
@ -5560,7 +5576,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
case '>':
|
case '>':
|
||||||
redir_fd = redirect_opt_num(&ctx.word);
|
redir_fd = redirect_opt_num(&ctx.word);
|
||||||
if (done_word(&ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
redir_style = REDIRECT_OVERWRITE;
|
redir_style = REDIRECT_OVERWRITE;
|
||||||
if (next == '>') {
|
if (next == '>') {
|
||||||
@ -5571,16 +5587,16 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
#if 0
|
#if 0
|
||||||
else if (next == '(') {
|
else if (next == '(') {
|
||||||
syntax_error(">(process) not supported");
|
syntax_error(">(process) not supported");
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (parse_redirect(&ctx, redir_fd, redir_style, input))
|
if (parse_redirect(&ctx, redir_fd, redir_style, input))
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
continue; /* get next char */
|
continue; /* get next char */
|
||||||
case '<':
|
case '<':
|
||||||
redir_fd = redirect_opt_num(&ctx.word);
|
redir_fd = redirect_opt_num(&ctx.word);
|
||||||
if (done_word(&ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
redir_style = REDIRECT_INPUT;
|
redir_style = REDIRECT_INPUT;
|
||||||
if (next == '<') {
|
if (next == '<') {
|
||||||
@ -5597,11 +5613,11 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
#if 0
|
#if 0
|
||||||
else if (next == '(') {
|
else if (next == '(') {
|
||||||
syntax_error("<(process) not supported");
|
syntax_error("<(process) not supported");
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (parse_redirect(&ctx, redir_fd, redir_style, input))
|
if (parse_redirect(&ctx, redir_fd, redir_style, input))
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
continue; /* get next char */
|
continue; /* get next char */
|
||||||
case '#':
|
case '#':
|
||||||
if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
|
if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
|
||||||
@ -5655,7 +5671,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
|
if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
|
||||||
debug_printf_parse("parse_stream parse error: "
|
debug_printf_parse("parse_stream parse error: "
|
||||||
"parse_dollar returned 0 (error)\n");
|
"parse_dollar returned 0 (error)\n");
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
continue; /* get next char */
|
continue; /* get next char */
|
||||||
case '"':
|
case '"':
|
||||||
@ -5671,7 +5687,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
if (ctx.is_assignment == NOT_ASSIGNMENT)
|
if (ctx.is_assignment == NOT_ASSIGNMENT)
|
||||||
ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
|
ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
|
||||||
if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
|
if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
|
ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
|
||||||
continue; /* get next char */
|
continue; /* get next char */
|
||||||
#if ENABLE_HUSH_TICK
|
#if ENABLE_HUSH_TICK
|
||||||
@ -5682,7 +5698,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
o_addchr(&ctx.word, '`');
|
o_addchr(&ctx.word, '`');
|
||||||
USE_FOR_NOMMU(pos = ctx.word.length;)
|
USE_FOR_NOMMU(pos = ctx.word.length;)
|
||||||
if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
|
if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
# if !BB_MMU
|
# if !BB_MMU
|
||||||
o_addstr(&ctx.as_string, ctx.word.data + pos);
|
o_addstr(&ctx.as_string, ctx.word.data + pos);
|
||||||
o_addchr(&ctx.as_string, '`');
|
o_addchr(&ctx.as_string, '`');
|
||||||
@ -5697,7 +5713,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
case_semi:
|
case_semi:
|
||||||
#endif
|
#endif
|
||||||
if (done_word(&ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
done_pipe(&ctx, PIPE_SEQ);
|
done_pipe(&ctx, PIPE_SEQ);
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
@ -5724,7 +5740,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
continue; /* get next char */
|
continue; /* get next char */
|
||||||
case '&':
|
case '&':
|
||||||
if (done_word(&ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
if (next == '&') {
|
if (next == '&') {
|
||||||
ch = i_getch(input);
|
ch = i_getch(input);
|
||||||
@ -5736,7 +5752,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
goto new_cmd;
|
goto new_cmd;
|
||||||
case '|':
|
case '|':
|
||||||
if (done_word(&ctx)) {
|
if (done_word(&ctx)) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
#if ENABLE_HUSH_CASE
|
#if ENABLE_HUSH_CASE
|
||||||
if (ctx.ctx_res_w == RES_MATCH)
|
if (ctx.ctx_res_w == RES_MATCH)
|
||||||
@ -5768,7 +5784,7 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
case '{': {
|
case '{': {
|
||||||
int n = parse_group(&ctx, input, ch);
|
int n = parse_group(&ctx, input, ch);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
goto parse_error;
|
goto parse_error_exitcode1;
|
||||||
}
|
}
|
||||||
debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
|
debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
|
||||||
heredoc_cnt += n;
|
heredoc_cnt += n;
|
||||||
@ -5786,16 +5802,16 @@ static struct pipe *parse_stream(char **pstring,
|
|||||||
* and it will match } earlier (not here). */
|
* and it will match } earlier (not here). */
|
||||||
G.last_exitcode = 2;
|
G.last_exitcode = 2;
|
||||||
syntax_error_unexpected_ch(ch);
|
syntax_error_unexpected_ch(ch);
|
||||||
goto parse_error2;
|
goto parse_error;
|
||||||
default:
|
default:
|
||||||
if (HUSH_DEBUG)
|
if (HUSH_DEBUG)
|
||||||
bb_error_msg_and_die("BUG: unexpected %c", ch);
|
bb_error_msg_and_die("BUG: unexpected %c", ch);
|
||||||
}
|
}
|
||||||
} /* while (1) */
|
} /* while (1) */
|
||||||
|
|
||||||
parse_error:
|
parse_error_exitcode1:
|
||||||
G.last_exitcode = 1;
|
G.last_exitcode = 1;
|
||||||
parse_error2:
|
parse_error:
|
||||||
{
|
{
|
||||||
struct parse_context *pctx;
|
struct parse_context *pctx;
|
||||||
IF_HAS_KEYWORDS(struct parse_context *p2;)
|
IF_HAS_KEYWORDS(struct parse_context *p2;)
|
||||||
|
Loading…
Reference in New Issue
Block a user