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 /shell | |
| 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
Diffstat (limited to 'shell')
| -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 |
