diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2020-10-31 03:34:07 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-10-31 03:34:07 +0100 |
| commit | d2241f59022c38d4b171e56eea42e216ecccfdd9 (patch) | |
| tree | edb75c2530f493c9e3f193f346d8125fe79c107f /shell | |
| parent | 112453acf24520b4655f9f36da41d8ac591b1a60 (diff) | |
| download | busybox-w32-d2241f59022c38d4b171e56eea42e216ecccfdd9.tar.gz busybox-w32-d2241f59022c38d4b171e56eea42e216ecccfdd9.tar.bz2 busybox-w32-d2241f59022c38d4b171e56eea42e216ecccfdd9.zip | |
shell: better support of [[ ]] bashism
Still rather rudimentary for ash
function old new delta
binop 433 589 +156
check_operator 65 101 +36
done_word 736 769 +33
test_main 405 418 +13
parse_stream 2227 2238 +11
ops_texts 124 133 +9
ops_table 80 86 +6
run_pipe 1557 1562 +5
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 8/0 up/down: 269/0) Total: 269 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 15 | ||||
| -rw-r--r-- | shell/hush.c | 57 | ||||
| -rw-r--r-- | shell/hush_test/hush-test2/andor1.right | 6 | ||||
| -rwxr-xr-x | shell/hush_test/hush-test2/andor1.tests | 7 | ||||
| -rw-r--r-- | shell/hush_test/hush-test2/noglob1.right | 2 | ||||
| -rwxr-xr-x | shell/hush_test/hush-test2/noglob1.tests | 3 | ||||
| -rw-r--r-- | shell/hush_test/hush-test2/strops1.right | 8 | ||||
| -rwxr-xr-x | shell/hush_test/hush-test2/strops1.tests | 15 | ||||
| -rw-r--r-- | shell/hush_test/hush-test2/strops2.right | 6 | ||||
| -rwxr-xr-x | shell/hush_test/hush-test2/strops2.tests | 12 | ||||
| -rw-r--r-- | shell/hush_test/hush-test2/strops3.right | 7 | ||||
| -rwxr-xr-x | shell/hush_test/hush-test2/strops3.tests | 13 |
12 files changed, 127 insertions, 24 deletions
diff --git a/shell/ash.c b/shell/ash.c index 58da0a2a0..cfcc0b818 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -207,17 +207,17 @@ | |||
| 207 | #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT | 207 | #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT |
| 208 | /* BASH_TEST2: [[ EXPR ]] | 208 | /* BASH_TEST2: [[ EXPR ]] |
| 209 | * Status of [[ support: | 209 | * Status of [[ support: |
| 210 | * We replace && and || with -a and -o | 210 | * && and || work as they should |
| 211 | * = is glob match operator, not equality operator: STR = GLOB | ||
| 212 | * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") | ||
| 213 | * == same as = | ||
| 214 | * add =~ regex match operator: STR =~ REGEX | ||
| 211 | * TODO: | 215 | * TODO: |
| 212 | * singleword+noglob expansion: | 216 | * singleword+noglob expansion: |
| 213 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? | 217 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? |
| 214 | * [[ /bin/n* ]]; echo 0:$? | 218 | * [[ /bin/n* ]]; echo 0:$? |
| 215 | * -a/-o are not AND/OR ops! (they are just strings) | ||
| 216 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) | 219 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) |
| 217 | * = is glob match operator, not equality operator: STR = GLOB | 220 | * ( ) < > should not have special meaning |
| 218 | * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") | ||
| 219 | * == same as = | ||
| 220 | * add =~ regex match operator: STR =~ REGEX | ||
| 221 | */ | 221 | */ |
| 222 | #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) | 222 | #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) |
| 223 | #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT | 223 | #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT |
| @@ -11823,7 +11823,8 @@ simplecmd(void) | |||
| 11823 | tokpushback = 1; | 11823 | tokpushback = 1; |
| 11824 | goto out; | 11824 | goto out; |
| 11825 | } | 11825 | } |
| 11826 | wordtext = (char *) (t == TAND ? "-a" : "-o"); | 11826 | /* pass "&&" or "||" to [[ ]] as literal args */ |
| 11827 | wordtext = (char *) (t == TAND ? "&&" : "||"); | ||
| 11827 | #endif | 11828 | #endif |
| 11828 | case TWORD: | 11829 | case TWORD: |
| 11829 | n = stzalloc(sizeof(struct narg)); | 11830 | n = stzalloc(sizeof(struct narg)); |
diff --git a/shell/hush.c b/shell/hush.c index bc6e6014f..7c1e1d748 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -84,13 +84,12 @@ | |||
| 84 | * [[ args ]] are CMD_SINGLEWORD_NOGLOB: | 84 | * [[ args ]] are CMD_SINGLEWORD_NOGLOB: |
| 85 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? | 85 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? |
| 86 | * [[ /bin/n* ]]; echo 0:$? | 86 | * [[ /bin/n* ]]; echo 0:$? |
| 87 | * = is glob match operator, not equality operator: STR = GLOB | ||
| 88 | * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") | ||
| 89 | * == same as = | ||
| 90 | * =~ is regex match operator: STR =~ REGEX | ||
| 87 | * TODO: | 91 | * 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) | 92 | * 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 GLOB, quoting is significant on char-by-char basis: a*cd"*") | ||
| 92 | * == same as = | ||
| 93 | * add =~ regex match operator: STR =~ REGEX | ||
| 94 | */ | 93 | */ |
| 95 | //config:config HUSH | 94 | //config:config HUSH |
| 96 | //config: bool "hush (68 kb)" | 95 | //config: bool "hush (68 kb)" |
| @@ -651,14 +650,16 @@ struct command { | |||
| 651 | smallint cmd_type; /* CMD_xxx */ | 650 | smallint cmd_type; /* CMD_xxx */ |
| 652 | #define CMD_NORMAL 0 | 651 | #define CMD_NORMAL 0 |
| 653 | #define CMD_SUBSHELL 1 | 652 | #define CMD_SUBSHELL 1 |
| 654 | #if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY | 653 | #if BASH_TEST2 |
| 655 | /* used for "[[ EXPR ]]", and to prevent word splitting and globbing in | 654 | /* used for "[[ EXPR ]]" */ |
| 656 | * "export v=t*" | 655 | # define CMD_TEST2_SINGLEWORD_NOGLOB 2 |
| 657 | */ | 656 | #endif |
| 658 | # define CMD_SINGLEWORD_NOGLOB 2 | 657 | #if ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY |
| 658 | /* used to prevent word splitting and globbing in "export v=t*" */ | ||
| 659 | # define CMD_SINGLEWORD_NOGLOB 3 | ||
| 659 | #endif | 660 | #endif |
| 660 | #if ENABLE_HUSH_FUNCTIONS | 661 | #if ENABLE_HUSH_FUNCTIONS |
| 661 | # define CMD_FUNCDEF 3 | 662 | # define CMD_FUNCDEF 4 |
| 662 | #endif | 663 | #endif |
| 663 | 664 | ||
| 664 | smalluint cmd_exitcode; | 665 | smalluint cmd_exitcode; |
| @@ -4112,6 +4113,14 @@ static int done_word(struct parse_context *ctx) | |||
| 4112 | ctx->ctx_dsemicolon = 0; | 4113 | ctx->ctx_dsemicolon = 0; |
| 4113 | } else | 4114 | } else |
| 4114 | # endif | 4115 | # endif |
| 4116 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
| 4117 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB | ||
| 4118 | && strcmp(ctx->word.data, "]]") == 0 | ||
| 4119 | ) { | ||
| 4120 | /* allow "[[ ]] >file" etc */ | ||
| 4121 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | ||
| 4122 | } else | ||
| 4123 | # endif | ||
| 4115 | if (!command->argv /* if it's the first word... */ | 4124 | if (!command->argv /* if it's the first word... */ |
| 4116 | # if ENABLE_HUSH_LOOPS | 4125 | # if ENABLE_HUSH_LOOPS |
| 4117 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ | 4126 | && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ |
| @@ -4146,11 +4155,13 @@ static int done_word(struct parse_context *ctx) | |||
| 4146 | (ctx->ctx_res_w == RES_SNTX)); | 4155 | (ctx->ctx_res_w == RES_SNTX)); |
| 4147 | return (ctx->ctx_res_w == RES_SNTX); | 4156 | return (ctx->ctx_res_w == RES_SNTX); |
| 4148 | } | 4157 | } |
| 4158 | # if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
| 4159 | if (strcmp(ctx->word.data, "[[") == 0) { | ||
| 4160 | command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; | ||
| 4161 | } else | ||
| 4162 | # endif | ||
| 4149 | # if defined(CMD_SINGLEWORD_NOGLOB) | 4163 | # if defined(CMD_SINGLEWORD_NOGLOB) |
| 4150 | if (0 | 4164 | if (0 |
| 4151 | # if BASH_TEST2 | ||
| 4152 | || strcmp(ctx->word.data, "[[") == 0 | ||
| 4153 | # endif | ||
| 4154 | /* In bash, local/export/readonly are special, args | 4165 | /* In bash, local/export/readonly are special, args |
| 4155 | * are assignments and therefore expansion of them | 4166 | * are assignments and therefore expansion of them |
| 4156 | * should be "one-word" expansion: | 4167 | * should be "one-word" expansion: |
| @@ -4172,7 +4183,8 @@ static int done_word(struct parse_context *ctx) | |||
| 4172 | ) { | 4183 | ) { |
| 4173 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; | 4184 | command->cmd_type = CMD_SINGLEWORD_NOGLOB; |
| 4174 | } | 4185 | } |
| 4175 | /* fall through */ | 4186 | # else |
| 4187 | { /* empty block to pair "if ... else" */ } | ||
| 4176 | # endif | 4188 | # endif |
| 4177 | } | 4189 | } |
| 4178 | #endif /* HAS_KEYWORDS */ | 4190 | #endif /* HAS_KEYWORDS */ |
| @@ -5354,9 +5366,15 @@ static struct pipe *parse_stream(char **pstring, | |||
| 5354 | if (ch != '\n') | 5366 | if (ch != '\n') |
| 5355 | next = i_peek_and_eat_bkslash_nl(input); | 5367 | next = i_peek_and_eat_bkslash_nl(input); |
| 5356 | 5368 | ||
| 5357 | is_special = "{}<>;&|()#" /* special outside of "str" */ | 5369 | is_special = "{}<>&|();#" /* special outside of "str" */ |
| 5358 | "$\"" IF_HUSH_TICK("`") /* always special */ | 5370 | "$\"" IF_HUSH_TICK("`") /* always special */ |
| 5359 | SPECIAL_VAR_SYMBOL_STR; | 5371 | SPECIAL_VAR_SYMBOL_STR; |
| 5372 | #if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
| 5373 | if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { | ||
| 5374 | /* In [[ ]], {}<>&|() are not special */ | ||
| 5375 | is_special += 8; | ||
| 5376 | } else | ||
| 5377 | #endif | ||
| 5360 | /* Are { and } special here? */ | 5378 | /* Are { and } special here? */ |
| 5361 | if (ctx.command->argv /* word [word]{... - non-special */ | 5379 | if (ctx.command->argv /* word [word]{... - non-special */ |
| 5362 | || ctx.word.length /* word{... - non-special */ | 5380 | || ctx.word.length /* word{... - non-special */ |
| @@ -6953,7 +6971,7 @@ static char **expand_strvec_to_strvec(char **argv) | |||
| 6953 | return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); | 6971 | return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); |
| 6954 | } | 6972 | } |
| 6955 | 6973 | ||
| 6956 | #if defined(CMD_SINGLEWORD_NOGLOB) | 6974 | #if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) |
| 6957 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) | 6975 | static char **expand_strvec_to_strvec_singleword_noglob(char **argv) |
| 6958 | { | 6976 | { |
| 6959 | return expand_variables(argv, EXP_FLAG_SINGLEWORD); | 6977 | return expand_variables(argv, EXP_FLAG_SINGLEWORD); |
| @@ -9133,6 +9151,11 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
| 9133 | } | 9151 | } |
| 9134 | 9152 | ||
| 9135 | /* Expand the rest into (possibly) many strings each */ | 9153 | /* Expand the rest into (possibly) many strings each */ |
| 9154 | #if defined(CMD_TEST2_SINGLEWORD_NOGLOB) | ||
| 9155 | if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) | ||
| 9156 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); | ||
| 9157 | else | ||
| 9158 | #endif | ||
| 9136 | #if defined(CMD_SINGLEWORD_NOGLOB) | 9159 | #if defined(CMD_SINGLEWORD_NOGLOB) |
| 9137 | if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) | 9160 | if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) |
| 9138 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); | 9161 | argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); |
diff --git a/shell/hush_test/hush-test2/andor1.right b/shell/hush_test/hush-test2/andor1.right new file mode 100644 index 000000000..038c7a681 --- /dev/null +++ b/shell/hush_test/hush-test2/andor1.right | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | 1:YES | ||
| 2 | 2:no | ||
| 3 | 3:YES | ||
| 4 | 4:YES | ||
| 5 | 5:no | ||
| 6 | 6:no | ||
diff --git a/shell/hush_test/hush-test2/andor1.tests b/shell/hush_test/hush-test2/andor1.tests new file mode 100755 index 000000000..c449de7e6 --- /dev/null +++ b/shell/hush_test/hush-test2/andor1.tests | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | e='' | ||
| 2 | [[ a && b ]] && echo 1:YES | ||
| 3 | [[ a && '' ]] || echo 2:no | ||
| 4 | [[ a || b ]] && echo 3:YES | ||
| 5 | [[ '' || b ]] && echo 4:YES | ||
| 6 | [[ "" || "$e" ]] || echo 5:no | ||
| 7 | [[ "" || $e ]] || echo 6:no | ||
diff --git a/shell/hush_test/hush-test2/noglob1.right b/shell/hush_test/hush-test2/noglob1.right new file mode 100644 index 000000000..d0c3f1d8e --- /dev/null +++ b/shell/hush_test/hush-test2/noglob1.right | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | 1:YES:0 | ||
| 2 | 2:YES:0 | ||
diff --git a/shell/hush_test/hush-test2/noglob1.tests b/shell/hush_test/hush-test2/noglob1.tests new file mode 100755 index 000000000..963bacbd3 --- /dev/null +++ b/shell/hush_test/hush-test2/noglob1.tests | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | v='*.tests' | ||
| 2 | [[ *.tests ]]; echo 1:YES:$? | ||
| 3 | [[ $v ]]; echo 2:YES:$? | ||
diff --git a/shell/hush_test/hush-test2/strops1.right b/shell/hush_test/hush-test2/strops1.right new file mode 100644 index 000000000..590496301 --- /dev/null +++ b/shell/hush_test/hush-test2/strops1.right | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | 1:YES:0 | ||
| 2 | 2:YES:0 | ||
| 3 | 3:YES:0 | ||
| 4 | 4:YES:0 | ||
| 5 | 5:YES:0 | ||
| 6 | 6:YES:0 | ||
| 7 | 7:YES:0 | ||
| 8 | 8:no:1 | ||
diff --git a/shell/hush_test/hush-test2/strops1.tests b/shell/hush_test/hush-test2/strops1.tests new file mode 100755 index 000000000..bb24e2a2f --- /dev/null +++ b/shell/hush_test/hush-test2/strops1.tests | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | v='*.z' | ||
| 2 | [[ a.z = *.z ]]; echo 1:YES:$? | ||
| 3 | [[ a.z == $v ]]; echo 2:YES:$? | ||
| 4 | |||
| 5 | # wildcards can match a slash | ||
| 6 | [[ a/b = a*b ]]; echo 3:YES:$? | ||
| 7 | [[ a/b == a?b ]]; echo 4:YES:$? | ||
| 8 | |||
| 9 | # wildcards can match a leading dot | ||
| 10 | [[ a/.b = a/*b ]]; echo 5:YES:$? | ||
| 11 | [[ a/.b == a/?b ]]; echo 6:YES:$? | ||
| 12 | |||
| 13 | # wildcards can be escaped | ||
| 14 | [[ abc = a*c ]]; echo 7:YES:$? | ||
| 15 | [[ abc == a\*c ]]; echo 8:no:$? | ||
diff --git a/shell/hush_test/hush-test2/strops2.right b/shell/hush_test/hush-test2/strops2.right new file mode 100644 index 000000000..8ddb4b0f0 --- /dev/null +++ b/shell/hush_test/hush-test2/strops2.right | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | 1:ERR2:2 | ||
| 2 | 2:YES:0 | ||
| 3 | 3:YES:0 | ||
| 4 | 4:YES:0 | ||
| 5 | 5:no:1 | ||
| 6 | 6:YES:0 | ||
diff --git a/shell/hush_test/hush-test2/strops2.tests b/shell/hush_test/hush-test2/strops2.tests new file mode 100755 index 000000000..ab325bc9f --- /dev/null +++ b/shell/hush_test/hush-test2/strops2.tests | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # malformed regex | ||
| 2 | [[ a =~ * ]]; echo 1:ERR2:$? | ||
| 3 | |||
| 4 | [[ a/b =~ a.b ]]; echo 2:YES:$? | ||
| 5 | [[ a/b =~ /*b ]]; echo 3:YES:$? | ||
| 6 | |||
| 7 | v='[]b.-]' | ||
| 8 | [[ a/.b] =~ $v ]]; echo 4:YES:$? | ||
| 9 | |||
| 10 | v=']b.-' | ||
| 11 | [[ a/.b] =~ $v ]]; echo 5:no:$? | ||
| 12 | [[ a/.b] =~ [$v] ]]; echo 6:YES:$? | ||
diff --git a/shell/hush_test/hush-test2/strops3.right b/shell/hush_test/hush-test2/strops3.right new file mode 100644 index 000000000..14cc04fdc --- /dev/null +++ b/shell/hush_test/hush-test2/strops3.right | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | 1:YES:0 | ||
| 2 | 2:YES:0 | ||
| 3 | 3:no:1 | ||
| 4 | 4:YES:0 | ||
| 5 | 2u:YES:0 | ||
| 6 | 3u:YES:0 | ||
| 7 | 4u:YES:0 | ||
diff --git a/shell/hush_test/hush-test2/strops3.tests b/shell/hush_test/hush-test2/strops3.tests new file mode 100755 index 000000000..927476671 --- /dev/null +++ b/shell/hush_test/hush-test2/strops3.tests | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # regex should accept '+' operator | ||
| 2 | [[ abcdef =~ a[b-z]+ ]]; echo 1:YES:$? | ||
| 3 | |||
| 4 | # newline matches by "match any" patterns | ||
| 5 | v=' | ||
| 6 | ' | ||
| 7 | [[ "$v" =~ . ]]; echo 2:YES:$? | ||
| 8 | [[ "$v" =~ "[$v]" ]]; echo 3:no:$? # hmm bash does return 1... why? | ||
| 9 | [[ "$v" =~ [^a] ]]; echo 4:YES:$? | ||
| 10 | # should work even without quotes: | ||
| 11 | [[ $v =~ . ]]; echo 2u:YES:$? | ||
| 12 | [[ $v =~ [$v] ]]; echo 3u:YES:$? | ||
| 13 | [[ $v =~ [^a] ]]; echo 4u:YES:$? | ||
