diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2016-09-30 01:49:53 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2016-09-30 01:49:53 +0200 |
commit | 4074d495577739d658ba04840b6a0876dfa236b0 (patch) | |
tree | 5d88d93040d4455cfed1cf468a85257ff3818628 | |
parent | 0448c55cc84294d330305066d8f0a16b56e04b8b (diff) | |
download | busybox-w32-4074d495577739d658ba04840b6a0876dfa236b0.tar.gz busybox-w32-4074d495577739d658ba04840b6a0876dfa236b0.tar.bz2 busybox-w32-4074d495577739d658ba04840b6a0876dfa236b0.zip |
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 <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 163 |
1 files 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 { | |||
470 | FILE *file; | 470 | FILE *file; |
471 | int (*get) (struct in_str *) FAST_FUNC; | 471 | int (*get) (struct in_str *) FAST_FUNC; |
472 | int (*peek) (struct in_str *) FAST_FUNC; | 472 | int (*peek) (struct in_str *) FAST_FUNC; |
473 | int (*peek2) (struct in_str *) FAST_FUNC; | ||
474 | } in_str; | 473 | } in_str; |
475 | #define i_getch(input) ((input)->get(input)) | 474 | #define i_getch(input) ((input)->get(input)) |
476 | #define i_peek(input) ((input)->peek(input)) | 475 | #define i_peek(input) ((input)->peek(input)) |
477 | #define i_peek2(input) ((input)->peek2(input)) | ||
478 | 476 | ||
479 | /* The descrip member of this structure is only used to make | 477 | /* The descrip member of this structure is only used to make |
480 | * debugging output pretty */ | 478 | * debugging output pretty */ |
@@ -2129,6 +2127,14 @@ static void reinit_unicode_for_hush(void) | |||
2129 | */ | 2127 | */ |
2130 | 2128 | ||
2131 | #if ENABLE_HUSH_INTERACTIVE | 2129 | #if ENABLE_HUSH_INTERACTIVE |
2130 | /* To test correct lineedit/interactive behavior, type from command line: | ||
2131 | * echo $P\ | ||
2132 | * \ | ||
2133 | * AT\ | ||
2134 | * H\ | ||
2135 | * \ | ||
2136 | * It excercises a lot of corner cases. | ||
2137 | */ | ||
2132 | static void cmdedit_update_prompt(void) | 2138 | static void cmdedit_update_prompt(void) |
2133 | { | 2139 | { |
2134 | if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { | 2140 | if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) { |
@@ -2159,7 +2165,7 @@ static const char *setup_prompt_string(int promptmode) | |||
2159 | prompt_str = G.PS2; | 2165 | prompt_str = G.PS2; |
2160 | } else | 2166 | } else |
2161 | prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; | 2167 | prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; |
2162 | debug_printf("result '%s'\n", prompt_str); | 2168 | debug_printf("prompt_str '%s'\n", prompt_str); |
2163 | return prompt_str; | 2169 | return prompt_str; |
2164 | } | 2170 | } |
2165 | static int get_user_input(struct in_str *i) | 2171 | static int get_user_input(struct in_str *i) |
@@ -2169,8 +2175,6 @@ static int get_user_input(struct in_str *i) | |||
2169 | 2175 | ||
2170 | prompt_str = setup_prompt_string(i->promptmode); | 2176 | prompt_str = setup_prompt_string(i->promptmode); |
2171 | # if ENABLE_FEATURE_EDITING | 2177 | # if ENABLE_FEATURE_EDITING |
2172 | /* Enable command line editing only while a command line | ||
2173 | * is actually being read */ | ||
2174 | do { | 2178 | do { |
2175 | reinit_unicode_for_hush(); | 2179 | reinit_unicode_for_hush(); |
2176 | G.flag_SIGINT = 0; | 2180 | G.flag_SIGINT = 0; |
@@ -2183,13 +2187,13 @@ static int get_user_input(struct in_str *i) | |||
2183 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ | 2187 | /* catch *SIGINT* etc (^C is handled by read_line_input) */ |
2184 | check_and_run_traps(); | 2188 | check_and_run_traps(); |
2185 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ | 2189 | } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ |
2186 | i->p = G.user_input_buf; | ||
2187 | if (r < 0) { | 2190 | if (r < 0) { |
2188 | /* EOF/error detected */ | 2191 | /* EOF/error detected */ |
2189 | G.user_input_buf[0] = '\0'; | 2192 | i->p = NULL; |
2190 | i->peek_buf[1] = i->peek_buf[0] = r = EOF; | 2193 | i->peek_buf[0] = r = EOF; |
2191 | return r; | 2194 | return r; |
2192 | } | 2195 | } |
2196 | i->p = G.user_input_buf; | ||
2193 | return (unsigned char)*i->p++; | 2197 | return (unsigned char)*i->p++; |
2194 | # else | 2198 | # else |
2195 | do { | 2199 | do { |
@@ -2209,18 +2213,45 @@ static int get_user_input(struct in_str *i) | |||
2209 | return r; | 2213 | return r; |
2210 | # endif | 2214 | # endif |
2211 | } | 2215 | } |
2212 | #endif /* INTERACTIVE */ | ||
2213 | |||
2214 | /* This is the magic location that prints prompts | 2216 | /* This is the magic location that prints prompts |
2215 | * and gets data back from the user */ | 2217 | * and gets data back from the user */ |
2218 | static int fgetc_interactive(struct in_str *i) | ||
2219 | { | ||
2220 | int ch; | ||
2221 | /* If it's interactive stdin, get new line. */ | ||
2222 | if (G_interactive_fd && i->file == stdin) { | ||
2223 | /* Returns first char (or EOF), the rest is in i->p[] */ | ||
2224 | ch = get_user_input(i); | ||
2225 | i->promptmode = 1; /* PS2 */ | ||
2226 | } else { | ||
2227 | /* Not stdin: script file, sourced file, etc */ | ||
2228 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2229 | } | ||
2230 | return ch; | ||
2231 | } | ||
2232 | #else | ||
2233 | static inline int fgetc_interactive(struct in_str *i) | ||
2234 | { | ||
2235 | int ch; | ||
2236 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2237 | return ch; | ||
2238 | } | ||
2239 | #endif /* INTERACTIVE */ | ||
2240 | |||
2216 | static int FAST_FUNC file_get(struct in_str *i) | 2241 | static int FAST_FUNC file_get(struct in_str *i) |
2217 | { | 2242 | { |
2218 | int ch; | 2243 | int ch; |
2219 | 2244 | ||
2220 | /* If there is data waiting, eat it up */ | 2245 | #if ENABLE_FEATURE_EDITING |
2246 | /* This can be stdin, check line editing char[] buffer */ | ||
2247 | if (i->p && *i->p != '\0') { | ||
2248 | ch = (unsigned char)*i->p++; | ||
2249 | goto out; | ||
2250 | } | ||
2251 | #endif | ||
2221 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | 2252 | /* peek_buf[] is an int array, not char. Can contain EOF. */ |
2222 | ch = i->peek_buf[0]; | 2253 | ch = i->peek_buf[0]; |
2223 | if (ch != '\0') { | 2254 | if (ch != 0) { |
2224 | int ch2 = i->peek_buf[1]; | 2255 | int ch2 = i->peek_buf[1]; |
2225 | i->peek_buf[0] = ch2; | 2256 | i->peek_buf[0] = ch2; |
2226 | if (ch2 == 0) /* very likely, avoid redundant write */ | 2257 | if (ch2 == 0) /* very likely, avoid redundant write */ |
@@ -2229,86 +2260,46 @@ static int FAST_FUNC file_get(struct in_str *i) | |||
2229 | goto out; | 2260 | goto out; |
2230 | } | 2261 | } |
2231 | 2262 | ||
2232 | #if ENABLE_HUSH_INTERACTIVE | 2263 | ch = fgetc_interactive(i); |
2233 | /* This can be stdin, check line editing char[] buffer */ | ||
2234 | if (i->p && *i->p != '\0') { | ||
2235 | ch = (unsigned char)*i->p++; | ||
2236 | goto out; | ||
2237 | } | ||
2238 | /* It's empty. | ||
2239 | * If it's interactive stdin, get new line. | ||
2240 | */ | ||
2241 | if (G_interactive_fd && i->file == stdin) { | ||
2242 | /* Returns first char (or EOF), the rest are in i->p[] */ | ||
2243 | ch = get_user_input(i); | ||
2244 | i->promptmode = 1; /* PS2 */ | ||
2245 | goto out; | ||
2246 | } | ||
2247 | /* Not stdin: script file */ | ||
2248 | #endif | ||
2249 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2250 | out: | 2264 | out: |
2251 | debug_printf("file_get: got '%c' %d\n", ch, ch); | 2265 | debug_printf("file_get: got '%c' %d\n", ch, ch); |
2252 | i->last_char = ch; | 2266 | i->last_char = ch; |
2253 | return ch; | 2267 | return ch; |
2254 | } | 2268 | } |
2255 | 2269 | ||
2256 | /* All callers guarantee this routine will never | ||
2257 | * be used right after a newline, so prompting is not needed. | ||
2258 | */ | ||
2259 | static int FAST_FUNC file_peek(struct in_str *i) | 2270 | static int FAST_FUNC file_peek(struct in_str *i) |
2260 | { | 2271 | { |
2261 | int ch; | 2272 | int ch; |
2262 | 2273 | ||
2263 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | 2274 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE |
2264 | ch = i->peek_buf[0]; | ||
2265 | if (ch != '\0') | ||
2266 | return ch; | ||
2267 | |||
2268 | #if ENABLE_HUSH_INTERACTIVE | ||
2269 | /* This can be stdin, check line editing char[] buffer */ | 2275 | /* This can be stdin, check line editing char[] buffer */ |
2270 | if (i->p && *i->p != '\0') | 2276 | if (i->p && *i->p != '\0') |
2271 | return (unsigned char)*i->p; | 2277 | return (unsigned char)*i->p; |
2272 | #endif | 2278 | #endif |
2279 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
2280 | ch = i->peek_buf[0]; | ||
2281 | if (ch != 0) | ||
2282 | return ch; | ||
2273 | 2283 | ||
2274 | do ch = fgetc(i->file); while (ch == '\0'); | 2284 | /* Need to get a new char */ |
2275 | i->peek_buf[0] = ch; | 2285 | ch = fgetc_interactive(i); |
2276 | i->peek_buf[1] = 0; | ||
2277 | debug_printf("file_peek: got '%c' %d\n", ch, ch); | 2286 | debug_printf("file_peek: got '%c' %d\n", ch, ch); |
2278 | return ch; | ||
2279 | } | ||
2280 | 2287 | ||
2281 | /* Only ever called if i_peek() was called, and did not return EOF */ | 2288 | /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */ |
2282 | static int FAST_FUNC file_peek2(struct in_str *i) | 2289 | #if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE |
2283 | { | 2290 | if (i->p) { |
2284 | int ch; | 2291 | i->p -= 1; |
2285 | 2292 | return ch; | |
2286 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
2287 | ch = i->peek_buf[0]; | ||
2288 | if (ch != 0) { | ||
2289 | /* peek_buf[] is not empty. Is there 2nd char? */ | ||
2290 | ch = i->peek_buf[1]; | ||
2291 | if (ch == 0) { | ||
2292 | /* We did not read it yet, get it now */ | ||
2293 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2294 | i->peek_buf[1] = ch; | ||
2295 | } | ||
2296 | goto out; | ||
2297 | } | 2293 | } |
2298 | |||
2299 | #if ENABLE_HUSH_INTERACTIVE | ||
2300 | /* This can be stdin, check line editing char[] buffer */ | ||
2301 | if (i->p && i->p[0] != '\0') | ||
2302 | ch = i->p[1]; | ||
2303 | #endif | 2294 | #endif |
2304 | out: | 2295 | i->peek_buf[0] = ch; |
2305 | debug_printf("file_peek2: got '%c' %d\n", ch, ch); | 2296 | /*i->peek_buf[1] = 0; - already is */ |
2306 | return ch; | 2297 | return ch; |
2307 | } | 2298 | } |
2308 | 2299 | ||
2309 | static int FAST_FUNC static_get(struct in_str *i) | 2300 | static int FAST_FUNC static_get(struct in_str *i) |
2310 | { | 2301 | { |
2311 | int ch = *i->p; | 2302 | int ch = (unsigned char)*i->p; |
2312 | if (ch != '\0') { | 2303 | if (ch != '\0') { |
2313 | i->p++; | 2304 | i->p++; |
2314 | i->last_char = ch; | 2305 | i->last_char = ch; |
@@ -2320,13 +2311,39 @@ static int FAST_FUNC static_get(struct in_str *i) | |||
2320 | static int FAST_FUNC static_peek(struct in_str *i) | 2311 | static int FAST_FUNC static_peek(struct in_str *i) |
2321 | { | 2312 | { |
2322 | /* Doesn't report EOF on NUL. None of the callers care. */ | 2313 | /* Doesn't report EOF on NUL. None of the callers care. */ |
2323 | return *i->p; | 2314 | return (unsigned char)*i->p; |
2324 | } | 2315 | } |
2325 | 2316 | ||
2326 | /* Only ever called if i_peek() was called, and did not return EOF */ | 2317 | /* Only ever called if i_peek() was called, and did not return EOF. |
2327 | static int FAST_FUNC static_peek2(struct in_str *i) | 2318 | * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL, |
2319 | * not end-of-line. Therefore we never need to read a new editing line here. | ||
2320 | */ | ||
2321 | static int i_peek2(struct in_str *i) | ||
2328 | { | 2322 | { |
2329 | return i->p[1]; | 2323 | int ch; |
2324 | |||
2325 | /* There are two cases when i->p[] buffer exists. | ||
2326 | * (1) it's a string in_str. | ||
2327 | * (2) It's a file, and we have s saved line editing buffer. | ||
2328 | * In both cases, we know that i->p[0] exists and not NUL, and | ||
2329 | * the peek2 result is in i->p[1]. | ||
2330 | */ | ||
2331 | if (i->p) | ||
2332 | return (unsigned char)i->p[1]; | ||
2333 | |||
2334 | /* Now we know it is a file-based in_str. */ | ||
2335 | |||
2336 | /* peek_buf[] is an int array, not char. Can contain EOF. */ | ||
2337 | /* Is there 2nd char? */ | ||
2338 | ch = i->peek_buf[1]; | ||
2339 | if (ch == 0) { | ||
2340 | /* We did not read it yet, get it now */ | ||
2341 | do ch = fgetc(i->file); while (ch == '\0'); | ||
2342 | i->peek_buf[1] = ch; | ||
2343 | } | ||
2344 | |||
2345 | debug_printf("file_peek2: got '%c' %d\n", ch, ch); | ||
2346 | return ch; | ||
2330 | } | 2347 | } |
2331 | 2348 | ||
2332 | static void setup_file_in_str(struct in_str *i, FILE *f) | 2349 | 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) | |||
2334 | memset(i, 0, sizeof(*i)); | 2351 | memset(i, 0, sizeof(*i)); |
2335 | i->get = file_get; | 2352 | i->get = file_get; |
2336 | i->peek = file_peek; | 2353 | i->peek = file_peek; |
2337 | i->peek2 = file_peek2; | ||
2338 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2354 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2339 | i->file = f; | 2355 | i->file = f; |
2340 | /* i->p = NULL; */ | 2356 | /* i->p = NULL; */ |
@@ -2345,7 +2361,6 @@ static void setup_string_in_str(struct in_str *i, const char *s) | |||
2345 | memset(i, 0, sizeof(*i)); | 2361 | memset(i, 0, sizeof(*i)); |
2346 | i->get = static_get; | 2362 | i->get = static_get; |
2347 | i->peek = static_peek; | 2363 | i->peek = static_peek; |
2348 | i->peek2 = static_peek2; | ||
2349 | /* i->promptmode = 0; - PS1 (memset did it) */ | 2364 | /* i->promptmode = 0; - PS1 (memset did it) */ |
2350 | i->p = s; | 2365 | i->p = s; |
2351 | } | 2366 | } |