From ff182a3d68462cb7ec38affa1afb04a06031862f Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sat, 5 Jul 2008 20:29:59 +0000 Subject: [PATCH] hush: support "for v; do ... done" syntax (implied 'in "$@"') --- shell/hush.c | 42 ++++++++++++++-------- shell/hush_test/hush-misc/empty_for2.right | 4 +++ shell/hush_test/hush-misc/empty_for2.tests | 6 ++++ 3 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 shell/hush_test/hush-misc/empty_for2.right create mode 100755 shell/hush_test/hush-misc/empty_for2.tests diff --git a/shell/hush.c b/shell/hush.c index 2c1d31c6d..f38f3752b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -508,7 +508,7 @@ static void syntax(const char *msg) /* Debug */ static void syntax_lineno(int line) { - void (*fp)(const char *s, ...); + void FAST_FUNC (*fp)(const char *s, ...); fp = (interactive_fd ? bb_error_msg : bb_error_msg_and_die); fp("syntax error hush.c:%d", line); @@ -2026,11 +2026,12 @@ static int run_list(struct pipe *pi) debug_printf_exec("run_list lvl %d return 1\n", run_list_level); return 1; } - if ((rpipe->res_word == RES_IN && rpipe->next->res_word == RES_IN && rpipe->next->progs[0].argv != NULL) - || (rpipe->res_word == RES_FOR && rpipe->next->res_word != RES_IN) + if (/* Extra statement after IN: "for a in a b; echo Hi; do ...; done" ? */ + (rpipe->res_word == RES_IN && rpipe->next->res_word == RES_IN && rpipe->next->progs[0].argv != NULL) + /* FOR not followed by IN or DO ("for var; do..." case)? */ + || (rpipe->res_word == RES_FOR && (rpipe->next->res_word != RES_IN && rpipe->next->res_word != RES_DO)) ) { - /* TODO: what is tested in the first condition? */ - syntax("malformed for"); /* 2nd condition: FOR not followed by IN */ + syntax("malformed for"); debug_printf_exec("run_list lvl %d return 1\n", run_list_level); return 1; } @@ -2116,12 +2117,25 @@ static int run_list(struct pipe *pi) if (rword == RES_FOR && pi->num_progs) { if (!for_lcur) { /* first loop through for */ - /* if no variable values after "in" we skip "for" */ - if (!pi->next->progs->argv) - continue; + + static const char encoded_dollar_at[] ALIGN1 = { + SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0' + }; /* encoded representation of "$@" */ + static const char *const encoded_dollar_at_argv[] = { + encoded_dollar_at, NULL + }; /* argv list with one element: "$@" */ + char **vals; + + vals = (char**)encoded_dollar_at_argv; + if (rpipe->next->res_word == RES_IN) { + /* if no variable values after "in" we skip "for" */ + if (!pi->next->progs->argv) + continue; + vals = pi->next->progs->argv; + } /* else: "for var; do..." -> assume "$@" list */ /* create list of variable values */ - debug_print_strings("for_list made from", pi->next->progs->argv); - for_list = expand_strvec_to_strvec(pi->next->progs->argv); + debug_print_strings("for_list made from", vals); + for_list = expand_strvec_to_strvec(vals); debug_print_strings("for_list", for_list); for_lcur = for_list; for_varname = pi->progs->argv[0]; @@ -2707,7 +2721,7 @@ static void unset_local_var(const char *name) } } -/* the src parameter allows us to peek forward to a possible &n syntax +/* The src parameter allows us to peek forward to a possible &n syntax * for file descriptor duplication, e.g., "2>&1". * Return code is 0 normally, 1 if a syntax error is detected in src. * Resource errors (in xmalloc) cause the process to exit */ @@ -2824,7 +2838,7 @@ static int reserved_word(const o_string *word, struct p_context *ctx) { "fi", RES_FI, FLAG_END }, #endif #if ENABLE_HUSH_LOOPS - { "for", RES_FOR, FLAG_IN | FLAG_START }, + { "for", RES_FOR, FLAG_IN | FLAG_DO | FLAG_START }, { "while", RES_WHILE, FLAG_DO | FLAG_START }, { "until", RES_UNTIL, FLAG_DO | FLAG_START }, { "in", RES_IN, FLAG_DO }, @@ -2936,12 +2950,12 @@ static int done_word(o_string *word, struct p_context *ctx) } } #endif - if (word->nonnull /* word had "xx" or 'xx' at least as part of it */ + if (word->nonnull /* word had "xx" or 'xx' at least as part of it. */ /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) /* (otherwise it's "abc".... and is already safe) */ ) { - /* but exclude "$@"! it expands to no word despite "" */ + /* but exclude "$@" - it expands to no word despite "" */ char *p = word->data; while (p[0] == SPECIAL_VAR_SYMBOL && (p[1] & 0x7f) == '@' diff --git a/shell/hush_test/hush-misc/empty_for2.right b/shell/hush_test/hush-misc/empty_for2.right new file mode 100644 index 000000000..1acee9eb8 --- /dev/null +++ b/shell/hush_test/hush-misc/empty_for2.right @@ -0,0 +1,4 @@ +PARAM:abc +PARAM:d e +PARAM:123 +OK: 0 diff --git a/shell/hush_test/hush-misc/empty_for2.tests b/shell/hush_test/hush-misc/empty_for2.tests new file mode 100755 index 000000000..2b12ec2c1 --- /dev/null +++ b/shell/hush_test/hush-misc/empty_for2.tests @@ -0,0 +1,6 @@ +if test $# = 0; then + exec "$THIS_SH" $0 abc "d e" 123 +fi +false +for v; do echo "PARAM:$v"; done +echo OK: $?