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