aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2020-10-31 03:34:07 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2020-10-31 03:34:07 +0100
commitd2241f59022c38d4b171e56eea42e216ecccfdd9 (patch)
treeedb75c2530f493c9e3f193f346d8125fe79c107f
parent112453acf24520b4655f9f36da41d8ac591b1a60 (diff)
downloadbusybox-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>
-rw-r--r--coreutils/test.c82
-rw-r--r--shell/ash.c15
-rw-r--r--shell/hush.c57
-rw-r--r--shell/hush_test/hush-test2/andor1.right6
-rwxr-xr-xshell/hush_test/hush-test2/andor1.tests7
-rw-r--r--shell/hush_test/hush-test2/noglob1.right2
-rwxr-xr-xshell/hush_test/hush-test2/noglob1.tests3
-rw-r--r--shell/hush_test/hush-test2/strops1.right8
-rwxr-xr-xshell/hush_test/hush-test2/strops1.tests15
-rw-r--r--shell/hush_test/hush-test2/strops2.right6
-rwxr-xr-xshell/hush_test/hush-test2/strops2.tests12
-rw-r--r--shell/hush_test/hush-test2/strops3.right7
-rwxr-xr-xshell/hush_test/hush-test2/strops3.tests13
13 files changed, 209 insertions, 24 deletions
diff --git a/coreutils/test.c b/coreutils/test.c
index a08986130..ac7b546a3 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -76,6 +76,8 @@
76//usage: "1\n" 76//usage: "1\n"
77 77
78#include "libbb.h" 78#include "libbb.h"
79#include <regex.h>
80#include <fnmatch.h>
79 81
80/* This is a NOFORK applet. Be very careful! */ 82/* This is a NOFORK applet. Be very careful! */
81 83
@@ -146,6 +148,14 @@
146 148
147#define TEST_DEBUG 0 149#define TEST_DEBUG 0
148 150
151#if ENABLE_TEST2 \
152 || (ENABLE_ASH_BASH_COMPAT && ENABLE_ASH_TEST) \
153 || (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
154# define BASH_TEST2 1
155#else
156# define BASH_TEST2 0
157#endif
158
149enum token { 159enum token {
150 EOI, 160 EOI,
151 161
@@ -184,6 +194,10 @@ enum token {
184 STRLT, 194 STRLT,
185 STRGT, 195 STRGT,
186 196
197#if BASH_TEST2
198 REGEX,
199#endif
200
187 INTEQ, /* int ops */ 201 INTEQ, /* int ops */
188 INTNE, 202 INTNE,
189 INTGE, 203 INTGE,
@@ -257,6 +271,9 @@ static const char *const TOKSTR[] = {
257 "STRNE", 271 "STRNE",
258 "STRLT", 272 "STRLT",
259 "STRGT", 273 "STRGT",
274#if BASH_TEST2
275 "REGEX",
276#endif
260 "INTEQ", 277 "INTEQ",
261 "INTNE", 278 "INTNE",
262 "INTGE", 279 "INTGE",
@@ -320,6 +337,9 @@ static const struct operator_t ops_table[] = {
320 { /* "!=" */ STRNE , BINOP }, 337 { /* "!=" */ STRNE , BINOP },
321 { /* "<" */ STRLT , BINOP }, 338 { /* "<" */ STRLT , BINOP },
322 { /* ">" */ STRGT , BINOP }, 339 { /* ">" */ STRGT , BINOP },
340#if BASH_TEST2
341 { /* "=~" */ REGEX , BINOP },
342#endif
323 { /* "-eq"*/ INTEQ , BINOP }, 343 { /* "-eq"*/ INTEQ , BINOP },
324 { /* "-ne"*/ INTNE , BINOP }, 344 { /* "-ne"*/ INTNE , BINOP },
325 { /* "-ge"*/ INTGE , BINOP }, 345 { /* "-ge"*/ INTGE , BINOP },
@@ -332,6 +352,10 @@ static const struct operator_t ops_table[] = {
332 { /* "!" */ UNOT , BUNOP }, 352 { /* "!" */ UNOT , BUNOP },
333 { /* "-a" */ BAND , BBINOP }, 353 { /* "-a" */ BAND , BBINOP },
334 { /* "-o" */ BOR , BBINOP }, 354 { /* "-o" */ BOR , BBINOP },
355#if BASH_TEST2
356 { /* "&&" */ BAND , BBINOP },
357 { /* "||" */ BOR , BBINOP },
358#endif
335 { /* "(" */ LPAREN , PAREN }, 359 { /* "(" */ LPAREN , PAREN },
336 { /* ")" */ RPAREN , PAREN }, 360 { /* ")" */ RPAREN , PAREN },
337}; 361};
@@ -365,6 +389,9 @@ static const char ops_texts[] ALIGN1 =
365 "!=" "\0" 389 "!=" "\0"
366 "<" "\0" 390 "<" "\0"
367 ">" "\0" 391 ">" "\0"
392#if BASH_TEST2
393 "=~" "\0"
394#endif
368 "-eq" "\0" 395 "-eq" "\0"
369 "-ne" "\0" 396 "-ne" "\0"
370 "-ge" "\0" 397 "-ge" "\0"
@@ -377,6 +404,10 @@ static const char ops_texts[] ALIGN1 =
377 "!" "\0" 404 "!" "\0"
378 "-a" "\0" 405 "-a" "\0"
379 "-o" "\0" 406 "-o" "\0"
407#if BASH_TEST2
408 "&&" "\0"
409 "||" "\0"
410#endif
380 "(" "\0" 411 "(" "\0"
381 ")" "\0" 412 ")" "\0"
382; 413;
@@ -397,6 +428,9 @@ struct test_statics {
397 const struct operator_t *last_operator; 428 const struct operator_t *last_operator;
398 gid_t *group_array; 429 gid_t *group_array;
399 int ngroups; 430 int ngroups;
431#if BASH_TEST2
432 bool bash_test2;
433#endif
400 jmp_buf leaving; 434 jmp_buf leaving;
401}; 435};
402 436
@@ -408,6 +442,7 @@ extern struct test_statics *const test_ptr_to_statics;
408#define last_operator (S.last_operator) 442#define last_operator (S.last_operator)
409#define group_array (S.group_array ) 443#define group_array (S.group_array )
410#define ngroups (S.ngroups ) 444#define ngroups (S.ngroups )
445#define bash_test2 (S.bash_test2 )
411#define leaving (S.leaving ) 446#define leaving (S.leaving )
412 447
413#define INIT_S() do { \ 448#define INIT_S() do { \
@@ -501,6 +536,20 @@ static enum token check_operator(const char *s)
501 n = index_in_strings(ops_texts, s); 536 n = index_in_strings(ops_texts, s);
502 if (n < 0) 537 if (n < 0)
503 return OPERAND; 538 return OPERAND;
539
540#if BASH_TEST2
541 if (ops_table[n].op_num == REGEX && !bash_test2) {
542 /* =~ is only for [[ ]] */
543 return OPERAND;
544 }
545 if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
546 /* [ ] accepts -a and -o but not && and || */
547 /* [[ ]] accepts && and || but not -a and -o */
548 if (bash_test2 == (s[0] == '-'))
549 return OPERAND;
550 }
551#endif
552
504 last_operator = &ops_table[n]; 553 last_operator = &ops_table[n];
505 return ops_table[n].op_num; 554 return ops_table[n].op_num;
506} 555}
@@ -536,6 +585,29 @@ static int binop(void)
536 /*if (op->op_num == INTLT)*/ 585 /*if (op->op_num == INTLT)*/
537 return val1 < val2; 586 return val1 < val2;
538 } 587 }
588#if BASH_TEST2
589 if (bash_test2) {
590 if (op->op_num == STREQ) {
591 val1 = fnmatch(opnd2, opnd1, 0);
592 return val1 == 0;
593 }
594 if (op->op_num == STRNE) {
595 val1 = fnmatch(opnd2, opnd1, 0);
596 return val1 != 0;
597 }
598 if (op->op_num == REGEX) {
599 regex_t re_buffer;
600 memset(&re_buffer, 0, sizeof(re_buffer));
601 if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE?
602 /* Bad regex */
603 longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
604 }
605 val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
606 regfree(&re_buffer);
607 return val1 == 0;
608 }
609 }
610#endif
539 if (is_str_op(op->op_num)) { 611 if (is_str_op(op->op_num)) {
540 val1 = strcmp(opnd1, opnd2); 612 val1 = strcmp(opnd1, opnd2);
541 if (op->op_num == STREQ) 613 if (op->op_num == STREQ)
@@ -824,6 +896,9 @@ int test_main(int argc, char **argv)
824{ 896{
825 int res; 897 int res;
826 const char *arg0; 898 const char *arg0;
899#if BASH_TEST2
900 bool bt2 = 0;
901#endif
827 902
828 arg0 = bb_basename(argv[0]); 903 arg0 = bb_basename(argv[0]);
829 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST) 904 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
@@ -840,6 +915,9 @@ int test_main(int argc, char **argv)
840 bb_simple_error_msg("missing ]]"); 915 bb_simple_error_msg("missing ]]");
841 return 2; 916 return 2;
842 } 917 }
918#if BASH_TEST2
919 bt2 = 1;
920#endif
843 } 921 }
844 argv[argc] = NULL; 922 argv[argc] = NULL;
845 } 923 }
@@ -848,6 +926,10 @@ int test_main(int argc, char **argv)
848 /* We must do DEINIT_S() prior to returning */ 926 /* We must do DEINIT_S() prior to returning */
849 INIT_S(); 927 INIT_S();
850 928
929#if BASH_TEST2
930 bash_test2 = bt2;
931#endif
932
851 res = setjmp(leaving); 933 res = setjmp(leaving);
852 if (res) 934 if (res)
853 goto ret; 935 goto ret;
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)
6957static char **expand_strvec_to_strvec_singleword_noglob(char **argv) 6975static 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 @@
11:YES
22:no
33:YES
44:YES
55:no
66: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 @@
1e=''
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 @@
11:YES:0
22: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 @@
1v='*.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 @@
11:YES:0
22:YES:0
33:YES:0
44:YES:0
55:YES:0
66:YES:0
77:YES:0
88: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 @@
1v='*.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 @@
11:ERR2:2
22:YES:0
33:YES:0
44:YES:0
55:no:1
66: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
7v='[]b.-]'
8[[ a/.b] =~ $v ]]; echo 4:YES:$?
9
10v=']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 @@
11:YES:0
22:YES:0
33:no:1
44:YES:0
52u:YES:0
63u:YES:0
74u: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
5v='
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:$?