diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-18 16:30:42 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-18 16:30:42 +0000 |
commit | 55789c6646d1134c6e75380e66a953b676acfde9 (patch) | |
tree | a4a620ba29f029bae2deb09792cc893b7ea2c03f | |
parent | ab876cd107fe6ca274f58bae3264396745d8e5f9 (diff) | |
download | busybox-w32-55789c6646d1134c6e75380e66a953b676acfde9.tar.gz busybox-w32-55789c6646d1134c6e75380e66a953b676acfde9.tar.bz2 busybox-w32-55789c6646d1134c6e75380e66a953b676acfde9.zip |
hush: fix a bug with backslashes improperly handled in unquoted variables.
with previous patch:
function old new delta
parse_stream 1638 1758 +120
expand_on_ifs 97 174 +77
free_pipe 206 237 +31
setup_redirect 217 220 +3
setup_redirects 143 144 +1
done_word 698 688 -10
free_strings 38 - -38
expand_variables 1451 1403 -48
------------------------------------------------------------------------------
(add/remove: 0/1 grow/shrink: 5/2 up/down: 232/-96) Total: 136 bytes
-rw-r--r-- | shell/hush.c | 166 |
1 files changed, 115 insertions, 51 deletions
diff --git a/shell/hush.c b/shell/hush.c index f81203e2b..b83265899 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -227,8 +227,8 @@ typedef enum { | |||
227 | REDIRECT_IO = 5 | 227 | REDIRECT_IO = 5 |
228 | } redir_type; | 228 | } redir_type; |
229 | 229 | ||
230 | /* The descrip member of this structure is only used to make debugging | 230 | /* The descrip member of this structure is only used to make |
231 | * output pretty */ | 231 | * debugging output pretty */ |
232 | static const struct { | 232 | static const struct { |
233 | int mode; | 233 | int mode; |
234 | signed char default_fd; | 234 | signed char default_fd; |
@@ -283,8 +283,8 @@ struct p_context { | |||
283 | }; | 283 | }; |
284 | 284 | ||
285 | struct redir_struct { | 285 | struct redir_struct { |
286 | struct redir_struct *next; /* pointer to the next redirect in the list */ | 286 | struct redir_struct *next; |
287 | redir_type type; /* type of redirection */ | 287 | smallint /*redir_type*/ rd_type; |
288 | int fd; /* file descriptor being redirected */ | 288 | int fd; /* file descriptor being redirected */ |
289 | int dup; /* -1, or file descriptor being duplicated */ | 289 | int dup; /* -1, or file descriptor being duplicated */ |
290 | char *rd_filename; /* filename */ | 290 | char *rd_filename; /* filename */ |
@@ -292,10 +292,10 @@ struct redir_struct { | |||
292 | 292 | ||
293 | struct child_prog { | 293 | struct child_prog { |
294 | pid_t pid; /* 0 if exited */ | 294 | pid_t pid; /* 0 if exited */ |
295 | smallint is_stopped; /* is the program currently running? */ | ||
296 | smallint subshell; /* flag, non-zero if group must be forked */ | ||
295 | char **argv; /* program name and arguments */ | 297 | char **argv; /* program name and arguments */ |
296 | struct pipe *group; /* if non-NULL, first in group or subshell */ | 298 | struct pipe *group; /* if non-NULL, first in group or subshell */ |
297 | smallint subshell; /* flag, non-zero if group must be forked */ | ||
298 | smallint is_stopped; /* is the program currently running? */ | ||
299 | struct redir_struct *redirects; /* I/O redirections */ | 299 | struct redir_struct *redirects; /* I/O redirections */ |
300 | struct pipe *family; /* pointer back to the child's parent pipe */ | 300 | struct pipe *family; /* pointer back to the child's parent pipe */ |
301 | }; | 301 | }; |
@@ -346,7 +346,13 @@ typedef struct { | |||
346 | smallint o_glob; | 346 | smallint o_glob; |
347 | smallint nonnull; | 347 | smallint nonnull; |
348 | smallint has_empty_slot; | 348 | smallint has_empty_slot; |
349 | smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ | ||
349 | } o_string; | 350 | } o_string; |
351 | enum { | ||
352 | MAYBE_ASSIGNMENT = 0, | ||
353 | DEFINITELY_ASSIGNMENT = 1, | ||
354 | NOT_ASSIGNMENT = 2, | ||
355 | }; | ||
350 | /* Used for initialization: o_string foo = NULL_O_STRING; */ | 356 | /* Used for initialization: o_string foo = NULL_O_STRING; */ |
351 | #define NULL_O_STRING { NULL } | 357 | #define NULL_O_STRING { NULL } |
352 | 358 | ||
@@ -588,6 +594,19 @@ static int is_assignment(const char *s) | |||
588 | return *s == '='; | 594 | return *s == '='; |
589 | } | 595 | } |
590 | 596 | ||
597 | /* Replace each \x with x in place, return ptr past NUL. */ | ||
598 | static char *unbackslash(char *src) | ||
599 | { | ||
600 | char *dst = src; | ||
601 | while (1) { | ||
602 | if (*src == '\\') | ||
603 | src++; | ||
604 | if ((*dst++ = *src++) == '\0') | ||
605 | break; | ||
606 | } | ||
607 | return dst; | ||
608 | } | ||
609 | |||
591 | static char **add_malloced_strings_to_strings(char **strings, char **add) | 610 | static char **add_malloced_strings_to_strings(char **strings, char **add) |
592 | { | 611 | { |
593 | int i; | 612 | int i; |
@@ -906,6 +925,19 @@ static void o_addstr(o_string *o, const char *str, int len) | |||
906 | o->data[o->length] = '\0'; | 925 | o->data[o->length] = '\0'; |
907 | } | 926 | } |
908 | 927 | ||
928 | static void o_addstr_duplicate_backslash(o_string *o, const char *str, int len) | ||
929 | { | ||
930 | while (len) { | ||
931 | o_addchr(o, *str); | ||
932 | if (*str++ == '\\' | ||
933 | && (*str != '*' && *str != '?' && *str != '[') | ||
934 | ) { | ||
935 | o_addchr(o, '\\'); | ||
936 | } | ||
937 | len--; | ||
938 | } | ||
939 | } | ||
940 | |||
909 | /* My analysis of quoting semantics tells me that state information | 941 | /* My analysis of quoting semantics tells me that state information |
910 | * is associated with a destination, not a source. | 942 | * is associated with a destination, not a source. |
911 | */ | 943 | */ |
@@ -1045,18 +1077,7 @@ static int o_get_last_ptr(o_string *o, int n) | |||
1045 | } | 1077 | } |
1046 | 1078 | ||
1047 | /* o_glob performs globbing on last list[], saving each result | 1079 | /* o_glob performs globbing on last list[], saving each result |
1048 | * as a new list[]. unbackslash() is just a helper */ | 1080 | * as a new list[]. */ |
1049 | static char *unbackslash(char *src) | ||
1050 | { | ||
1051 | char *dst = src; | ||
1052 | while (1) { | ||
1053 | if (*src == '\\') | ||
1054 | src++; | ||
1055 | if ((*dst++ = *src++) == '\0') | ||
1056 | break; | ||
1057 | } | ||
1058 | return dst; | ||
1059 | } | ||
1060 | static int o_glob(o_string *o, int n) | 1081 | static int o_glob(o_string *o, int n) |
1061 | { | 1082 | { |
1062 | glob_t globdata; | 1083 | glob_t globdata; |
@@ -1310,7 +1331,8 @@ static int setup_redirects(struct child_prog *prog, int squirrel[]) | |||
1310 | } | 1331 | } |
1311 | if (redir->dup == -1) { | 1332 | if (redir->dup == -1) { |
1312 | char *p; | 1333 | char *p; |
1313 | mode = redir_table[redir->type].mode; | 1334 | mode = redir_table[redir->rd_type].mode; |
1335 | //TODO: check redir to names like '\\' | ||
1314 | p = expand_string_to_string(redir->rd_filename); | 1336 | p = expand_string_to_string(redir->rd_filename); |
1315 | openfd = open_or_warn(p, mode); | 1337 | openfd = open_or_warn(p, mode); |
1316 | free(p); | 1338 | free(p); |
@@ -1765,6 +1787,7 @@ static int run_pipe(struct pipe *pi) | |||
1765 | for (i = 0; is_assignment(argv[i]); i++) { | 1787 | for (i = 0; is_assignment(argv[i]); i++) { |
1766 | p = expand_string_to_string(argv[i]); | 1788 | p = expand_string_to_string(argv[i]); |
1767 | putenv(p); | 1789 | putenv(p); |
1790 | //FIXME: do we leak p?! | ||
1768 | } | 1791 | } |
1769 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { | 1792 | for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { |
1770 | if (strcmp(argv[i], x->cmd) == 0) { | 1793 | if (strcmp(argv[i], x->cmd) == 0) { |
@@ -2300,7 +2323,10 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
2300 | while (1) { | 2323 | while (1) { |
2301 | int word_len = strcspn(str, ifs); | 2324 | int word_len = strcspn(str, ifs); |
2302 | if (word_len) { | 2325 | if (word_len) { |
2303 | o_addstr(output, str, word_len); | 2326 | if (output->o_quote || !output->o_glob) |
2327 | o_addQstr(output, str, word_len); | ||
2328 | else /* protect backslashes against globbing up :) */ | ||
2329 | o_addstr_duplicate_backslash(output, str, word_len); | ||
2304 | str += word_len; | 2330 | str += word_len; |
2305 | } | 2331 | } |
2306 | if (!*str) /* EOL - do not finalize word */ | 2332 | if (!*str) /* EOL - do not finalize word */ |
@@ -2324,7 +2350,8 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
2324 | static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | 2350 | static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) |
2325 | { | 2351 | { |
2326 | /* or_mask is either 0 (normal case) or 0x80 | 2352 | /* or_mask is either 0 (normal case) or 0x80 |
2327 | * (expansion of right-hand side of assignment == 1-element expand) */ | 2353 | * (expansion of right-hand side of assignment == 1-element expand. |
2354 | * It will also do no globbing, and thus we must not backslash-quote!) */ | ||
2328 | 2355 | ||
2329 | char first_ch, ored_ch; | 2356 | char first_ch, ored_ch; |
2330 | int i; | 2357 | int i; |
@@ -2372,6 +2399,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2372 | break; | 2399 | break; |
2373 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ | 2400 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ |
2374 | while (global_argv[i]) { | 2401 | while (global_argv[i]) { |
2402 | //see expand_on_ifs below - same?? | ||
2375 | n = expand_on_ifs(output, n, global_argv[i]); | 2403 | n = expand_on_ifs(output, n, global_argv[i]); |
2376 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, global_argc-1); | 2404 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, global_argc-1); |
2377 | if (global_argv[i++][0] && global_argv[i]) { | 2405 | if (global_argv[i++][0] && global_argv[i]) { |
@@ -2429,9 +2457,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2429 | arg[0] = first_ch & 0x7f; | 2457 | arg[0] = first_ch & 0x7f; |
2430 | if (isdigit(arg[0])) { | 2458 | if (isdigit(arg[0])) { |
2431 | i = xatoi_u(arg); | 2459 | i = xatoi_u(arg); |
2432 | val = NULL; | ||
2433 | if (i < global_argc) | 2460 | if (i < global_argc) |
2434 | val = global_argv[i]; | 2461 | val = global_argv[i]; |
2462 | /* else val remains NULL: $N with too big N */ | ||
2435 | } else | 2463 | } else |
2436 | val = lookup_param(arg); | 2464 | val = lookup_param(arg); |
2437 | arg[0] = first_ch; | 2465 | arg[0] = first_ch; |
@@ -2442,11 +2470,16 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2442 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | 2470 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ |
2443 | debug_printf_expand("unquoted '%s', output->o_quote:%d\n", val, output->o_quote); | 2471 | debug_printf_expand("unquoted '%s', output->o_quote:%d\n", val, output->o_quote); |
2444 | if (val) { | 2472 | if (val) { |
2473 | /* unquoted var's contents should be globbed, so don't quote */ | ||
2474 | smallint sv = output->o_quote; | ||
2475 | output->o_quote = 0; | ||
2445 | n = expand_on_ifs(output, n, val); | 2476 | n = expand_on_ifs(output, n, val); |
2446 | val = NULL; | 2477 | val = NULL; |
2478 | output->o_quote = sv; | ||
2447 | } | 2479 | } |
2448 | } else /* quoted $VAR, val will be appended below */ | 2480 | } else { /* quoted $VAR, val will be appended below */ |
2449 | debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote); | 2481 | debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote); |
2482 | } | ||
2450 | } | 2483 | } |
2451 | if (val) { | 2484 | if (val) { |
2452 | o_addQstr(output, val, strlen(val)); ///maybe q? | 2485 | o_addQstr(output, val, strlen(val)); ///maybe q? |
@@ -2485,6 +2518,7 @@ static char **expand_variables(char **argv, int or_mask) | |||
2485 | 2518 | ||
2486 | if (or_mask & 0x100) { | 2519 | if (or_mask & 0x100) { |
2487 | output.o_quote = 1; | 2520 | output.o_quote = 1; |
2521 | /* why? */ | ||
2488 | output.o_glob = 1; | 2522 | output.o_glob = 1; |
2489 | } | 2523 | } |
2490 | 2524 | ||
@@ -2687,7 +2721,7 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style, | |||
2687 | child->redirects = redir; | 2721 | child->redirects = redir; |
2688 | } | 2722 | } |
2689 | 2723 | ||
2690 | redir->type = style; | 2724 | redir->rd_type = style; |
2691 | redir->fd = (fd == -1) ? redir_table[style].default_fd : fd; | 2725 | redir->fd = (fd == -1) ? redir_table[style].default_fd : fd; |
2692 | 2726 | ||
2693 | debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); | 2727 | debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); |
@@ -2858,6 +2892,14 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
2858 | { | 2892 | { |
2859 | struct child_prog *child = ctx->child; | 2893 | struct child_prog *child = ctx->child; |
2860 | 2894 | ||
2895 | /* If this word wasn't an assignment, next ones definitely | ||
2896 | * can't be assignments. Even if they look like ones. */ | ||
2897 | if (word->o_assignment != DEFINITELY_ASSIGNMENT) { | ||
2898 | word->o_assignment = NOT_ASSIGNMENT; | ||
2899 | } else { | ||
2900 | word->o_assignment = MAYBE_ASSIGNMENT; | ||
2901 | } | ||
2902 | |||
2861 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, child); | 2903 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, child); |
2862 | if (word->length == 0 && word->nonnull == 0) { | 2904 | if (word->length == 0 && word->nonnull == 0) { |
2863 | debug_printf_parse("done_word return 0: true null, ignored\n"); | 2905 | debug_printf_parse("done_word return 0: true null, ignored\n"); |
@@ -2867,6 +2909,7 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
2867 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here | 2909 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here |
2868 | * only if run as "bash", not "sh" */ | 2910 | * only if run as "bash", not "sh" */ |
2869 | ctx->pending_redirect->rd_filename = xstrdup(word->data); | 2911 | ctx->pending_redirect->rd_filename = xstrdup(word->data); |
2912 | word->o_assignment = NOT_ASSIGNMENT; | ||
2870 | debug_printf("word stored in rd_filename: '%s'\n", word->data); | 2913 | debug_printf("word stored in rd_filename: '%s'\n", word->data); |
2871 | } else { | 2914 | } else { |
2872 | if (child->group) { /* TODO: example how to trigger? */ | 2915 | if (child->group) { /* TODO: example how to trigger? */ |
@@ -2878,15 +2921,18 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
2878 | debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); | 2921 | debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); |
2879 | if (reserved_word(word, ctx)) { | 2922 | if (reserved_word(word, ctx)) { |
2880 | o_reset(word); | 2923 | o_reset(word); |
2924 | word->o_assignment = NOT_ASSIGNMENT; | ||
2881 | debug_printf_parse("done_word return %d\n", (ctx->res_w == RES_SNTX)); | 2925 | debug_printf_parse("done_word return %d\n", (ctx->res_w == RES_SNTX)); |
2882 | return (ctx->res_w == RES_SNTX); | 2926 | return (ctx->res_w == RES_SNTX); |
2883 | } | 2927 | } |
2884 | } | 2928 | } |
2885 | if (word->nonnull | 2929 | if (word->nonnull /* we saw "xx" or 'xx' */ |
2886 | /* && word->data[0] != */ | 2930 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ |
2931 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | ||
2932 | /* (otherwise it's "abc".... and is already safe) */ | ||
2887 | ) { | 2933 | ) { |
2888 | /* Insert "empty variable" reference, this makes e.g. "", '', | 2934 | /* Insert "empty variable" reference, this makes |
2889 | * $empty"" etc to not disappear */ | 2935 | * e.g. "", $empty"" etc to not disappear */ |
2890 | o_addchr(word, SPECIAL_VAR_SYMBOL); | 2936 | o_addchr(word, SPECIAL_VAR_SYMBOL); |
2891 | o_addchr(word, SPECIAL_VAR_SYMBOL); | 2937 | o_addchr(word, SPECIAL_VAR_SYMBOL); |
2892 | } | 2938 | } |
@@ -3107,14 +3153,14 @@ static int process_command_subs(o_string *dest, | |||
3107 | continue; | 3153 | continue; |
3108 | } | 3154 | } |
3109 | while (eol_cnt) { | 3155 | while (eol_cnt) { |
3110 | o_addQchr(dest, '\n'); | 3156 | o_addchr(dest, '\n'); |
3111 | eol_cnt--; | 3157 | eol_cnt--; |
3112 | } | 3158 | } |
3113 | /* Even unquoted `echo '\'` results in two backslashes | 3159 | // /* Even unquoted `echo '\'` results in two backslashes |
3114 | * (which are converted into one by globbing later) */ | 3160 | // * (which are converted into one by globbing later) */ |
3115 | if (!dest->o_quote && ch == '\\') { | 3161 | // if (!dest->o_quote && ch == '\\') { |
3116 | o_addchr(dest, ch); | 3162 | // o_addchr(dest, ch); |
3117 | } | 3163 | // } |
3118 | o_addQchr(dest, ch); | 3164 | o_addQchr(dest, ch); |
3119 | } | 3165 | } |
3120 | 3166 | ||
@@ -3364,13 +3410,19 @@ static int handle_dollar(o_string *dest, struct in_str *input) | |||
3364 | return 0; | 3410 | return 0; |
3365 | } | 3411 | } |
3366 | 3412 | ||
3367 | /* Return code is 0 for normal exit, 1 for syntax error */ | 3413 | /* Scan input, call done_word() whenever full IFS delemited word was seen. |
3414 | * call done_pipe if '\n' was seen (and end_trigger != NULL) | ||
3415 | * Return if (non-quoted) char in end_trigger was seen; or on parse error. */ | ||
3416 | /* Return code is 0 if end_trigger char is met, | ||
3417 | * -1 on EOF (but if end_trigger == NULL then return 0) | ||
3418 | * 1 for syntax error */ | ||
3368 | static int parse_stream(o_string *dest, struct p_context *ctx, | 3419 | static int parse_stream(o_string *dest, struct p_context *ctx, |
3369 | struct in_str *input, const char *end_trigger) | 3420 | struct in_str *input, const char *end_trigger) |
3370 | { | 3421 | { |
3371 | int ch, m; | 3422 | int ch, m; |
3372 | int redir_fd; | 3423 | int redir_fd; |
3373 | redir_type redir_style; | 3424 | redir_type redir_style; |
3425 | int shadow_quote = dest->o_quote; | ||
3374 | int next; | 3426 | int next; |
3375 | 3427 | ||
3376 | /* Only double-quote state is handled in the state variable dest->o_quote. | 3428 | /* Only double-quote state is handled in the state variable dest->o_quote. |
@@ -3385,13 +3437,14 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3385 | ch = i_getch(input); | 3437 | ch = i_getch(input); |
3386 | if (ch != EOF) { | 3438 | if (ch != EOF) { |
3387 | m = charmap[ch]; | 3439 | m = charmap[ch]; |
3388 | if (ch != '\n') | 3440 | if (ch != '\n') { |
3389 | next = i_peek(input); | 3441 | next = i_peek(input); |
3442 | } | ||
3390 | } | 3443 | } |
3391 | debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n", | 3444 | debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n", |
3392 | ch, ch, m, dest->o_quote); | 3445 | ch, ch, m, dest->o_quote); |
3393 | if (m == CHAR_ORDINARY | 3446 | if (m == CHAR_ORDINARY |
3394 | || (m != CHAR_SPECIAL && dest->o_quote) | 3447 | || (m != CHAR_SPECIAL && shadow_quote) |
3395 | ) { | 3448 | ) { |
3396 | if (ch == EOF) { | 3449 | if (ch == EOF) { |
3397 | syntax("unterminated \""); | 3450 | syntax("unterminated \""); |
@@ -3399,6 +3452,12 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3399 | return 1; | 3452 | return 1; |
3400 | } | 3453 | } |
3401 | o_addQchr(dest, ch); | 3454 | o_addQchr(dest, ch); |
3455 | if (dest->o_assignment == MAYBE_ASSIGNMENT | ||
3456 | && ch == '=' | ||
3457 | && is_assignment(dest->data) | ||
3458 | ) { | ||
3459 | dest->o_assignment = DEFINITELY_ASSIGNMENT; | ||
3460 | } | ||
3402 | continue; | 3461 | continue; |
3403 | } | 3462 | } |
3404 | if (m == CHAR_IFS) { | 3463 | if (m == CHAR_IFS) { |
@@ -3416,11 +3475,12 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3416 | } | 3475 | } |
3417 | } | 3476 | } |
3418 | if (end_trigger) { | 3477 | if (end_trigger) { |
3419 | if (!dest->o_quote && strchr(end_trigger, ch)) { | 3478 | if (!shadow_quote && strchr(end_trigger, ch)) { |
3420 | /* Special case: (...word) makes last word terminate, | 3479 | /* Special case: (...word) makes last word terminate, |
3421 | * as if ';' is seen */ | 3480 | * as if ';' is seen */ |
3422 | if (ch == ')') { | 3481 | if (ch == ')') { |
3423 | done_word(dest, ctx); | 3482 | done_word(dest, ctx); |
3483 | //err chk? | ||
3424 | done_pipe(ctx, PIPE_SEQ); | 3484 | done_pipe(ctx, PIPE_SEQ); |
3425 | } | 3485 | } |
3426 | if (ctx->res_w == RES_NONE) { | 3486 | if (ctx->res_w == RES_NONE) { |
@@ -3431,9 +3491,16 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3431 | } | 3491 | } |
3432 | if (m == CHAR_IFS) | 3492 | if (m == CHAR_IFS) |
3433 | continue; | 3493 | continue; |
3494 | |||
3495 | if (dest->o_assignment == MAYBE_ASSIGNMENT) { | ||
3496 | /* ch is a special char and thus this word | ||
3497 | * cannot be an assignment: */ | ||
3498 | dest->o_assignment = NOT_ASSIGNMENT; | ||
3499 | } | ||
3500 | |||
3434 | switch (ch) { | 3501 | switch (ch) { |
3435 | case '#': | 3502 | case '#': |
3436 | if (dest->length == 0 && !dest->o_quote) { | 3503 | if (dest->length == 0 && !shadow_quote) { |
3437 | while (1) { | 3504 | while (1) { |
3438 | ch = i_peek(input); | 3505 | ch = i_peek(input); |
3439 | if (ch == EOF || ch == '\n') | 3506 | if (ch == EOF || ch == '\n') |
@@ -3459,7 +3526,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3459 | * an ! appearing in double quotes is escaped using | 3526 | * an ! appearing in double quotes is escaped using |
3460 | * a backslash. The backslash preceding the ! is not removed." | 3527 | * a backslash. The backslash preceding the ! is not removed." |
3461 | */ | 3528 | */ |
3462 | if (dest->o_quote) { | 3529 | if (shadow_quote) { //NOT SURE dest->o_quote) { |
3463 | if (strchr("$`\"\\", next) != NULL) { | 3530 | if (strchr("$`\"\\", next) != NULL) { |
3464 | o_addqchr(dest, i_getch(input)); | 3531 | o_addqchr(dest, i_getch(input)); |
3465 | } else { | 3532 | } else { |
@@ -3487,18 +3554,23 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3487 | } | 3554 | } |
3488 | if (ch == '\'') | 3555 | if (ch == '\'') |
3489 | break; | 3556 | break; |
3490 | o_addqchr(dest, ch); | 3557 | if (dest->o_assignment == NOT_ASSIGNMENT) |
3558 | o_addqchr(dest, ch); | ||
3559 | else | ||
3560 | o_addchr(dest, ch); | ||
3491 | } | 3561 | } |
3492 | break; | 3562 | break; |
3493 | case '"': | 3563 | case '"': |
3494 | dest->nonnull = 1; | 3564 | dest->nonnull = 1; |
3495 | dest->o_quote ^= 1; /* invert */ | 3565 | shadow_quote ^= 1; /* invert */ |
3566 | if (dest->o_assignment == NOT_ASSIGNMENT) | ||
3567 | dest->o_quote ^= 1; | ||
3496 | break; | 3568 | break; |
3497 | #if ENABLE_HUSH_TICK | 3569 | #if ENABLE_HUSH_TICK |
3498 | case '`': { | 3570 | case '`': { |
3499 | //int pos = dest->length; | 3571 | //int pos = dest->length; |
3500 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 3572 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3501 | o_addchr(dest, dest->o_quote ? 0x80 | '`' : '`'); | 3573 | o_addchr(dest, shadow_quote /*or dest->o_quote??*/ ? 0x80 | '`' : '`'); |
3502 | add_till_backquote(dest, input); | 3574 | add_till_backquote(dest, input); |
3503 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 3575 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3504 | //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); | 3576 | //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); |
@@ -3585,14 +3657,6 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3585 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); | 3657 | bb_error_msg_and_die("BUG: unexpected %c\n", ch); |
3586 | } | 3658 | } |
3587 | } /* while (1) */ | 3659 | } /* while (1) */ |
3588 | /* Complain if quote? No, maybe we just finished a command substitution | ||
3589 | * that was quoted. Example: | ||
3590 | * $ echo "`cat foo` plus more" | ||
3591 | * and we just got the EOF generated by the subshell that ran "cat foo" | ||
3592 | * The only real complaint is if we got an EOF when end_trigger != NULL, | ||
3593 | * that is, we were really supposed to get end_trigger, and never got | ||
3594 | * one before the EOF. Can't use the standard "syntax error" return code, | ||
3595 | * so that parse_stream_outer can distinguish the EOF and exit smoothly. */ | ||
3596 | debug_printf_parse("parse_stream return %d\n", -(end_trigger != NULL)); | 3660 | debug_printf_parse("parse_stream return %d\n", -(end_trigger != NULL)); |
3597 | if (end_trigger) | 3661 | if (end_trigger) |
3598 | return -1; | 3662 | return -1; |