hexdump: accept hex numbers in -n, closes 16195
[busybox-git.git] / coreutils / test.c
blobb63e33cc0ed3a22c5d62da33794e68eecdb8719e
1 /* vi: set sw=4 ts=4: */
2 /*
3 * test implementation for busybox
5 * Copyright (c) by a whole pile of folks:
7 * test(1); version 7-like -- author Erik Baalbergen
8 * modified by Eric Gisin to be used as built-in.
9 * modified by Arnold Robbins to add SVR3 compatibility
10 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
11 * modified by J.T. Conklin for NetBSD.
12 * modified by Herbert Xu to be used as built-in in ash.
13 * modified by Erik Andersen <andersen@codepoet.org> to be used
14 * in busybox.
15 * modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty).
17 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
19 * Original copyright notice states:
20 * "This program is in the Public Domain."
22 //config:config TEST
23 //config: bool "test (4.4 kb)"
24 //config: default y
25 //config: help
26 //config: test is used to check file types and compare values,
27 //config: returning an appropriate exit code. The bash shell
28 //config: has test built in, ash can build it in optionally.
29 //config:
30 //config:config TEST1
31 //config: bool "test as ["
32 //config: default y
33 //config: help
34 //config: Provide test command in the "[ EXPR ]" form
35 //config:
36 //config:config TEST2
37 //config: bool "test as [["
38 //config: default y
39 //config: help
40 //config: Provide test command in the "[[ EXPR ]]" form
41 //config:
42 //config:config FEATURE_TEST_64
43 //config: bool "Extend test to 64 bit"
44 //config: default y
45 //config: depends on TEST || TEST1 || TEST2 || ASH_TEST || HUSH_TEST
46 //config: help
47 //config: Enable 64-bit support in test.
49 //applet:IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
50 //applet:IF_TEST1(APPLET_NOFORK([, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
51 //applet:IF_TEST2(APPLET_NOFORK([[, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
53 //kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
54 //kbuild:lib-$(CONFIG_TEST1) += test.o test_ptr_hack.o
55 //kbuild:lib-$(CONFIG_TEST2) += test.o test_ptr_hack.o
57 //kbuild:lib-$(CONFIG_ASH_TEST) += test.o test_ptr_hack.o
58 //kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o
60 /* "test --help" is special-cased to ignore --help */
61 //usage:#define test_trivial_usage NOUSAGE_STR
62 //usage:#define test_full_usage ""
63 //usage:
64 //usage:#define test_example_usage
65 //usage: "$ test 1 -eq 2\n"
66 //usage: "$ echo $?\n"
67 //usage: "1\n"
68 //usage: "$ test 1 -eq 1\n"
69 //usage: "$ echo $?\n"
70 //usage: "0\n"
71 //usage: "$ [ -d /etc ]\n"
72 //usage: "$ echo $?\n"
73 //usage: "0\n"
74 //usage: "$ [ -d /junk ]\n"
75 //usage: "$ echo $?\n"
76 //usage: "1\n"
78 #include "libbb.h"
79 #include <regex.h>
80 #include <fnmatch.h>
82 /* This is a NOFORK applet. Be very careful! */
84 /* test_main() is called from shells, and we need to be extra careful here.
85 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
86 * state. */
88 /* test(1) accepts the following grammar:
89 oexpr ::= aexpr | aexpr "-o" oexpr ;
90 aexpr ::= nexpr | nexpr "-a" aexpr ;
91 nexpr ::= primary | "!" primary
92 primary ::= unary-operator operand
93 | operand binary-operator operand
94 | operand
95 | "(" oexpr ")"
97 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
98 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
100 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
101 "-nt"|"-ot"|"-ef";
102 operand ::= <any legal UNIX file name>
105 /* TODO: handle [[ expr ]] bashism bash-compatibly.
106 * [[ ]] is meant to be a "better [ ]", with less weird syntax
107 * and without the risk of variables and quoted strings misinterpreted
108 * as operators.
109 * This will require support from shells - we need to know quote status
110 * of each parameter (see below).
112 * Word splitting and pathname expansion should NOT be performed:
113 * # a="a b"; [[ $a = "a b" ]] && echo YES
114 * YES
115 * # [[ /bin/m* ]] && echo YES
116 * YES
118 * =~ should do regexp match
119 * = and == should do pattern match against right side:
120 * # [[ *a* == bab ]] && echo YES
121 * # [[ bab == *a* ]] && echo YES
122 * YES
123 * != does the negated == (i.e., also with pattern matching).
124 * Pattern matching is quotation-sensitive:
125 * # [[ bab == "b"a* ]] && echo YES
126 * YES
127 * # [[ bab == b"a*" ]] && echo YES
129 * Conditional operators such as -f must be unquoted literals to be recognized:
130 * # [[ -e /bin ]] && echo YES
131 * YES
132 * # [[ '-e' /bin ]] && echo YES
133 * bash: conditional binary operator expected...
134 * # A='-e'; [[ $A /bin ]] && echo YES
135 * bash: conditional binary operator expected...
137 * || and && should work as -o and -a work in [ ]
138 * -a and -o aren't recognized (&& and || are to be used instead)
139 * ( and ) do not need to be quoted unlike in [ ]:
140 * # [[ ( abc ) && '' ]] && echo YES
141 * # [[ ( abc ) || '' ]] && echo YES
142 * YES
143 * # [[ ( abc ) -o '' ]] && echo YES
144 * bash: syntax error in conditional expression...
146 * Apart from the above, [[ expr ]] should work as [ expr ]
149 #define TEST_DEBUG 0
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
159 enum token {
160 EOI,
162 FILRD, /* file access */
163 FILWR,
164 FILEX,
166 FILEXIST,
168 FILREG, /* file type */
169 FILDIR,
170 FILCDEV,
171 FILBDEV,
172 FILFIFO,
173 FILSOCK,
175 FILSYM,
176 FILGZ,
177 FILTT,
179 FILSUID, /* file bit */
180 FILSGID,
181 FILSTCK,
183 FILNT, /* file ops */
184 FILOT,
185 FILEQ,
187 FILUID,
188 FILGID,
190 STREZ, /* str ops */
191 STRNZ,
192 STREQ,
193 STRNE,
194 STRLT,
195 STRGT,
197 #if BASH_TEST2
198 REGEX,
199 #endif
201 INTEQ, /* int ops */
202 INTNE,
203 INTGE,
204 INTGT,
205 INTLE,
206 INTLT,
208 UNOT,
209 BAND,
210 BOR,
211 LPAREN,
212 RPAREN,
213 OPERAND
215 #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
216 #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
217 #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
218 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
219 #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
220 #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
222 #if TEST_DEBUG
223 int depth;
224 #define nest_msg(...) do { \
225 depth++; \
226 fprintf(stderr, "%*s", depth*2, ""); \
227 fprintf(stderr, __VA_ARGS__); \
228 } while (0)
229 #define unnest_msg(...) do { \
230 fprintf(stderr, "%*s", depth*2, ""); \
231 fprintf(stderr, __VA_ARGS__); \
232 depth--; \
233 } while (0)
234 #define dbg_msg(...) do { \
235 fprintf(stderr, "%*s", depth*2, ""); \
236 fprintf(stderr, __VA_ARGS__); \
237 } while (0)
238 #define unnest_msg_and_return(expr, ...) do { \
239 number_t __res = (expr); \
240 fprintf(stderr, "%*s", depth*2, ""); \
241 fprintf(stderr, __VA_ARGS__, res); \
242 depth--; \
243 return __res; \
244 } while (0)
245 static const char *const TOKSTR[] ALIGN_PTR = {
246 "EOI",
247 "FILRD",
248 "FILWR",
249 "FILEX",
250 "FILEXIST",
251 "FILREG",
252 "FILDIR",
253 "FILCDEV",
254 "FILBDEV",
255 "FILFIFO",
256 "FILSOCK",
257 "FILSYM",
258 "FILGZ",
259 "FILTT",
260 "FILSUID",
261 "FILSGID",
262 "FILSTCK",
263 "FILNT",
264 "FILOT",
265 "FILEQ",
266 "FILUID",
267 "FILGID",
268 "STREZ",
269 "STRNZ",
270 "STREQ",
271 "STRNE",
272 "STRLT",
273 "STRGT",
274 #if BASH_TEST2
275 "REGEX",
276 #endif
277 "INTEQ",
278 "INTNE",
279 "INTGE",
280 "INTGT",
281 "INTLE",
282 "INTLT",
283 "UNOT",
284 "BAND",
285 "BOR",
286 "LPAREN",
287 "RPAREN",
288 "OPERAND"
290 #else
291 #define nest_msg(...) ((void)0)
292 #define unnest_msg(...) ((void)0)
293 #define dbg_msg(...) ((void)0)
294 #define unnest_msg_and_return(expr, ...) return expr
295 #endif
297 enum {
298 UNOP,
299 BINOP,
300 BUNOP,
301 BBINOP,
302 PAREN
305 struct operator_t {
306 unsigned char op_num, op_type;
309 static const struct operator_t ops_table[] ALIGN2 = {
310 { /* "-r" */ FILRD , UNOP },
311 { /* "-w" */ FILWR , UNOP },
312 { /* "-x" */ FILEX , UNOP },
313 { /* "-e" */ FILEXIST, UNOP },
314 { /* "-f" */ FILREG , UNOP },
315 { /* "-d" */ FILDIR , UNOP },
316 { /* "-c" */ FILCDEV , UNOP },
317 { /* "-b" */ FILBDEV , UNOP },
318 { /* "-p" */ FILFIFO , UNOP },
319 { /* "-u" */ FILSUID , UNOP },
320 { /* "-g" */ FILSGID , UNOP },
321 { /* "-k" */ FILSTCK , UNOP },
322 { /* "-s" */ FILGZ , UNOP },
323 { /* "-t" */ FILTT , UNOP },
324 { /* "-z" */ STREZ , UNOP },
325 { /* "-n" */ STRNZ , UNOP },
326 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */
328 { /* "-O" */ FILUID , UNOP },
329 { /* "-G" */ FILGID , UNOP },
330 { /* "-L" */ FILSYM , UNOP },
331 { /* "-S" */ FILSOCK , UNOP },
332 { /* "=" */ STREQ , BINOP },
333 /* "==" is bashism, http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
334 * lists only "=" as comparison operator.
336 { /* "==" */ STREQ , BINOP },
337 { /* "!=" */ STRNE , BINOP },
338 { /* "<" */ STRLT , BINOP },
339 { /* ">" */ STRGT , BINOP },
340 #if BASH_TEST2
341 { /* "=~" */ REGEX , BINOP },
342 #endif
343 { /* "-eq"*/ INTEQ , BINOP },
344 { /* "-ne"*/ INTNE , BINOP },
345 { /* "-ge"*/ INTGE , BINOP },
346 { /* "-gt"*/ INTGT , BINOP },
347 { /* "-le"*/ INTLE , BINOP },
348 { /* "-lt"*/ INTLT , BINOP },
349 { /* "-nt"*/ FILNT , BINOP },
350 { /* "-ot"*/ FILOT , BINOP },
351 { /* "-ef"*/ FILEQ , BINOP },
352 { /* "!" */ UNOT , BUNOP },
353 { /* "-a" */ BAND , BBINOP },
354 { /* "-o" */ BOR , BBINOP },
355 #if BASH_TEST2
356 { /* "&&" */ BAND , BBINOP },
357 { /* "||" */ BOR , BBINOP },
358 #endif
359 { /* "(" */ LPAREN , PAREN },
360 { /* ")" */ RPAREN , PAREN },
362 /* Please keep these two tables in sync */
363 static const char ops_texts[] ALIGN1 =
364 "-r" "\0"
365 "-w" "\0"
366 "-x" "\0"
367 "-e" "\0"
368 "-f" "\0"
369 "-d" "\0"
370 "-c" "\0"
371 "-b" "\0"
372 "-p" "\0"
373 "-u" "\0"
374 "-g" "\0"
375 "-k" "\0"
376 "-s" "\0"
377 "-t" "\0"
378 "-z" "\0"
379 "-n" "\0"
380 "-h" "\0"
382 "-O" "\0"
383 "-G" "\0"
384 "-L" "\0"
385 "-S" "\0"
386 "=" "\0"
387 /* "==" is bashism */
388 "==" "\0"
389 "!=" "\0"
390 "<" "\0"
391 ">" "\0"
392 #if BASH_TEST2
393 "=~" "\0"
394 #endif
395 "-eq" "\0"
396 "-ne" "\0"
397 "-ge" "\0"
398 "-gt" "\0"
399 "-le" "\0"
400 "-lt" "\0"
401 "-nt" "\0"
402 "-ot" "\0"
403 "-ef" "\0"
404 "!" "\0"
405 "-a" "\0"
406 "-o" "\0"
407 #if BASH_TEST2
408 "&&" "\0"
409 "||" "\0"
410 #endif
411 "(" "\0"
412 ")" "\0"
416 #if ENABLE_FEATURE_TEST_64
417 typedef int64_t number_t;
418 #else
419 typedef int number_t;
420 #endif
423 /* We try to minimize both static and stack usage. */
424 struct test_statics {
425 char **args;
426 /* set only by check_operator(), either to bogus struct
427 * or points to matching operator_t struct. Never NULL. */
428 const struct operator_t *last_operator;
429 struct cached_groupinfo *groupinfo;
430 #if BASH_TEST2
431 bool bash_test2;
432 #endif
433 jmp_buf leaving;
436 /* See test_ptr_hack.c */
437 extern struct test_statics *BB_GLOBAL_CONST test_ptr_to_statics;
439 #define S (*test_ptr_to_statics)
440 #define args (S.args )
441 #define last_operator (S.last_operator)
442 #define groupinfo (S.groupinfo )
443 #define bash_test2 (S.bash_test2 )
444 #define leaving (S.leaving )
446 #define INIT_S() do { \
447 XZALLOC_CONST_PTR(&test_ptr_to_statics, sizeof(S)); \
448 } while (0)
449 #define DEINIT_S() do { \
450 free(test_ptr_to_statics); \
451 } while (0)
453 static number_t primary(enum token n);
455 static void syntax(const char *op, const char *msg) NORETURN;
456 static void syntax(const char *op, const char *msg)
458 if (op && *op) {
459 bb_error_msg("%s: %s", op, msg);
460 } else {
461 bb_error_msg("%s: %s"+4, msg);
463 longjmp(leaving, 2);
466 /* atoi with error detection */
467 //XXX: FIXME: duplicate of existing libbb function?
468 static number_t getn(const char *s)
470 char *p;
471 #if ENABLE_FEATURE_TEST_64
472 long long r;
473 #else
474 long r;
475 #endif
477 errno = 0;
478 #if ENABLE_FEATURE_TEST_64
479 r = strtoll(s, &p, 10);
480 #else
481 r = strtol(s, &p, 10);
482 #endif
484 if (errno != 0)
485 syntax(s, "out of range");
487 if (p == s || *(skip_whitespace(p)) != '\0')
488 syntax(s, "bad number");
490 return r;
493 /* UNUSED
494 static int newerf(const char *f1, const char *f2)
496 struct stat b1, b2;
498 return (stat(f1, &b1) == 0 &&
499 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
502 static int olderf(const char *f1, const char *f2)
504 struct stat b1, b2;
506 return (stat(f1, &b1) == 0 &&
507 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
510 static int equalf(const char *f1, const char *f2)
512 struct stat b1, b2;
514 return (stat(f1, &b1) == 0 &&
515 stat(f2, &b2) == 0 &&
516 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
521 static enum token check_operator(const char *s)
523 static const struct operator_t no_op = {
524 .op_num = -1,
525 .op_type = -1
527 int n;
529 last_operator = &no_op;
530 if (s == NULL)
531 return EOI;
532 n = index_in_strings(ops_texts, s);
533 if (n < 0)
534 return OPERAND;
536 #if BASH_TEST2
537 if (ops_table[n].op_num == REGEX && !bash_test2) {
538 /* =~ is only for [[ ]] */
539 return OPERAND;
541 if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
542 /* [ ] accepts -a and -o but not && and || */
543 /* [[ ]] accepts && and || but not -a and -o */
544 if (bash_test2 == (s[0] == '-'))
545 return OPERAND;
547 #endif
549 last_operator = &ops_table[n];
550 return ops_table[n].op_num;
554 static int binop(void)
556 const char *opnd1, *opnd2;
557 const struct operator_t *op;
558 number_t val1, val2;
560 opnd1 = *args;
561 check_operator(*++args);
562 op = last_operator;
564 opnd2 = *++args;
565 if (opnd2 == NULL)
566 syntax(args[-1], "argument expected");
568 if (is_int_op(op->op_num)) {
569 val1 = getn(opnd1);
570 val2 = getn(opnd2);
571 if (op->op_num == INTEQ)
572 return val1 == val2;
573 if (op->op_num == INTNE)
574 return val1 != val2;
575 if (op->op_num == INTGE)
576 return val1 >= val2;
577 if (op->op_num == INTGT)
578 return val1 > val2;
579 if (op->op_num == INTLE)
580 return val1 <= val2;
581 /*if (op->op_num == INTLT)*/
582 return val1 < val2;
584 #if BASH_TEST2
585 if (bash_test2) {
586 if (op->op_num == STREQ) {
587 val1 = fnmatch(opnd2, opnd1, 0);
588 return val1 == 0;
590 if (op->op_num == STRNE) {
591 val1 = fnmatch(opnd2, opnd1, 0);
592 return val1 != 0;
594 if (op->op_num == REGEX) {
595 regex_t re_buffer;
596 memset(&re_buffer, 0, sizeof(re_buffer));
597 if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE?
598 /* Bad regex */
599 longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
601 val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
602 regfree(&re_buffer);
603 return val1 == 0;
606 #endif
607 if (is_str_op(op->op_num)) {
608 val1 = strcmp(opnd1, opnd2);
609 if (op->op_num == STREQ)
610 return val1 == 0;
611 if (op->op_num == STRNE)
612 return val1 != 0;
613 if (op->op_num == STRLT)
614 return val1 < 0;
615 /*if (op->op_num == STRGT)*/
616 return val1 > 0;
618 /* We are sure that these three are by now the only binops we didn't check
619 * yet, so we do not check if the class is correct:
621 /* if (is_file_op(op->op_num)) */
623 struct stat b1, b2;
625 if (stat(opnd1, &b1) || stat(opnd2, &b2))
626 return 0; /* false, since at least one stat failed */
627 if (op->op_num == FILNT)
628 return b1.st_mtime > b2.st_mtime;
629 if (op->op_num == FILOT)
630 return b1.st_mtime < b2.st_mtime;
631 /*if (op->op_num == FILEQ)*/
632 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
634 /*return 1; - NOTREACHED */
637 /* Return non-zero if GID is one that we have in our groups list. */
638 static int is_a_group_member(gid_t gid)
640 /* Short-circuit if possible, maybe saving a call to getgroups(). */
641 if (gid == get_cached_egid(&groupinfo->egid))
642 return 1;
644 return is_in_supplementary_groups(groupinfo, gid);
648 * Similar to what access(2) does, but uses the effective uid and gid.
649 * Doesn't make the mistake of telling root that any file is executable.
650 * Returns non-zero if the file is accessible.
652 static int test_st_mode(struct stat *st, int mode)
654 enum { ANY_IX = S_IXUSR | S_IXGRP | S_IXOTH };
655 unsigned euid;
657 if (mode == X_OK) {
658 /* Do we already know with no extra syscalls? */
659 //if (!S_ISREG(st->st_mode))
660 // return 0; /* not a regular file */
661 // ^^^ bash 5.2.15 "test -x" does not check this!
662 if ((st->st_mode & ANY_IX) == 0)
663 return 0; /* no one can execute */
664 if ((st->st_mode & ANY_IX) == ANY_IX)
665 return 1; /* anyone can execute */
668 euid = get_cached_euid(&groupinfo->euid);
669 if (euid == 0) {
670 /* Root can read or write any file. */
671 if (mode != X_OK)
672 return 1;
674 /* Root can execute any file that has any one of the execute
675 * bits set. */
676 mode = S_IXUSR | S_IXGRP | S_IXOTH;
677 } else if (st->st_uid == euid) /* owner */
678 mode <<= 6;
679 else if (is_a_group_member(st->st_gid))
680 mode <<= 3;
682 return st->st_mode & mode;
686 static int filstat(char *nm, enum token mode)
688 struct stat s;
689 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
691 if (mode == FILSYM) {
692 #ifdef S_IFLNK
693 if (lstat(nm, &s) == 0) {
694 i = S_IFLNK;
695 goto filetype;
697 #endif
698 return 0;
701 if (stat(nm, &s) != 0)
702 return 0;
703 if (mode == FILEXIST)
704 return 1;
705 if (is_file_access(mode)) {
706 if (mode == FILRD)
707 i = R_OK;
708 if (mode == FILWR)
709 i = W_OK;
710 if (mode == FILEX)
711 i = X_OK;
712 return test_st_mode(&s, i);
714 if (is_file_type(mode)) {
715 if (mode == FILREG)
716 i = S_IFREG;
717 if (mode == FILDIR)
718 i = S_IFDIR;
719 if (mode == FILCDEV)
720 i = S_IFCHR;
721 if (mode == FILBDEV)
722 i = S_IFBLK;
723 if (mode == FILFIFO) {
724 #ifdef S_IFIFO
725 i = S_IFIFO;
726 #else
727 return 0;
728 #endif
730 if (mode == FILSOCK) {
731 #ifdef S_IFSOCK
732 i = S_IFSOCK;
733 #else
734 return 0;
735 #endif
737 filetype:
738 return ((s.st_mode & S_IFMT) == i);
740 if (is_file_bit(mode)) {
741 if (mode == FILSUID)
742 i = S_ISUID;
743 if (mode == FILSGID)
744 i = S_ISGID;
745 if (mode == FILSTCK)
746 i = S_ISVTX;
747 return ((s.st_mode & i) != 0);
749 if (mode == FILGZ)
750 return s.st_size != 0L; /* shorter than "> 0" test */
751 if (mode == FILUID)
752 return s.st_uid == geteuid();
753 if (mode == FILGID)
754 return s.st_gid == getegid();
755 return 1; /* NOTREACHED */
759 static number_t nexpr(enum token n)
761 number_t res;
763 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
764 if (n == UNOT) {
765 n = check_operator(*++args);
766 if (n == EOI) {
767 /* special case: [ ! ], [ a -a ! ] are valid */
768 /* IOW, "! ARG" may miss ARG */
769 args--;
770 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
771 return 1;
773 res = !nexpr(n);
774 unnest_msg("<nexpr:%lld\n", res);
775 return res;
777 res = primary(n);
778 unnest_msg("<nexpr:%lld\n", res);
779 return res;
783 static number_t aexpr(enum token n)
785 number_t res;
787 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
788 res = nexpr(n);
789 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
790 if (check_operator(*++args) == BAND) {
791 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
792 res = aexpr(check_operator(*++args)) && res;
793 unnest_msg("<aexpr:%lld\n", res);
794 return res;
796 args--;
797 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
798 return res;
802 static number_t oexpr(enum token n)
804 number_t res;
806 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
807 res = aexpr(n);
808 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
809 if (check_operator(*++args) == BOR) {
810 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
811 res = oexpr(check_operator(*++args)) || res;
812 unnest_msg("<oexpr:%lld\n", res);
813 return res;
815 args--;
816 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
817 return res;
821 static number_t primary(enum token n)
823 #if TEST_DEBUG
824 number_t res = res; /* for compiler */
825 #else
826 number_t res;
827 #endif
828 const struct operator_t *args0_op;
830 nest_msg(">primary(%s)\n", TOKSTR[n]);
831 if (n == EOI) {
832 syntax(NULL, "argument expected");
834 if (n == LPAREN) {
835 res = oexpr(check_operator(*++args));
836 if (check_operator(*++args) != RPAREN)
837 syntax(NULL, "closing paren expected");
838 unnest_msg("<primary:%lld\n", res);
839 return res;
842 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
843 * do the same */
844 args0_op = last_operator;
845 /* last_operator = operator at args[1] */
846 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
847 if (args[2]) {
848 // coreutils also does this:
849 // if (args[3] && args[0] = "-l" && args[2] is BINOP)
850 // return binop(1 /* prepended by -l */);
851 if (last_operator->op_type == BINOP)
852 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
855 /* check "is args[0] unop?" second */
856 if (args0_op->op_type == UNOP) {
857 /* unary expression */
858 if (args[1] == NULL)
859 // syntax(args0_op->op_text, "argument expected");
860 goto check_emptiness;
861 args++;
862 if (n == STREZ)
863 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
864 if (n == STRNZ)
865 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
866 if (n == FILTT)
867 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
868 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
871 /*check_operator(args[1]); - already done */
872 if (last_operator->op_type == BINOP) {
873 /* args[2] is known to be NULL, isn't it bound to fail? */
874 unnest_msg_and_return(binop(), "<primary:%lld\n");
876 check_emptiness:
877 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
881 int FAST_FUNC test_main2(struct cached_groupinfo *pgroupinfo, int argc, char **argv)
883 int res;
884 const char *arg0;
885 #if BASH_TEST2
886 bool bt2 = 0;
887 #endif
889 arg0 = bb_basename(argv[0]);
890 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
891 && (arg0[0] == '[')
893 --argc;
894 if (!arg0[1]) { /* "[" ? */
895 if (NOT_LONE_CHAR(argv[argc], ']')) {
896 bb_simple_error_msg("missing ]");
897 return 2;
899 } else { /* assuming "[[" */
900 if (strcmp(argv[argc], "]]") != 0) {
901 bb_simple_error_msg("missing ]]");
902 return 2;
904 #if BASH_TEST2
905 bt2 = 1;
906 #endif
908 argv[argc] = NULL;
910 /* argc is unused after this point */
912 /* We must do DEINIT_S() prior to returning */
913 INIT_S();
914 groupinfo = pgroupinfo;
916 #if BASH_TEST2
917 bash_test2 = bt2;
918 #endif
920 res = setjmp(leaving);
921 if (res)
922 goto ret;
924 /* resetting ngroups is probably unnecessary. it will
925 * force a new call to getgroups(), which prevents using
926 * group data fetched during a previous call. but the
927 * only way the group data could be stale is if there's
928 * been an intervening call to setgroups(), and this
929 * isn't likely in the case of a shell. paranoia
930 * prevails...
932 /*ngroups = 0; - done by INIT_S() */
934 argv++;
935 args = argv;
937 /* Implement special cases from POSIX.2, section 4.62.4.
938 * Testcase: "test '(' = '('"
939 * The general parser would misinterpret '(' as group start.
941 if (1) {
942 int negate = 0;
943 again:
944 if (!argv[0]) {
945 /* "test" */
946 res = 1;
947 goto ret_special;
949 if (!argv[1]) {
950 /* "test [!] arg" */
951 res = (argv[0][0] == '\0');
952 goto ret_special;
954 if (argv[2]) {
955 if (!argv[3]) {
957 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
958 * """ 3 arguments:
959 * If $2 is a binary primary, perform the binary test of $1 and $3.
960 * """
962 check_operator(argv[1]);
963 if (last_operator->op_type == BINOP) {
964 /* "test [!] arg1 <binary_op> arg2" */
965 args = argv;
966 res = (binop() == 0);
967 ret_special:
968 /* If there was leading "!" op... */
969 res ^= negate;
970 goto ret;
972 /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
973 * Looks like this works without additional coding.
975 goto check_negate;
977 /* argv[3] exists (at least 4 args), is it exactly 4 args? */
978 if (!argv[4]) {
980 * """ 4 arguments:
981 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
982 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
983 * """
984 * Example why code below is necessary: test '(' ! -e ')'
986 if (LONE_CHAR(argv[0], '(')
987 && LONE_CHAR(argv[3], ')')
989 /* "test [!] ( x y )" */
990 argv[3] = NULL;
991 argv++;
995 check_negate:
996 if (LONE_CHAR(argv[0], '!')) {
997 argv++;
998 negate ^= 1;
999 goto again;
1003 res = !oexpr(check_operator(*args));
1005 if (*args != NULL && *++args != NULL) {
1006 /* Examples:
1007 * test 3 -lt 5 6
1008 * test -t 1 2
1010 bb_error_msg("%s: unknown operand", *args);
1011 res = 2;
1013 ret:
1014 DEINIT_S();
1015 return res;
1018 int test_main(int argc, char **argv)
1020 struct cached_groupinfo info;
1021 int r;
1023 info.euid = -1;
1024 info.egid = -1;
1025 info.ngroups = 0;
1026 info.supplementary_array = NULL;
1027 r = test_main2(&info, argc, argv);
1028 free(info.supplementary_array);
1030 return r;