diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2016-09-29 18:02:37 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2016-09-29 18:02:37 +0200 |
commit | d17a91db6e0817644445f28515d68b3033388eb4 (patch) | |
tree | 427cb084faadaad8a3027564100b4f75e21bb5b8 | |
parent | 459293b1c536515fbe7fafbae9932aefadb2fbaf (diff) | |
download | busybox-w32-d17a91db6e0817644445f28515d68b3033388eb4.tar.gz busybox-w32-d17a91db6e0817644445f28515d68b3033388eb4.tar.bz2 busybox-w32-d17a91db6e0817644445f28515d68b3033388eb4.zip |
hush: rework input char buffering to allow more than one-deep peek
...this time with actual hush.c changes too :)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 193 |
1 files changed, 125 insertions, 68 deletions
diff --git a/shell/hush.c b/shell/hush.c index d7d152c10..e3dcf2c38 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -462,19 +462,19 @@ static const char *const assignment_flag[] = { | |||
462 | 462 | ||
463 | typedef struct in_str { | 463 | typedef struct in_str { |
464 | const char *p; | 464 | const char *p; |
465 | /* eof_flag=1: last char in ->p is really an EOF */ | ||
466 | char eof_flag; /* meaningless if ->p == NULL */ | ||
467 | char peek_buf[2]; | ||
468 | #if ENABLE_HUSH_INTERACTIVE | 465 | #if ENABLE_HUSH_INTERACTIVE |
469 | smallint promptmode; /* 0: PS1, 1: PS2 */ | 466 | smallint promptmode; /* 0: PS1, 1: PS2 */ |
470 | #endif | 467 | #endif |
468 | int peek_buf[2]; | ||
471 | int last_char; | 469 | int last_char; |
472 | FILE *file; | 470 | FILE *file; |
473 | int (*get) (struct in_str *) FAST_FUNC; | 471 | int (*get) (struct in_str *) FAST_FUNC; |
474 | int (*peek) (struct in_str *) FAST_FUNC; | 472 | int (*peek) (struct in_str *) FAST_FUNC; |
473 | int (*peek2) (struct in_str *) FAST_FUNC; | ||
475 | } in_str; | 474 | } in_str; |
476 | #define i_getch(input) ((input)->get(input)) | 475 | #define i_getch(input) ((input)->get(input)) |
477 | #define i_peek(input) ((input)->peek(input)) | 476 | #define i_peek(input) ((input)->peek(input)) |
477 | #define i_peek2(input) ((input)->peek2(input)) | ||
478 | 478 | ||
479 | /* The descrip member of this structure is only used to make | 479 | /* The descrip member of this structure is only used to make |
480 | * debugging output pretty */ | 480 | * debugging output pretty */ |
@@ -2122,28 +2122,11 @@ static void reinit_unicode_for_hush(void) | |||
2122 | } | 2122 | } |
2123 | } | 2123 | } |
2124 | 2124 | ||
2125 | |||
2126 | /* | 2125 | /* |
2127 | * in_str support | 2126 | * in_str support (strings, and "strings" read from files). |
2128 | */ | 2127 | */ |
2129 | static int FAST_FUNC static_get(struct in_str *i) | ||
2130 | { | ||
2131 | int ch = *i->p; | ||
2132 | if (ch != '\0') { | ||
2133 | i->p++; | ||
2134 | i->last_char = ch; | ||
2135 | return ch; | ||
2136 | } | ||
2137 | return EOF; | ||
2138 | } | ||
2139 | |||
2140 | static int FAST_FUNC static_peek(struct in_str *i) | ||
2141 | { | ||
2142 | return *i->p; | ||
2143 | } | ||
2144 | 2128 | ||
2145 | #if ENABLE_HUSH_INTERACTIVE | 2129 | #if ENABLE_HUSH_INTERACTIVE |
2146 | |||
2147 | static void cmdedit_update_prompt(void) | 2130 | static void cmdedit_update_prompt(void) |
2148 | { | 2131 | { |
2149 | if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { | 2132 | if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { |
@@ -2157,7 +2140,6 @@ static void cmdedit_update_prompt(void) | |||
2157 | if (G.PS2 == NULL) | 2140 | if (G.PS2 == NULL) |
2158 | G.PS2 = "> "; | 2141 | G.PS2 = "> "; |
2159 | } | 2142 | } |
2160 | |||
2161 | static const char *setup_prompt_string(int promptmode) | 2143 | static const char *setup_prompt_string(int promptmode) |
2162 | { | 2144 | { |
2163 | const char *prompt_str; | 2145 | const char *prompt_str; |
@@ -2178,8 +2160,7 @@ static const char *setup_prompt_string(int promptmode) | |||
2178 | debug_printf("result '%s'\n", prompt_str); | 2160 | debug_printf("result '%s'\n", prompt_str); |
2179 | return prompt_str; | 2161 | return prompt_str; |
2180 | } | 2162 | } |
2181 | 2163 | static int get_user_input(struct in_str *i) | |
2182 | static void get_user_input(struct in_str *i) | ||
2183 | { | 2164 | { |
2184 | int r; | 2165 | int r; |
2185 | const char *prompt_str; | 2166 | const char *prompt_str; |
@@ -2197,11 +2178,14 @@ static void get_user_input(struct in_str *i) | |||
2197 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 2178 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
2198 | check_and_run_traps(); | 2179 | check_and_run_traps(); |
2199 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 2180 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
2200 | i->eof_flag = (r < 0); | 2181 | i->p = G.user_input_buf; |
2201 | if (i->eof_flag) { /* EOF/error detected */ | 2182 | if (r < 0) { |
2202 | G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ | 2183 | /* EOF/error detected */ |
2203 | G.user_input_buf[1] = '\0'; | 2184 | G.user_input_buf[0] = '\0'; |
2185 | i->peek_buf[1] = i->peek_buf[0] = r = EOF; | ||
2186 | return r; | ||
2204 | } | 2187 | } |
2188 | return (unsigned char)*i->p++; | ||
2205 | # else | 2189 | # else |
2206 | do { | 2190 | do { |
2207 | G.flag_SIGINT = 0; | 2191 | G.flag_SIGINT = 0; |
@@ -2215,14 +2199,11 @@ static void get_user_input(struct in_str *i) | |||
2215 | fputs(prompt_str, stdout); | 2199 | fputs(prompt_str, stdout); |
2216 | } | 2200 | } |
2217 | fflush_all(); | 2201 | fflush_all(); |
2218 | G.user_input_buf[0] = r = fgetc(i->file); | 2202 | r = fgetc(i->file); |
2219 | /*G.user_input_buf[1] = '\0'; - already is and never changed */ | 2203 | } while (G.flag_SIGINT || r == '\0'); |
2220 | } while (G.flag_SIGINT); | 2204 | return r; |
2221 | i->eof_flag = (r == EOF); | ||
2222 | # endif | 2205 | # endif |
2223 | i->p = G.user_input_buf; | ||
2224 | } | 2206 | } |
2225 | |||
2226 | #endif /* INTERACTIVE */ | 2207 | #endif /* INTERACTIVE */ |
2227 | 2208 | ||
2228 | /* This is the magic location that prints prompts | 2209 | /* This is the magic location that prints prompts |
@@ -2232,28 +2213,36 @@ static int FAST_FUNC file_get(struct in_str *i) | |||
2232 | int ch; | 2213 | int ch; |
2233 | 2214 | ||
2234 | /* If there is data waiting, eat it up */ | 2215 | /* If there is data waiting, eat it up */ |
2235 | if (i->p && *i->p) { | 2216 | /* peek_buf[] is an int array, not char. Can contain EOF. */ |
2236 | #if ENABLE_HUSH_INTERACTIVE | 2217 | ch = i->peek_buf[0]; |
2237 | take_cached: | 2218 | if (ch != '\0') { |
2238 | #endif | 2219 | int ch2 = i->peek_buf[1]; |
2239 | ch = *i->p++; | 2220 | i->peek_buf[0] = ch2; |
2240 | if (i->eof_flag && !*i->p) | 2221 | if (ch2 == 0) /* very likely, avoid redundant write */ |
2241 | ch = EOF; | 2222 | goto out; |
2242 | /* note: ch is never NUL */ | 2223 | i->peek_buf[1] = 0; |
2243 | } else { | 2224 | goto out; |
2244 | /* need to double check i->file because we might be doing something | 2225 | } |
2245 | * more complicated by now, like sourcing or substituting. */ | 2226 | |
2246 | #if ENABLE_HUSH_INTERACTIVE | 2227 | #if ENABLE_HUSH_INTERACTIVE |
2247 | if (G_interactive_fd && i->file == stdin) { | 2228 | /* This can be stdin, check line editing char[] buffer */ |
2248 | do { | 2229 | if (i->p && *i->p != '\0') { |
2249 | get_user_input(i); | 2230 | ch = (unsigned char)*i->p++; |
2250 | } while (!*i->p); /* need non-empty line */ | 2231 | goto out; |
2251 | i->promptmode = 1; /* PS2 */ | 2232 | } |
2252 | goto take_cached; | 2233 | /* It's empty. |
2253 | } | 2234 | * If it's interactive stdin, get new line. |
2254 | #endif | 2235 | */ |
2255 | do ch = fgetc(i->file); while (ch == '\0'); | 2236 | if (G_interactive_fd && i->file == stdin) { |
2237 | /* Returns first char (or EOF), the rest are in i->p[] */ | ||
2238 | ch = get_user_input(i); | ||
2239 | i->promptmode = 1; /* PS2 */ | ||
2240 | goto out; | ||
2256 | } | 2241 | } |
2242 | /* Not stdin: script file */ | ||
2243 | #endif | ||
2244 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2245 | out: | ||
2257 | debug_printf("file_get: got '%c' %d\n", ch, ch); | 2246 | debug_printf("file_get: got '%c' %d\n", ch, ch); |
2258 | i->last_char = ch; | 2247 | i->last_char = ch; |
2259 | return ch; | 2248 | return ch; |
@@ -2265,26 +2254,82 @@ static int FAST_FUNC file_get(struct in_str *i) | |||
2265 | static int FAST_FUNC file_peek(struct in_str *i) | 2254 | static int FAST_FUNC file_peek(struct in_str *i) |
2266 | { | 2255 | { |
2267 | int ch; | 2256 | int ch; |
2268 | if (i->p && *i->p) { | 2257 | |
2269 | if (i->eof_flag && !i->p[1]) | 2258 | /* peek_buf[] is an int array, not char. Can contain EOF. */ |
2270 | return EOF; | 2259 | ch = i->peek_buf[0]; |
2271 | return *i->p; | 2260 | if (ch != '\0') |
2272 | /* note: ch is never NUL */ | 2261 | return ch; |
2273 | } | 2262 | |
2263 | #if ENABLE_HUSH_INTERACTIVE | ||
2264 | /* This can be stdin, check line editing char[] buffer */ | ||
2265 | if (i->p && *i->p != '\0') | ||
2266 | return (unsigned char)*i->p; | ||
2267 | #endif | ||
2268 | |||
2274 | do ch = fgetc(i->file); while (ch == '\0'); | 2269 | do ch = fgetc(i->file); while (ch == '\0'); |
2275 | i->eof_flag = (ch == EOF); | ||
2276 | i->peek_buf[0] = ch; | 2270 | i->peek_buf[0] = ch; |
2277 | i->peek_buf[1] = '\0'; | 2271 | i->peek_buf[1] = 0; |
2278 | i->p = i->peek_buf; | ||
2279 | debug_printf("file_peek: got '%c' %d\n", ch, ch); | 2272 | debug_printf("file_peek: got '%c' %d\n", ch, ch); |
2280 | return ch; | 2273 | return ch; |
2281 | } | 2274 | } |
2282 | 2275 | ||
2276 | /* Only ever called if i_peek() was called, and did not return EOF */ | ||
2277 | static int FAST_FUNC file_peek2(struct in_str *i) | ||
2278 | { | ||
2279 | int ch; | ||
2280 | |||
2281 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
2282 | ch = i->peek_buf[0]; | ||
2283 | if (ch != 0) { | ||
2284 | /* peek_buf[] is not empty. Is there 2nd char? */ | ||
2285 | ch = i->peek_buf[1]; | ||
2286 | if (ch == 0) { | ||
2287 | /* We did not read it yet, get it now */ | ||
2288 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2289 | i->peek_buf[1] = ch; | ||
2290 | } | ||
2291 | goto out; | ||
2292 | } | ||
2293 | |||
2294 | #if ENABLE_HUSH_INTERACTIVE | ||
2295 | /* This can be stdin, check line editing char[] buffer */ | ||
2296 | if (i->p && i->p[0] != '\0') | ||
2297 | ch = i->p[1]; | ||
2298 | #endif | ||
2299 | out: | ||
2300 | debug_printf("file_peek2: got '%c' %d\n", ch, ch); | ||
2301 | return ch; | ||
2302 | } | ||
2303 | |||
2304 | static int FAST_FUNC static_get(struct in_str *i) | ||
2305 | { | ||
2306 | int ch = *i->p; | ||
2307 | if (ch != '\0') { | ||
2308 | i->p++; | ||
2309 | i->last_char = ch; | ||
2310 | return ch; | ||
2311 | } | ||
2312 | return EOF; | ||
2313 | } | ||
2314 | |||
2315 | static int FAST_FUNC static_peek(struct in_str *i) | ||
2316 | { | ||
2317 | /* Doesn't report EOF on NUL. None of the callers care. */ | ||
2318 | return *i->p; | ||
2319 | } | ||
2320 | |||
2321 | /* Only ever called if i_peek() was called, and did not return EOF */ | ||
2322 | static int FAST_FUNC static_peek2(struct in_str *i) | ||
2323 | { | ||
2324 | return i->p[1]; | ||
2325 | } | ||
2326 | |||
2283 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2327 | static void setup_file_in_str(struct in_str *i, FILE *f) |
2284 | { | 2328 | { |
2285 | memset(i, 0, sizeof(*i)); | 2329 | memset(i, 0, sizeof(*i)); |
2286 | i->peek = file_peek; | ||
2287 | i->get = file_get; | 2330 | i->get = file_get; |
2331 | i->peek = file_peek; | ||
2332 | i->peek2 = file_peek2; | ||
2288 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2333 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2289 | i->file = f; | 2334 | i->file = f; |
2290 | /* i->p = NULL; */ | 2335 | /* i->p = NULL; */ |
@@ -2293,11 +2338,11 @@ static void setup_file_in_str(struct in_str *i, FILE *f) | |||
2293 | static void setup_string_in_str(struct in_str *i, const char *s) | 2338 | static void setup_string_in_str(struct in_str *i, const char *s) |
2294 | { | 2339 | { |
2295 | memset(i, 0, sizeof(*i)); | 2340 | memset(i, 0, sizeof(*i)); |
2296 | i->peek = static_peek; | ||
2297 | i->get = static_get; | 2341 | i->get = static_get; |
2342 | i->peek = static_peek; | ||
2343 | i->peek2 = static_peek2; | ||
2298 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2344 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2299 | i->p = s; | 2345 | i->p = s; |
2300 | /* i->eof_flag = 0; */ | ||
2301 | } | 2346 | } |
2302 | 2347 | ||
2303 | 2348 | ||
@@ -4040,9 +4085,21 @@ static int parse_dollar(o_string *as_string, | |||
4040 | debug_printf_parse(": '%c'\n", ch); | 4085 | debug_printf_parse(": '%c'\n", ch); |
4041 | o_addchr(dest, ch | quote_mask); | 4086 | o_addchr(dest, ch | quote_mask); |
4042 | quote_mask = 0; | 4087 | quote_mask = 0; |
4088 | next_ch: | ||
4043 | ch = i_peek(input); | 4089 | ch = i_peek(input); |
4044 | if (!isalnum(ch) && ch != '_') | 4090 | if (!isalnum(ch) && ch != '_') { |
4091 | if (ch == '\\') { | ||
4092 | /* If backslash+newline, skip it */ | ||
4093 | int ch2 = i_peek2(input); | ||
4094 | if (ch2 == '\n') { | ||
4095 | i_getch(input); | ||
4096 | i_getch(input); | ||
4097 | goto next_ch; | ||
4098 | } | ||
4099 | } | ||
4100 | /* End of variable name reached */ | ||
4045 | break; | 4101 | break; |
4102 | } | ||
4046 | ch = i_getch(input); | 4103 | ch = i_getch(input); |
4047 | nommu_addchr(as_string, ch); | 4104 | nommu_addchr(as_string, ch); |
4048 | } | 4105 | } |