summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-08-04 00:46:07 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-08-04 00:46:07 +0000
commit2b576b8e76ee0dc548f46489e2546b7ed70d080d (patch)
treea04f9fb858ba1180f04319e656a0b43623655bca
parentc8bec9a0852ce6ce09a67d8a688623e32735378e (diff)
downloadbusybox-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.c128
-rw-r--r--shell/hush_test/hush-misc/assignment1.right9
-rwxr-xr-xshell/hush_test/hush-misc/assignment1.tests42
-rw-r--r--shell/hush_test/hush-misc/assignment2.rigth2
-rwxr-xr-xshell/hush_test/hush-misc/assignment2.tests4
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
319struct child_prog { 319struct 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);
524static int setup_redirects(struct child_prog *prog, int squirrel[]); 526static int setup_redirects(struct child_prog *prog, int squirrel[]);
525static int run_list(struct pipe *pi); 527static 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
530static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded) NORETURN; 534static void pseudo_exec_argv(char **ptrs2free, char **argv, int assignment_cnt, char **argv_expanded) NORETURN;
531static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) NORETURN; 535static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) NORETURN;
532static int run_pipe(struct pipe *pi); 536static 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) */
1393static void pseudo_exec_argv(char **ptrs2free, char **argv, char **argv_expanded) 1397static 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
1466static void pseudo_exec(char **ptrs2free, struct child_prog *child, char **argv_expanded) 1470static 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
2905static int reserved_word(const o_string *word, struct p_context *ctx) 2907static 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,
3367static int parse_group(o_string *dest, struct p_context *ctx, 3370static 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 @@
1if1:0
2while1:0
3until1:0
4if2:0
5while2:0
6until2:0
7if3:0
8while3:0
9until3: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
3if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi
4echo if1:$?
5while a=1 true; do a=1 true; break; done
6echo while1:$?
7until a=1 false; do a=1 true; break; done
8echo until1:$?
9
10if 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
16echo if2:$?
17while a=1 true
18 do a=1 true
19 break
20 done
21echo while2:$?
22until a=1 false
23 do a=1 true
24 break
25 done
26echo until2:$?
27
28if
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
34echo if3:$?
35while
36 a=1 true; do
37 a=1 true; break; done
38echo while3:$?
39until
40 a=1 false; do
41 a=1 true; break; done
42echo 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 @@
1hush: can't exec 'a=b': No such file or directory
21
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
2a''=b true
3echo $?
4# (buglet: $? should be 127. it is currently 1)