diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-14 11:00:17 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-14 11:00:17 +0000 |
commit | a84420062a1e7a6e149b7d1ca77e1a787e55d45b (patch) | |
tree | daf1c7d301155bab1982f591a76534b413b80653 | |
parent | 6108dc4df6195ca88d26aea31c6f62dc9ad13c63 (diff) | |
download | busybox-w32-a84420062a1e7a6e149b7d1ca77e1a787e55d45b.tar.gz busybox-w32-a84420062a1e7a6e149b7d1ca77e1a787e55d45b.tar.bz2 busybox-w32-a84420062a1e7a6e149b7d1ca77e1a787e55d45b.zip |
hush: support "! cmd | cmd" negation
function old new delta
done_word 749 791 +42
run_list 1821 1859 +38
checkjobs 334 351 +17
done_pipe 61 74 +13
static.reserved_list 132 144 +12
initialize_context 53 45 -8
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/1 up/down: 122/-8) Total: 114 bytes
-rw-r--r-- | shell/hush.c | 203 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/syntax_err_negate.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/syntax_err_negate.tests | 2 | ||||
-rw-r--r-- | shell/hush_test/hush-parsing/negate.right | 35 | ||||
-rwxr-xr-x | shell/hush_test/hush-parsing/negate.tests | 16 | ||||
-rwxr-xr-x | shell/hush_test/run-all | 4 |
6 files changed, 172 insertions, 90 deletions
diff --git a/shell/hush.c b/shell/hush.c index 8b218aba9..222fd9fb5 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -20,10 +20,10 @@ | |||
20 | * rewrites. | 20 | * rewrites. |
21 | * | 21 | * |
22 | * Other credits: | 22 | * Other credits: |
23 | * o_addchr() derived from similar w_addchar function in glibc-2.2 | 23 | * o_addchr() derived from similar w_addchar function in glibc-2.2. |
24 | * setup_redirect(), redirect_opt_num(), and big chunks of main() | 24 | * setup_redirect(), redirect_opt_num(), and big chunks of main() |
25 | * and many builtins derived from contributions by Erik Andersen | 25 | * and many builtins derived from contributions by Erik Andersen |
26 | * miscellaneous bugfixes from Matt Kraai | 26 | * miscellaneous bugfixes from Matt Kraai. |
27 | * | 27 | * |
28 | * There are two big (and related) architecture differences between | 28 | * There are two big (and related) architecture differences between |
29 | * this parser and the lash parser. One is that this version is | 29 | * this parser and the lash parser. One is that this version is |
@@ -38,7 +38,6 @@ | |||
38 | * | 38 | * |
39 | * Bash grammar not implemented: (how many of these were in original sh?) | 39 | * Bash grammar not implemented: (how many of these were in original sh?) |
40 | * $_ | 40 | * $_ |
41 | * ! negation operator for pipes | ||
42 | * &> and >& redirection of stdout+stderr | 41 | * &> and >& redirection of stdout+stderr |
43 | * Brace Expansion | 42 | * Brace Expansion |
44 | * Tilde Expansion | 43 | * Tilde Expansion |
@@ -54,24 +53,16 @@ | |||
54 | * reserved word execution woefully incomplete and buggy | 53 | * reserved word execution woefully incomplete and buggy |
55 | * to-do: | 54 | * to-do: |
56 | * port selected bugfixes from post-0.49 busybox lash - done? | 55 | * port selected bugfixes from post-0.49 busybox lash - done? |
57 | * finish implementing reserved words: for, while, until, do, done | ||
58 | * change { and } from special chars to reserved words | 56 | * change { and } from special chars to reserved words |
59 | * builtins: break, continue, eval, return, set, trap, ulimit | 57 | * builtins: break, continue, eval, return, set, trap, ulimit |
60 | * test magic exec | 58 | * test magic exec |
61 | * handle children going into background | ||
62 | * clean up recognition of null pipes | ||
63 | * check setting of global_argc and global_argv | 59 | * check setting of global_argc and global_argv |
64 | * control-C handling, probably with longjmp | ||
65 | * follow IFS rules more precisely, including update semantics | 60 | * follow IFS rules more precisely, including update semantics |
66 | * figure out what to do with backslash-newline | 61 | * figure out what to do with backslash-newline |
67 | * explain why we use signal instead of sigaction | ||
68 | * propagate syntax errors, die on resource errors? | 62 | * propagate syntax errors, die on resource errors? |
69 | * continuation lines, both explicit and implicit - done? | 63 | * continuation lines, both explicit and implicit - done? |
70 | * memory leak finding and plugging - done? | 64 | * memory leak finding and plugging - done? |
71 | * more testing, especially quoting rules and redirection | ||
72 | * document how quoting rules not precisely followed for variable assignments | ||
73 | * maybe change charmap[] to use 2-bit entries | 65 | * maybe change charmap[] to use 2-bit entries |
74 | * (eventually) remove all the printf's | ||
75 | * | 66 | * |
76 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | 67 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
77 | */ | 68 | */ |
@@ -244,26 +235,6 @@ typedef enum { | |||
244 | RES_XXXX = 12, | 235 | RES_XXXX = 12, |
245 | RES_SNTX = 13 | 236 | RES_SNTX = 13 |
246 | } reserved_style; | 237 | } reserved_style; |
247 | enum { | ||
248 | FLAG_END = (1 << RES_NONE ), | ||
249 | #if ENABLE_HUSH_IF | ||
250 | FLAG_IF = (1 << RES_IF ), | ||
251 | FLAG_THEN = (1 << RES_THEN ), | ||
252 | FLAG_ELIF = (1 << RES_ELIF ), | ||
253 | FLAG_ELSE = (1 << RES_ELSE ), | ||
254 | FLAG_FI = (1 << RES_FI ), | ||
255 | #endif | ||
256 | #if ENABLE_HUSH_LOOPS | ||
257 | FLAG_FOR = (1 << RES_FOR ), | ||
258 | FLAG_WHILE = (1 << RES_WHILE), | ||
259 | FLAG_UNTIL = (1 << RES_UNTIL), | ||
260 | FLAG_DO = (1 << RES_DO ), | ||
261 | FLAG_DONE = (1 << RES_DONE ), | ||
262 | FLAG_IN = (1 << RES_IN ), | ||
263 | #endif | ||
264 | FLAG_START = (1 << RES_XXXX ), | ||
265 | }; | ||
266 | |||
267 | /* This holds pointers to the various results of parsing */ | 238 | /* This holds pointers to the various results of parsing */ |
268 | struct p_context { | 239 | struct p_context { |
269 | struct child_prog *child; | 240 | struct child_prog *child; |
@@ -271,7 +242,8 @@ struct p_context { | |||
271 | struct pipe *pipe; | 242 | struct pipe *pipe; |
272 | struct redir_struct *pending_redirect; | 243 | struct redir_struct *pending_redirect; |
273 | smallint res_w; | 244 | smallint res_w; |
274 | smallint parse_type; /* bitmask of PARSEFLAG_xxx, defines type of parser : ";$" common or special symbol */ | 245 | smallint parse_type; /* bitmask of PARSEFLAG_xxx, defines type of parser: ";$" common or special symbol */ |
246 | smallint ctx_inverted; /* "! cmd | cmd" */ | ||
275 | int old_flag; /* bitmask of FLAG_xxx, for figuring out valid reserved words */ | 247 | int old_flag; /* bitmask of FLAG_xxx, for figuring out valid reserved words */ |
276 | struct p_context *stack; | 248 | struct p_context *stack; |
277 | /* How about quoting status? */ | 249 | /* How about quoting status? */ |
@@ -313,6 +285,7 @@ struct pipe { | |||
313 | char *cmdbuf; /* buffer various argv's point into */ | 285 | char *cmdbuf; /* buffer various argv's point into */ |
314 | struct child_prog *progs; /* array of commands in pipe */ | 286 | struct child_prog *progs; /* array of commands in pipe */ |
315 | int job_context; /* bitmask defining current context */ | 287 | int job_context; /* bitmask defining current context */ |
288 | smallint pi_inverted; /* "! cmd | cmd" */ | ||
316 | smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */ | 289 | smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */ |
317 | smallint res_word; /* needed for if, for, while, until... */ | 290 | smallint res_word; /* needed for if, for, while, until... */ |
318 | }; | 291 | }; |
@@ -524,7 +497,7 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struc | |||
524 | static void initialize_context(struct p_context *ctx); | 497 | static void initialize_context(struct p_context *ctx); |
525 | static int done_word(o_string *dest, struct p_context *ctx); | 498 | static int done_word(o_string *dest, struct p_context *ctx); |
526 | static int done_command(struct p_context *ctx); | 499 | static int done_command(struct p_context *ctx); |
527 | static int done_pipe(struct p_context *ctx, pipe_style type); | 500 | static void done_pipe(struct p_context *ctx, pipe_style type); |
528 | /* primary string parsing: */ | 501 | /* primary string parsing: */ |
529 | static int redirect_dup_num(struct in_str *input); | 502 | static int redirect_dup_num(struct in_str *input); |
530 | static int redirect_opt_num(o_string *o); | 503 | static int redirect_opt_num(o_string *o); |
@@ -1648,9 +1621,6 @@ static void pseudo_exec_argv(char **ptrs2free, char **argv) | |||
1648 | */ | 1621 | */ |
1649 | static void pseudo_exec(char **ptrs2free, struct child_prog *child) | 1622 | static void pseudo_exec(char **ptrs2free, struct child_prog *child) |
1650 | { | 1623 | { |
1651 | // FIXME: buggy wrt NOMMU! Must not modify any global data | ||
1652 | // until it does exec/_exit, but currently it does | ||
1653 | // (puts malloc'ed stuff into environment) | ||
1654 | if (child->argv) | 1624 | if (child->argv) |
1655 | pseudo_exec_argv(ptrs2free, child->argv); | 1625 | pseudo_exec_argv(ptrs2free, child->argv); |
1656 | 1626 | ||
@@ -1691,8 +1661,10 @@ static const char *get_cmdtext(struct pipe *pi) | |||
1691 | if (pi->cmdtext) | 1661 | if (pi->cmdtext) |
1692 | return pi->cmdtext; | 1662 | return pi->cmdtext; |
1693 | argv = pi->progs[0].argv; | 1663 | argv = pi->progs[0].argv; |
1694 | if (!argv || !argv[0]) | 1664 | if (!argv || !argv[0]) { |
1695 | return (pi->cmdtext = xzalloc(1)); | 1665 | pi->cmdtext = xzalloc(1); |
1666 | return pi->cmdtext; | ||
1667 | } | ||
1696 | 1668 | ||
1697 | len = 0; | 1669 | len = 0; |
1698 | do len += strlen(*argv) + 1; while (*++argv); | 1670 | do len += strlen(*argv) + 1; while (*++argv); |
@@ -1833,9 +1805,12 @@ static int checkjobs(struct pipe* fg_pipe) | |||
1833 | if (dead) { | 1805 | if (dead) { |
1834 | fg_pipe->progs[i].pid = 0; | 1806 | fg_pipe->progs[i].pid = 0; |
1835 | fg_pipe->running_progs--; | 1807 | fg_pipe->running_progs--; |
1836 | if (i == fg_pipe->num_progs - 1) | 1808 | if (i == fg_pipe->num_progs - 1) { |
1837 | /* last process gives overall exitstatus */ | 1809 | /* last process gives overall exitstatus */ |
1838 | rcode = WEXITSTATUS(status); | 1810 | rcode = WEXITSTATUS(status); |
1811 | if (fg_pipe->pi_inverted) | ||
1812 | rcode = !rcode; | ||
1813 | } | ||
1839 | } else { | 1814 | } else { |
1840 | fg_pipe->progs[i].is_stopped = 1; | 1815 | fg_pipe->progs[i].is_stopped = 1; |
1841 | fg_pipe->stopped_progs++; | 1816 | fg_pipe->stopped_progs++; |
@@ -1966,6 +1941,8 @@ static int run_pipe(struct pipe *pi) | |||
1966 | rcode = run_list(child->group) & 0xff; | 1941 | rcode = run_list(child->group) & 0xff; |
1967 | restore_redirects(squirrel); | 1942 | restore_redirects(squirrel); |
1968 | debug_printf_exec("run_pipe return %d\n", rcode); | 1943 | debug_printf_exec("run_pipe return %d\n", rcode); |
1944 | if (pi->pi_inverted) | ||
1945 | rcode = !rcode; | ||
1969 | return rcode; | 1946 | return rcode; |
1970 | } | 1947 | } |
1971 | 1948 | ||
@@ -2007,6 +1984,8 @@ static int run_pipe(struct pipe *pi) | |||
2007 | free(argv_expanded); | 1984 | free(argv_expanded); |
2008 | restore_redirects(squirrel); | 1985 | restore_redirects(squirrel); |
2009 | debug_printf_exec("run_pipe return %d\n", rcode); | 1986 | debug_printf_exec("run_pipe return %d\n", rcode); |
1987 | if (pi->pi_inverted) | ||
1988 | rcode = !rcode; | ||
2010 | return rcode; | 1989 | return rcode; |
2011 | } | 1990 | } |
2012 | } | 1991 | } |
@@ -2023,6 +2002,8 @@ static int run_pipe(struct pipe *pi) | |||
2023 | free(argv_expanded); | 2002 | free(argv_expanded); |
2024 | restore_redirects(squirrel); | 2003 | restore_redirects(squirrel); |
2025 | debug_printf_exec("run_pipe return %d\n", rcode); | 2004 | debug_printf_exec("run_pipe return %d\n", rcode); |
2005 | if (pi->pi_inverted) | ||
2006 | rcode = !rcode; | ||
2026 | return rcode; | 2007 | return rcode; |
2027 | } | 2008 | } |
2028 | } | 2009 | } |
@@ -2985,7 +2966,8 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style, | |||
2985 | 2966 | ||
2986 | /* Check for a '2>&1' type redirect */ | 2967 | /* Check for a '2>&1' type redirect */ |
2987 | redir->dup = redirect_dup_num(input); | 2968 | redir->dup = redirect_dup_num(input); |
2988 | if (redir->dup == -2) return 1; /* syntax error */ | 2969 | if (redir->dup == -2) |
2970 | return 1; /* syntax error */ | ||
2989 | if (redir->dup != -1) { | 2971 | if (redir->dup != -1) { |
2990 | /* Erik had a check here that the file descriptor in question | 2972 | /* Erik had a check here that the file descriptor in question |
2991 | * is legit; I postpone that to "run time" | 2973 | * is legit; I postpone that to "run time" |
@@ -3005,10 +2987,7 @@ static struct pipe *new_pipe(void) | |||
3005 | { | 2987 | { |
3006 | struct pipe *pi; | 2988 | struct pipe *pi; |
3007 | pi = xzalloc(sizeof(struct pipe)); | 2989 | pi = xzalloc(sizeof(struct pipe)); |
3008 | /*pi->num_progs = 0;*/ | 2990 | /*pi->followup = 0; - deliberately invalid value */ |
3009 | /*pi->progs = NULL;*/ | ||
3010 | /*pi->next = NULL;*/ | ||
3011 | /*pi->followup = 0; invalid */ | ||
3012 | if (RES_NONE) | 2991 | if (RES_NONE) |
3013 | pi->res_word = RES_NONE; | 2992 | pi->res_word = RES_NONE; |
3014 | return pi; | 2993 | return pi; |
@@ -3016,29 +2995,50 @@ static struct pipe *new_pipe(void) | |||
3016 | 2995 | ||
3017 | static void initialize_context(struct p_context *ctx) | 2996 | static void initialize_context(struct p_context *ctx) |
3018 | { | 2997 | { |
3019 | ctx->child = NULL; | 2998 | smallint sv = ctx->parse_type; |
2999 | memset(ctx, 0, sizeof(*ctx)); | ||
3000 | ctx->parse_type = sv; | ||
3020 | ctx->pipe = ctx->list_head = new_pipe(); | 3001 | ctx->pipe = ctx->list_head = new_pipe(); |
3021 | ctx->pending_redirect = NULL; | 3002 | /* Create the memory for child, roughly: |
3022 | ctx->res_w = RES_NONE; | 3003 | * ctx->pipe->progs = new struct child_prog; |
3023 | //only ctx->parse_type is not touched... is this intentional? | 3004 | * ctx->pipe->progs[0].family = ctx->pipe; |
3024 | ctx->old_flag = 0; | 3005 | * ctx->child = &ctx->pipe->progs[0]; |
3025 | ctx->stack = NULL; | 3006 | */ |
3026 | done_command(ctx); /* creates the memory for working child */ | 3007 | done_command(ctx); |
3027 | } | 3008 | } |
3028 | 3009 | ||
3029 | /* normal return is 0 | 3010 | /* If a reserved word is found and processed, parse context is modified |
3030 | * if a reserved word is found, and processed, return 1 | 3011 | * and 1 is returned. |
3031 | * should handle if, then, elif, else, fi, for, while, until, do, done. | 3012 | * Handles if, then, elif, else, fi, for, while, until, do, done. |
3032 | * case, function, and select are obnoxious, save those for later. | 3013 | * case, function, and select are obnoxious, save those for later. |
3033 | */ | 3014 | */ |
3034 | #if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS | 3015 | #if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS |
3035 | static int reserved_word(o_string *dest, struct p_context *ctx) | 3016 | static int reserved_word(const o_string *word, struct p_context *ctx) |
3036 | { | 3017 | { |
3037 | struct reserved_combo { | 3018 | struct reserved_combo { |
3038 | char literal[7]; | 3019 | char literal[7]; |
3039 | unsigned char code; | 3020 | unsigned char res; |
3040 | int flag; | 3021 | int flag; |
3041 | }; | 3022 | }; |
3023 | enum { | ||
3024 | FLAG_END = (1 << RES_NONE ), | ||
3025 | #if ENABLE_HUSH_IF | ||
3026 | FLAG_IF = (1 << RES_IF ), | ||
3027 | FLAG_THEN = (1 << RES_THEN ), | ||
3028 | FLAG_ELIF = (1 << RES_ELIF ), | ||
3029 | FLAG_ELSE = (1 << RES_ELSE ), | ||
3030 | FLAG_FI = (1 << RES_FI ), | ||
3031 | #endif | ||
3032 | #if ENABLE_HUSH_LOOPS | ||
3033 | FLAG_FOR = (1 << RES_FOR ), | ||
3034 | FLAG_WHILE = (1 << RES_WHILE), | ||
3035 | FLAG_UNTIL = (1 << RES_UNTIL), | ||
3036 | FLAG_DO = (1 << RES_DO ), | ||
3037 | FLAG_DONE = (1 << RES_DONE ), | ||
3038 | FLAG_IN = (1 << RES_IN ), | ||
3039 | #endif | ||
3040 | FLAG_START = (1 << RES_XXXX ), | ||
3041 | }; | ||
3042 | /* Mostly a list of accepted follow-up reserved words. | 3042 | /* Mostly a list of accepted follow-up reserved words. |
3043 | * FLAG_END means we are done with the sequence, and are ready | 3043 | * FLAG_END means we are done with the sequence, and are ready |
3044 | * to turn the compound list into a command. | 3044 | * to turn the compound list into a command. |
@@ -3046,6 +3046,7 @@ static int reserved_word(o_string *dest, struct p_context *ctx) | |||
3046 | */ | 3046 | */ |
3047 | static const struct reserved_combo reserved_list[] = { | 3047 | static const struct reserved_combo reserved_list[] = { |
3048 | #if ENABLE_HUSH_IF | 3048 | #if ENABLE_HUSH_IF |
3049 | { "!", RES_NONE, 0 }, | ||
3049 | { "if", RES_IF, FLAG_THEN | FLAG_START }, | 3050 | { "if", RES_IF, FLAG_THEN | FLAG_START }, |
3050 | { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, | 3051 | { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
3051 | { "elif", RES_ELIF, FLAG_THEN }, | 3052 | { "elif", RES_ELIF, FLAG_THEN }, |
@@ -3053,9 +3054,9 @@ static int reserved_word(o_string *dest, struct p_context *ctx) | |||
3053 | { "fi", RES_FI, FLAG_END }, | 3054 | { "fi", RES_FI, FLAG_END }, |
3054 | #endif | 3055 | #endif |
3055 | #if ENABLE_HUSH_LOOPS | 3056 | #if ENABLE_HUSH_LOOPS |
3056 | { "for", RES_FOR, FLAG_IN | FLAG_START }, | 3057 | { "for", RES_FOR, FLAG_IN | FLAG_START }, |
3057 | { "while", RES_WHILE, FLAG_DO | FLAG_START }, | 3058 | { "while", RES_WHILE, FLAG_DO | FLAG_START }, |
3058 | { "until", RES_UNTIL, FLAG_DO | FLAG_START }, | 3059 | { "until", RES_UNTIL, FLAG_DO | FLAG_START }, |
3059 | { "in", RES_IN, FLAG_DO }, | 3060 | { "in", RES_IN, FLAG_DO }, |
3060 | { "do", RES_DO, FLAG_DONE }, | 3061 | { "do", RES_DO, FLAG_DONE }, |
3061 | { "done", RES_DONE, FLAG_END } | 3062 | { "done", RES_DONE, FLAG_END } |
@@ -3065,9 +3066,23 @@ static int reserved_word(o_string *dest, struct p_context *ctx) | |||
3065 | const struct reserved_combo *r; | 3066 | const struct reserved_combo *r; |
3066 | 3067 | ||
3067 | for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { | 3068 | for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { |
3068 | if (strcmp(dest->data, r->literal) != 0) | 3069 | if (strcmp(word->data, r->literal) != 0) |
3069 | continue; | 3070 | continue; |
3070 | debug_printf("found reserved word %s, code %d\n", r->literal, r->code); | 3071 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); |
3072 | if (r->flag == 0) { /* '!' */ | ||
3073 | if (ctx->res_w == RES_IN) { | ||
3074 | /* 'for a in ! a b c; ...' - ! isn't a keyword here */ | ||
3075 | break; | ||
3076 | } | ||
3077 | if (ctx->res_w == RES_FOR /* example: 'for ! a' */ | ||
3078 | || ctx->ctx_inverted /* bash doesn't accept '! ! true' */ | ||
3079 | ) { | ||
3080 | syntax(NULL); | ||
3081 | ctx->res_w = RES_SNTX; | ||
3082 | } | ||
3083 | ctx->ctx_inverted = 1; | ||
3084 | return 1; | ||
3085 | } | ||
3071 | if (r->flag & FLAG_START) { | 3086 | if (r->flag & FLAG_START) { |
3072 | struct p_context *new; | 3087 | struct p_context *new; |
3073 | debug_printf("push stack\n"); | 3088 | debug_printf("push stack\n"); |
@@ -3075,7 +3090,6 @@ static int reserved_word(o_string *dest, struct p_context *ctx) | |||
3075 | if (ctx->res_w == RES_IN || ctx->res_w == RES_FOR) { | 3090 | if (ctx->res_w == RES_IN || ctx->res_w == RES_FOR) { |
3076 | syntax("malformed for"); /* example: 'for if' */ | 3091 | syntax("malformed for"); /* example: 'for if' */ |
3077 | ctx->res_w = RES_SNTX; | 3092 | ctx->res_w = RES_SNTX; |
3078 | o_reset(dest); | ||
3079 | return 1; | 3093 | return 1; |
3080 | } | 3094 | } |
3081 | #endif | 3095 | #endif |
@@ -3083,13 +3097,12 @@ static int reserved_word(o_string *dest, struct p_context *ctx) | |||
3083 | *new = *ctx; /* physical copy */ | 3097 | *new = *ctx; /* physical copy */ |
3084 | initialize_context(ctx); | 3098 | initialize_context(ctx); |
3085 | ctx->stack = new; | 3099 | ctx->stack = new; |
3086 | } else if (ctx->res_w == RES_NONE || !(ctx->old_flag & (1 << r->code))) { | 3100 | } else if (ctx->res_w == RES_NONE || !(ctx->old_flag & (1 << r->res))) { |
3087 | syntax(NULL); | 3101 | syntax(NULL); |
3088 | ctx->res_w = RES_SNTX; | 3102 | ctx->res_w = RES_SNTX; |
3089 | o_reset(dest); | ||
3090 | return 1; | 3103 | return 1; |
3091 | } | 3104 | } |
3092 | ctx->res_w = r->code; | 3105 | ctx->res_w = r->res; |
3093 | ctx->old_flag = r->flag; | 3106 | ctx->old_flag = r->flag; |
3094 | if (ctx->old_flag & FLAG_END) { | 3107 | if (ctx->old_flag & FLAG_END) { |
3095 | struct p_context *old; | 3108 | struct p_context *old; |
@@ -3101,52 +3114,55 @@ static int reserved_word(o_string *dest, struct p_context *ctx) | |||
3101 | *ctx = *old; /* physical copy */ | 3114 | *ctx = *old; /* physical copy */ |
3102 | free(old); | 3115 | free(old); |
3103 | } | 3116 | } |
3104 | o_reset(dest); | ||
3105 | return 1; | 3117 | return 1; |
3106 | } | 3118 | } |
3107 | return 0; | 3119 | return 0; |
3108 | } | 3120 | } |
3109 | #else | 3121 | #else |
3110 | #define reserved_word(dest, ctx) ((int)0) | 3122 | #define reserved_word(word, ctx) ((int)0) |
3111 | #endif | 3123 | #endif |
3112 | 3124 | ||
3113 | /* Normal return is 0. | 3125 | /* Word is complete, look at it and update parsing context. |
3126 | * Normal return is 0. | ||
3114 | * Syntax or xglob errors return 1. */ | 3127 | * Syntax or xglob errors return 1. */ |
3115 | static int done_word(o_string *dest, struct p_context *ctx) | 3128 | static int done_word(o_string *word, struct p_context *ctx) |
3116 | { | 3129 | { |
3117 | struct child_prog *child = ctx->child; | 3130 | struct child_prog *child = ctx->child; |
3118 | char ***glob_target; | 3131 | char ***glob_target; |
3119 | int gr; | 3132 | int gr; |
3120 | 3133 | ||
3121 | debug_printf_parse("done_word entered: '%s' %p\n", dest->data, child); | 3134 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, child); |
3122 | if (dest->length == 0 && !dest->nonnull) { | 3135 | if (word->length == 0 && !word->nonnull) { |
3123 | debug_printf_parse("done_word return 0: true null, ignored\n"); | 3136 | debug_printf_parse("done_word return 0: true null, ignored\n"); |
3124 | return 0; | 3137 | return 0; |
3125 | } | 3138 | } |
3126 | if (ctx->pending_redirect) { | 3139 | if (ctx->pending_redirect) { |
3127 | glob_target = &ctx->pending_redirect->glob_word; | 3140 | glob_target = &ctx->pending_redirect->glob_word; |
3128 | } else { | 3141 | } else { |
3129 | if (child->group) { | 3142 | if (child->group) { /* TODO: example how to trigger? */ |
3130 | syntax(NULL); | 3143 | syntax(NULL); |
3131 | debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n"); | 3144 | debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n"); |
3132 | return 1; | 3145 | return 1; |
3133 | } | 3146 | } |
3134 | if (!child->argv && (ctx->parse_type & PARSEFLAG_SEMICOLON)) { | 3147 | if (!child->argv && (ctx->parse_type & PARSEFLAG_SEMICOLON)) { |
3135 | debug_printf_parse(": checking '%s' for reserved-ness\n", dest->data); | 3148 | debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); |
3136 | if (reserved_word(dest, ctx)) { | 3149 | if (reserved_word(word, ctx)) { |
3150 | o_reset(word); | ||
3137 | debug_printf_parse("done_word return %d\n", (ctx->res_w == RES_SNTX)); | 3151 | debug_printf_parse("done_word return %d\n", (ctx->res_w == RES_SNTX)); |
3138 | return (ctx->res_w == RES_SNTX); | 3152 | return (ctx->res_w == RES_SNTX); |
3139 | } | 3153 | } |
3140 | } | 3154 | } |
3141 | glob_target = &child->argv; | 3155 | glob_target = &child->argv; |
3142 | } | 3156 | } |
3143 | gr = xglob(dest, glob_target); | 3157 | //BUG! globbing should be done after variable expansion! |
3158 | //See glob_and_vars testcase | ||
3159 | gr = xglob(word, glob_target); | ||
3144 | if (gr != 0) { | 3160 | if (gr != 0) { |
3145 | debug_printf_parse("done_word return 1: xglob returned %d\n", gr); | 3161 | debug_printf_parse("done_word return 1: xglob returned %d\n", gr); |
3146 | return 1; | 3162 | return 1; |
3147 | } | 3163 | } |
3148 | 3164 | ||
3149 | o_reset(dest); | 3165 | o_reset(word); |
3150 | if (ctx->pending_redirect) { | 3166 | if (ctx->pending_redirect) { |
3151 | /* NB: don't free_strings(ctx->pending_redirect->glob_word) here */ | 3167 | /* NB: don't free_strings(ctx->pending_redirect->glob_word) here */ |
3152 | if (ctx->pending_redirect->glob_word | 3168 | if (ctx->pending_redirect->glob_word |
@@ -3162,8 +3178,11 @@ static int done_word(o_string *dest, struct p_context *ctx) | |||
3162 | ctx->pending_redirect = NULL; | 3178 | ctx->pending_redirect = NULL; |
3163 | } | 3179 | } |
3164 | #if ENABLE_HUSH_LOOPS | 3180 | #if ENABLE_HUSH_LOOPS |
3165 | if (ctx->res_w == RES_FOR) { | 3181 | if (ctx->res_w == RES_FOR) { /* comment? */ |
3166 | done_word(dest, ctx); | 3182 | //TESTING |
3183 | //looks like (word->length == 0 && !word->nonnull) is true here, always | ||
3184 | //(due to o_reset). done_word would return at once. Why then? | ||
3185 | // done_word(word, ctx); | ||
3167 | done_pipe(ctx, PIPE_SEQ); | 3186 | done_pipe(ctx, PIPE_SEQ); |
3168 | } | 3187 | } |
3169 | #endif | 3188 | #endif |
@@ -3200,10 +3219,6 @@ static int done_command(struct p_context *ctx) | |||
3200 | child = &pi->progs[pi->num_progs]; | 3219 | child = &pi->progs[pi->num_progs]; |
3201 | 3220 | ||
3202 | memset(child, 0, sizeof(*child)); | 3221 | memset(child, 0, sizeof(*child)); |
3203 | /*child->redirects = NULL;*/ | ||
3204 | /*child->argv = NULL;*/ | ||
3205 | /*child->is_stopped = 0;*/ | ||
3206 | /*child->group = NULL;*/ | ||
3207 | child->family = pi; | 3222 | child->family = pi; |
3208 | 3223 | ||
3209 | ctx->child = child; | 3224 | ctx->child = child; |
@@ -3212,7 +3227,7 @@ static int done_command(struct p_context *ctx) | |||
3212 | return pi->num_progs; /* used only for 0/nonzero check */ | 3227 | return pi->num_progs; /* used only for 0/nonzero check */ |
3213 | } | 3228 | } |
3214 | 3229 | ||
3215 | static int done_pipe(struct p_context *ctx, pipe_style type) | 3230 | static void done_pipe(struct p_context *ctx, pipe_style type) |
3216 | { | 3231 | { |
3217 | struct pipe *new_p; | 3232 | struct pipe *new_p; |
3218 | int not_null; | 3233 | int not_null; |
@@ -3221,6 +3236,8 @@ static int done_pipe(struct p_context *ctx, pipe_style type) | |||
3221 | not_null = done_command(ctx); /* implicit closure of previous command */ | 3236 | not_null = done_command(ctx); /* implicit closure of previous command */ |
3222 | ctx->pipe->followup = type; | 3237 | ctx->pipe->followup = type; |
3223 | ctx->pipe->res_word = ctx->res_w; | 3238 | ctx->pipe->res_word = ctx->res_w; |
3239 | ctx->pipe->pi_inverted = ctx->ctx_inverted; | ||
3240 | ctx->ctx_inverted = 0; | ||
3224 | /* Without this check, even just <enter> on command line generates | 3241 | /* Without this check, even just <enter> on command line generates |
3225 | * tree of three NOPs (!). Which is harmless but annoying. | 3242 | * tree of three NOPs (!). Which is harmless but annoying. |
3226 | * IOW: it is safe to do it unconditionally. */ | 3243 | * IOW: it is safe to do it unconditionally. */ |
@@ -3228,11 +3245,15 @@ static int done_pipe(struct p_context *ctx, pipe_style type) | |||
3228 | new_p = new_pipe(); | 3245 | new_p = new_pipe(); |
3229 | ctx->pipe->next = new_p; | 3246 | ctx->pipe->next = new_p; |
3230 | ctx->pipe = new_p; | 3247 | ctx->pipe = new_p; |
3231 | ctx->child = NULL; | 3248 | ctx->child = NULL; /* needed! */ |
3232 | done_command(ctx); /* set up new pipe to accept commands */ | 3249 | /* Create the memory for child, roughly: |
3250 | * ctx->pipe->progs = new struct child_prog; | ||
3251 | * ctx->pipe->progs[0].family = ctx->pipe; | ||
3252 | * ctx->child = &ctx->pipe->progs[0]; | ||
3253 | */ | ||
3254 | done_command(ctx); | ||
3233 | } | 3255 | } |
3234 | debug_printf_parse("done_pipe return 0\n"); | 3256 | debug_printf_parse("done_pipe return\n"); |
3235 | return 0; | ||
3236 | } | 3257 | } |
3237 | 3258 | ||
3238 | /* peek ahead in the in_str to find out if we have a "&n" construct, | 3259 | /* peek ahead in the in_str to find out if we have a "&n" construct, |
@@ -3826,7 +3847,8 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3826 | break; | 3847 | break; |
3827 | case ')': | 3848 | case ')': |
3828 | case '}': | 3849 | case '}': |
3829 | syntax("unexpected }"); /* Proper use of this character is caught by end_trigger */ | 3850 | /* proper use of this character is caught by end_trigger */ |
3851 | syntax("unexpected } or )"); | ||
3830 | debug_printf_parse("parse_stream return 1: unexpected '}'\n"); | 3852 | debug_printf_parse("parse_stream return 1: unexpected '}'\n"); |
3831 | return 1; | 3853 | return 1; |
3832 | default: | 3854 | default: |
@@ -3880,10 +3902,14 @@ static void update_charmap(void) | |||
3880 | * from builtin_source() and builtin_eval() */ | 3902 | * from builtin_source() and builtin_eval() */ |
3881 | static int parse_and_run_stream(struct in_str *inp, int parse_flag) | 3903 | static int parse_and_run_stream(struct in_str *inp, int parse_flag) |
3882 | { | 3904 | { |
3905 | //TODO: PARSEFLAG_SEMICOLON bit is always set in parse_flag. fishy | ||
3906 | //TODO: PARSEFLAG_REPARSING bit is never set (grep for it). wow | ||
3883 | struct p_context ctx; | 3907 | struct p_context ctx; |
3884 | o_string temp = NULL_O_STRING; | 3908 | o_string temp = NULL_O_STRING; |
3885 | int rcode; | 3909 | int rcode; |
3886 | do { | 3910 | do { |
3911 | // It always has PARSEFLAG_SEMICOLON, can we remove all checks for this bit? | ||
3912 | // After that, the whole parse_type fiels is not needed. | ||
3887 | ctx.parse_type = parse_flag; | 3913 | ctx.parse_type = parse_flag; |
3888 | initialize_context(&ctx); | 3914 | initialize_context(&ctx); |
3889 | update_charmap(); | 3915 | update_charmap(); |
@@ -3922,6 +3948,7 @@ static int parse_and_run_stream(struct in_str *inp, int parse_flag) | |||
3922 | 3948 | ||
3923 | static int parse_and_run_string(const char *s, int parse_flag) | 3949 | static int parse_and_run_string(const char *s, int parse_flag) |
3924 | { | 3950 | { |
3951 | //TODO: PARSEFLAG_SEMICOLON bit is always set in parse_flag. fishy | ||
3925 | struct in_str input; | 3952 | struct in_str input; |
3926 | setup_string_in_str(&input, s); | 3953 | setup_string_in_str(&input, s); |
3927 | return parse_and_run_stream(&input, parse_flag); | 3954 | return parse_and_run_stream(&input, parse_flag); |
diff --git a/shell/hush_test/hush-misc/syntax_err_negate.right b/shell/hush_test/hush-misc/syntax_err_negate.right new file mode 100644 index 000000000..d1e7654f5 --- /dev/null +++ b/shell/hush_test/hush-misc/syntax_err_negate.right | |||
@@ -0,0 +1,2 @@ | |||
1 | bash 3.2 fails this | ||
2 | hush: syntax error | ||
diff --git a/shell/hush_test/hush-misc/syntax_err_negate.tests b/shell/hush_test/hush-misc/syntax_err_negate.tests new file mode 100755 index 000000000..d61b1b09f --- /dev/null +++ b/shell/hush_test/hush-misc/syntax_err_negate.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | echo bash 3.2 fails this | ||
2 | ! ! true | ||
diff --git a/shell/hush_test/hush-parsing/negate.right b/shell/hush_test/hush-parsing/negate.right new file mode 100644 index 000000000..01166012e --- /dev/null +++ b/shell/hush_test/hush-parsing/negate.right | |||
@@ -0,0 +1,35 @@ | |||
1 | ! printing ! | ||
2 | 0 | ||
3 | 1 | ||
4 | 1 | ||
5 | 0 | ||
6 | 0 | ||
7 | 0 | ||
8 | ! | ||
9 | a | ||
10 | b | ||
11 | c | ||
12 | ! 1 | ||
13 | a 1 | ||
14 | b 1 | ||
15 | c 1 | ||
16 | ! 1 | ||
17 | a 1 | ||
18 | b 1 | ||
19 | c 1 | ||
20 | 0 | ||
21 | 0 | ||
22 | 0 | ||
23 | 0 | ||
24 | 1 | ||
25 | 1 | ||
26 | 1 | ||
27 | 1 | ||
28 | 0 | ||
29 | 0 | ||
30 | 0 | ||
31 | 0 | ||
32 | 1 | ||
33 | 1 | ||
34 | 1 | ||
35 | 1 | ||
diff --git a/shell/hush_test/hush-parsing/negate.tests b/shell/hush_test/hush-parsing/negate.tests new file mode 100755 index 000000000..72e731fe9 --- /dev/null +++ b/shell/hush_test/hush-parsing/negate.tests | |||
@@ -0,0 +1,16 @@ | |||
1 | echo ! printing ! | ||
2 | ! false | ||
3 | echo $? | ||
4 | ! true | ||
5 | echo $? | ||
6 | if ! false; then false; echo $?; fi | ||
7 | echo $? | ||
8 | if ! false; then ! false; echo $?; fi | ||
9 | echo $? | ||
10 | for a in ! a b c; do echo $a; done | ||
11 | for a in ! a b c; do ! echo -n "$a "; echo $?; done | ||
12 | for a in ! a b c; do ! /bin/echo -n "$a "; echo $?; done | ||
13 | for a in ! a b c; do ! echo -n "$a " | false; echo $?; done | ||
14 | for a in ! a b c; do ! echo -n "$a " | true; echo $?; done | ||
15 | for a in ! a b c; do ! { echo -n "$a " | false; }; echo $?; done | ||
16 | for a in ! a b c; do ! { echo -n "$a " | true; }; echo $?; done | ||
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all index 805f75ad5..5cec85af6 100755 --- a/shell/hush_test/run-all +++ b/shell/hush_test/run-all | |||
@@ -1,8 +1,8 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/sh |
2 | 2 | ||
3 | test -x hush || { | 3 | test -x hush || { |
4 | echo "No ./hush?! Perhaps you want to run 'ln -s ../../busybox hush'" | 4 | echo "No ./hush - creating a link to ../../busybox" |
5 | exit | 5 | ln -s ../../busybox hush |
6 | } | 6 | } |
7 | 7 | ||
8 | PATH="$PWD:$PATH" # for hush and recho/zecho/printenv | 8 | PATH="$PWD:$PATH" # for hush and recho/zecho/printenv |