diff options
author | Denys Vlasenko | 2020-10-31 03:34:07 +0100 |
---|---|---|
committer | Denys Vlasenko | 2020-10-31 03:34:07 +0100 |
commit | d2241f59022c38d4b171e56eea42e216ecccfdd9 (patch) | |
tree | edb75c2530f493c9e3f193f346d8125fe79c107f /shell | |
parent | 112453acf24520b4655f9f36da41d8ac591b1a60 (diff) | |
download | busybox-d2241f59022c38d4b171e56eea42e216ecccfdd9.zip busybox-d2241f59022c38d4b171e56eea42e216ecccfdd9.tar.gz |
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 58da0a2..cfcc0b8 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -207,17 +207,17 @@ #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT /* BASH_TEST2: [[ EXPR ]] * Status of [[ support: - * We replace && and || with -a and -o + * && and || work as they should + * = is glob match operator, not equality operator: STR = GLOB + * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") + * == same as = + * add =~ regex match operator: STR =~ REGEX * TODO: * singleword+noglob expansion: * v='a b'; [[ $v = 'a b' ]]; echo 0:$? * [[ /bin/n* ]]; echo 0:$? - * -a/-o are not AND/OR ops! (they are just strings) * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) - * = is glob match operator, not equality operator: STR = GLOB - * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") - * == same as = - * add =~ regex match operator: STR =~ REGEX + * ( ) < > should not have special meaning */ #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT @@ -11823,7 +11823,8 @@ simplecmd(void) tokpushback = 1; goto out; } - wordtext = (char *) (t == TAND ? "-a" : "-o"); + /* pass "&&" or "||" to [[ ]] as literal args */ + wordtext = (char *) (t == TAND ? "&&" : "||"); #endif case TWORD: n = stzalloc(sizeof(struct narg)); diff --git a/shell/hush.c b/shell/hush.c index bc6e601..7c1e1d7 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -84,13 +84,12 @@ * [[ args ]] are CMD_SINGLEWORD_NOGLOB: * v='a b'; [[ $v = 'a b' ]]; echo 0:$? * [[ /bin/n* ]]; echo 0:$? + * = is glob match operator, not equality operator: STR = GLOB + * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") + * == same as = + * =~ is regex match operator: STR =~ REGEX * TODO: - * &&/|| are AND/OR ops, -a/-o are not * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) - * = is glob match operator, not equality operator: STR = GLOB - * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") - * == same as = - * add =~ regex match operator: STR =~ REGEX */ //config:config HUSH //config: bool "hush (68 kb)" @@ -651,14 +650,16 @@ struct command { smallint cmd_type; /* CMD_xxx */ #define CMD_NORMAL 0 #define CMD_SUBSHELL 1 -#if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY -/* used for "[[ EXPR ]]", and to prevent word splitting and globbing in - * "export v=t*" - */ -# define CMD_SINGLEWORD_NOGLOB 2 +#if BASH_TEST2 +/* used for "[[ EXPR ]]" */ +# define CMD_TEST2_SINGLEWORD_NOGLOB 2 +#endif +#if ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY +/* used to prevent word splitting and globbing in "export v=t*" */ +# define CMD_SINGLEWORD_NOGLOB 3 #endif #if ENABLE_HUSH_FUNCTIONS -# define CMD_FUNCDEF 3 +# define CMD_FUNCDEF 4 #endif smalluint cmd_exitcode; @@ -4112,6 +4113,14 @@ static int done_word(struct parse_context *ctx) ctx->ctx_dsemicolon = 0; } else # endif +# if defined(CMD_TEST2_SINGLEWORD_NOGLOB) + if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB + && strcmp(ctx->word.data, "]]") == 0 + ) { + /* allow "[[ ]] >file" etc */ + command->cmd_type = CMD_SINGLEWORD_NOGLOB; + } else +# endif if (!command->argv /* if it's the first word... */ # if ENABLE_HUSH_LOOPS && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ @@ -4146,11 +4155,13 @@ static int done_word(struct parse_context *ctx) (ctx->ctx_res_w == RES_SNTX)); return (ctx->ctx_res_w == RES_SNTX); } +# if defined(CMD_TEST2_SINGLEWORD_NOGLOB) + if (strcmp(ctx->word.data, "[[") == 0) { + command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB; + } else +# endif # if defined(CMD_SINGLEWORD_NOGLOB) if (0 -# if BASH_TEST2 - || strcmp(ctx->word.data, "[[") == 0 -# endif /* In bash, local/export/readonly are special, args * are assignments and therefore expansion of them * should be "one-word" expansion: @@ -4172,7 +4183,8 @@ static int done_word(struct parse_context *ctx) ) { command->cmd_type = CMD_SINGLEWORD_NOGLOB; } - /* fall through */ +# else + { /* empty block to pair "if ... else" */ } # endif } #endif /* HAS_KEYWORDS */ @@ -5354,9 +5366,15 @@ static struct pipe *parse_stream(char **pstring, if (ch != '\n') next = i_peek_and_eat_bkslash_nl(input); - is_special = "{}<>;&|()#" /* special outside of "str" */ + is_special = "{}<>&|();#" /* special outside of "str" */ "$\"" IF_HUSH_TICK("`") /* always special */ SPECIAL_VAR_SYMBOL_STR; +#if defined(CMD_TEST2_SINGLEWORD_NOGLOB) + if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) { + /* In [[ ]], {}<>&|() are not special */ + is_special += 8; + } else +#endif /* Are { and } special here? */ if (ctx.command->argv /* word [word]{... - non-special */ || ctx.word.length /* word{... - non-special */ @@ -6953,7 +6971,7 @@ static char **expand_strvec_to_strvec(char **argv) return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS); } -#if defined(CMD_SINGLEWORD_NOGLOB) +#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB) static char **expand_strvec_to_strvec_singleword_noglob(char **argv) { return expand_variables(argv, EXP_FLAG_SINGLEWORD); @@ -9133,6 +9151,11 @@ static NOINLINE int run_pipe(struct pipe *pi) } /* Expand the rest into (possibly) many strings each */ +#if defined(CMD_TEST2_SINGLEWORD_NOGLOB) + if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) + argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); + else +#endif #if defined(CMD_SINGLEWORD_NOGLOB) if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) 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 0000000..038c7a6 --- /dev/null +++ b/shell/hush_test/hush-test2/andor1.right @@ -0,0 +1,6 @@ +1:YES +2:no +3:YES +4:YES +5:no +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 0000000..c449de7 --- /dev/null +++ b/shell/hush_test/hush-test2/andor1.tests @@ -0,0 +1,7 @@ +e='' +[[ a && b ]] && echo 1:YES +[[ a && '' ]] || echo 2:no +[[ a || b ]] && echo 3:YES +[[ '' || b ]] && echo 4:YES +[[ "" || "$e" ]] || echo 5:no +[[ "" || $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 0000000..d0c3f1d --- /dev/null +++ b/shell/hush_test/hush-test2/noglob1.right @@ -0,0 +1,2 @@ +1:YES:0 +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 0000000..963bacb --- /dev/null +++ b/shell/hush_test/hush-test2/noglob1.tests @@ -0,0 +1,3 @@ +v='*.tests' +[[ *.tests ]]; echo 1:YES:$? +[[ $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 0000000..5904963 --- /dev/null +++ b/shell/hush_test/hush-test2/strops1.right @@ -0,0 +1,8 @@ +1:YES:0 +2:YES:0 +3:YES:0 +4:YES:0 +5:YES:0 +6:YES:0 +7:YES:0 +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 0000000..bb24e2a --- /dev/null +++ b/shell/hush_test/hush-test2/strops1.tests @@ -0,0 +1,15 @@ +v='*.z' +[[ a.z = *.z ]]; echo 1:YES:$? +[[ a.z == $v ]]; echo 2:YES:$? + +# wildcards can match a slash +[[ a/b = a*b ]]; echo 3:YES:$? +[[ a/b == a?b ]]; echo 4:YES:$? + +# wildcards can match a leading dot +[[ a/.b = a/*b ]]; echo 5:YES:$? +[[ a/.b == a/?b ]]; echo 6:YES:$? + +# wildcards can be escaped +[[ abc = a*c ]]; echo 7:YES:$? +[[ 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 0000000..8ddb4b0 --- /dev/null +++ b/shell/hush_test/hush-test2/strops2.right @@ -0,0 +1,6 @@ +1:ERR2:2 +2:YES:0 +3:YES:0 +4:YES:0 +5:no:1 +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 0000000..ab325bc --- /dev/null +++ b/shell/hush_test/hush-test2/strops2.tests @@ -0,0 +1,12 @@ +# malformed regex +[[ a =~ * ]]; echo 1:ERR2:$? + +[[ a/b =~ a.b ]]; echo 2:YES:$? +[[ a/b =~ /*b ]]; echo 3:YES:$? + +v='[]b.-]' +[[ a/.b] =~ $v ]]; echo 4:YES:$? + +v=']b.-' +[[ a/.b] =~ $v ]]; echo 5:no:$? +[[ 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 0000000..14cc04f --- /dev/null +++ b/shell/hush_test/hush-test2/strops3.right @@ -0,0 +1,7 @@ +1:YES:0 +2:YES:0 +3:no:1 +4:YES:0 +2u:YES:0 +3u:YES:0 +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 0000000..9274766 --- /dev/null +++ b/shell/hush_test/hush-test2/strops3.tests @@ -0,0 +1,13 @@ +# regex should accept '+' operator +[[ abcdef =~ a[b-z]+ ]]; echo 1:YES:$? + +# newline matches by "match any" patterns +v=' +' +[[ "$v" =~ . ]]; echo 2:YES:$? +[[ "$v" =~ "[$v]" ]]; echo 3:no:$? # hmm bash does return 1... why? +[[ "$v" =~ [^a] ]]; echo 4:YES:$? +# should work even without quotes: +[[ $v =~ . ]]; echo 2u:YES:$? +[[ $v =~ [$v] ]]; echo 3u:YES:$? +[[ $v =~ [^a] ]]; echo 4u:YES:$? |