diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-06 10:01:13 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-06 10:01:13 +0000 |
commit | 733e3fbc2fb6eb0195cbcc02724d186b1b820df6 (patch) | |
tree | 81ba6758a3b20c84b56e42473d427368b9180c0d | |
parent | d48e81f0cda73aca49cd852212a62e879cf35b86 (diff) | |
download | busybox-w32-733e3fbc2fb6eb0195cbcc02724d186b1b820df6.tar.gz busybox-w32-733e3fbc2fb6eb0195cbcc02724d186b1b820df6.tar.bz2 busybox-w32-733e3fbc2fb6eb0195cbcc02724d186b1b820df6.zip |
hush: support "for if in do done then; do echo $if; done" case
function old new delta
done_pipe 83 95 +12
parse_stream 1758 1764 +6
done_word 674 647 -27
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 18/-27) Total: -9 bytes
-rw-r--r-- | shell/hush.c | 59 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/for_with_keywords.right | 4 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/for_with_keywords.tests | 2 |
3 files changed, 41 insertions, 24 deletions
diff --git a/shell/hush.c b/shell/hush.c index f38f3752b..c71ddb14f 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1344,7 +1344,7 @@ static int setup_redirects(struct child_prog *prog, int squirrel[]) | |||
1344 | if (redir->dup == -1) { | 1344 | if (redir->dup == -1) { |
1345 | char *p; | 1345 | char *p; |
1346 | mode = redir_table[redir->rd_type].mode; | 1346 | mode = redir_table[redir->rd_type].mode; |
1347 | //TODO: check redir to names like '\\' | 1347 | //TODO: check redir for names like '\\' |
1348 | p = expand_string_to_string(redir->rd_filename); | 1348 | p = expand_string_to_string(redir->rd_filename); |
1349 | openfd = open_or_warn(p, mode); | 1349 | openfd = open_or_warn(p, mode); |
1350 | free(p); | 1350 | free(p); |
@@ -2854,17 +2854,7 @@ static int reserved_word(const o_string *word, struct p_context *ctx) | |||
2854 | continue; | 2854 | continue; |
2855 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); | 2855 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); |
2856 | if (r->flag == 0) { /* '!' */ | 2856 | if (r->flag == 0) { /* '!' */ |
2857 | #if ENABLE_HUSH_LOOPS | 2857 | if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ |
2858 | if (ctx->ctx_res_w == RES_IN) { | ||
2859 | /* 'for a in ! a b c; ...' - ! isn't a keyword here */ | ||
2860 | break; | ||
2861 | } | ||
2862 | #endif | ||
2863 | if (ctx->ctx_inverted /* bash doesn't accept '! ! true' */ | ||
2864 | #if ENABLE_HUSH_LOOPS | ||
2865 | || ctx->ctx_res_w == RES_FOR /* example: 'for ! a' */ | ||
2866 | #endif | ||
2867 | ) { | ||
2868 | syntax(NULL); | 2858 | syntax(NULL); |
2869 | IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;) | 2859 | IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;) |
2870 | } | 2860 | } |
@@ -2885,7 +2875,7 @@ static int reserved_word(const o_string *word, struct p_context *ctx) | |||
2885 | *new = *ctx; /* physical copy */ | 2875 | *new = *ctx; /* physical copy */ |
2886 | initialize_context(ctx); | 2876 | initialize_context(ctx); |
2887 | ctx->stack = new; | 2877 | ctx->stack = new; |
2888 | } else if (ctx->ctx_res_w == RES_NONE || !(ctx->old_flag & (1 << r->res))) { | 2878 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { |
2889 | syntax(NULL); | 2879 | syntax(NULL); |
2890 | ctx->ctx_res_w = RES_SNTX; | 2880 | ctx->ctx_res_w = RES_SNTX; |
2891 | return 1; | 2881 | return 1; |
@@ -2940,7 +2930,10 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
2940 | return 1; | 2930 | return 1; |
2941 | } | 2931 | } |
2942 | #if HAS_KEYWORDS | 2932 | #if HAS_KEYWORDS |
2943 | if (!child->argv) { /* if it's the first word... */ | 2933 | if (!child->argv /* if it's the first word... */ |
2934 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | ||
2935 | && ctx->ctx_res_w != RES_IN | ||
2936 | ) { | ||
2944 | debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); | 2937 | debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); |
2945 | if (reserved_word(word, ctx)) { | 2938 | if (reserved_word(word, ctx)) { |
2946 | o_reset(word); | 2939 | o_reset(word); |
@@ -2953,9 +2946,9 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
2953 | if (word->nonnull /* word had "xx" or 'xx' at least as part of it. */ | 2946 | if (word->nonnull /* word had "xx" or 'xx' at least as part of it. */ |
2954 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ | 2947 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ |
2955 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | 2948 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) |
2956 | /* (otherwise it's "abc".... and is already safe) */ | 2949 | /* (otherwise it's known to be not empty and is already safe) */ |
2957 | ) { | 2950 | ) { |
2958 | /* but exclude "$@" - it expands to no word despite "" */ | 2951 | /* exclude "$@" - it can expand to no word despite "" */ |
2959 | char *p = word->data; | 2952 | char *p = word->data; |
2960 | while (p[0] == SPECIAL_VAR_SYMBOL | 2953 | while (p[0] == SPECIAL_VAR_SYMBOL |
2961 | && (p[1] & 0x7f) == '@' | 2954 | && (p[1] & 0x7f) == '@' |
@@ -2964,7 +2957,9 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
2964 | p += 3; | 2957 | p += 3; |
2965 | } | 2958 | } |
2966 | if (p == word->data || p[0] != '\0') { | 2959 | if (p == word->data || p[0] != '\0') { |
2967 | /* Insert "empty variable" reference, this makes | 2960 | /* saw no "$@", or not only "$@" but some |
2961 | * real text is there too */ | ||
2962 | /* insert "empty variable" reference, this makes | ||
2968 | * e.g. "", $empty"" etc to not disappear */ | 2963 | * e.g. "", $empty"" etc to not disappear */ |
2969 | o_addchr(word, SPECIAL_VAR_SYMBOL); | 2964 | o_addchr(word, SPECIAL_VAR_SYMBOL); |
2970 | o_addchr(word, SPECIAL_VAR_SYMBOL); | 2965 | o_addchr(word, SPECIAL_VAR_SYMBOL); |
@@ -2979,8 +2974,13 @@ static int done_word(o_string *word, struct p_context *ctx) | |||
2979 | 2974 | ||
2980 | #if ENABLE_HUSH_LOOPS | 2975 | #if ENABLE_HUSH_LOOPS |
2981 | /* Force FOR to have just one word (variable name) */ | 2976 | /* Force FOR to have just one word (variable name) */ |
2982 | if (ctx->ctx_res_w == RES_FOR) | 2977 | /* NB: basically, this makes hush see "for v in ..." syntax as if |
2978 | * as it is "for v; in ...". FOR and IN become two pipe structs | ||
2979 | * in parse tree. */ | ||
2980 | if (ctx->ctx_res_w == RES_FOR) { | ||
2981 | //TODO: check that child->argv[0] is a valid variable name! | ||
2983 | done_pipe(ctx, PIPE_SEQ); | 2982 | done_pipe(ctx, PIPE_SEQ); |
2983 | } | ||
2984 | #endif | 2984 | #endif |
2985 | debug_printf_parse("done_word return 0\n"); | 2985 | debug_printf_parse("done_word return 0\n"); |
2986 | return 0; | 2986 | return 0; |
@@ -3030,19 +3030,28 @@ static void done_pipe(struct p_context *ctx, pipe_style type) | |||
3030 | debug_printf_parse("done_pipe entered, followup %d\n", type); | 3030 | debug_printf_parse("done_pipe entered, followup %d\n", type); |
3031 | not_null = done_command(ctx); /* implicit closure of previous command */ | 3031 | not_null = done_command(ctx); /* implicit closure of previous command */ |
3032 | ctx->pipe->followup = type; | 3032 | ctx->pipe->followup = type; |
3033 | IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;) | ||
3034 | IF_HAS_KEYWORDS(ctx->pipe->pi_inverted = ctx->ctx_inverted;) | 3033 | IF_HAS_KEYWORDS(ctx->pipe->pi_inverted = ctx->ctx_inverted;) |
3035 | IF_HAS_KEYWORDS(ctx->ctx_inverted = 0;) | 3034 | IF_HAS_KEYWORDS(ctx->ctx_inverted = 0;) |
3035 | IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;) | ||
3036 | /* Without this check, even just <enter> on command line generates | 3036 | /* Without this check, even just <enter> on command line generates |
3037 | * tree of three NOPs (!). Which is harmless but annoying. | 3037 | * tree of three NOPs (!). Which is harmless but annoying. |
3038 | * IOW: it is safe to do it unconditionally. | 3038 | * IOW: it is safe to do it unconditionally. |
3039 | * RES_IN case is for "for a in; do ..." (empty IN set) | 3039 | * RES_NONE case is for "for a in; do ..." (empty IN set) |
3040 | * to work. */ | 3040 | * to work, possibly other cases too. */ |
3041 | if (not_null USE_HUSH_LOOPS(|| ctx->pipe->res_word == RES_IN)) { | 3041 | if (not_null IF_HAS_KEYWORDS(|| ctx->ctx_res_w != RES_NONE)) { |
3042 | struct pipe *new_p = new_pipe(); | 3042 | struct pipe *new_p = new_pipe(); |
3043 | ctx->pipe->next = new_p; | 3043 | ctx->pipe->next = new_p; |
3044 | ctx->pipe = new_p; | 3044 | ctx->pipe = new_p; |
3045 | ctx->child = NULL; /* needed! */ | 3045 | ctx->child = NULL; /* needed! */ |
3046 | /* RES_IF, RES_WHILE etc are "sticky" - | ||
3047 | * they remain set for commands inside if/while. | ||
3048 | * This is used to control execution. | ||
3049 | * RES_FOR and RES_IN are NOT sticky (needed to support | ||
3050 | * cases where variable or value happens to match a keyword): | ||
3051 | */ | ||
3052 | if (ctx->ctx_res_w == RES_FOR | ||
3053 | || ctx->ctx_res_w == RES_IN) | ||
3054 | ctx->ctx_res_w = RES_NONE; | ||
3046 | /* Create the memory for child, roughly: | 3055 | /* Create the memory for child, roughly: |
3047 | * ctx->pipe->progs = new struct child_prog; | 3056 | * ctx->pipe->progs = new struct child_prog; |
3048 | * ctx->pipe->progs[0].family = ctx->pipe; | 3057 | * ctx->pipe->progs[0].family = ctx->pipe; |
@@ -3444,7 +3453,7 @@ static int handle_dollar(o_string *dest, struct in_str *input) | |||
3444 | return 0; | 3453 | return 0; |
3445 | } | 3454 | } |
3446 | 3455 | ||
3447 | /* Scan input, call done_word() whenever full IFS delemited word was seen. | 3456 | /* Scan input, call done_word() whenever full IFS delimited word was seen. |
3448 | * call done_pipe if '\n' was seen (and end_trigger != NULL) | 3457 | * call done_pipe if '\n' was seen (and end_trigger != NULL) |
3449 | * Return if (non-quoted) char in end_trigger was seen; or on parse error. */ | 3458 | * Return if (non-quoted) char in end_trigger was seen; or on parse error. */ |
3450 | /* Return code is 0 if end_trigger char is met, | 3459 | /* Return code is 0 if end_trigger char is met, |
@@ -3517,7 +3526,9 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3517 | //err chk? | 3526 | //err chk? |
3518 | done_pipe(ctx, PIPE_SEQ); | 3527 | done_pipe(ctx, PIPE_SEQ); |
3519 | } | 3528 | } |
3520 | if (!HAS_KEYWORDS IF_HAS_KEYWORDS(|| ctx->ctx_res_w == RES_NONE)) { | 3529 | if (!HAS_KEYWORDS |
3530 | IF_HAS_KEYWORDS(|| (ctx->ctx_res_w == RES_NONE && ctx->old_flag == 0)) | ||
3531 | ) { | ||
3521 | debug_printf_parse("parse_stream return 0: end_trigger char found\n"); | 3532 | debug_printf_parse("parse_stream return 0: end_trigger char found\n"); |
3522 | return 0; | 3533 | return 0; |
3523 | } | 3534 | } |
diff --git a/shell/hush_test/hush-misc/for_with_keywords.right b/shell/hush_test/hush-misc/for_with_keywords.right new file mode 100644 index 000000000..eb04e9af9 --- /dev/null +++ b/shell/hush_test/hush-misc/for_with_keywords.right | |||
@@ -0,0 +1,4 @@ | |||
1 | do | ||
2 | done | ||
3 | then | ||
4 | OK: 0 | ||
diff --git a/shell/hush_test/hush-misc/for_with_keywords.tests b/shell/hush_test/hush-misc/for_with_keywords.tests new file mode 100755 index 000000000..a8b8e4264 --- /dev/null +++ b/shell/hush_test/hush-misc/for_with_keywords.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | for if in do done then; do echo $if; done | ||
2 | echo OK: $? | ||