diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2020-11-16 13:00:44 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-11-16 13:00:44 +0100 |
| commit | dc9c10a7b29c93a36ff17c562cb14e11eb169f19 (patch) | |
| tree | e3ec11dd1d0d0bf0fa37df32d26a7c583052b59d | |
| parent | efe99b59c6cd3d9de91ebfc39f9870b8708e5b92 (diff) | |
| download | busybox-w32-dc9c10a7b29c93a36ff17c562cb14e11eb169f19.tar.gz busybox-w32-dc9c10a7b29c93a36ff17c562cb14e11eb169f19.tar.bz2 busybox-w32-dc9c10a7b29c93a36ff17c562cb14e11eb169f19.zip | |
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>
| -rw-r--r-- | shell/hush.c | 86 |
1 files 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 { | |||
| 916 | char opt_c; | 916 | char opt_c; |
| 917 | #if ENABLE_HUSH_INTERACTIVE | 917 | #if ENABLE_HUSH_INTERACTIVE |
| 918 | smallint promptmode; /* 0: PS1, 1: PS2 */ | 918 | smallint promptmode; /* 0: PS1, 1: PS2 */ |
| 919 | # if ENABLE_FEATURE_EDITING | ||
| 920 | smallint flag_ctrlC; /* when set, suppresses syntax error messages */ | ||
| 921 | # endif | ||
| 919 | #endif | 922 | #endif |
| 920 | smallint flag_SIGINT; | 923 | smallint flag_SIGINT; |
| 921 | #if ENABLE_HUSH_LOOPS | 924 | #if ENABLE_HUSH_LOOPS |
| @@ -1425,7 +1428,10 @@ static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg) | |||
| 1425 | 1428 | ||
| 1426 | static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) | 1429 | static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) |
| 1427 | { | 1430 | { |
| 1428 | bb_error_msg("syntax error: unterminated %s", s); | 1431 | #if ENABLE_FEATURE_EDITING |
| 1432 | if (!G.flag_ctrlC) | ||
| 1433 | #endif | ||
| 1434 | bb_error_msg("syntax error: unterminated %s", s); | ||
| 1429 | //? source4.tests fails: in bash, echo ${^} in script does not terminate the script | 1435 | //? source4.tests fails: in bash, echo ${^} in script does not terminate the script |
| 1430 | // die_if_script(); | 1436 | // die_if_script(); |
| 1431 | } | 1437 | } |
| @@ -2629,26 +2635,27 @@ static int get_user_input(struct in_str *i) | |||
| 2629 | # if ENABLE_FEATURE_EDITING | 2635 | # if ENABLE_FEATURE_EDITING |
| 2630 | for (;;) { | 2636 | for (;;) { |
| 2631 | reinit_unicode_for_hush(); | 2637 | reinit_unicode_for_hush(); |
| 2632 | if (G.flag_SIGINT) { | 2638 | G.flag_SIGINT = 0; |
| 2633 | /* There was ^C'ed, make it look prettier: */ | ||
| 2634 | bb_putchar('\n'); | ||
| 2635 | G.flag_SIGINT = 0; | ||
| 2636 | } | ||
| 2637 | /* buglet: SIGINT will not make new prompt to appear _at once_, | 2639 | /* buglet: SIGINT will not make new prompt to appear _at once_, |
| 2638 | * only after <Enter>. (^C works immediately) */ | 2640 | * only after <Enter>. (^C works immediately) */ |
| 2639 | r = read_line_input(G.line_input_state, prompt_str, | 2641 | r = read_line_input(G.line_input_state, prompt_str, |
| 2640 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 | 2642 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 |
| 2641 | ); | 2643 | ); |
| 2642 | /* read_line_input intercepts ^C, "convert" it to SIGINT */ | 2644 | /* read_line_input intercepts ^C, "convert" it to SIGINT */ |
| 2643 | if (r == 0) | 2645 | if (r == 0) { |
| 2646 | G.flag_ctrlC = 1; | ||
| 2644 | raise(SIGINT); | 2647 | raise(SIGINT); |
| 2648 | } | ||
| 2645 | check_and_run_traps(); | 2649 | check_and_run_traps(); |
| 2646 | if (r != 0 && !G.flag_SIGINT) | 2650 | if (r != 0 && !G.flag_SIGINT) |
| 2647 | break; | 2651 | break; |
| 2648 | /* ^C or SIGINT: repeat */ | 2652 | /* ^C or SIGINT: return EOF */ |
| 2649 | /* bash prints ^C even on real SIGINT (non-kbd generated) */ | 2653 | /* bash prints ^C even on real SIGINT (non-kbd generated) */ |
| 2650 | write(STDOUT_FILENO, "^C", 2); | 2654 | write(STDOUT_FILENO, "^C\n", 3); |
| 2651 | G.last_exitcode = 128 + SIGINT; | 2655 | G.last_exitcode = 128 + SIGINT; |
| 2656 | i->p = NULL; | ||
| 2657 | i->peek_buf[0] = r = EOF; | ||
| 2658 | return r; | ||
| 2652 | } | 2659 | } |
| 2653 | if (r < 0) { | 2660 | if (r < 0) { |
| 2654 | /* EOF/error detected */ | 2661 | /* EOF/error detected */ |
| @@ -5260,22 +5267,31 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5260 | ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); | 5267 | ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS)); |
| 5261 | if (ch == EOF) { | 5268 | if (ch == EOF) { |
| 5262 | struct pipe *pi; | 5269 | struct pipe *pi; |
| 5263 | 5270 | #if ENABLE_FEATURE_EDITING | |
| 5271 | if (G.flag_ctrlC) { | ||
| 5272 | /* testcase: interactively entering | ||
| 5273 | * 'qwe <cr> ^C | ||
| 5274 | * should not leave input in PS2 mode, waiting to close single quote. | ||
| 5275 | */ | ||
| 5276 | G.flag_ctrlC = 0; | ||
| 5277 | goto parse_error; | ||
| 5278 | } | ||
| 5279 | #endif | ||
| 5264 | if (heredoc_cnt) { | 5280 | if (heredoc_cnt) { |
| 5265 | syntax_error_unterm_str("here document"); | 5281 | syntax_error_unterm_str("here document"); |
| 5266 | goto parse_error; | 5282 | goto parse_error_exitcode1; |
| 5267 | } | 5283 | } |
| 5268 | if (end_trigger == ')') { | 5284 | if (end_trigger == ')') { |
| 5269 | syntax_error_unterm_ch('('); | 5285 | syntax_error_unterm_ch('('); |
| 5270 | goto parse_error; | 5286 | goto parse_error_exitcode1; |
| 5271 | } | 5287 | } |
| 5272 | if (end_trigger == '}') { | 5288 | if (end_trigger == '}') { |
| 5273 | syntax_error_unterm_ch('{'); | 5289 | syntax_error_unterm_ch('{'); |
| 5274 | goto parse_error; | 5290 | goto parse_error_exitcode1; |
| 5275 | } | 5291 | } |
| 5276 | 5292 | ||
| 5277 | if (done_word(&ctx)) { | 5293 | if (done_word(&ctx)) { |
| 5278 | goto parse_error; | 5294 | goto parse_error_exitcode1; |
| 5279 | } | 5295 | } |
| 5280 | o_free_and_set_NULL(&ctx.word); | 5296 | o_free_and_set_NULL(&ctx.word); |
| 5281 | done_pipe(&ctx, PIPE_SEQ); | 5297 | done_pipe(&ctx, PIPE_SEQ); |
| @@ -5345,7 +5361,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5345 | while (1) { | 5361 | while (1) { |
| 5346 | if (ch == EOF) { | 5362 | if (ch == EOF) { |
| 5347 | syntax_error_unterm_ch('\''); | 5363 | syntax_error_unterm_ch('\''); |
| 5348 | goto parse_error; | 5364 | goto parse_error_exitcode1; |
| 5349 | } | 5365 | } |
| 5350 | nommu_addchr(&ctx.as_string, ch); | 5366 | nommu_addchr(&ctx.as_string, ch); |
| 5351 | if (ch == '\'') | 5367 | if (ch == '\'') |
| @@ -5424,7 +5440,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5424 | /* ch == last eaten whitespace char */ | 5440 | /* ch == last eaten whitespace char */ |
| 5425 | #endif | 5441 | #endif |
| 5426 | if (done_word(&ctx)) { | 5442 | if (done_word(&ctx)) { |
| 5427 | goto parse_error; | 5443 | goto parse_error_exitcode1; |
| 5428 | } | 5444 | } |
| 5429 | if (ch == '\n') { | 5445 | if (ch == '\n') { |
| 5430 | /* Is this a case when newline is simply ignored? | 5446 | /* Is this a case when newline is simply ignored? |
| @@ -5467,7 +5483,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5467 | if (heredoc_cnt) { | 5483 | if (heredoc_cnt) { |
| 5468 | heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); | 5484 | heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); |
| 5469 | if (heredoc_cnt != 0) | 5485 | if (heredoc_cnt != 0) |
| 5470 | goto parse_error; | 5486 | goto parse_error_exitcode1; |
| 5471 | } | 5487 | } |
| 5472 | ctx.is_assignment = MAYBE_ASSIGNMENT; | 5488 | ctx.is_assignment = MAYBE_ASSIGNMENT; |
| 5473 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | 5489 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); |
| @@ -5517,7 +5533,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5517 | #endif | 5533 | #endif |
| 5518 | ) { | 5534 | ) { |
| 5519 | if (done_word(&ctx)) { | 5535 | if (done_word(&ctx)) { |
| 5520 | goto parse_error; | 5536 | goto parse_error_exitcode1; |
| 5521 | } | 5537 | } |
| 5522 | done_pipe(&ctx, PIPE_SEQ); | 5538 | done_pipe(&ctx, PIPE_SEQ); |
| 5523 | ctx.is_assignment = MAYBE_ASSIGNMENT; | 5539 | ctx.is_assignment = MAYBE_ASSIGNMENT; |
| @@ -5538,7 +5554,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5538 | /* Example: bare "{ }", "()" */ | 5554 | /* Example: bare "{ }", "()" */ |
| 5539 | G.last_exitcode = 2; /* bash compat */ | 5555 | G.last_exitcode = 2; /* bash compat */ |
| 5540 | syntax_error_unexpected_ch(ch); | 5556 | syntax_error_unexpected_ch(ch); |
| 5541 | goto parse_error2; | 5557 | goto parse_error; |
| 5542 | } | 5558 | } |
| 5543 | if (heredoc_cnt_ptr) | 5559 | if (heredoc_cnt_ptr) |
| 5544 | *heredoc_cnt_ptr = heredoc_cnt; | 5560 | *heredoc_cnt_ptr = heredoc_cnt; |
| @@ -5560,7 +5576,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5560 | case '>': | 5576 | case '>': |
| 5561 | redir_fd = redirect_opt_num(&ctx.word); | 5577 | redir_fd = redirect_opt_num(&ctx.word); |
| 5562 | if (done_word(&ctx)) { | 5578 | if (done_word(&ctx)) { |
| 5563 | goto parse_error; | 5579 | goto parse_error_exitcode1; |
| 5564 | } | 5580 | } |
| 5565 | redir_style = REDIRECT_OVERWRITE; | 5581 | redir_style = REDIRECT_OVERWRITE; |
| 5566 | if (next == '>') { | 5582 | if (next == '>') { |
| @@ -5571,16 +5587,16 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5571 | #if 0 | 5587 | #if 0 |
| 5572 | else if (next == '(') { | 5588 | else if (next == '(') { |
| 5573 | syntax_error(">(process) not supported"); | 5589 | syntax_error(">(process) not supported"); |
| 5574 | goto parse_error; | 5590 | goto parse_error_exitcode1; |
| 5575 | } | 5591 | } |
| 5576 | #endif | 5592 | #endif |
| 5577 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) | 5593 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) |
| 5578 | goto parse_error; | 5594 | goto parse_error_exitcode1; |
| 5579 | continue; /* get next char */ | 5595 | continue; /* get next char */ |
| 5580 | case '<': | 5596 | case '<': |
| 5581 | redir_fd = redirect_opt_num(&ctx.word); | 5597 | redir_fd = redirect_opt_num(&ctx.word); |
| 5582 | if (done_word(&ctx)) { | 5598 | if (done_word(&ctx)) { |
| 5583 | goto parse_error; | 5599 | goto parse_error_exitcode1; |
| 5584 | } | 5600 | } |
| 5585 | redir_style = REDIRECT_INPUT; | 5601 | redir_style = REDIRECT_INPUT; |
| 5586 | if (next == '<') { | 5602 | if (next == '<') { |
| @@ -5597,11 +5613,11 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5597 | #if 0 | 5613 | #if 0 |
| 5598 | else if (next == '(') { | 5614 | else if (next == '(') { |
| 5599 | syntax_error("<(process) not supported"); | 5615 | syntax_error("<(process) not supported"); |
| 5600 | goto parse_error; | 5616 | goto parse_error_exitcode1; |
| 5601 | } | 5617 | } |
| 5602 | #endif | 5618 | #endif |
| 5603 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) | 5619 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) |
| 5604 | goto parse_error; | 5620 | goto parse_error_exitcode1; |
| 5605 | continue; /* get next char */ | 5621 | continue; /* get next char */ |
| 5606 | case '#': | 5622 | case '#': |
| 5607 | if (ctx.word.length == 0 && !ctx.word.has_quoted_part) { | 5623 | if (ctx.word.length == 0 && !ctx.word.has_quoted_part) { |
| @@ -5655,7 +5671,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5655 | if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) { | 5671 | if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) { |
| 5656 | debug_printf_parse("parse_stream parse error: " | 5672 | debug_printf_parse("parse_stream parse error: " |
| 5657 | "parse_dollar returned 0 (error)\n"); | 5673 | "parse_dollar returned 0 (error)\n"); |
| 5658 | goto parse_error; | 5674 | goto parse_error_exitcode1; |
| 5659 | } | 5675 | } |
| 5660 | continue; /* get next char */ | 5676 | continue; /* get next char */ |
| 5661 | case '"': | 5677 | case '"': |
| @@ -5671,7 +5687,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5671 | if (ctx.is_assignment == NOT_ASSIGNMENT) | 5687 | if (ctx.is_assignment == NOT_ASSIGNMENT) |
| 5672 | ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; | 5688 | ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS; |
| 5673 | if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) | 5689 | if (!encode_string(&ctx.as_string, &ctx.word, input, '"')) |
| 5674 | goto parse_error; | 5690 | goto parse_error_exitcode1; |
| 5675 | ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; | 5691 | ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS; |
| 5676 | continue; /* get next char */ | 5692 | continue; /* get next char */ |
| 5677 | #if ENABLE_HUSH_TICK | 5693 | #if ENABLE_HUSH_TICK |
| @@ -5682,7 +5698,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5682 | o_addchr(&ctx.word, '`'); | 5698 | o_addchr(&ctx.word, '`'); |
| 5683 | USE_FOR_NOMMU(pos = ctx.word.length;) | 5699 | USE_FOR_NOMMU(pos = ctx.word.length;) |
| 5684 | if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0)) | 5700 | if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0)) |
| 5685 | goto parse_error; | 5701 | goto parse_error_exitcode1; |
| 5686 | # if !BB_MMU | 5702 | # if !BB_MMU |
| 5687 | o_addstr(&ctx.as_string, ctx.word.data + pos); | 5703 | o_addstr(&ctx.as_string, ctx.word.data + pos); |
| 5688 | o_addchr(&ctx.as_string, '`'); | 5704 | o_addchr(&ctx.as_string, '`'); |
| @@ -5697,7 +5713,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5697 | case_semi: | 5713 | case_semi: |
| 5698 | #endif | 5714 | #endif |
| 5699 | if (done_word(&ctx)) { | 5715 | if (done_word(&ctx)) { |
| 5700 | goto parse_error; | 5716 | goto parse_error_exitcode1; |
| 5701 | } | 5717 | } |
| 5702 | done_pipe(&ctx, PIPE_SEQ); | 5718 | done_pipe(&ctx, PIPE_SEQ); |
| 5703 | #if ENABLE_HUSH_CASE | 5719 | #if ENABLE_HUSH_CASE |
| @@ -5724,7 +5740,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5724 | continue; /* get next char */ | 5740 | continue; /* get next char */ |
| 5725 | case '&': | 5741 | case '&': |
| 5726 | if (done_word(&ctx)) { | 5742 | if (done_word(&ctx)) { |
| 5727 | goto parse_error; | 5743 | goto parse_error_exitcode1; |
| 5728 | } | 5744 | } |
| 5729 | if (next == '&') { | 5745 | if (next == '&') { |
| 5730 | ch = i_getch(input); | 5746 | ch = i_getch(input); |
| @@ -5736,7 +5752,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5736 | goto new_cmd; | 5752 | goto new_cmd; |
| 5737 | case '|': | 5753 | case '|': |
| 5738 | if (done_word(&ctx)) { | 5754 | if (done_word(&ctx)) { |
| 5739 | goto parse_error; | 5755 | goto parse_error_exitcode1; |
| 5740 | } | 5756 | } |
| 5741 | #if ENABLE_HUSH_CASE | 5757 | #if ENABLE_HUSH_CASE |
| 5742 | if (ctx.ctx_res_w == RES_MATCH) | 5758 | if (ctx.ctx_res_w == RES_MATCH) |
| @@ -5768,7 +5784,7 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5768 | case '{': { | 5784 | case '{': { |
| 5769 | int n = parse_group(&ctx, input, ch); | 5785 | int n = parse_group(&ctx, input, ch); |
| 5770 | if (n < 0) { | 5786 | if (n < 0) { |
| 5771 | goto parse_error; | 5787 | goto parse_error_exitcode1; |
| 5772 | } | 5788 | } |
| 5773 | debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); | 5789 | debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); |
| 5774 | heredoc_cnt += n; | 5790 | heredoc_cnt += n; |
| @@ -5786,16 +5802,16 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5786 | * and it will match } earlier (not here). */ | 5802 | * and it will match } earlier (not here). */ |
| 5787 | G.last_exitcode = 2; | 5803 | G.last_exitcode = 2; |
| 5788 | syntax_error_unexpected_ch(ch); | 5804 | syntax_error_unexpected_ch(ch); |
| 5789 | goto parse_error2; | 5805 | goto parse_error; |
| 5790 | default: | 5806 | default: |
| 5791 | if (HUSH_DEBUG) | 5807 | if (HUSH_DEBUG) |
| 5792 | bb_error_msg_and_die("BUG: unexpected %c", ch); | 5808 | bb_error_msg_and_die("BUG: unexpected %c", ch); |
| 5793 | } | 5809 | } |
| 5794 | } /* while (1) */ | 5810 | } /* while (1) */ |
| 5795 | 5811 | ||
| 5796 | parse_error: | 5812 | parse_error_exitcode1: |
| 5797 | G.last_exitcode = 1; | 5813 | G.last_exitcode = 1; |
| 5798 | parse_error2: | 5814 | parse_error: |
| 5799 | { | 5815 | { |
| 5800 | struct parse_context *pctx; | 5816 | struct parse_context *pctx; |
| 5801 | IF_HAS_KEYWORDS(struct parse_context *p2;) | 5817 | IF_HAS_KEYWORDS(struct parse_context *p2;) |
