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>
diff --git a/coreutils/test.c b/coreutils/test.c
index a089861..ac7b546 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -76,6 +76,8 @@
//usage: "1\n"
#include "libbb.h"
+#include <regex.h>
+#include <fnmatch.h>
/* This is a NOFORK applet. Be very careful! */
@@ -146,6 +148,14 @@
#define TEST_DEBUG 0
+#if ENABLE_TEST2 \
+ || (ENABLE_ASH_BASH_COMPAT && ENABLE_ASH_TEST) \
+ || (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
+# define BASH_TEST2 1
+#else
+# define BASH_TEST2 0
+#endif
+
enum token {
EOI,
@@ -184,6 +194,10 @@
STRLT,
STRGT,
+#if BASH_TEST2
+ REGEX,
+#endif
+
INTEQ, /* int ops */
INTNE,
INTGE,
@@ -257,6 +271,9 @@
"STRNE",
"STRLT",
"STRGT",
+#if BASH_TEST2
+ "REGEX",
+#endif
"INTEQ",
"INTNE",
"INTGE",
@@ -320,6 +337,9 @@
{ /* "!=" */ STRNE , BINOP },
{ /* "<" */ STRLT , BINOP },
{ /* ">" */ STRGT , BINOP },
+#if BASH_TEST2
+ { /* "=~" */ REGEX , BINOP },
+#endif
{ /* "-eq"*/ INTEQ , BINOP },
{ /* "-ne"*/ INTNE , BINOP },
{ /* "-ge"*/ INTGE , BINOP },
@@ -332,6 +352,10 @@
{ /* "!" */ UNOT , BUNOP },
{ /* "-a" */ BAND , BBINOP },
{ /* "-o" */ BOR , BBINOP },
+#if BASH_TEST2
+ { /* "&&" */ BAND , BBINOP },
+ { /* "||" */ BOR , BBINOP },
+#endif
{ /* "(" */ LPAREN , PAREN },
{ /* ")" */ RPAREN , PAREN },
};
@@ -365,6 +389,9 @@
"!=" "\0"
"<" "\0"
">" "\0"
+#if BASH_TEST2
+ "=~" "\0"
+#endif
"-eq" "\0"
"-ne" "\0"
"-ge" "\0"
@@ -377,6 +404,10 @@
"!" "\0"
"-a" "\0"
"-o" "\0"
+#if BASH_TEST2
+ "&&" "\0"
+ "||" "\0"
+#endif
"(" "\0"
")" "\0"
;
@@ -397,6 +428,9 @@
const struct operator_t *last_operator;
gid_t *group_array;
int ngroups;
+#if BASH_TEST2
+ bool bash_test2;
+#endif
jmp_buf leaving;
};
@@ -408,6 +442,7 @@
#define last_operator (S.last_operator)
#define group_array (S.group_array )
#define ngroups (S.ngroups )
+#define bash_test2 (S.bash_test2 )
#define leaving (S.leaving )
#define INIT_S() do { \
@@ -501,6 +536,20 @@
n = index_in_strings(ops_texts, s);
if (n < 0)
return OPERAND;
+
+#if BASH_TEST2
+ if (ops_table[n].op_num == REGEX && !bash_test2) {
+ /* =~ is only for [[ ]] */
+ return OPERAND;
+ }
+ if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
+ /* [ ] accepts -a and -o but not && and || */
+ /* [[ ]] accepts && and || but not -a and -o */
+ if (bash_test2 == (s[0] == '-'))
+ return OPERAND;
+ }
+#endif
+
last_operator = &ops_table[n];
return ops_table[n].op_num;
}
@@ -536,6 +585,29 @@
/*if (op->op_num == INTLT)*/
return val1 < val2;
}
+#if BASH_TEST2
+ if (bash_test2) {
+ if (op->op_num == STREQ) {
+ val1 = fnmatch(opnd2, opnd1, 0);
+ return val1 == 0;
+ }
+ if (op->op_num == STRNE) {
+ val1 = fnmatch(opnd2, opnd1, 0);
+ return val1 != 0;
+ }
+ if (op->op_num == REGEX) {
+ regex_t re_buffer;
+ memset(&re_buffer, 0, sizeof(re_buffer));
+ if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE?
+ /* Bad regex */
+ longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
+ }
+ val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
+ regfree(&re_buffer);
+ return val1 == 0;
+ }
+ }
+#endif
if (is_str_op(op->op_num)) {
val1 = strcmp(opnd1, opnd2);
if (op->op_num == STREQ)
@@ -824,6 +896,9 @@
{
int res;
const char *arg0;
+#if BASH_TEST2
+ bool bt2 = 0;
+#endif
arg0 = bb_basename(argv[0]);
if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
@@ -840,6 +915,9 @@
bb_simple_error_msg("missing ]]");
return 2;
}
+#if BASH_TEST2
+ bt2 = 1;
+#endif
}
argv[argc] = NULL;
}
@@ -848,6 +926,10 @@
/* We must do DEINIT_S() prior to returning */
INIT_S();
+#if BASH_TEST2
+ bash_test2 = bt2;
+#endif
+
res = setjmp(leaving);
if (res)
goto ret;