diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 70 |
1 files changed, 49 insertions, 21 deletions
diff --git a/shell/hush.c b/shell/hush.c index e9cec1cc9..ab7263381 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -63,7 +63,6 @@ | |||
63 | * reserved words: function select | 63 | * reserved words: function select |
64 | * advanced test: [[ ]] | 64 | * advanced test: [[ ]] |
65 | * process substitution: <(list) and >(list) | 65 | * process substitution: <(list) and >(list) |
66 | * =~: regex operator | ||
67 | * let EXPR [EXPR...] | 66 | * let EXPR [EXPR...] |
68 | * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) | 67 | * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION) |
69 | * If the last arg evaluates to 0, let returns 1; 0 otherwise. | 68 | * If the last arg evaluates to 0, let returns 1; 0 otherwise. |
@@ -84,13 +83,12 @@ | |||
84 | * [[ args ]] are CMD_SINGLEWORD_NOGLOB: | 83 | * [[ args ]] are CMD_SINGLEWORD_NOGLOB: |
85 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? | 84 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? |
86 | * [[ /bin/n* ]]; echo 0:$? | 85 | * [[ /bin/n* ]]; echo 0:$? |
86 | * = is glob match operator, not equality operator: STR = GLOB | ||
87 | * == same as = | ||
88 | * =~ is regex match operator: STR =~ REGEX | ||
87 | * TODO: | 89 | * TODO: |
88 | * &&/|| are AND/OR ops, -a/-o are not | ||
89 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) | 90 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) |
90 | * = is glob match operator, not equality operator: STR = GLOB | 91 | * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*" |
91 | * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") | ||
92 | * == same as = | ||
93 | * add =~ regex match operator: STR =~ REGEX | ||
94 | */ | 92 | */ |
95 | //config:config HUSH | 93 | //config:config HUSH |
96 | //config: bool "hush (68 kb)" | 94 | //config: bool "hush (68 kb)" |
@@ -651,14 +649,16 @@ struct command { | |||
651 | smallint cmd_type; /* CMD_xxx */ | 649 | smallint cmd_type; /* CMD_xxx */ |
652 | #define CMD_NORMAL 0 | 650 | #define CMD_NORMAL 0 |
653 | #define CMD_SUBSHELL 1 | 651 | #define CMD_SUBSHELL 1 |
654 | #if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY | 652 | #if BASH_TEST2 |
655 | /* used for "[[ EXPR ]]", and to prevent word splitting and globbing in | 653 | /* used for "[[ EXPR ]]" */ |
656 | * "export v=t*" | 654 | # define CMD_TEST2_SINGLEWORD_NOGLOB 2 |
657 | */ | 655 | #endif |
658 | # define CMD_SINGLEWORD_NOGLOB 2 | 656 | #if ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY |
657 | /* used to prevent word splitting and globbing in "export v=t*" */ | ||
658 | # define CMD_SINGLEWORD_NOGLOB 3 | ||
659 | #endif | 659 | #endif |
660 | #if ENABLE_HUSH_FUNCTIONS | 660 | #if ENABLE_HUSH_FUNCTIONS |
661 | # define CMD_FUNCDEF 3 | 661 | # define CMD_FUNCDEF 4 |
662 | #endif | 662 | #endif |
663 | 663 | ||
664 | smalluint cmd_exitcode; | 664 | smalluint cmd_exitcode; |
@@ -4112,6 +4112,14 @@ static int done_word(struct parse_context *ctx) | |||
4112 | ctx->ctx_dsemicolon = 0; | 4112 | ctx->ctx_dsemicolon = 0; |
4113 | } else | 4113 | } else |
4114 | # endif | 4114 | # endif |
4115 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
4116 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB | ||
4117 | && strcmp(ctx->word.data, "]]") == 0 | ||
4118 | ) { | ||
4119 | /* allow "[[ ]] >file" etc */ | ||
4120 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | ||
4121 | } else | ||
4122 | # endif | ||
4115 | if (!command->argv /* if it's the first word... */ | 4123 | if (!command->argv /* if it's the first word... */ |
4116 | # if ENABLE_HUSH_LOOPS | 4124 | # if ENABLE_HUSH_LOOPS |
4117 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | 4125 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ |
@@ -4146,11 +4154,13 @@ static int done_word(struct parse_context *ctx) | |||
4146 | (ctx->ctx_res_w == RES_SNTX)); | 4154 | (ctx->ctx_res_w == RES_SNTX)); |
4147 | return (ctx->ctx_res_w == RES_SNTX); | 4155 | return (ctx->ctx_res_w == RES_SNTX); |
4148 | } | 4156 | } |
4157 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
4158 | if (strcmp(ctx->word.data, "[[") == 0) { | ||
4159 | command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; | ||
4160 | } else | ||
4161 | # endif | ||
4149 | # if defined(CMD_SINGLEWORD_NOGLOB) | 4162 | # if defined(CMD_SINGLEWORD_NOGLOB) |
4150 | if (0 | 4163 | if (0 |
4151 | # if BASH_TEST2 | ||
4152 | || strcmp(ctx->word.data, "[[") == 0 | ||
4153 | # endif | ||
4154 | /* In bash, local/export/readonly are special, args | 4164 | /* In bash, local/export/readonly are special, args |
4155 | * are assignments and therefore expansion of them | 4165 | * are assignments and therefore expansion of them |
4156 | * should be "one-word" expansion: | 4166 | * should be "one-word" expansion: |
@@ -4172,7 +4182,8 @@ static int done_word(struct parse_context *ctx) | |||
4172 | ) { | 4182 | ) { |
4173 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | 4183 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; |
4174 | } | 4184 | } |
4175 | /* fall through */ | 4185 | # else |
4186 | { /* empty block to pair "if ... else" */ } | ||
4176 | # endif | 4187 | # endif |
4177 | } | 4188 | } |
4178 | #endif /* HAS_KEYWORDS */ | 4189 | #endif /* HAS_KEYWORDS */ |
@@ -5354,9 +5365,15 @@ static struct pipe *parse_stream(char **pstring, | |||
5354 | if (ch != '\n') | 5365 | if (ch != '\n') |
5355 | next = i_peek_and_eat_bkslash_nl(input); | 5366 | next = i_peek_and_eat_bkslash_nl(input); |
5356 | 5367 | ||
5357 | is_special = "{}<>;&|()#" /* special outside of "str" */ | 5368 | is_special = "{}<>&|();#" /* special outside of "str" */ |
5358 | "$\"" IF_HUSH_TICK("`") /* always special */ | 5369 | "$\"" IF_HUSH_TICK("`") /* always special */ |
5359 | SPECIAL_VAR_SYMBOL_STR; | 5370 | SPECIAL_VAR_SYMBOL_STR; |
5371 | #if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
5372 | if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { | ||
5373 | /* In [[ ]], {}<>&|() are not special */ | ||
5374 | is_special += 8; | ||
5375 | } else | ||
5376 | #endif | ||
5360 | /* Are { and } special here? */ | 5377 | /* Are { and } special here? */ |
5361 | if (ctx.command->argv /* word [word]{... - non-special */ | 5378 | if (ctx.command->argv /* word [word]{... - non-special */ |
5362 | || ctx.word.length /* word{... - non-special */ | 5379 | || ctx.word.length /* word{... - non-special */ |
@@ -6953,7 +6970,7 @@ static char **expand_strvec_to_strvec(char **argv) | |||
6953 | return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); | 6970 | return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); |
6954 | } | 6971 | } |
6955 | 6972 | ||
6956 | #if defined(CMD_SINGLEWORD_NOGLOB) | 6973 | #if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) |
6957 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) | 6974 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) |
6958 | { | 6975 | { |
6959 | return expand_variables(argv, EXP_FLAG_SINGLEWORD); | 6976 | return expand_variables(argv, EXP_FLAG_SINGLEWORD); |
@@ -8698,9 +8715,15 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
8698 | */ | 8715 | */ |
8699 | if (WIFSIGNALED(status)) { | 8716 | if (WIFSIGNALED(status)) { |
8700 | int sig = WTERMSIG(status); | 8717 | int sig = WTERMSIG(status); |
8701 | if (i == fg_pipe->num_cmds-1) | 8718 | if (G.run_list_level == 1 |
8702 | /* TODO: use strsignal() instead for bash compat? but that's bloat... */ | 8719 | /* ^^^^^ Do not print in nested contexts, example: |
8703 | puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig)); | 8720 | * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137" |
8721 | */ | ||
8722 | && i == fg_pipe->num_cmds-1 | ||
8723 | ) { | ||
8724 | /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */ | ||
8725 | puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig)); | ||
8726 | } | ||
8704 | /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ | 8727 | /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */ |
8705 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? | 8728 | /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here? |
8706 | * Maybe we need to use sig | 128? */ | 8729 | * Maybe we need to use sig | 128? */ |
@@ -9127,6 +9150,11 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
9127 | } | 9150 | } |
9128 | 9151 | ||
9129 | /* Expand the rest into (possibly) many strings each */ | 9152 | /* Expand the rest into (possibly) many strings each */ |
9153 | #if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
9154 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) | ||
9155 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); | ||
9156 | else | ||
9157 | #endif | ||
9130 | #if defined(CMD_SINGLEWORD_NOGLOB) | 9158 | #if defined(CMD_SINGLEWORD_NOGLOB) |
9131 | if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) | 9159 | if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) |
9132 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); | 9160 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); |