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