loadfont,setfont: make them NOEXEC
[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 #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 static void initialize_group_array(void)
567 {
568         group_array = bb_getgroups(&ngroups, NULL);
569 }
570
571 /* Return non-zero if GID is one that we have in our groups list. */
572 //XXX: FIXME: duplicate of existing libbb function?
573 // see toplevel TODO file:
574 // possible code duplication ingroup() and is_a_group_member()
575 static int is_a_group_member(gid_t gid)
576 {
577         int i;
578
579         /* Short-circuit if possible, maybe saving a call to getgroups(). */
580         if (gid == getgid() || gid == getegid())
581                 return 1;
582
583         if (ngroups == 0)
584                 initialize_group_array();
585
586         /* Search through the list looking for GID. */
587         for (i = 0; i < ngroups; i++)
588                 if (gid == group_array[i])
589                         return 1;
590
591         return 0;
592 }
593
594
595 /* Do the same thing access(2) does, but use the effective uid and gid,
596    and don't make the mistake of telling root that any file is
597    executable. */
598 static int test_eaccess(struct stat *st, int mode)
599 {
600         unsigned int euid = geteuid();
601
602         if (euid == 0) {
603                 /* Root can read or write any file. */
604                 if (mode != X_OK)
605                         return 0;
606
607                 /* Root can execute any file that has any one of the execute
608                  * bits set. */
609                 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
610                         return 0;
611         }
612
613         if (st->st_uid == euid)  /* owner */
614                 mode <<= 6;
615         else if (is_a_group_member(st->st_gid))
616                 mode <<= 3;
617
618         if (st->st_mode & mode)
619                 return 0;
620
621         return -1;
622 }
623
624
625 static int filstat(char *nm, enum token mode)
626 {
627         struct stat s;
628         unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
629
630         if (mode == FILSYM) {
631 #ifdef S_IFLNK
632                 if (lstat(nm, &s) == 0) {
633                         i = S_IFLNK;
634                         goto filetype;
635                 }
636 #endif
637                 return 0;
638         }
639
640         if (stat(nm, &s) != 0)
641                 return 0;
642         if (mode == FILEXIST)
643                 return 1;
644         if (is_file_access(mode)) {
645                 if (mode == FILRD)
646                         i = R_OK;
647                 if (mode == FILWR)
648                         i = W_OK;
649                 if (mode == FILEX)
650                         i = X_OK;
651                 return test_eaccess(&s, i) == 0;
652         }
653         if (is_file_type(mode)) {
654                 if (mode == FILREG)
655                         i = S_IFREG;
656                 if (mode == FILDIR)
657                         i = S_IFDIR;
658                 if (mode == FILCDEV)
659                         i = S_IFCHR;
660                 if (mode == FILBDEV)
661                         i = S_IFBLK;
662                 if (mode == FILFIFO) {
663 #ifdef S_IFIFO
664                         i = S_IFIFO;
665 #else
666                         return 0;
667 #endif
668                 }
669                 if (mode == FILSOCK) {
670 #ifdef S_IFSOCK
671                         i = S_IFSOCK;
672 #else
673                         return 0;
674 #endif
675                 }
676  filetype:
677                 return ((s.st_mode & S_IFMT) == i);
678         }
679         if (is_file_bit(mode)) {
680                 if (mode == FILSUID)
681                         i = S_ISUID;
682                 if (mode == FILSGID)
683                         i = S_ISGID;
684                 if (mode == FILSTCK)
685                         i = S_ISVTX;
686                 return ((s.st_mode & i) != 0);
687         }
688         if (mode == FILGZ)
689                 return s.st_size > 0L;
690         if (mode == FILUID)
691                 return s.st_uid == geteuid();
692         if (mode == FILGID)
693                 return s.st_gid == getegid();
694         return 1; /* NOTREACHED */
695 }
696
697
698 static number_t nexpr(enum token n)
699 {
700         number_t res;
701
702         nest_msg(">nexpr(%s)\n", TOKSTR[n]);
703         if (n == UNOT) {
704                 n = check_operator(*++args);
705                 if (n == EOI) {
706                         /* special case: [ ! ], [ a -a ! ] are valid */
707                         /* IOW, "! ARG" may miss ARG */
708                         args--;
709                         unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
710                         return 1;
711                 }
712                 res = !nexpr(n);
713                 unnest_msg("<nexpr:%lld\n", res);
714                 return res;
715         }
716         res = primary(n);
717         unnest_msg("<nexpr:%lld\n", res);
718         return res;
719 }
720
721
722 static number_t aexpr(enum token n)
723 {
724         number_t res;
725
726         nest_msg(">aexpr(%s)\n", TOKSTR[n]);
727         res = nexpr(n);
728         dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
729         if (check_operator(*++args) == BAND) {
730                 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
731                 res = aexpr(check_operator(*++args)) && res;
732                 unnest_msg("<aexpr:%lld\n", res);
733                 return res;
734         }
735         args--;
736         unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
737         return res;
738 }
739
740
741 static number_t oexpr(enum token n)
742 {
743         number_t res;
744
745         nest_msg(">oexpr(%s)\n", TOKSTR[n]);
746         res = aexpr(n);
747         dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
748         if (check_operator(*++args) == BOR) {
749                 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
750                 res = oexpr(check_operator(*++args)) || res;
751                 unnest_msg("<oexpr:%lld\n", res);
752                 return res;
753         }
754         args--;
755         unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
756         return res;
757 }
758
759
760 static number_t primary(enum token n)
761 {
762 #if TEST_DEBUG
763         number_t res = res; /* for compiler */
764 #else
765         number_t res;
766 #endif
767         const struct operator_t *args0_op;
768
769         nest_msg(">primary(%s)\n", TOKSTR[n]);
770         if (n == EOI) {
771                 syntax(NULL, "argument expected");
772         }
773         if (n == LPAREN) {
774                 res = oexpr(check_operator(*++args));
775                 if (check_operator(*++args) != RPAREN)
776                         syntax(NULL, "closing paren expected");
777                 unnest_msg("<primary:%lld\n", res);
778                 return res;
779         }
780
781         /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
782          * do the same */
783         args0_op = last_operator;
784         /* last_operator = operator at args[1] */
785         if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
786                 if (args[2]) {
787                         // coreutils also does this:
788                         // if (args[3] && args[0]="-l" && args[2] is BINOP)
789                         //      return binop(1 /* prepended by -l */);
790                         if (last_operator->op_type == BINOP)
791                                 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
792                 }
793         }
794         /* check "is args[0] unop?" second */
795         if (args0_op->op_type == UNOP) {
796                 /* unary expression */
797                 if (args[1] == NULL)
798 //                      syntax(args0_op->op_text, "argument expected");
799                         goto check_emptiness;
800                 args++;
801                 if (n == STREZ)
802                         unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
803                 if (n == STRNZ)
804                         unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
805                 if (n == FILTT)
806                         unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
807                 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
808         }
809
810         /*check_operator(args[1]); - already done */
811         if (last_operator->op_type == BINOP) {
812                 /* args[2] is known to be NULL, isn't it bound to fail? */
813                 unnest_msg_and_return(binop(), "<primary:%lld\n");
814         }
815  check_emptiness:
816         unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
817 }
818
819
820 int test_main(int argc, char **argv)
821 {
822         int res;
823         const char *arg0;
824
825         arg0 = bb_basename(argv[0]);
826         if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
827          && (arg0[0] == '[')
828         ) {
829                 --argc;
830                 if (!arg0[1]) { /* "[" ? */
831                         if (NOT_LONE_CHAR(argv[argc], ']')) {
832                                 bb_error_msg("missing ]");
833                                 return 2;
834                         }
835                 } else { /* assuming "[[" */
836                         if (strcmp(argv[argc], "]]") != 0) {
837                                 bb_error_msg("missing ]]");
838                                 return 2;
839                         }
840                 }
841                 argv[argc] = NULL;
842         }
843         /* argc is unused after this point */
844
845         /* We must do DEINIT_S() prior to returning */
846         INIT_S();
847
848         res = setjmp(leaving);
849         if (res)
850                 goto ret;
851
852         /* resetting ngroups is probably unnecessary.  it will
853          * force a new call to getgroups(), which prevents using
854          * group data fetched during a previous call.  but the
855          * only way the group data could be stale is if there's
856          * been an intervening call to setgroups(), and this
857          * isn't likely in the case of a shell.  paranoia
858          * prevails...
859          */
860         /*ngroups = 0; - done by INIT_S() */
861
862         argv++;
863         args = argv;
864
865         /* Implement special cases from POSIX.2, section 4.62.4.
866          * Testcase: "test '(' = '('"
867          * The general parser would misinterpret '(' as group start.
868          */
869         if (1) {
870                 int negate = 0;
871  again:
872                 if (!argv[0]) {
873                         /* "test" */
874                         res = 1;
875                         goto ret_special;
876                 }
877                 if (!argv[1]) {
878                         /* "test [!] arg" */
879                         res = (argv[0][0] == '\0');
880                         goto ret_special;
881                 }
882                 if (argv[2]) {
883                         if (!argv[3]) {
884                                 /*
885                                  * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
886                                  * """ 3 arguments:
887                                  * If $2 is a binary primary, perform the binary test of $1 and $3.
888                                  * """
889                                  */
890                                 check_operator(argv[1]);
891                                 if (last_operator->op_type == BINOP) {
892                                         /* "test [!] arg1 <binary_op> arg2" */
893                                         args = argv;
894                                         res = (binop() == 0);
895  ret_special:
896                                         /* If there was leading "!" op... */
897                                         res ^= negate;
898                                         goto ret;
899                                 }
900                                 /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
901                                  * Looks like this works without additional coding.
902                                  */
903                                 goto check_negate;
904                         }
905                         /* argv[3] exists (at least 4 args), is it exactly 4 args? */
906                         if (!argv[4]) {
907                                 /*
908                                  * """ 4 arguments:
909                                  * If $1 is '!', negate the three-argument test of $2, $3, and $4.
910                                  * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
911                                  * """
912                                  * Example why code below is necessary: test '(' ! -e ')'
913                                  */
914                                 if (LONE_CHAR(argv[0], '(')
915                                  && LONE_CHAR(argv[3], ')')
916                                 ) {
917                                         /* "test [!] ( x y )" */
918                                         argv[3] = NULL;
919                                         argv++;
920                                 }
921                         }
922                 }
923  check_negate:
924                 if (LONE_CHAR(argv[0], '!')) {
925                         argv++;
926                         negate ^= 1;
927                         goto again;
928                 }
929         }
930
931         res = !oexpr(check_operator(*args));
932
933         if (*args != NULL && *++args != NULL) {
934                 /* Examples:
935                  * test 3 -lt 5 6
936                  * test -t 1 2
937                  */
938                 bb_error_msg("%s: unknown operand", *args);
939                 res = 2;
940         }
941  ret:
942         DEINIT_S();
943         return res;
944 }