factor: factor2 variable is unused now, drop it
[oweals/busybox.git] / coreutils / test.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * test implementation for busybox
4  *
5  * Copyright (c) by a whole pile of folks:
6  *
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).
16  *
17  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
18  *
19  * Original copyright notice states:
20  *     "This program is in the Public Domain."
21  */
22 //config:config TEST
23 //config:       bool "test"
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.
48
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))
52
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
56
57 //kbuild:lib-$(CONFIG_ASH_TEST)  += test.o test_ptr_hack.o
58 //kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o
59
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"
77
78 #include "libbb.h"
79 #include <setjmp.h>
80
81 /* This is a NOFORK applet. Be very careful! */
82
83 /* test_main() is called from shells, and we need to be extra careful here.
84  * This is true regardless of PREFER_APPLETS and SH_STANDALONE
85  * state. */
86
87 /* test(1) accepts the following grammar:
88         oexpr   ::= aexpr | aexpr "-o" oexpr ;
89         aexpr   ::= nexpr | nexpr "-a" aexpr ;
90         nexpr   ::= primary | "!" primary
91         primary ::= unary-operator operand
92                 | operand binary-operator operand
93                 | operand
94                 | "(" oexpr ")"
95                 ;
96         unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
97                 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
98
99         binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
100                         "-nt"|"-ot"|"-ef";
101         operand ::= <any legal UNIX file name>
102 */
103
104 /* TODO: handle [[ expr ]] bashism bash-compatibly.
105  * [[ ]] is meant to be a "better [ ]", with less weird syntax
106  * and without the risk of variables and quoted strings misinterpreted
107  * as operators.
108  * This will require support from shells - we need to know quote status
109  * of each parameter (see below).
110  *
111  * Word splitting and pathname expansion should NOT be performed:
112  *      # a="a b"; [[ $a = "a b" ]] && echo YES
113  *      YES
114  *      # [[ /bin/m* ]] && echo YES
115  *      YES
116  *
117  * =~ should do regexp match
118  * = and == should do pattern match against right side:
119  *      # [[ *a* == bab ]] && echo YES
120  *      # [[ bab == *a* ]] && echo YES
121  *      YES
122  * != does the negated == (i.e., also with pattern matching).
123  * Pattern matching is quotation-sensitive:
124  *      # [[ bab == "b"a* ]] && echo YES
125  *      YES
126  *      # [[ bab == b"a*" ]] && echo YES
127  *
128  * Conditional operators such as -f must be unquoted literals to be recognized:
129  *      # [[ -e /bin ]] && echo YES
130  *      YES
131  *      # [[ '-e' /bin ]] && echo YES
132  *      bash: conditional binary operator expected...
133  *      # A='-e'; [[ $A /bin ]] && echo YES
134  *      bash: conditional binary operator expected...
135  *
136  * || and && should work as -o and -a work in [ ]
137  * -a and -o aren't recognized (&& and || are to be used instead)
138  * ( and ) do not need to be quoted unlike in [ ]:
139  *      # [[ ( abc ) && '' ]] && echo YES
140  *      # [[ ( abc ) || '' ]] && echo YES
141  *      YES
142  *      # [[ ( abc ) -o '' ]] && echo YES
143  *      bash: syntax error in conditional expression...
144  *
145  * Apart from the above, [[ expr ]] should work as [ expr ]
146  */
147
148 #define TEST_DEBUG 0
149
150 enum token {
151         EOI,
152
153         FILRD, /* file access */
154         FILWR,
155         FILEX,
156
157         FILEXIST,
158
159         FILREG, /* file type */
160         FILDIR,
161         FILCDEV,
162         FILBDEV,
163         FILFIFO,
164         FILSOCK,
165
166         FILSYM,
167         FILGZ,
168         FILTT,
169
170         FILSUID, /* file bit */
171         FILSGID,
172         FILSTCK,
173
174         FILNT, /* file ops */
175         FILOT,
176         FILEQ,
177
178         FILUID,
179         FILGID,
180
181         STREZ, /* str ops */
182         STRNZ,
183         STREQ,
184         STRNE,
185         STRLT,
186         STRGT,
187
188         INTEQ, /* int ops */
189         INTNE,
190         INTGE,
191         INTGT,
192         INTLE,
193         INTLT,
194
195         UNOT,
196         BAND,
197         BOR,
198         LPAREN,
199         RPAREN,
200         OPERAND
201 };
202 #define is_int_op(a)      (((unsigned char)((a) - INTEQ)) <= 5)
203 #define is_str_op(a)      (((unsigned char)((a) - STREZ)) <= 5)
204 #define is_file_op(a)     (((unsigned char)((a) - FILNT)) <= 2)
205 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
206 #define is_file_type(a)   (((unsigned char)((a) - FILREG)) <= 5)
207 #define is_file_bit(a)    (((unsigned char)((a) - FILSUID)) <= 2)
208
209 #if TEST_DEBUG
210 int depth;
211 #define nest_msg(...) do { \
212         depth++; \
213         fprintf(stderr, "%*s", depth*2, ""); \
214         fprintf(stderr, __VA_ARGS__); \
215 } while (0)
216 #define unnest_msg(...) do { \
217         fprintf(stderr, "%*s", depth*2, ""); \
218         fprintf(stderr, __VA_ARGS__); \
219         depth--; \
220 } while (0)
221 #define dbg_msg(...) do { \
222         fprintf(stderr, "%*s", depth*2, ""); \
223         fprintf(stderr, __VA_ARGS__); \
224 } while (0)
225 #define unnest_msg_and_return(expr, ...) do { \
226         number_t __res = (expr); \
227         fprintf(stderr, "%*s", depth*2, ""); \
228         fprintf(stderr, __VA_ARGS__, res); \
229         depth--; \
230         return __res; \
231 } while (0)
232 static const char *const TOKSTR[] = {
233         "EOI",
234         "FILRD",
235         "FILWR",
236         "FILEX",
237         "FILEXIST",
238         "FILREG",
239         "FILDIR",
240         "FILCDEV",
241         "FILBDEV",
242         "FILFIFO",
243         "FILSOCK",
244         "FILSYM",
245         "FILGZ",
246         "FILTT",
247         "FILSUID",
248         "FILSGID",
249         "FILSTCK",
250         "FILNT",
251         "FILOT",
252         "FILEQ",
253         "FILUID",
254         "FILGID",
255         "STREZ",
256         "STRNZ",
257         "STREQ",
258         "STRNE",
259         "STRLT",
260         "STRGT",
261         "INTEQ",
262         "INTNE",
263         "INTGE",
264         "INTGT",
265         "INTLE",
266         "INTLT",
267         "UNOT",
268         "BAND",
269         "BOR",
270         "LPAREN",
271         "RPAREN",
272         "OPERAND"
273 };
274 #else
275 #define nest_msg(...)   ((void)0)
276 #define unnest_msg(...) ((void)0)
277 #define dbg_msg(...)    ((void)0)
278 #define unnest_msg_and_return(expr, ...) return expr
279 #endif
280
281 enum {
282         UNOP,
283         BINOP,
284         BUNOP,
285         BBINOP,
286         PAREN
287 };
288
289 struct operator_t {
290         unsigned char op_num, op_type;
291 };
292
293 static const struct operator_t ops_table[] = {
294         { /* "-r" */ FILRD   , UNOP   },
295         { /* "-w" */ FILWR   , UNOP   },
296         { /* "-x" */ FILEX   , UNOP   },
297         { /* "-e" */ FILEXIST, UNOP   },
298         { /* "-f" */ FILREG  , UNOP   },
299         { /* "-d" */ FILDIR  , UNOP   },
300         { /* "-c" */ FILCDEV , UNOP   },
301         { /* "-b" */ FILBDEV , UNOP   },
302         { /* "-p" */ FILFIFO , UNOP   },
303         { /* "-u" */ FILSUID , UNOP   },
304         { /* "-g" */ FILSGID , UNOP   },
305         { /* "-k" */ FILSTCK , UNOP   },
306         { /* "-s" */ FILGZ   , UNOP   },
307         { /* "-t" */ FILTT   , UNOP   },
308         { /* "-z" */ STREZ   , UNOP   },
309         { /* "-n" */ STRNZ   , UNOP   },
310         { /* "-h" */ FILSYM  , UNOP   },    /* for backwards compat */
311
312         { /* "-O" */ FILUID  , UNOP   },
313         { /* "-G" */ FILGID  , UNOP   },
314         { /* "-L" */ FILSYM  , UNOP   },
315         { /* "-S" */ FILSOCK , UNOP   },
316         { /* "="  */ STREQ   , BINOP  },
317         { /* "==" */ STREQ   , BINOP  },
318         { /* "!=" */ STRNE   , BINOP  },
319         { /* "<"  */ STRLT   , BINOP  },
320         { /* ">"  */ STRGT   , BINOP  },
321         { /* "-eq"*/ INTEQ   , BINOP  },
322         { /* "-ne"*/ INTNE   , BINOP  },
323         { /* "-ge"*/ INTGE   , BINOP  },
324         { /* "-gt"*/ INTGT   , BINOP  },
325         { /* "-le"*/ INTLE   , BINOP  },
326         { /* "-lt"*/ INTLT   , BINOP  },
327         { /* "-nt"*/ FILNT   , BINOP  },
328         { /* "-ot"*/ FILOT   , BINOP  },
329         { /* "-ef"*/ FILEQ   , BINOP  },
330         { /* "!"  */ UNOT    , BUNOP  },
331         { /* "-a" */ BAND    , BBINOP },
332         { /* "-o" */ BOR     , BBINOP },
333         { /* "("  */ LPAREN  , PAREN  },
334         { /* ")"  */ RPAREN  , PAREN  },
335 };
336 /* Please keep these two tables in sync */
337 static const char ops_texts[] ALIGN1 =
338         "-r"  "\0"
339         "-w"  "\0"
340         "-x"  "\0"
341         "-e"  "\0"
342         "-f"  "\0"
343         "-d"  "\0"
344         "-c"  "\0"
345         "-b"  "\0"
346         "-p"  "\0"
347         "-u"  "\0"
348         "-g"  "\0"
349         "-k"  "\0"
350         "-s"  "\0"
351         "-t"  "\0"
352         "-z"  "\0"
353         "-n"  "\0"
354         "-h"  "\0"
355
356         "-O"  "\0"
357         "-G"  "\0"
358         "-L"  "\0"
359         "-S"  "\0"
360         "="   "\0"
361         "=="  "\0"
362         "!="  "\0"
363         "<"   "\0"
364         ">"   "\0"
365         "-eq" "\0"
366         "-ne" "\0"
367         "-ge" "\0"
368         "-gt" "\0"
369         "-le" "\0"
370         "-lt" "\0"
371         "-nt" "\0"
372         "-ot" "\0"
373         "-ef" "\0"
374         "!"   "\0"
375         "-a"  "\0"
376         "-o"  "\0"
377         "("   "\0"
378         ")"   "\0"
379 ;
380
381
382 #if ENABLE_FEATURE_TEST_64
383 typedef int64_t number_t;
384 #else
385 typedef int number_t;
386 #endif
387
388
389 /* We try to minimize both static and stack usage. */
390 struct test_statics {
391         char **args;
392         /* set only by check_operator(), either to bogus struct
393          * or points to matching operator_t struct. Never NULL. */
394         const struct operator_t *last_operator;
395         gid_t *group_array;
396         int ngroups;
397         jmp_buf leaving;
398 };
399
400 /* See test_ptr_hack.c */
401 extern struct test_statics *const test_ptr_to_statics;
402
403 #define S (*test_ptr_to_statics)
404 #define args            (S.args         )
405 #define last_operator   (S.last_operator)
406 #define group_array     (S.group_array  )
407 #define ngroups         (S.ngroups      )
408 #define leaving         (S.leaving      )
409
410 #define INIT_S() do { \
411         (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
412         barrier(); \
413 } while (0)
414 #define DEINIT_S() do { \
415         free(group_array); \
416         free(test_ptr_to_statics); \
417 } while (0)
418
419 static number_t primary(enum token n);
420
421 static void syntax(const char *op, const char *msg) NORETURN;
422 static void syntax(const char *op, const char *msg)
423 {
424         if (op && *op) {
425                 bb_error_msg("%s: %s", op, msg);
426         } else {
427                 bb_error_msg("%s: %s"+4, msg);
428         }
429         longjmp(leaving, 2);
430 }
431
432 /* atoi with error detection */
433 //XXX: FIXME: duplicate of existing libbb function?
434 static number_t getn(const char *s)
435 {
436         char *p;
437 #if ENABLE_FEATURE_TEST_64
438         long long r;
439 #else
440         long r;
441 #endif
442
443         errno = 0;
444 #if ENABLE_FEATURE_TEST_64
445         r = strtoll(s, &p, 10);
446 #else
447         r = strtol(s, &p, 10);
448 #endif
449
450         if (errno != 0)
451                 syntax(s, "out of range");
452
453         if (p == s || *(skip_whitespace(p)) != '\0')
454                 syntax(s, "bad number");
455
456         return r;
457 }
458
459 /* UNUSED
460 static int newerf(const char *f1, const char *f2)
461 {
462         struct stat b1, b2;
463
464         return (stat(f1, &b1) == 0 &&
465                         stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
466 }
467
468 static int olderf(const char *f1, const char *f2)
469 {
470         struct stat b1, b2;
471
472         return (stat(f1, &b1) == 0 &&
473                         stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
474 }
475
476 static int equalf(const char *f1, const char *f2)
477 {
478         struct stat b1, b2;
479
480         return (stat(f1, &b1) == 0 &&
481                         stat(f2, &b2) == 0 &&
482                         b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
483 }
484 */
485
486
487 static enum token check_operator(const char *s)
488 {
489         static const struct operator_t no_op = {
490                 .op_num = -1,
491                 .op_type = -1
492         };
493         int n;
494
495         last_operator = &no_op;
496         if (s == NULL)
497                 return EOI;
498         n = index_in_strings(ops_texts, s);
499         if (n < 0)
500                 return OPERAND;
501         last_operator = &ops_table[n];
502         return ops_table[n].op_num;
503 }
504
505
506 static int binop(void)
507 {
508         const char *opnd1, *opnd2;
509         const struct operator_t *op;
510         number_t val1, val2;
511
512         opnd1 = *args;
513         check_operator(*++args);
514         op = last_operator;
515
516         opnd2 = *++args;
517         if (opnd2 == NULL)
518                 syntax(args[-1], "argument expected");
519
520         if (is_int_op(op->op_num)) {
521                 val1 = getn(opnd1);
522                 val2 = getn(opnd2);
523                 if (op->op_num == INTEQ)
524                         return val1 == val2;
525                 if (op->op_num == INTNE)
526                         return val1 != val2;
527                 if (op->op_num == INTGE)
528                         return val1 >= val2;
529                 if (op->op_num == INTGT)
530                         return val1 >  val2;
531                 if (op->op_num == INTLE)
532                         return val1 <= val2;
533                 /*if (op->op_num == INTLT)*/
534                 return val1 <  val2;
535         }
536         if (is_str_op(op->op_num)) {
537                 val1 = strcmp(opnd1, opnd2);
538                 if (op->op_num == STREQ)
539                         return val1 == 0;
540                 if (op->op_num == STRNE)
541                         return val1 != 0;
542                 if (op->op_num == STRLT)
543                         return val1 < 0;
544                 /*if (op->op_num == STRGT)*/
545                 return val1 > 0;
546         }
547         /* We are sure that these three are by now the only binops we didn't check
548          * yet, so we do not check if the class is correct:
549          */
550 /*      if (is_file_op(op->op_num)) */
551         {
552                 struct stat b1, b2;
553
554                 if (stat(opnd1, &b1) || stat(opnd2, &b2))
555                         return 0; /* false, since at least one stat failed */
556                 if (op->op_num == FILNT)
557                         return b1.st_mtime > b2.st_mtime;
558                 if (op->op_num == FILOT)
559                         return b1.st_mtime < b2.st_mtime;
560                 /*if (op->op_num == FILEQ)*/
561                 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
562         }
563         /*return 1; - NOTREACHED */
564 }
565
566
567 static void initialize_group_array(void)
568 {
569         int n;
570
571         /* getgroups may be expensive, try to use it only once */
572         ngroups = 32;
573         do {
574                 /* FIXME: ash tries so hard to not die on OOM,
575                  * and we spoil it with just one xrealloc here */
576                 /* We realloc, because test_main can be entered repeatedly by shell.
577                  * Testcase (ash): 'while true; do test -x some_file; done'
578                  * and watch top. (some_file must have owner != you) */
579                 n = ngroups;
580                 group_array = xrealloc(group_array, n * sizeof(gid_t));
581                 ngroups = getgroups(n, group_array);
582         } while (ngroups > n);
583 }
584
585
586 /* Return non-zero if GID is one that we have in our groups list. */
587 //XXX: FIXME: duplicate of existing libbb function?
588 // see toplevel TODO file:
589 // possible code duplication ingroup() and is_a_group_member()
590 static int is_a_group_member(gid_t gid)
591 {
592         int i;
593
594         /* Short-circuit if possible, maybe saving a call to getgroups(). */
595         if (gid == getgid() || gid == getegid())
596                 return 1;
597
598         if (ngroups == 0)
599                 initialize_group_array();
600
601         /* Search through the list looking for GID. */
602         for (i = 0; i < ngroups; i++)
603                 if (gid == group_array[i])
604                         return 1;
605
606         return 0;
607 }
608
609
610 /* Do the same thing access(2) does, but use the effective uid and gid,
611    and don't make the mistake of telling root that any file is
612    executable. */
613 static int test_eaccess(char *path, int mode)
614 {
615         struct stat st;
616         unsigned int euid = geteuid();
617
618         if (stat(path, &st) < 0)
619                 return -1;
620
621         if (euid == 0) {
622                 /* Root can read or write any file. */
623                 if (mode != X_OK)
624                         return 0;
625
626                 /* Root can execute any file that has any one of the execute
627                  * bits set. */
628                 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
629                         return 0;
630         }
631
632         if (st.st_uid == euid)  /* owner */
633                 mode <<= 6;
634         else if (is_a_group_member(st.st_gid))
635                 mode <<= 3;
636
637         if (st.st_mode & mode)
638                 return 0;
639
640         return -1;
641 }
642
643
644 static int filstat(char *nm, enum token mode)
645 {
646         struct stat s;
647         unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
648
649         if (mode == FILSYM) {
650 #ifdef S_IFLNK
651                 if (lstat(nm, &s) == 0) {
652                         i = S_IFLNK;
653                         goto filetype;
654                 }
655 #endif
656                 return 0;
657         }
658
659         if (stat(nm, &s) != 0)
660                 return 0;
661         if (mode == FILEXIST)
662                 return 1;
663         if (is_file_access(mode)) {
664                 if (mode == FILRD)
665                         i = R_OK;
666                 if (mode == FILWR)
667                         i = W_OK;
668                 if (mode == FILEX)
669                         i = X_OK;
670                 return test_eaccess(nm, i) == 0;
671         }
672         if (is_file_type(mode)) {
673                 if (mode == FILREG)
674                         i = S_IFREG;
675                 if (mode == FILDIR)
676                         i = S_IFDIR;
677                 if (mode == FILCDEV)
678                         i = S_IFCHR;
679                 if (mode == FILBDEV)
680                         i = S_IFBLK;
681                 if (mode == FILFIFO) {
682 #ifdef S_IFIFO
683                         i = S_IFIFO;
684 #else
685                         return 0;
686 #endif
687                 }
688                 if (mode == FILSOCK) {
689 #ifdef S_IFSOCK
690                         i = S_IFSOCK;
691 #else
692                         return 0;
693 #endif
694                 }
695  filetype:
696                 return ((s.st_mode & S_IFMT) == i);
697         }
698         if (is_file_bit(mode)) {
699                 if (mode == FILSUID)
700                         i = S_ISUID;
701                 if (mode == FILSGID)
702                         i = S_ISGID;
703                 if (mode == FILSTCK)
704                         i = S_ISVTX;
705                 return ((s.st_mode & i) != 0);
706         }
707         if (mode == FILGZ)
708                 return s.st_size > 0L;
709         if (mode == FILUID)
710                 return s.st_uid == geteuid();
711         if (mode == FILGID)
712                 return s.st_gid == getegid();
713         return 1; /* NOTREACHED */
714 }
715
716
717 static number_t nexpr(enum token n)
718 {
719         number_t res;
720
721         nest_msg(">nexpr(%s)\n", TOKSTR[n]);
722         if (n == UNOT) {
723                 n = check_operator(*++args);
724                 if (n == EOI) {
725                         /* special case: [ ! ], [ a -a ! ] are valid */
726                         /* IOW, "! ARG" may miss ARG */
727                         args--;
728                         unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
729                         return 1;
730                 }
731                 res = !nexpr(n);
732                 unnest_msg("<nexpr:%lld\n", res);
733                 return res;
734         }
735         res = primary(n);
736         unnest_msg("<nexpr:%lld\n", res);
737         return res;
738 }
739
740
741 static number_t aexpr(enum token n)
742 {
743         number_t res;
744
745         nest_msg(">aexpr(%s)\n", TOKSTR[n]);
746         res = nexpr(n);
747         dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
748         if (check_operator(*++args) == BAND) {
749                 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
750                 res = aexpr(check_operator(*++args)) && res;
751                 unnest_msg("<aexpr:%lld\n", res);
752                 return res;
753         }
754         args--;
755         unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
756         return res;
757 }
758
759
760 static number_t oexpr(enum token n)
761 {
762         number_t res;
763
764         nest_msg(">oexpr(%s)\n", TOKSTR[n]);
765         res = aexpr(n);
766         dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
767         if (check_operator(*++args) == BOR) {
768                 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
769                 res = oexpr(check_operator(*++args)) || res;
770                 unnest_msg("<oexpr:%lld\n", res);
771                 return res;
772         }
773         args--;
774         unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
775         return res;
776 }
777
778
779 static number_t primary(enum token n)
780 {
781 #if TEST_DEBUG
782         number_t res = res; /* for compiler */
783 #else
784         number_t res;
785 #endif
786         const struct operator_t *args0_op;
787
788         nest_msg(">primary(%s)\n", TOKSTR[n]);
789         if (n == EOI) {
790                 syntax(NULL, "argument expected");
791         }
792         if (n == LPAREN) {
793                 res = oexpr(check_operator(*++args));
794                 if (check_operator(*++args) != RPAREN)
795                         syntax(NULL, "closing paren expected");
796                 unnest_msg("<primary:%lld\n", res);
797                 return res;
798         }
799
800         /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
801          * do the same */
802         args0_op = last_operator;
803         /* last_operator = operator at args[1] */
804         if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
805                 if (args[2]) {
806                         // coreutils also does this:
807                         // if (args[3] && args[0]="-l" && args[2] is BINOP)
808                         //      return binop(1 /* prepended by -l */);
809                         if (last_operator->op_type == BINOP)
810                                 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
811                 }
812         }
813         /* check "is args[0] unop?" second */
814         if (args0_op->op_type == UNOP) {
815                 /* unary expression */
816                 if (args[1] == NULL)
817 //                      syntax(args0_op->op_text, "argument expected");
818                         goto check_emptiness;
819                 args++;
820                 if (n == STREZ)
821                         unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
822                 if (n == STRNZ)
823                         unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
824                 if (n == FILTT)
825                         unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
826                 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
827         }
828
829         /*check_operator(args[1]); - already done */
830         if (last_operator->op_type == BINOP) {
831                 /* args[2] is known to be NULL, isn't it bound to fail? */
832                 unnest_msg_and_return(binop(), "<primary:%lld\n");
833         }
834  check_emptiness:
835         unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
836 }
837
838
839 int test_main(int argc, char **argv)
840 {
841         int res;
842         const char *arg0;
843
844         arg0 = bb_basename(argv[0]);
845         if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
846          && (arg0[0] == '[')
847         ) {
848                 --argc;
849                 if (!arg0[1]) { /* "[" ? */
850                         if (NOT_LONE_CHAR(argv[argc], ']')) {
851                                 bb_error_msg("missing ]");
852                                 return 2;
853                         }
854                 } else { /* assuming "[[" */
855                         if (strcmp(argv[argc], "]]") != 0) {
856                                 bb_error_msg("missing ]]");
857                                 return 2;
858                         }
859                 }
860                 argv[argc] = NULL;
861         }
862         /* argc is unused after this point */
863
864         /* We must do DEINIT_S() prior to returning */
865         INIT_S();
866
867         res = setjmp(leaving);
868         if (res)
869                 goto ret;
870
871         /* resetting ngroups is probably unnecessary.  it will
872          * force a new call to getgroups(), which prevents using
873          * group data fetched during a previous call.  but the
874          * only way the group data could be stale is if there's
875          * been an intervening call to setgroups(), and this
876          * isn't likely in the case of a shell.  paranoia
877          * prevails...
878          */
879         /*ngroups = 0; - done by INIT_S() */
880
881         argv++;
882         args = argv;
883
884         /* Implement special cases from POSIX.2, section 4.62.4.
885          * Testcase: "test '(' = '('"
886          * The general parser would misinterpret '(' as group start.
887          */
888         if (1) {
889                 int negate = 0;
890  again:
891                 if (!argv[0]) {
892                         /* "test" */
893                         res = 1;
894                         goto ret_special;
895                 }
896                 if (!argv[1]) {
897                         /* "test [!] arg" */
898                         res = (argv[0][0] == '\0');
899                         goto ret_special;
900                 }
901                 if (argv[2] && !argv[3]) {
902                         check_operator(argv[1]);
903                         if (last_operator->op_type == BINOP) {
904                                 /* "test [!] arg1 <binary_op> arg2" */
905                                 args = argv;
906                                 res = (binop() == 0);
907  ret_special:
908                                 /* If there was leading "!" op... */
909                                 res ^= negate;
910                                 goto ret;
911                         }
912                 }
913                 if (LONE_CHAR(argv[0], '!')) {
914                         argv++;
915                         negate ^= 1;
916                         goto again;
917                 }
918         }
919
920         res = !oexpr(check_operator(*args));
921
922         if (*args != NULL && *++args != NULL) {
923                 /* Examples:
924                  * test 3 -lt 5 6
925                  * test -t 1 2
926                  */
927                 bb_error_msg("%s: unknown operand", *args);
928                 res = 2;
929         }
930  ret:
931         DEINIT_S();
932         return res;
933 }