From 4074d495577739d658ba04840b6a0876dfa236b0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 30 Sep 2016 01:49:53 +0200 Subject: [PATCH] hush: fix interactive input handling of backslash+newline function old new delta fgetc_interactive - 258 +258 i_peek_and_eat_bkslash_nl 43 93 +50 static_peek2 7 - -7 setup_string_in_str 46 39 -7 setup_file_in_str 47 40 -7 file_peek 72 52 -20 expand_vars_to_list 1167 1143 -24 file_peek2 74 - -74 file_get 326 65 -261 ------------------------------------------------------------------------------ (add/remove: 1/2 grow/shrink: 1/5 up/down: 308/-400) Total: -92 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 163 ++++++++++++++++++++++++++++----------------------- 1 file changed, 89 insertions(+), 74 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index d0b53339d..1674598b6 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -470,11 +470,9 @@ typedef struct in_str { FILE *file; int (*get) (struct in_str *) FAST_FUNC; int (*peek) (struct in_str *) FAST_FUNC; - int (*peek2) (struct in_str *) FAST_FUNC; } in_str; #define i_getch(input) ((input)->get(input)) #define i_peek(input) ((input)->peek(input)) -#define i_peek2(input) ((input)->peek2(input)) /* The descrip member of this structure is only used to make * debugging output pretty */ @@ -2129,6 +2127,14 @@ static void reinit_unicode_for_hush(void) */ #if ENABLE_HUSH_INTERACTIVE +/* To test correct lineedit/interactive behavior, type from command line: + * echo $P\ + * \ + * AT\ + * H\ + * \ + * It excercises a lot of corner cases. + */ static void cmdedit_update_prompt(void) { if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { @@ -2159,7 +2165,7 @@ static const char *setup_prompt_string(int promptmode) prompt_str = G.PS2; } else prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; - debug_printf("result '%s'\n", prompt_str); + debug_printf("prompt_str '%s'\n", prompt_str); return prompt_str; } static int get_user_input(struct in_str *i) @@ -2169,8 +2175,6 @@ static int get_user_input(struct in_str *i) prompt_str = setup_prompt_string(i->promptmode); # if ENABLE_FEATURE_EDITING - /* Enable command line editing only while a command line - * is actually being read */ do { reinit_unicode_for_hush(); G.flag_SIGINT = 0; @@ -2183,13 +2187,13 @@ static int get_user_input(struct in_str *i) /* catch *SIGINT* etc (^C is handled by read_line_input) */ check_and_run_traps(); } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ - i->p = G.user_input_buf; if (r < 0) { /* EOF/error detected */ - G.user_input_buf[0] = '\0'; - i->peek_buf[1] = i->peek_buf[0] = r = EOF; + i->p = NULL; + i->peek_buf[0] = r = EOF; return r; } + i->p = G.user_input_buf; return (unsigned char)*i->p++; # else do { @@ -2209,18 +2213,45 @@ static int get_user_input(struct in_str *i) return r; # endif } -#endif /* INTERACTIVE */ - /* This is the magic location that prints prompts * and gets data back from the user */ +static int fgetc_interactive(struct in_str *i) +{ + int ch; + /* If it's interactive stdin, get new line. */ + if (G_interactive_fd && i->file == stdin) { + /* Returns first char (or EOF), the rest is in i->p[] */ + ch = get_user_input(i); + i->promptmode = 1; /* PS2 */ + } else { + /* Not stdin: script file, sourced file, etc */ + do ch = fgetc(i->file); while (ch == '\0'); + } + return ch; +} +#else +static inline int fgetc_interactive(struct in_str *i) +{ + int ch; + do ch = fgetc(i->file); while (ch == '\0'); + return ch; +} +#endif /* INTERACTIVE */ + static int FAST_FUNC file_get(struct in_str *i) { int ch; - /* If there is data waiting, eat it up */ +#if ENABLE_FEATURE_EDITING + /* This can be stdin, check line editing char[] buffer */ + if (i->p && *i->p != '\0') { + ch = (unsigned char)*i->p++; + goto out; + } +#endif /* peek_buf[] is an int array, not char. Can contain EOF. */ ch = i->peek_buf[0]; - if (ch != '\0') { + if (ch != 0) { int ch2 = i->peek_buf[1]; i->peek_buf[0] = ch2; if (ch2 == 0) /* very likely, avoid redundant write */ @@ -2229,86 +2260,46 @@ static int FAST_FUNC file_get(struct in_str *i) goto out; } -#if ENABLE_HUSH_INTERACTIVE - /* This can be stdin, check line editing char[] buffer */ - if (i->p && *i->p != '\0') { - ch = (unsigned char)*i->p++; - goto out; - } - /* It's empty. - * If it's interactive stdin, get new line. - */ - if (G_interactive_fd && i->file == stdin) { - /* Returns first char (or EOF), the rest are in i->p[] */ - ch = get_user_input(i); - i->promptmode = 1; /* PS2 */ - goto out; - } - /* Not stdin: script file */ -#endif - do ch = fgetc(i->file); while (ch == '\0'); + ch = fgetc_interactive(i); out: debug_printf("file_get: got '%c' %d\n", ch, ch); i->last_char = ch; return ch; } -/* All callers guarantee this routine will never - * be used right after a newline, so prompting is not needed. - */ static int FAST_FUNC file_peek(struct in_str *i) { int ch; - /* peek_buf[] is an int array, not char. Can contain EOF. */ - ch = i->peek_buf[0]; - if (ch != '\0') - return ch; - -#if ENABLE_HUSH_INTERACTIVE +#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE /* This can be stdin, check line editing char[] buffer */ if (i->p && *i->p != '\0') return (unsigned char)*i->p; #endif + /* peek_buf[] is an int array, not char. Can contain EOF. */ + ch = i->peek_buf[0]; + if (ch != 0) + return ch; - do ch = fgetc(i->file); while (ch == '\0'); - i->peek_buf[0] = ch; - i->peek_buf[1] = 0; + /* Need to get a new char */ + ch = fgetc_interactive(i); debug_printf("file_peek: got '%c' %d\n", ch, ch); - return ch; -} -/* Only ever called if i_peek() was called, and did not return EOF */ -static int FAST_FUNC file_peek2(struct in_str *i) -{ - int ch; - - /* peek_buf[] is an int array, not char. Can contain EOF. */ - ch = i->peek_buf[0]; - if (ch != 0) { - /* peek_buf[] is not empty. Is there 2nd char? */ - ch = i->peek_buf[1]; - if (ch == 0) { - /* We did not read it yet, get it now */ - do ch = fgetc(i->file); while (ch == '\0'); - i->peek_buf[1] = ch; - } - goto out; + /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */ +#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE + if (i->p) { + i->p -= 1; + return ch; } - -#if ENABLE_HUSH_INTERACTIVE - /* This can be stdin, check line editing char[] buffer */ - if (i->p && i->p[0] != '\0') - ch = i->p[1]; #endif - out: - debug_printf("file_peek2: got '%c' %d\n", ch, ch); + i->peek_buf[0] = ch; + /*i->peek_buf[1] = 0; - already is */ return ch; } static int FAST_FUNC static_get(struct in_str *i) { - int ch = *i->p; + int ch = (unsigned char)*i->p; if (ch != '\0') { i->p++; i->last_char = ch; @@ -2320,13 +2311,39 @@ static int FAST_FUNC static_get(struct in_str *i) static int FAST_FUNC static_peek(struct in_str *i) { /* Doesn't report EOF on NUL. None of the callers care. */ - return *i->p; + return (unsigned char)*i->p; } -/* Only ever called if i_peek() was called, and did not return EOF */ -static int FAST_FUNC static_peek2(struct in_str *i) +/* Only ever called if i_peek() was called, and did not return EOF. + * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, + * not end-of-line. Therefore we never need to read a new editing line here. + */ +static int i_peek2(struct in_str *i) { - return i->p[1]; + int ch; + + /* There are two cases when i->p[] buffer exists. + * (1) it's a string in_str. + * (2) It's a file, and we have s saved line editing buffer. + * In both cases, we know that i->p[0] exists and not NUL, and + * the peek2 result is in i->p[1]. + */ + if (i->p) + return (unsigned char)i->p[1]; + + /* Now we know it is a file-based in_str. */ + + /* peek_buf[] is an int array, not char. Can contain EOF. */ + /* Is there 2nd char? */ + ch = i->peek_buf[1]; + if (ch == 0) { + /* We did not read it yet, get it now */ + do ch = fgetc(i->file); while (ch == '\0'); + i->peek_buf[1] = ch; + } + + debug_printf("file_peek2: got '%c' %d\n", ch, ch); + return ch; } static void setup_file_in_str(struct in_str *i, FILE *f) @@ -2334,7 +2351,6 @@ static void setup_file_in_str(struct in_str *i, FILE *f) memset(i, 0, sizeof(*i)); i->get = file_get; i->peek = file_peek; - i->peek2 = file_peek2; /* i->promptmode = 0; - PS1 (memset did it) */ i->file = f; /* i->p = NULL; */ @@ -2345,7 +2361,6 @@ static void setup_string_in_str(struct in_str *i, const char *s) memset(i, 0, sizeof(*i)); i->get = static_get; i->peek = static_peek; - i->peek2 = static_peek2; /* i->promptmode = 0; - PS1 (memset did it) */ i->p = s; } -- 2.25.1