diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-08-04 00:46:07 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-08-04 00:46:07 +0000 |
commit | 2b576b8e76ee0dc548f46489e2546b7ed70d080d (patch) | |
tree | a04f9fb858ba1180f04319e656a0b43623655bca | |
parent | c8bec9a0852ce6ce09a67d8a688623e32735378e (diff) | |
download | busybox-w32-2b576b8e76ee0dc548f46489e2546b7ed70d080d.tar.gz busybox-w32-2b576b8e76ee0dc548f46489e2546b7ed70d080d.tar.bz2 busybox-w32-2b576b8e76ee0dc548f46489e2546b7ed70d080d.zip |
hush: fix mishandling of a'b'c=fff as assignments. They are not.
function old new delta
parse_stream 1920 2004 +84
done_word 715 752 +37
parse_and_run_stream 328 333 +5
builtin_exec 25 29 +4
pseudo_exec_argv 138 139 +1
run_list 2006 1999 -7
is_assignment 215 134 -81
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/2 up/down: 131/-88) Total: 43 bytes
-rw-r--r-- | shell/hush.c | 128 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/assignment1.right | 9 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/assignment1.tests | 42 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/assignment2.rigth | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/assignment2.tests | 4 |
5 files changed, 127 insertions, 58 deletions
diff --git a/shell/hush.c b/shell/hush.c index ce25e127d..78622d106 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -318,10 +318,12 @@ struct redir_struct { | |||
318 | 318 | ||
319 | struct child_prog { | 319 | struct child_prog { |
320 | pid_t pid; /* 0 if exited */ | 320 | pid_t pid; /* 0 if exited */ |
321 | int assignment_cnt; /* how many argv[i] are assignments? */ | ||
321 | smallint is_stopped; /* is the program currently running? */ | 322 | smallint is_stopped; /* is the program currently running? */ |
322 | smallint subshell; /* flag, non-zero if group must be forked */ | 323 | smallint subshell; /* flag, non-zero if group must be forked */ |
324 | struct pipe *group; /* if non-NULL, this "prog" is {} group, | ||
325 | * subshell, or a compound statement */ | ||
323 | char **argv; /* program name and arguments */ | 326 | char **argv; /* program name and arguments */ |
324 | struct pipe *group; /* if non-NULL, first in group or subshell */ | ||
325 | struct redir_struct *redirects; /* I/O redirections */ | 327 | struct redir_struct *redirects; /* I/O redirections */ |
326 | }; | 328 | }; |
327 | /* argv vector may contain variable references (^Cvar^C, ^C0^C etc) | 329 | /* argv vector may contain variable references (^Cvar^C, ^C0^C etc) |
@@ -377,6 +379,7 @@ enum { | |||
377 | MAYBE_ASSIGNMENT = 0, | 379 | MAYBE_ASSIGNMENT = 0, |
378 | DEFINITELY_ASSIGNMENT = 1, | 380 | DEFINITELY_ASSIGNMENT = 1, |
379 | NOT_ASSIGNMENT = 2, | 381 | NOT_ASSIGNMENT = 2, |
382 | WORD_IS_KEYWORD = 3, /* not assigment, but next word may be: "if v=xyz cmd;" */ | ||
380 | }; | 383 | }; |
381 | /* Used for initialization: o_string foo = NULL_O_STRING; */ | 384 | /* Used for initialization: o_string foo = NULL_O_STRING; */ |
382 | #define NULL_O_STRING { NULL } | 385 | #define NULL_O_STRING { NULL } |
@@ -430,7 +433,6 @@ struct globals { | |||
430 | pid_t last_bg_pid; | 433 | pid_t last_bg_pid; |
431 | #if ENABLE_HUSH_JOB | 434 | #if ENABLE_HUSH_JOB |
432 | int run_list_level; | 435 | int run_list_level; |
433 | // pid_t saved_task_pgrp; | ||
434 | pid_t saved_tty_pgrp; | 436 | pid_t saved_tty_pgrp; |
435 | int last_jobid; | 437 | int last_jobid; |
436 | struct pipe *job_list; | 438 | struct pipe *job_list; |
@@ -524,10 +526,12 @@ static int free_pipe(struct pipe *pi, int indent); | |||
524 | static int setup_redirects(struct child_prog *prog, int squirrel[]); | 526 | static int setup_redirects(struct child_prog *prog, int squirrel[]); |
525 | static int run_list(struct pipe *pi); | 527 | static int run_list(struct pipe *pi); |
526 | #if BB_MMU | 528 | #if BB_MMU |
527 | #define pseudo_exec_argv(ptrs2free, argv, argv_expanded) pseudo_exec_argv(argv, argv_expanded) | 529 | #define pseudo_exec_argv(ptrs2free, argv, assignment_cnt, argv_expanded) \ |
528 | #define pseudo_exec(ptrs2free, child, argv_expanded) pseudo_exec(child, argv_expanded) | 530 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) |
531 | #define pseudo_exec(ptrs2free, child, argv_expanded) \ | ||
532 | pseudo_exec(child, argv_expanded) | ||
529 | #endif | 533 | #endif |
530 | static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded) NORETURN; | 534 | static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; |
531 | static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) NORETURN; | 535 | static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) NORETURN; |
532 | static int run_pipe(struct pipe *pi); | 536 | static int run_pipe(struct pipe *pi); |
533 | /* data structure manipulation: */ | 537 | /* data structure manipulation: */ |
@@ -1390,13 +1394,13 @@ static void restore_redirects(int squirrel[]) | |||
1390 | * XXX no exit() here. If you don't exec, use _exit instead. | 1394 | * XXX no exit() here. If you don't exec, use _exit instead. |
1391 | * The at_exit handlers apparently confuse the calling process, | 1395 | * The at_exit handlers apparently confuse the calling process, |
1392 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ | 1396 | * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ |
1393 | static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded) | 1397 | static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, char **argv_expanded) |
1394 | { | 1398 | { |
1395 | int i, rcode; | 1399 | int i, rcode; |
1396 | char *p; | 1400 | char *p; |
1397 | const struct built_in_command *x; | 1401 | const struct built_in_command *x; |
1398 | 1402 | ||
1399 | for (i = 0; is_assignment(argv[i]); i++) { | 1403 | for (i = 0; i < assignment_cnt; i++) { |
1400 | debug_printf_exec("pid %d environment modification: %s\n", | 1404 | debug_printf_exec("pid %d environment modification: %s\n", |
1401 | getpid(), argv[i]); | 1405 | getpid(), argv[i]); |
1402 | p = expand_string_to_string(argv[i]); | 1406 | p = expand_string_to_string(argv[i]); |
@@ -1466,7 +1470,7 @@ static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded | |||
1466 | static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) | 1470 | static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) |
1467 | { | 1471 | { |
1468 | if (child->argv) | 1472 | if (child->argv) |
1469 | pseudo_exec_argv(ptrs2free, child->argv, argv_expanded); | 1473 | pseudo_exec_argv(ptrs2free, child->argv, child->assignment_cnt, argv_expanded); |
1470 | 1474 | ||
1471 | if (child->group) { | 1475 | if (child->group) { |
1472 | #if !BB_MMU | 1476 | #if !BB_MMU |
@@ -1713,8 +1717,6 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) | |||
1713 | p = getpgid(0); /* pgid of our process */ | 1717 | p = getpgid(0); /* pgid of our process */ |
1714 | debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p); | 1718 | debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p); |
1715 | tcsetpgrp(G.interactive_fd, p); | 1719 | tcsetpgrp(G.interactive_fd, p); |
1716 | // if (tcsetpgrp(G.interactive_fd, p) && errno != ENOTTY) | ||
1717 | // bb_perror_msg("tcsetpgrp-4a"); | ||
1718 | return rcode; | 1720 | return rcode; |
1719 | } | 1721 | } |
1720 | #endif | 1722 | #endif |
@@ -1780,8 +1782,7 @@ static int run_pipe(struct pipe *pi) | |||
1780 | argv = child->argv; | 1782 | argv = child->argv; |
1781 | 1783 | ||
1782 | if (single_and_fg && argv != NULL) { | 1784 | if (single_and_fg && argv != NULL) { |
1783 | for (i = 0; is_assignment(argv[i]); i++) | 1785 | i = child->assignment_cnt; |
1784 | continue; | ||
1785 | if (i != 0 && argv[i] == NULL) { | 1786 | if (i != 0 && argv[i] == NULL) { |
1786 | /* assignments, but no command: set local environment */ | 1787 | /* assignments, but no command: set local environment */ |
1787 | for (i = 0; argv[i] != NULL; i++) { | 1788 | for (i = 0; argv[i] != NULL; i++) { |
@@ -1793,7 +1794,7 @@ static int run_pipe(struct pipe *pi) | |||
1793 | } | 1794 | } |
1794 | 1795 | ||
1795 | /* Expand assignments into one string each */ | 1796 | /* Expand assignments into one string each */ |
1796 | for (i = 0; is_assignment(argv[i]); i++) { | 1797 | for (i = 0; i < child->assignment_cnt; i++) { |
1797 | p = expand_string_to_string(argv[i]); | 1798 | p = expand_string_to_string(argv[i]); |
1798 | putenv(p); | 1799 | putenv(p); |
1799 | //FIXME: do we leak p?! | 1800 | //FIXME: do we leak p?! |
@@ -1991,7 +1992,7 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
1991 | struct child_prog *child = &pi->progs[prn]; | 1992 | struct child_prog *child = &pi->progs[prn]; |
1992 | char **argv = child->argv; | 1993 | char **argv = child->argv; |
1993 | 1994 | ||
1994 | fprintf(stderr, "%*s prog %d", lvl*2, "", prn); | 1995 | fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, child->assignment_cnt); |
1995 | if (child->group) { | 1996 | if (child->group) { |
1996 | fprintf(stderr, " group %s: (argv=%p)\n", | 1997 | fprintf(stderr, " group %s: (argv=%p)\n", |
1997 | (child->subshell ? "()" : "{}"), | 1998 | (child->subshell ? "()" : "{}"), |
@@ -2165,30 +2166,31 @@ static int run_list(struct pipe *pi) | |||
2165 | vals = (char**)encoded_dollar_at_argv; | 2166 | vals = (char**)encoded_dollar_at_argv; |
2166 | if (pi->next->res_word == RES_IN) { | 2167 | if (pi->next->res_word == RES_IN) { |
2167 | /* if no variable values after "in" we skip "for" */ | 2168 | /* if no variable values after "in" we skip "for" */ |
2168 | if (!pi->next->progs->argv) | 2169 | if (!pi->next->progs[0].argv) |
2169 | break; | 2170 | break; |
2170 | vals = pi->next->progs->argv; | 2171 | vals = pi->next->progs[0].argv; |
2171 | } /* else: "for var; do..." -> assume "$@" list */ | 2172 | } /* else: "for var; do..." -> assume "$@" list */ |
2172 | /* create list of variable values */ | 2173 | /* create list of variable values */ |
2173 | debug_print_strings("for_list made from", vals); | 2174 | debug_print_strings("for_list made from", vals); |
2174 | for_list = expand_strvec_to_strvec(vals); | 2175 | for_list = expand_strvec_to_strvec(vals); |
2175 | for_lcur = for_list; | 2176 | for_lcur = for_list; |
2176 | debug_print_strings("for_list", for_list); | 2177 | debug_print_strings("for_list", for_list); |
2177 | for_varname = pi->progs->argv[0]; | 2178 | for_varname = pi->progs[0].argv[0]; |
2178 | pi->progs->argv[0] = NULL; | 2179 | pi->progs[0].argv[0] = NULL; |
2179 | } | 2180 | } |
2180 | free(pi->progs->argv[0]); | 2181 | free(pi->progs[0].argv[0]); |
2181 | if (!*for_lcur) { | 2182 | if (!*for_lcur) { |
2182 | /* "for" loop is over, clean up */ | 2183 | /* "for" loop is over, clean up */ |
2183 | free(for_list); | 2184 | free(for_list); |
2184 | for_list = NULL; | 2185 | for_list = NULL; |
2185 | for_lcur = NULL; | 2186 | for_lcur = NULL; |
2186 | pi->progs->argv[0] = for_varname; | 2187 | pi->progs[0].argv[0] = for_varname; |
2187 | break; | 2188 | break; |
2188 | } | 2189 | } |
2189 | /* insert next value from for_lcur */ | 2190 | /* insert next value from for_lcur */ |
2190 | //TODO: does it need escaping? | 2191 | //TODO: does it need escaping? |
2191 | pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); | 2192 | pi->progs[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); |
2193 | pi->progs[0].assignment_cnt = 1; | ||
2192 | } | 2194 | } |
2193 | if (rword == RES_IN) /* "for v IN list;..." - "in" has no cmds anyway */ | 2195 | if (rword == RES_IN) /* "for v IN list;..." - "in" has no cmds anyway */ |
2194 | continue; | 2196 | continue; |
@@ -2902,11 +2904,12 @@ static void initialize_context(struct p_context *ctx) | |||
2902 | * case, function, and select are obnoxious, save those for later. | 2904 | * case, function, and select are obnoxious, save those for later. |
2903 | */ | 2905 | */ |
2904 | #if HAS_KEYWORDS | 2906 | #if HAS_KEYWORDS |
2905 | static int reserved_word(const o_string *word, struct p_context *ctx) | 2907 | static int reserved_word(o_string *word, struct p_context *ctx) |
2906 | { | 2908 | { |
2907 | struct reserved_combo { | 2909 | struct reserved_combo { |
2908 | char literal[7]; | 2910 | char literal[6]; |
2909 | unsigned char res; | 2911 | unsigned char res; |
2912 | unsigned char assignment_flag; | ||
2910 | int flag; | 2913 | int flag; |
2911 | }; | 2914 | }; |
2912 | enum { | 2915 | enum { |
@@ -2939,29 +2942,29 @@ static int reserved_word(const o_string *word, struct p_context *ctx) | |||
2939 | */ | 2942 | */ |
2940 | static const struct reserved_combo reserved_list[] = { | 2943 | static const struct reserved_combo reserved_list[] = { |
2941 | #if ENABLE_HUSH_IF | 2944 | #if ENABLE_HUSH_IF |
2942 | { "!", RES_NONE, 0 }, | 2945 | { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, |
2943 | { "if", RES_IF, FLAG_THEN | FLAG_START }, | 2946 | { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, |
2944 | { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, | 2947 | { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
2945 | { "elif", RES_ELIF, FLAG_THEN }, | 2948 | { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, |
2946 | { "else", RES_ELSE, FLAG_FI }, | 2949 | { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, |
2947 | { "fi", RES_FI, FLAG_END }, | 2950 | { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, |
2948 | #endif | 2951 | #endif |
2949 | #if ENABLE_HUSH_LOOPS | 2952 | #if ENABLE_HUSH_LOOPS |
2950 | { "for", RES_FOR, FLAG_IN | FLAG_DO | FLAG_START }, | 2953 | { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, |
2951 | { "while", RES_WHILE, FLAG_DO | FLAG_START }, | 2954 | { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, |
2952 | { "until", RES_UNTIL, FLAG_DO | FLAG_START }, | 2955 | { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, |
2953 | { "in", RES_IN, FLAG_DO }, | 2956 | { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, |
2954 | { "do", RES_DO, FLAG_DONE }, | 2957 | { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, |
2955 | { "done", RES_DONE, FLAG_END }, | 2958 | { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, |
2956 | #endif | 2959 | #endif |
2957 | #if ENABLE_HUSH_CASE | 2960 | #if ENABLE_HUSH_CASE |
2958 | { "case", RES_CASE, FLAG_MATCH | FLAG_START }, | 2961 | { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, |
2959 | { "esac", RES_ESAC, FLAG_END }, | 2962 | { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, |
2960 | #endif | 2963 | #endif |
2961 | }; | 2964 | }; |
2962 | #if ENABLE_HUSH_CASE | 2965 | #if ENABLE_HUSH_CASE |
2963 | static const struct reserved_combo reserved_match = { | 2966 | static const struct reserved_combo reserved_match = { |
2964 | "" /* "match" */, RES_MATCH, FLAG_MATCH | FLAG_ESAC | 2967 | "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC |
2965 | }; | 2968 | }; |
2966 | #endif | 2969 | #endif |
2967 | const struct reserved_combo *r; | 2970 | const struct reserved_combo *r; |
@@ -3008,6 +3011,7 @@ static int reserved_word(const o_string *word, struct p_context *ctx) | |||
3008 | *ctx = *old; /* physical copy */ | 3011 | *ctx = *old; /* physical copy */ |
3009 | free(old); | 3012 | free(old); |
3010 | } | 3013 | } |
3014 | word->o_assignment = r->assignment_flag; | ||
3011 | return 1; | 3015 | return 1; |
3012 | } | 3016 | } |
3013 | return 0; | 3017 | return 0; |
@@ -3021,17 +3025,22 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
3021 | struct child_prog *child = ctx->child; | 3025 | struct child_prog *child = ctx->child; |
3022 | 3026 | ||
3023 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, child); | 3027 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, child); |
3028 | if (word->length == 0 && word->nonnull == 0) { | ||
3029 | debug_printf_parse("done_word return 0: true null, ignored\n"); | ||
3030 | return 0; | ||
3031 | } | ||
3024 | /* If this word wasn't an assignment, next ones definitely | 3032 | /* If this word wasn't an assignment, next ones definitely |
3025 | * can't be assignments. Even if they look like ones. */ | 3033 | * can't be assignments. Even if they look like ones. */ |
3026 | if (word->o_assignment != DEFINITELY_ASSIGNMENT) { | 3034 | if (word->o_assignment != DEFINITELY_ASSIGNMENT |
3035 | && word->o_assignment != WORD_IS_KEYWORD | ||
3036 | ) { | ||
3027 | word->o_assignment = NOT_ASSIGNMENT; | 3037 | word->o_assignment = NOT_ASSIGNMENT; |
3028 | } else { | 3038 | } else { |
3039 | if (word->o_assignment == DEFINITELY_ASSIGNMENT) | ||
3040 | child->assignment_cnt++; | ||
3029 | word->o_assignment = MAYBE_ASSIGNMENT; | 3041 | word->o_assignment = MAYBE_ASSIGNMENT; |
3030 | } | 3042 | } |
3031 | if (word->length == 0 && word->nonnull == 0) { | 3043 | |
3032 | debug_printf_parse("done_word return 0: true null, ignored\n"); | ||
3033 | return 0; | ||
3034 | } | ||
3035 | if (ctx->pending_redirect) { | 3044 | if (ctx->pending_redirect) { |
3036 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here | 3045 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here |
3037 | * only if run as "bash", not "sh" */ | 3046 | * only if run as "bash", not "sh" */ |
@@ -3066,7 +3075,6 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
3066 | debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); | 3075 | debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); |
3067 | if (reserved_word(word, ctx)) { | 3076 | if (reserved_word(word, ctx)) { |
3068 | o_reset(word); | 3077 | o_reset(word); |
3069 | word->o_assignment = NOT_ASSIGNMENT; | ||
3070 | debug_printf_parse("done_word return %d\n", (ctx->ctx_res_w == RES_SNTX)); | 3078 | debug_printf_parse("done_word return %d\n", (ctx->ctx_res_w == RES_SNTX)); |
3071 | return (ctx->ctx_res_w == RES_SNTX); | 3079 | return (ctx->ctx_res_w == RES_SNTX); |
3072 | } | 3080 | } |
@@ -3343,11 +3351,6 @@ static int process_command_subs(o_string *dest, | |||
3343 | o_addchr(dest, '\n'); | 3351 | o_addchr(dest, '\n'); |
3344 | eol_cnt--; | 3352 | eol_cnt--; |
3345 | } | 3353 | } |
3346 | // /* Even unquoted `echo '\'` results in two backslashes | ||
3347 | // * (which are converted into one by globbing later) */ | ||
3348 | // if (!dest->o_quote && ch == '\\') { | ||
3349 | // o_addchr(dest, ch); | ||
3350 | // } | ||
3351 | o_addQchr(dest, ch); | 3354 | o_addQchr(dest, ch); |
3352 | } | 3355 | } |
3353 | 3356 | ||
@@ -3367,6 +3370,9 @@ static int process_command_subs(o_string *dest, | |||
3367 | static int parse_group(o_string *dest, struct p_context *ctx, | 3370 | static int parse_group(o_string *dest, struct p_context *ctx, |
3368 | struct in_str *input, int ch) | 3371 | struct in_str *input, int ch) |
3369 | { | 3372 | { |
3373 | /* NB: parse_group may create and use its own o_string, | ||
3374 | * without any code changes. It just so happens that code is smaller | ||
3375 | * if we (ab)use caller's one. */ | ||
3370 | int rcode; | 3376 | int rcode; |
3371 | const char *endch = NULL; | 3377 | const char *endch = NULL; |
3372 | struct p_context sub; | 3378 | struct p_context sub; |
@@ -3624,7 +3630,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3624 | * A single-quote triggers a bypass of the main loop until its mate is | 3630 | * A single-quote triggers a bypass of the main loop until its mate is |
3625 | * found. When recursing, quote state is passed in via dest->o_quote. */ | 3631 | * found. When recursing, quote state is passed in via dest->o_quote. */ |
3626 | 3632 | ||
3627 | debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger); | 3633 | debug_printf_parse("parse_stream entered, end_trigger='%s' dest->o_assignment:%d\n", end_trigger, dest->o_assignment); |
3628 | 3634 | ||
3629 | while (1) { | 3635 | while (1) { |
3630 | m = CHAR_IFS; | 3636 | m = CHAR_IFS; |
@@ -3647,7 +3653,8 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3647 | return 1; | 3653 | return 1; |
3648 | } | 3654 | } |
3649 | o_addQchr(dest, ch); | 3655 | o_addQchr(dest, ch); |
3650 | if (dest->o_assignment == MAYBE_ASSIGNMENT | 3656 | if ((dest->o_assignment == MAYBE_ASSIGNMENT |
3657 | || dest->o_assignment == WORD_IS_KEYWORD) | ||
3651 | && ch == '=' | 3658 | && ch == '=' |
3652 | && is_assignment(dest->data) | 3659 | && is_assignment(dest->data) |
3653 | ) { | 3660 | ) { |
@@ -3676,6 +3683,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3676 | } | 3683 | } |
3677 | #endif | 3684 | #endif |
3678 | done_pipe(ctx, PIPE_SEQ); | 3685 | done_pipe(ctx, PIPE_SEQ); |
3686 | dest->o_assignment = MAYBE_ASSIGNMENT; | ||
3679 | } | 3687 | } |
3680 | } | 3688 | } |
3681 | if (end_trigger) { | 3689 | if (end_trigger) { |
@@ -3686,6 +3694,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3686 | done_word(dest, ctx); | 3694 | done_word(dest, ctx); |
3687 | //err chk? | 3695 | //err chk? |
3688 | done_pipe(ctx, PIPE_SEQ); | 3696 | done_pipe(ctx, PIPE_SEQ); |
3697 | dest->o_assignment = MAYBE_ASSIGNMENT; | ||
3689 | } | 3698 | } |
3690 | if (!HAS_KEYWORDS | 3699 | if (!HAS_KEYWORDS |
3691 | IF_HAS_KEYWORDS(|| (ctx->ctx_res_w == RES_NONE && ctx->old_flag == 0)) | 3700 | IF_HAS_KEYWORDS(|| (ctx->ctx_res_w == RES_NONE && ctx->old_flag == 0)) |
@@ -3841,6 +3850,10 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3841 | } | 3850 | } |
3842 | } | 3851 | } |
3843 | #endif | 3852 | #endif |
3853 | new_cmd: | ||
3854 | /* We just finished a cmd. New one may start | ||
3855 | * with an assignment */ | ||
3856 | dest->o_assignment = MAYBE_ASSIGNMENT; | ||
3844 | break; | 3857 | break; |
3845 | case '&': | 3858 | case '&': |
3846 | done_word(dest, ctx); | 3859 | done_word(dest, ctx); |
@@ -3850,14 +3863,14 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3850 | } else { | 3863 | } else { |
3851 | done_pipe(ctx, PIPE_BG); | 3864 | done_pipe(ctx, PIPE_BG); |
3852 | } | 3865 | } |
3853 | break; | 3866 | goto new_cmd; |
3854 | case '|': | 3867 | case '|': |
3855 | done_word(dest, ctx); | 3868 | done_word(dest, ctx); |
3856 | #if ENABLE_HUSH_CASE | 3869 | #if ENABLE_HUSH_CASE |
3857 | if (ctx->ctx_res_w == RES_MATCH) | 3870 | if (ctx->ctx_res_w == RES_MATCH) |
3858 | break; /* we are in case's "word | word)" */ | 3871 | break; /* we are in case's "word | word)" */ |
3859 | #endif | 3872 | #endif |
3860 | if (next == '|') { | 3873 | if (next == '|') { /* || */ |
3861 | i_getch(input); | 3874 | i_getch(input); |
3862 | done_pipe(ctx, PIPE_OR); | 3875 | done_pipe(ctx, PIPE_OR); |
3863 | } else { | 3876 | } else { |
@@ -3866,7 +3879,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3866 | * "echo foo 2| cat" yields "foo 2". */ | 3879 | * "echo foo 2| cat" yields "foo 2". */ |
3867 | done_command(ctx); | 3880 | done_command(ctx); |
3868 | } | 3881 | } |
3869 | break; | 3882 | goto new_cmd; |
3870 | case '(': | 3883 | case '(': |
3871 | #if ENABLE_HUSH_CASE | 3884 | #if ENABLE_HUSH_CASE |
3872 | /* "case... in [(]word)..." - skip '(' */ | 3885 | /* "case... in [(]word)..." - skip '(' */ |
@@ -3881,7 +3894,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3881 | debug_printf_parse("parse_stream return 1: parse_group returned non-0\n"); | 3894 | debug_printf_parse("parse_stream return 1: parse_group returned non-0\n"); |
3882 | return 1; | 3895 | return 1; |
3883 | } | 3896 | } |
3884 | break; | 3897 | goto new_cmd; |
3885 | case ')': | 3898 | case ')': |
3886 | #if ENABLE_HUSH_CASE | 3899 | #if ENABLE_HUSH_CASE |
3887 | if (ctx->ctx_res_w == RES_MATCH) | 3900 | if (ctx->ctx_res_w == RES_MATCH) |
@@ -3947,6 +3960,7 @@ static int parse_and_run_stream(struct in_str *inp, int parse_flag) | |||
3947 | /* We will stop & execute after each ';' or '\n'. | 3960 | /* We will stop & execute after each ';' or '\n'. |
3948 | * Example: "sleep 9999; echo TEST" + ctrl-C: | 3961 | * Example: "sleep 9999; echo TEST" + ctrl-C: |
3949 | * TEST should be printed */ | 3962 | * TEST should be printed */ |
3963 | temp.o_assignment = MAYBE_ASSIGNMENT; | ||
3950 | rcode = parse_stream(&temp, &ctx, inp, ";\n"); | 3964 | rcode = parse_stream(&temp, &ctx, inp, ";\n"); |
3951 | #if HAS_KEYWORDS | 3965 | #if HAS_KEYWORDS |
3952 | if (rcode != 1 && ctx.old_flag != 0) { | 3966 | if (rcode != 1 && ctx.old_flag != 0) { |
@@ -4006,9 +4020,7 @@ static void setup_job_control(void) | |||
4006 | { | 4020 | { |
4007 | pid_t shell_pgrp; | 4021 | pid_t shell_pgrp; |
4008 | 4022 | ||
4009 | // G.saved_task_pgrp = | ||
4010 | shell_pgrp = getpgrp(); | 4023 | shell_pgrp = getpgrp(); |
4011 | // debug_printf_jobs("saved_task_pgrp=%d\n", G.saved_task_pgrp); | ||
4012 | close_on_exec_on(G.interactive_fd); | 4024 | close_on_exec_on(G.interactive_fd); |
4013 | 4025 | ||
4014 | /* If we were ran as 'hush &', | 4026 | /* If we were ran as 'hush &', |
@@ -4302,7 +4314,7 @@ static int builtin_exec(char **argv) | |||
4302 | char **ptrs2free = alloc_ptrs(argv); | 4314 | char **ptrs2free = alloc_ptrs(argv); |
4303 | #endif | 4315 | #endif |
4304 | // FIXME: if exec fails, bash does NOT exit! We do... | 4316 | // FIXME: if exec fails, bash does NOT exit! We do... |
4305 | pseudo_exec_argv(ptrs2free, argv + 1, NULL); | 4317 | pseudo_exec_argv(ptrs2free, argv + 1, 0, NULL); |
4306 | /* never returns */ | 4318 | /* never returns */ |
4307 | } | 4319 | } |
4308 | } | 4320 | } |
diff --git a/shell/hush_test/hush-misc/assignment1.right b/shell/hush_test/hush-misc/assignment1.right new file mode 100644 index 000000000..d0a13d3d8 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment1.right | |||
@@ -0,0 +1,9 @@ | |||
1 | if1:0 | ||
2 | while1:0 | ||
3 | until1:0 | ||
4 | if2:0 | ||
5 | while2:0 | ||
6 | until2:0 | ||
7 | if3:0 | ||
8 | while3:0 | ||
9 | until3:0 | ||
diff --git a/shell/hush_test/hush-misc/assignment1.tests b/shell/hush_test/hush-misc/assignment1.tests new file mode 100755 index 000000000..033b35250 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment1.tests | |||
@@ -0,0 +1,42 @@ | |||
1 | # Assignments after some keywords should still work | ||
2 | |||
3 | if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi | ||
4 | echo if1:$? | ||
5 | while a=1 true; do a=1 true; break; done | ||
6 | echo while1:$? | ||
7 | until a=1 false; do a=1 true; break; done | ||
8 | echo until1:$? | ||
9 | |||
10 | if a=1 true | ||
11 | then a=1 true | ||
12 | elif a=1 true | ||
13 | then a=1 true | ||
14 | else a=1 true | ||
15 | fi | ||
16 | echo if2:$? | ||
17 | while a=1 true | ||
18 | do a=1 true | ||
19 | break | ||
20 | done | ||
21 | echo while2:$? | ||
22 | until a=1 false | ||
23 | do a=1 true | ||
24 | break | ||
25 | done | ||
26 | echo until2:$? | ||
27 | |||
28 | if | ||
29 | a=1 true; then | ||
30 | a=1 true; elif | ||
31 | a=1 true; then | ||
32 | a=1 true; else | ||
33 | a=1 true; fi | ||
34 | echo if3:$? | ||
35 | while | ||
36 | a=1 true; do | ||
37 | a=1 true; break; done | ||
38 | echo while3:$? | ||
39 | until | ||
40 | a=1 false; do | ||
41 | a=1 true; break; done | ||
42 | echo until3:$? | ||
diff --git a/shell/hush_test/hush-misc/assignment2.rigth b/shell/hush_test/hush-misc/assignment2.rigth new file mode 100644 index 000000000..591552cde --- /dev/null +++ b/shell/hush_test/hush-misc/assignment2.rigth | |||
@@ -0,0 +1,2 @@ | |||
1 | hush: can't exec 'a=b': No such file or directory | ||
2 | 1 | ||
diff --git a/shell/hush_test/hush-misc/assignment2.tests b/shell/hush_test/hush-misc/assignment2.tests new file mode 100755 index 000000000..540e01ec2 --- /dev/null +++ b/shell/hush_test/hush-misc/assignment2.tests | |||
@@ -0,0 +1,4 @@ | |||
1 | # This must not be interpreted as an assignment | ||
2 | a''=b true | ||
3 | echo $? | ||
4 | # (buglet: $? should be 127. it is currently 1) | ||