19 |
* Original copyright notice states: |
* Original copyright notice states: |
20 |
* "This program is in the Public Domain." |
* "This program is in the Public Domain." |
21 |
*/ |
*/ |
|
|
|
22 |
#include "libbb.h" |
#include "libbb.h" |
23 |
#include <setjmp.h> |
#include <setjmp.h> |
24 |
|
|
28 |
* This is true regardless of PREFER_APPLETS and STANDALONE_SHELL |
* This is true regardless of PREFER_APPLETS and STANDALONE_SHELL |
29 |
* state. */ |
* state. */ |
30 |
|
|
|
|
|
31 |
/* test(1) accepts the following grammar: |
/* test(1) accepts the following grammar: |
32 |
oexpr ::= aexpr | aexpr "-o" oexpr ; |
oexpr ::= aexpr | aexpr "-o" oexpr ; |
33 |
aexpr ::= nexpr | nexpr "-a" aexpr ; |
aexpr ::= nexpr | nexpr "-a" aexpr ; |
45 |
operand ::= <any legal UNIX file name> |
operand ::= <any legal UNIX file name> |
46 |
*/ |
*/ |
47 |
|
|
48 |
|
/* TODO: handle [[ expr ]] bashism bash-compatibly. |
49 |
|
* [[ ]] is meant to be a "better [ ]", with less weird syntax |
50 |
|
* and without the risk of variables and quoted strings misinterpreted |
51 |
|
* as operators. |
52 |
|
* This will require support from shells - we need to know quote status |
53 |
|
* of each parameter (see below). |
54 |
|
* |
55 |
|
* Word splitting and pathname expansion should NOT be performed: |
56 |
|
* # a="a b"; [[ $a = "a b" ]] && echo YES |
57 |
|
* YES |
58 |
|
* # [[ /bin/m* ]] && echo YES |
59 |
|
* YES |
60 |
|
* |
61 |
|
* =~ should do regexp match |
62 |
|
* = and == should do pattern match against right side: |
63 |
|
* # [[ *a* == bab ]] && echo YES |
64 |
|
* # [[ bab == *a* ]] && echo YES |
65 |
|
* YES |
66 |
|
* != does the negated == (i.e., also with pattern matching). |
67 |
|
* Pattern matching is quotation-sensitive: |
68 |
|
* # [[ bab == "b"a* ]] && echo YES |
69 |
|
* YES |
70 |
|
* # [[ bab == b"a*" ]] && echo YES |
71 |
|
* |
72 |
|
* Conditional operators such as -f must be unquoted literals to be recognized: |
73 |
|
* # [[ -e /bin ]] && echo YES |
74 |
|
* YES |
75 |
|
* # [[ '-e' /bin ]] && echo YES |
76 |
|
* bash: conditional binary operator expected... |
77 |
|
* # A='-e'; [[ $A /bin ]] && echo YES |
78 |
|
* bash: conditional binary operator expected... |
79 |
|
* |
80 |
|
* || and && should work as -o and -a work in [ ] |
81 |
|
* -a and -o aren't recognized (&& and || are to be used instead) |
82 |
|
* ( and ) do not need to be quoted unlike in [ ]: |
83 |
|
* # [[ ( abc ) && '' ]] && echo YES |
84 |
|
* # [[ ( abc ) || '' ]] && echo YES |
85 |
|
* YES |
86 |
|
* # [[ ( abc ) -o '' ]] && echo YES |
87 |
|
* bash: syntax error in conditional expression... |
88 |
|
* |
89 |
|
* Apart from the above, [[ expr ]] should work as [ expr ] |
90 |
|
*/ |
91 |
|
|
92 |
#define TEST_DEBUG 0 |
#define TEST_DEBUG 0 |
93 |
|
|
94 |
enum token { |
enum token { |
95 |
EOI, |
EOI, |
96 |
FILRD, |
|
97 |
|
FILRD, /* file access */ |
98 |
FILWR, |
FILWR, |
99 |
FILEX, |
FILEX, |
100 |
|
|
101 |
FILEXIST, |
FILEXIST, |
102 |
FILREG, |
|
103 |
|
FILREG, /* file type */ |
104 |
FILDIR, |
FILDIR, |
105 |
FILCDEV, |
FILCDEV, |
106 |
FILBDEV, |
FILBDEV, |
107 |
FILFIFO, |
FILFIFO, |
108 |
FILSOCK, |
FILSOCK, |
109 |
|
|
110 |
FILSYM, |
FILSYM, |
111 |
FILGZ, |
FILGZ, |
112 |
FILTT, |
FILTT, |
113 |
FILSUID, |
|
114 |
|
FILSUID, /* file bit */ |
115 |
FILSGID, |
FILSGID, |
116 |
FILSTCK, |
FILSTCK, |
117 |
FILNT, |
|
118 |
|
FILNT, /* file ops */ |
119 |
FILOT, |
FILOT, |
120 |
FILEQ, |
FILEQ, |
121 |
|
|
122 |
FILUID, |
FILUID, |
123 |
FILGID, |
FILGID, |
124 |
STREZ, |
|
125 |
|
STREZ, /* str ops */ |
126 |
STRNZ, |
STRNZ, |
127 |
STREQ, |
STREQ, |
128 |
STRNE, |
STRNE, |
129 |
STRLT, |
STRLT, |
130 |
STRGT, |
STRGT, |
131 |
INTEQ, |
|
132 |
|
INTEQ, /* int ops */ |
133 |
INTNE, |
INTNE, |
134 |
INTGE, |
INTGE, |
135 |
INTGT, |
INTGT, |
136 |
INTLE, |
INTLE, |
137 |
INTLT, |
INTLT, |
138 |
|
|
139 |
UNOT, |
UNOT, |
140 |
BAND, |
BAND, |
141 |
BOR, |
BOR, |
222 |
#define unnest_msg_and_return(expr, ...) return expr |
#define unnest_msg_and_return(expr, ...) return expr |
223 |
#endif |
#endif |
224 |
|
|
225 |
enum token_types { |
enum { |
226 |
UNOP, |
UNOP, |
227 |
BINOP, |
BINOP, |
228 |
BUNOP, |
BUNOP, |
231 |
}; |
}; |
232 |
|
|
233 |
struct operator_t { |
struct operator_t { |
|
char op_text[4]; |
|
234 |
unsigned char op_num, op_type; |
unsigned char op_num, op_type; |
235 |
}; |
}; |
236 |
|
|
237 |
static const struct operator_t ops[] = { |
static const struct operator_t ops_table[] = { |
238 |
{ "-r", FILRD , UNOP }, |
{ /* "-r" */ FILRD , UNOP }, |
239 |
{ "-w", FILWR , UNOP }, |
{ /* "-w" */ FILWR , UNOP }, |
240 |
{ "-x", FILEX , UNOP }, |
{ /* "-x" */ FILEX , UNOP }, |
241 |
{ "-e", FILEXIST, UNOP }, |
{ /* "-e" */ FILEXIST, UNOP }, |
242 |
{ "-f", FILREG , UNOP }, |
{ /* "-f" */ FILREG , UNOP }, |
243 |
{ "-d", FILDIR , UNOP }, |
{ /* "-d" */ FILDIR , UNOP }, |
244 |
{ "-c", FILCDEV , UNOP }, |
{ /* "-c" */ FILCDEV , UNOP }, |
245 |
{ "-b", FILBDEV , UNOP }, |
{ /* "-b" */ FILBDEV , UNOP }, |
246 |
{ "-p", FILFIFO , UNOP }, |
{ /* "-p" */ FILFIFO , UNOP }, |
247 |
{ "-u", FILSUID , UNOP }, |
{ /* "-u" */ FILSUID , UNOP }, |
248 |
{ "-g", FILSGID , UNOP }, |
{ /* "-g" */ FILSGID , UNOP }, |
249 |
{ "-k", FILSTCK , UNOP }, |
{ /* "-k" */ FILSTCK , UNOP }, |
250 |
{ "-s", FILGZ , UNOP }, |
{ /* "-s" */ FILGZ , UNOP }, |
251 |
{ "-t", FILTT , UNOP }, |
{ /* "-t" */ FILTT , UNOP }, |
252 |
{ "-z", STREZ , UNOP }, |
{ /* "-z" */ STREZ , UNOP }, |
253 |
{ "-n", STRNZ , UNOP }, |
{ /* "-n" */ STRNZ , UNOP }, |
254 |
{ "-h", FILSYM , UNOP }, /* for backwards compat */ |
{ /* "-h" */ FILSYM , UNOP }, /* for backwards compat */ |
255 |
|
|
256 |
{ "-O" , FILUID , UNOP }, |
{ /* "-O" */ FILUID , UNOP }, |
257 |
{ "-G" , FILGID , UNOP }, |
{ /* "-G" */ FILGID , UNOP }, |
258 |
{ "-L" , FILSYM , UNOP }, |
{ /* "-L" */ FILSYM , UNOP }, |
259 |
{ "-S" , FILSOCK, UNOP }, |
{ /* "-S" */ FILSOCK , UNOP }, |
260 |
{ "=" , STREQ , BINOP }, |
{ /* "=" */ STREQ , BINOP }, |
261 |
{ "==" , STREQ , BINOP }, |
{ /* "==" */ STREQ , BINOP }, |
262 |
{ "!=" , STRNE , BINOP }, |
{ /* "!=" */ STRNE , BINOP }, |
263 |
{ "<" , STRLT , BINOP }, |
{ /* "<" */ STRLT , BINOP }, |
264 |
{ ">" , STRGT , BINOP }, |
{ /* ">" */ STRGT , BINOP }, |
265 |
{ "-eq", INTEQ , BINOP }, |
{ /* "-eq"*/ INTEQ , BINOP }, |
266 |
{ "-ne", INTNE , BINOP }, |
{ /* "-ne"*/ INTNE , BINOP }, |
267 |
{ "-ge", INTGE , BINOP }, |
{ /* "-ge"*/ INTGE , BINOP }, |
268 |
{ "-gt", INTGT , BINOP }, |
{ /* "-gt"*/ INTGT , BINOP }, |
269 |
{ "-le", INTLE , BINOP }, |
{ /* "-le"*/ INTLE , BINOP }, |
270 |
{ "-lt", INTLT , BINOP }, |
{ /* "-lt"*/ INTLT , BINOP }, |
271 |
{ "-nt", FILNT , BINOP }, |
{ /* "-nt"*/ FILNT , BINOP }, |
272 |
{ "-ot", FILOT , BINOP }, |
{ /* "-ot"*/ FILOT , BINOP }, |
273 |
{ "-ef", FILEQ , BINOP }, |
{ /* "-ef"*/ FILEQ , BINOP }, |
274 |
{ "!" , UNOT , BUNOP }, |
{ /* "!" */ UNOT , BUNOP }, |
275 |
{ "-a" , BAND , BBINOP }, |
{ /* "-a" */ BAND , BBINOP }, |
276 |
{ "-o" , BOR , BBINOP }, |
{ /* "-o" */ BOR , BBINOP }, |
277 |
{ "(" , LPAREN , PAREN }, |
{ /* "(" */ LPAREN , PAREN }, |
278 |
{ ")" , RPAREN , PAREN }, |
{ /* ")" */ RPAREN , PAREN }, |
279 |
}; |
}; |
280 |
|
/* Please keep these two tables in sync */ |
281 |
|
static const char ops_texts[] ALIGN1 = |
282 |
|
"-r" "\0" |
283 |
|
"-w" "\0" |
284 |
|
"-x" "\0" |
285 |
|
"-e" "\0" |
286 |
|
"-f" "\0" |
287 |
|
"-d" "\0" |
288 |
|
"-c" "\0" |
289 |
|
"-b" "\0" |
290 |
|
"-p" "\0" |
291 |
|
"-u" "\0" |
292 |
|
"-g" "\0" |
293 |
|
"-k" "\0" |
294 |
|
"-s" "\0" |
295 |
|
"-t" "\0" |
296 |
|
"-z" "\0" |
297 |
|
"-n" "\0" |
298 |
|
"-h" "\0" |
299 |
|
|
300 |
|
"-O" "\0" |
301 |
|
"-G" "\0" |
302 |
|
"-L" "\0" |
303 |
|
"-S" "\0" |
304 |
|
"=" "\0" |
305 |
|
"==" "\0" |
306 |
|
"!=" "\0" |
307 |
|
"<" "\0" |
308 |
|
">" "\0" |
309 |
|
"-eq" "\0" |
310 |
|
"-ne" "\0" |
311 |
|
"-ge" "\0" |
312 |
|
"-gt" "\0" |
313 |
|
"-le" "\0" |
314 |
|
"-lt" "\0" |
315 |
|
"-nt" "\0" |
316 |
|
"-ot" "\0" |
317 |
|
"-ef" "\0" |
318 |
|
"!" "\0" |
319 |
|
"-a" "\0" |
320 |
|
"-o" "\0" |
321 |
|
"(" "\0" |
322 |
|
")" "\0" |
323 |
|
; |
324 |
|
|
325 |
|
|
326 |
#if ENABLE_FEATURE_TEST_64 |
#if ENABLE_FEATURE_TEST_64 |
427 |
*/ |
*/ |
428 |
|
|
429 |
|
|
430 |
static enum token check_operator(char *s) |
static enum token check_operator(const char *s) |
431 |
{ |
{ |
432 |
static const struct operator_t no_op = { |
static const struct operator_t no_op = { |
433 |
.op_num = -1, |
.op_num = -1, |
434 |
.op_type = -1 |
.op_type = -1 |
435 |
}; |
}; |
436 |
const struct operator_t *op; |
int n; |
437 |
|
|
438 |
last_operator = &no_op; |
last_operator = &no_op; |
439 |
if (s == NULL) { |
if (s == NULL) |
440 |
return EOI; |
return EOI; |
441 |
} |
n = index_in_strings(ops_texts, s); |
442 |
|
if (n < 0) |
443 |
op = ops; |
return OPERAND; |
444 |
do { |
last_operator = &ops_table[n]; |
445 |
if (strcmp(s, op->op_text) == 0) { |
return ops_table[n].op_num; |
|
last_operator = op; |
|
|
return op->op_num; |
|
|
} |
|
|
op++; |
|
|
} while (op < ops + ARRAY_SIZE(ops)); |
|
|
|
|
|
return OPERAND; |
|
446 |
} |
} |
447 |
|
|
448 |
|
|
458 |
|
|
459 |
opnd2 = *++args; |
opnd2 = *++args; |
460 |
if (opnd2 == NULL) |
if (opnd2 == NULL) |
461 |
syntax(op->op_text, "argument expected"); |
syntax(args[-1], "argument expected"); |
462 |
|
|
463 |
if (is_int_op(op->op_num)) { |
if (is_int_op(op->op_num)) { |
464 |
val1 = getn(opnd1); |
val1 = getn(opnd1); |
473 |
return val1 > val2; |
return val1 > val2; |
474 |
if (op->op_num == INTLE) |
if (op->op_num == INTLE) |
475 |
return val1 <= val2; |
return val1 <= val2; |
476 |
if (op->op_num == INTLT) |
/*if (op->op_num == INTLT)*/ |
477 |
return val1 < val2; |
return val1 < val2; |
478 |
} |
} |
479 |
if (is_str_op(op->op_num)) { |
if (is_str_op(op->op_num)) { |
480 |
val1 = strcmp(opnd1, opnd2); |
val1 = strcmp(opnd1, opnd2); |
484 |
return val1 != 0; |
return val1 != 0; |
485 |
if (op->op_num == STRLT) |
if (op->op_num == STRLT) |
486 |
return val1 < 0; |
return val1 < 0; |
487 |
if (op->op_num == STRGT) |
/*if (op->op_num == STRGT)*/ |
488 |
return val1 > 0; |
return val1 > 0; |
489 |
} |
} |
490 |
/* We are sure that these three are by now the only binops we didn't check |
/* We are sure that these three are by now the only binops we didn't check |
491 |
* yet, so we do not check if the class is correct: |
* yet, so we do not check if the class is correct: |
500 |
return b1.st_mtime > b2.st_mtime; |
return b1.st_mtime > b2.st_mtime; |
501 |
if (op->op_num == FILOT) |
if (op->op_num == FILOT) |
502 |
return b1.st_mtime < b2.st_mtime; |
return b1.st_mtime < b2.st_mtime; |
503 |
if (op->op_num == FILEQ) |
/*if (op->op_num == FILEQ)*/ |
504 |
return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; |
return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; |
505 |
} |
} |
506 |
return 1; /* NOTREACHED */ |
/*return 1; - NOTREACHED */ |
507 |
} |
} |
508 |
|
|
509 |
|
|
510 |
static void initialize_group_array(void) |
static void initialize_group_array(void) |
511 |
{ |
{ |
512 |
ngroups = getgroups(0, NULL); |
int n; |
513 |
if (ngroups > 0) { |
|
514 |
|
/* getgroups may be expensive, try to use it only once */ |
515 |
|
ngroups = 32; |
516 |
|
do { |
517 |
/* FIXME: ash tries so hard to not die on OOM, |
/* FIXME: ash tries so hard to not die on OOM, |
518 |
* and we spoil it with just one xrealloc here */ |
* and we spoil it with just one xrealloc here */ |
519 |
/* We realloc, because test_main can be entered repeatedly by shell. |
/* We realloc, because test_main can be entered repeatedly by shell. |
520 |
* Testcase (ash): 'while true; do test -x some_file; done' |
* Testcase (ash): 'while true; do test -x some_file; done' |
521 |
* and watch top. (some_file must have owner != you) */ |
* and watch top. (some_file must have owner != you) */ |
522 |
group_array = xrealloc(group_array, ngroups * sizeof(gid_t)); |
n = ngroups; |
523 |
getgroups(ngroups, group_array); |
group_array = xrealloc(group_array, n * sizeof(gid_t)); |
524 |
} |
ngroups = getgroups(n, group_array); |
525 |
|
} while (ngroups > n); |
526 |
} |
} |
527 |
|
|
528 |
|
|
663 |
|
|
664 |
nest_msg(">nexpr(%s)\n", TOKSTR[n]); |
nest_msg(">nexpr(%s)\n", TOKSTR[n]); |
665 |
if (n == UNOT) { |
if (n == UNOT) { |
666 |
res = !nexpr(check_operator(*++args)); |
n = check_operator(*++args); |
667 |
|
if (n == EOI) { |
668 |
|
/* special case: [ ! ], [ a -a ! ] are valid */ |
669 |
|
/* IOW, "! ARG" may miss ARG */ |
670 |
|
unnest_msg("<nexpr:1 (!EOI)\n"); |
671 |
|
return 1; |
672 |
|
} |
673 |
|
res = !nexpr(n); |
674 |
unnest_msg("<nexpr:%lld\n", res); |
unnest_msg("<nexpr:%lld\n", res); |
675 |
return res; |
return res; |
676 |
} |
} |
816 |
* isn't likely in the case of a shell. paranoia |
* isn't likely in the case of a shell. paranoia |
817 |
* prevails... |
* prevails... |
818 |
*/ |
*/ |
819 |
ngroups = 0; |
/*ngroups = 0; - done by INIT_S() */ |
820 |
|
|
821 |
//argc--; |
//argc--; |
822 |
argv++; |
argv++; |
841 |
check_operator(argv[1]); |
check_operator(argv[1]); |
842 |
if (last_operator->op_type == BINOP) { |
if (last_operator->op_type == BINOP) { |
843 |
/* "test [!] arg1 <binary_op> arg2" */ |
/* "test [!] arg1 <binary_op> arg2" */ |
844 |
args = &argv[0]; |
args = argv; |
845 |
res = (binop() == 0); |
res = (binop() == 0); |
846 |
goto ret; |
goto ret; |
847 |
} |
} |
854 |
argv--; |
argv--; |
855 |
} |
} |
856 |
#endif |
#endif |
857 |
args = &argv[0]; |
args = argv; |
858 |
res = !oexpr(check_operator(*args)); |
res = !oexpr(check_operator(*args)); |
859 |
|
|
860 |
if (*args != NULL && *++args != NULL) { |
if (*args != NULL && *++args != NULL) { |