hush: support "for v; do ... done" syntax (implied 'in "$@"')

This commit is contained in:
Denis Vlasenko 2008-07-05 20:29:59 +00:00
parent afdcd12ed7
commit ff182a3d68
3 changed files with 38 additions and 14 deletions

View File

@ -508,7 +508,7 @@ static void syntax(const char *msg)
/* Debug */ /* Debug */
static void syntax_lineno(int line) 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 = (interactive_fd ? bb_error_msg : bb_error_msg_and_die);
fp("syntax error hush.c:%d", line); 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); debug_printf_exec("run_list lvl %d return 1\n", run_list_level);
return 1; return 1;
} }
if ((rpipe->res_word == RES_IN && rpipe->next->res_word == RES_IN && rpipe->next->progs[0].argv != NULL) if (/* Extra statement after IN: "for a in a b; echo Hi; do ...; done" ? */
|| (rpipe->res_word == RES_FOR && rpipe->next->res_word != RES_IN) (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");
syntax("malformed for"); /* 2nd condition: FOR not followed by IN */
debug_printf_exec("run_list lvl %d return 1\n", run_list_level); debug_printf_exec("run_list lvl %d return 1\n", run_list_level);
return 1; return 1;
} }
@ -2116,12 +2117,25 @@ static int run_list(struct pipe *pi)
if (rword == RES_FOR && pi->num_progs) { if (rword == RES_FOR && pi->num_progs) {
if (!for_lcur) { if (!for_lcur) {
/* first loop through for */ /* first loop through for */
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 no variable values after "in" we skip "for" */
if (!pi->next->progs->argv) if (!pi->next->progs->argv)
continue; continue;
vals = pi->next->progs->argv;
} /* else: "for var; do..." -> assume "$@" list */
/* create list of variable values */ /* create list of variable values */
debug_print_strings("for_list made from", pi->next->progs->argv); debug_print_strings("for_list made from", vals);
for_list = expand_strvec_to_strvec(pi->next->progs->argv); for_list = expand_strvec_to_strvec(vals);
debug_print_strings("for_list", for_list); debug_print_strings("for_list", for_list);
for_lcur = for_list; for_lcur = for_list;
for_varname = pi->progs->argv[0]; 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". * for file descriptor duplication, e.g., "2>&1".
* Return code is 0 normally, 1 if a syntax error is detected in src. * Return code is 0 normally, 1 if a syntax error is detected in src.
* Resource errors (in xmalloc) cause the process to exit */ * 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 }, { "fi", RES_FI, FLAG_END },
#endif #endif
#if ENABLE_HUSH_LOOPS #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 }, { "while", RES_WHILE, FLAG_DO | FLAG_START },
{ "until", RES_UNTIL, FLAG_DO | FLAG_START }, { "until", RES_UNTIL, FLAG_DO | FLAG_START },
{ "in", RES_IN, FLAG_DO }, { "in", RES_IN, FLAG_DO },
@ -2936,12 +2950,12 @@ static int done_word(o_string *word, struct p_context *ctx)
} }
} }
#endif #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`...): */ /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */
&& (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL)
/* (otherwise it's "abc".... and is already safe) */ /* (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; char *p = word->data;
while (p[0] == SPECIAL_VAR_SYMBOL while (p[0] == SPECIAL_VAR_SYMBOL
&& (p[1] & 0x7f) == '@' && (p[1] & 0x7f) == '@'

View File

@ -0,0 +1,4 @@
PARAM:abc
PARAM:d e
PARAM:123
OK: 0

View File

@ -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: $?