From dc9c10a7b29c93a36ff17c562cb14e11eb169f19 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 16 Nov 2020 13:00:44 +0100 Subject: [PATCH] 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 --- shell/hush.c | 86 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index ab7263381..f1a7e07ee 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -916,6 +916,9 @@ struct globals { char opt_c; #if ENABLE_HUSH_INTERACTIVE smallint promptmode; /* 0: PS1, 1: PS2 */ +# if ENABLE_FEATURE_EDITING + smallint flag_ctrlC; /* when set, suppresses syntax error messages */ +# endif #endif smallint flag_SIGINT; #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) { - 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 // die_if_script(); } @@ -2629,26 +2635,27 @@ static int get_user_input(struct in_str *i) # if ENABLE_FEATURE_EDITING for (;;) { reinit_unicode_for_hush(); - if (G.flag_SIGINT) { - /* There was ^C'ed, make it look prettier: */ - bb_putchar('\n'); - G.flag_SIGINT = 0; - } + G.flag_SIGINT = 0; /* buglet: SIGINT will not make new prompt to appear _at once_, * only after . (^C works immediately) */ r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 ); /* read_line_input intercepts ^C, "convert" it to SIGINT */ - if (r == 0) + if (r == 0) { + G.flag_ctrlC = 1; raise(SIGINT); + } check_and_run_traps(); if (r != 0 && !G.flag_SIGINT) break; - /* ^C or SIGINT: repeat */ + /* ^C or SIGINT: return EOF */ /* 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; + i->p = NULL; + i->peek_buf[0] = r = EOF; + return r; } if (r < 0) { /* 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)); if (ch == EOF) { struct pipe *pi; - +#if ENABLE_FEATURE_EDITING + if (G.flag_ctrlC) { + /* testcase: interactively entering + * 'qwe ^C + * should not leave input in PS2 mode, waiting to close single quote. + */ + G.flag_ctrlC = 0; + goto parse_error; + } +#endif if (heredoc_cnt) { syntax_error_unterm_str("here document"); - goto parse_error; + goto parse_error_exitcode1; } if (end_trigger == ')') { syntax_error_unterm_ch('('); - goto parse_error; + goto parse_error_exitcode1; } if (end_trigger == '}') { syntax_error_unterm_ch('{'); - goto parse_error; + goto parse_error_exitcode1; } if (done_word(&ctx)) { - goto parse_error; + goto parse_error_exitcode1; } o_free_and_set_NULL(&ctx.word); done_pipe(&ctx, PIPE_SEQ); @@ -5345,7 +5361,7 @@ static struct pipe *parse_stream(char **pstring, while (1) { if (ch == EOF) { syntax_error_unterm_ch('\''); - goto parse_error; + goto parse_error_exitcode1; } nommu_addchr(&ctx.as_string, ch); if (ch == '\'') @@ -5424,7 +5440,7 @@ static struct pipe *parse_stream(char **pstring, /* ch == last eaten whitespace char */ #endif if (done_word(&ctx)) { - goto parse_error; + goto parse_error_exitcode1; } if (ch == '\n') { /* Is this a case when newline is simply ignored? @@ -5467,7 +5483,7 @@ static struct pipe *parse_stream(char **pstring, if (heredoc_cnt) { heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); if (heredoc_cnt != 0) - goto parse_error; + goto parse_error_exitcode1; } ctx.is_assignment = MAYBE_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 ) { if (done_word(&ctx)) { - goto parse_error; + goto parse_error_exitcode1; } done_pipe(&ctx, PIPE_SEQ); ctx.is_assignment = MAYBE_ASSIGNMENT; @@ -5538,7 +5554,7 @@ static struct pipe *parse_stream(char **pstring, /* Example: bare "{ }", "()" */ G.last_exitcode = 2; /* bash compat */ syntax_error_unexpected_ch(ch); - goto parse_error2; + goto parse_error; } if (heredoc_cnt_ptr) *heredoc_cnt_ptr = heredoc_cnt; @@ -5560,7 +5576,7 @@ static struct pipe *parse_stream(char **pstring, case '>': redir_fd = redirect_opt_num(&ctx.word); if (done_word(&ctx)) { - goto parse_error; + goto parse_error_exitcode1; } redir_style = REDIRECT_OVERWRITE; if (next == '>') { @@ -5571,16 +5587,16 @@ static struct pipe *parse_stream(char **pstring, #if 0 else if (next == '(') { syntax_error(">(process) not supported"); - goto parse_error; + goto parse_error_exitcode1; } #endif if (parse_redirect(&ctx, redir_fd, redir_style, input)) - goto parse_error; + goto parse_error_exitcode1; continue; /* get next char */ case '<': redir_fd = redirect_opt_num(&ctx.word); if (done_word(&ctx)) { - goto parse_error; + goto parse_error_exitcode1; } redir_style = REDIRECT_INPUT; if (next == '<') { @@ -5597,11 +5613,11 @@ static struct pipe *parse_stream(char **pstring, #if 0 else if (next == '(') { syntax_error("<(process) not supported"); - goto parse_error; + goto parse_error_exitcode1; } #endif if (parse_redirect(&ctx, redir_fd, redir_style, input)) - goto parse_error; + goto parse_error_exitcode1; continue; /* get next char */ case '#': 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)) { debug_printf_parse("parse_stream parse error: " "parse_dollar returned 0 (error)\n"); - goto parse_error; + goto parse_error_exitcode1; } continue; /* get next char */ case '"': @@ -5671,7 +5687,7 @@ static struct pipe *parse_stream(char **pstring, if (ctx.is_assignment == NOT_ASSIGNMENT) ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; 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; continue; /* get next char */ #if ENABLE_HUSH_TICK @@ -5682,7 +5698,7 @@ static struct pipe *parse_stream(char **pstring, o_addchr(&ctx.word, '`'); USE_FOR_NOMMU(pos = ctx.word.length;) if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0)) - goto parse_error; + goto parse_error_exitcode1; # if !BB_MMU o_addstr(&ctx.as_string, ctx.word.data + pos); o_addchr(&ctx.as_string, '`'); @@ -5697,7 +5713,7 @@ static struct pipe *parse_stream(char **pstring, case_semi: #endif if (done_word(&ctx)) { - goto parse_error; + goto parse_error_exitcode1; } done_pipe(&ctx, PIPE_SEQ); #if ENABLE_HUSH_CASE @@ -5724,7 +5740,7 @@ static struct pipe *parse_stream(char **pstring, continue; /* get next char */ case '&': if (done_word(&ctx)) { - goto parse_error; + goto parse_error_exitcode1; } if (next == '&') { ch = i_getch(input); @@ -5736,7 +5752,7 @@ static struct pipe *parse_stream(char **pstring, goto new_cmd; case '|': if (done_word(&ctx)) { - goto parse_error; + goto parse_error_exitcode1; } #if ENABLE_HUSH_CASE if (ctx.ctx_res_w == RES_MATCH) @@ -5768,7 +5784,7 @@ static struct pipe *parse_stream(char **pstring, case '{': { int n = parse_group(&ctx, input, ch); if (n < 0) { - goto parse_error; + goto parse_error_exitcode1; } debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); heredoc_cnt += n; @@ -5786,16 +5802,16 @@ static struct pipe *parse_stream(char **pstring, * and it will match } earlier (not here). */ G.last_exitcode = 2; syntax_error_unexpected_ch(ch); - goto parse_error2; + goto parse_error; default: if (HUSH_DEBUG) bb_error_msg_and_die("BUG: unexpected %c", ch); } } /* while (1) */ - parse_error: + parse_error_exitcode1: G.last_exitcode = 1; - parse_error2: + parse_error: { struct parse_context *pctx; IF_HAS_KEYWORDS(struct parse_context *p2;)