[git commit] hush: make interactive ^C break out of PS2 mode

Denys Vlasenko vda.linux at googlemail.com
Mon Nov 16 12:00:44 UTC 2020


commit: https://git.busybox.net/busybox/commit/?id=dc9c10a7b29c93a36ff17c562cb14e11eb169f19
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at googlemail.com>
---
 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 <Enter>. (^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 <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) {
 				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;)


More information about the busybox-cvs mailing list