typo fix in comment
[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
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" is special-cased to ignore --help */
43 //usage:#define test_trivial_usage NOUSAGE_STR
44 //usage:#define test_full_usage ""
45 //usage:
46 //usage:#define test_example_usage
47 //usage:       "$ test 1 -eq 2\n"
48 //usage:       "$ echo $?\n"
49 //usage:       "1\n"
50 //usage:       "$ test 1 -eq 1\n"
51 //usage:       "$ echo $?\n"
52 //usage:       "0\n"
53 //usage:       "$ [ -d /etc ]\n"
54 //usage:       "$ echo $?\n"
55 //usage:       "0\n"
56 //usage:       "$ [ -d /junk ]\n"
57 //usage:       "$ echo $?\n"
58 //usage:       "1\n"
59
60 #include "libbb.h"
61 #include <setjmp.h>
62
63 /* This is a NOFORK applet. Be very careful! */
64
65 /* test_main() is called from shells, and we need to be extra careful here.
66  * This is true regardless of PREFER_APPLETS and SH_STANDALONE
67  * state. */
68
69 /* test(1) accepts the following grammar:
70         oexpr   ::= aexpr | aexpr "-o" oexpr ;
71         aexpr   ::= nexpr | nexpr "-a" aexpr ;
72         nexpr   ::= primary | "!" primary
73         primary ::= unary-operator operand
74                 | operand binary-operator operand
75                 | operand
76                 | "(" oexpr ")"
77                 ;
78         unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
79                 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
80
81         binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
82                         "-nt"|"-ot"|"-ef";
83         operand ::= <any legal UNIX file name>
84 */
85
86 /* TODO: handle [[ expr ]] bashism bash-compatibly.
87  * [[ ]] is meant to be a "better [ ]", with less weird syntax
88  * and without the risk of variables and quoted strings misinterpreted
89  * as operators.
90  * This will require support from shells - we need to know quote status
91  * of each parameter (see below).
92  *
93  * Word splitting and pathname expansion should NOT be performed:
94  *      # a="a b"; [[ $a = "a b" ]] && echo YES
95  *      YES
96  *      # [[ /bin/m* ]] && echo YES
97  *      YES
98  *
99  * =~ should do regexp match
100  * = and == should do pattern match against right side:
101  *      # [[ *a* == bab ]] && echo YES
102  *      # [[ bab == *a* ]] && echo YES
103  *      YES
104  * != does the negated == (i.e., also with pattern matching).
105  * Pattern matching is quotation-sensitive:
106  *      # [[ bab == "b"a* ]] && echo YES
107  *      YES
108  *      # [[ bab == b"a*" ]] && echo YES
109  *
110  * Conditional operators such as -f must be unquoted literals to be recognized:
111  *      # [[ -e /bin ]] && echo YES
112  *      YES
113  *      # [[ '-e' /bin ]] && echo YES
114  *      bash: conditional binary operator expected...
115  *      # A='-e'; [[ $A /bin ]] && echo YES
116  *      bash: conditional binary operator expected...
117  *
118  * || and && should work as -o and -a work in [ ]
119  * -a and -o aren't recognized (&& and || are to be used instead)
120  * ( and ) do not need to be quoted unlike in [ ]:
121  *      # [[ ( abc ) && '' ]] && echo YES
122  *      # [[ ( abc ) || '' ]] && echo YES
123  *      YES
124  *      # [[ ( abc ) -o '' ]] && echo YES
125  *      bash: syntax error in conditional expression...
126  *
127  * Apart from the above, [[ expr ]] should work as [ expr ]
128  */
129
130 #define TEST_DEBUG 0
131
132 enum token {
133         EOI,
134
135         FILRD, /* file access */
136         FILWR,
137         FILEX,
138
139         FILEXIST,
140
141         FILREG, /* file type */
142         FILDIR,
143         FILCDEV,
144         FILBDEV,
145         FILFIFO,
146         FILSOCK,
147
148         FILSYM,
149         FILGZ,
150         FILTT,
151
152         FILSUID, /* file bit */
153         FILSGID,
154         FILSTCK,
155
156         FILNT, /* file ops */
157         FILOT,
158         FILEQ,
159
160         FILUID,
161         FILGID,
162
163         STREZ, /* str ops */
164         STRNZ,
165         STREQ,
166         STRNE,
167         STRLT,
168         STRGT,
169
170         INTEQ, /* int ops */
171         INTNE,
172         INTGE,
173         INTGT,
174         INTLE,
175         INTLT,
176
177         UNOT,
178         BAND,
179         BOR,
180         LPAREN,
181         RPAREN,
182         OPERAND
183 };
184 #define is_int_op(a)      (((unsigned char)((a) - INTEQ)) <= 5)
185 #define is_str_op(a)      (((unsigned char)((a) - STREZ)) <= 5)
186 #define is_file_op(a)     (((unsigned char)((a) - FILNT)) <= 2)
187 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
188 #define is_file_type(a)   (((unsigned char)((a) - FILREG)) <= 5)
189 #define is_file_bit(a)    (((unsigned char)((a) - FILSUID)) <= 2)
190
191 #if TEST_DEBUG
192 int depth;
193 #define nest_msg(...) do { \
194         depth++; \
195         fprintf(stderr, "%*s", depth*2, ""); \
196         fprintf(stderr, __VA_ARGS__); \
197 } while (0)
198 #define unnest_msg(...) do { \
199         fprintf(stderr, "%*s", depth*2, ""); \
200         fprintf(stderr, __VA_ARGS__); \
201         depth--; \
202 } while (0)
203 #define dbg_msg(...) do { \
204         fprintf(stderr, "%*s", depth*2, ""); \
205         fprintf(stderr, __VA_ARGS__); \
206 } while (0)
207 #define unnest_msg_and_return(expr, ...) do { \
208         number_t __res = (expr); \
209         fprintf(stderr, "%*s", depth*2, ""); \
210         fprintf(stderr, __VA_ARGS__, res); \
211         depth--; \
212         return __res; \
213 } while (0)
214 static const char *const TOKSTR[] = {
215         "EOI",
216         "FILRD",
217         "FILWR",
218         "FILEX",
219         "FILEXIST",
220         "FILREG",
221         "FILDIR",
222         "FILCDEV",
223         "FILBDEV",
224         "FILFIFO",
225         "FILSOCK",
226         "FILSYM",
227         "FILGZ",
228         "FILTT",
229         "FILSUID",
230         "FILSGID",
231         "FILSTCK",
232         "FILNT",
233         "FILOT",
234         "FILEQ",
235         "FILUID",
236         "FILGID",
237         "STREZ",
238         "STRNZ",
239         "STREQ",
240         "STRNE",
241         "STRLT",
242         "STRGT",
243         "INTEQ",
244         "INTNE",
245         "INTGE",
246         "INTGT",
247         "INTLE",
248         "INTLT",
249         "UNOT",
250         "BAND",
251         "BOR",
252         "LPAREN",
253         "RPAREN",
254         "OPERAND"
255 };
256 #else
257 #define nest_msg(...)   ((void)0)
258 #define unnest_msg(...) ((void)0)
259 #define dbg_msg(...)    ((void)0)
260 #define unnest_msg_and_return(expr, ...) return expr
261 #endif
262
263 enum {
264         UNOP,
265         BINOP,
266         BUNOP,
267         BBINOP,
268         PAREN
269 };
270
271 struct operator_t {
272         unsigned char op_num, op_type;
273 };
274
275 static const struct operator_t ops_table[] = {
276         { /* "-r" */ FILRD   , UNOP   },
277         { /* "-w" */ FILWR   , UNOP   },
278         { /* "-x" */ FILEX   , UNOP   },
279         { /* "-e" */ FILEXIST, UNOP   },
280         { /* "-f" */ FILREG  , UNOP   },
281         { /* "-d" */ FILDIR  , UNOP   },
282         { /* "-c" */ FILCDEV , UNOP   },
283         { /* "-b" */ FILBDEV , UNOP   },
284         { /* "-p" */ FILFIFO , UNOP   },
285         { /* "-u" */ FILSUID , UNOP   },
286         { /* "-g" */ FILSGID , UNOP   },
287         { /* "-k" */ FILSTCK , UNOP   },
288         { /* "-s" */ FILGZ   , UNOP   },
289         { /* "-t" */ FILTT   , UNOP   },
290         { /* "-z" */ STREZ   , UNOP   },
291         { /* "-n" */ STRNZ   , UNOP   },
292         { /* "-h" */ FILSYM  , UNOP   },    /* for backwards compat */
293
294         { /* "-O" */ FILUID  , UNOP   },
295         { /* "-G" */ FILGID  , UNOP   },
296         { /* "-L" */ FILSYM  , UNOP   },
297         { /* "-S" */ FILSOCK , UNOP   },
298         { /* "="  */ STREQ   , BINOP  },
299         { /* "==" */ STREQ   , BINOP  },
300         { /* "!=" */ STRNE   , BINOP  },
301         { /* "<"  */ STRLT   , BINOP  },
302         { /* ">"  */ STRGT   , BINOP  },
303         { /* "-eq"*/ INTEQ   , BINOP  },
304         { /* "-ne"*/ INTNE   , BINOP  },
305         { /* "-ge"*/ INTGE   , BINOP  },
306         { /* "-gt"*/ INTGT   , BINOP  },
307         { /* "-le"*/ INTLE   , BINOP  },
308         { /* "-lt"*/ INTLT   , BINOP  },
309         { /* "-nt"*/ FILNT   , BINOP  },
310         { /* "-ot"*/ FILOT   , BINOP  },
311         { /* "-ef"*/ FILEQ   , BINOP  },
312         { /* "!"  */ UNOT    , BUNOP  },
313         { /* "-a" */ BAND    , BBINOP },
314         { /* "-o" */ BOR     , BBINOP },
315         { /* "("  */ LPAREN  , PAREN  },
316         { /* ")"  */ RPAREN  , PAREN  },
317 };
318 /* Please keep these two tables in sync */
319 static const char ops_texts[] ALIGN1 =
320         "-r"  "\0"
321         "-w"  "\0"
322         "-x"  "\0"
323         "-e"  "\0"
324         "-f"  "\0"
325         "-d"  "\0"
326         "-c"  "\0"
327         "-b"  "\0"
328         "-p"  "\0"
329         "-u"  "\0"
330         "-g"  "\0"
331         "-k"  "\0"
332         "-s"  "\0"
333         "-t"  "\0"
334         "-z"  "\0"
335         "-n"  "\0"
336         "-h"  "\0"
337
338         "-O"  "\0"
339         "-G"  "\0"
340         "-L"  "\0"
341         "-S"  "\0"
342         "="   "\0"
343         "=="  "\0"
344         "!="  "\0"
345         "<"   "\0"
346         ">"   "\0"
347         "-eq" "\0"
348         "-ne" "\0"
349         "-ge" "\0"
350         "-gt" "\0"
351         "-le" "\0"
352         "-lt" "\0"
353         "-nt" "\0"
354         "-ot" "\0"
355         "-ef" "\0"
356         "!"   "\0"
357         "-a"  "\0"
358         "-o"  "\0"
359         "("   "\0"
360         ")"   "\0"
361 ;
362
363
364 #if ENABLE_FEATURE_TEST_64
365 typedef int64_t number_t;
366 #else
367 typedef int number_t;
368 #endif
369
370
371 /* We try to minimize both static and stack usage. */
372 struct test_statics {
373         char **args;
374         /* set only by check_operator(), either to bogus struct
375          * or points to matching operator_t struct. Never NULL. */
376         const struct operator_t *last_operator;
377         gid_t *group_array;
378         int ngroups;
379         jmp_buf leaving;
380 };
381
382 /* See test_ptr_hack.c */
383 extern struct test_statics *const test_ptr_to_statics;
384
385 #define S (*test_ptr_to_statics)
386 #define args            (S.args         )
387 #define last_operator   (S.last_operator)
388 #define group_array     (S.group_array  )
389 #define ngroups         (S.ngroups      )
390 #define leaving         (S.leaving      )
391
392 #define INIT_S() do { \
393         (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
394         barrier(); \
395 } while (0)
396 #define DEINIT_S() do { \
397         free(test_ptr_to_statics); \
398 } while (0)
399
400 static number_t primary(enum token n);
401
402 static void syntax(const char *op, const char *msg) NORETURN;
403 static void syntax(const char *op, const char *msg)
404 {
405         if (op && *op) {
406                 bb_error_msg("%s: %s", op, msg);
407         } else {
408                 bb_error_msg("%s: %s"+4, msg);
409         }
410         longjmp(leaving, 2);
411 }
412
413 /* atoi with error detection */
414 //XXX: FIXME: duplicate of existing libbb function?
415 static number_t getn(const char *s)
416 {
417         char *p;
418 #if ENABLE_FEATURE_TEST_64
419         long long r;
420 #else
421         long r;
422 #endif
423
424         errno = 0;
425 #if ENABLE_FEATURE_TEST_64
426         r = strtoll(s, &p, 10);
427 #else
428         r = strtol(s, &p, 10);
429 #endif
430
431         if (errno != 0)
432                 syntax(s, "out of range");
433
434         if (p == s || *(skip_whitespace(p)) != '\0')
435                 syntax(s, "bad number");
436
437         return r;
438 }
439
440 /* UNUSED
441 static int newerf(const char *f1, const char *f2)
442 {
443         struct stat b1, b2;
444
445         return (stat(f1, &b1) == 0 &&
446                         stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
447 }
448
449 static int olderf(const char *f1, const char *f2)
450 {
451         struct stat b1, b2;
452
453         return (stat(f1, &b1) == 0 &&
454                         stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
455 }
456
457 static int equalf(const char *f1, const char *f2)
458 {
459         struct stat b1, b2;
460
461         return (stat(f1, &b1) == 0 &&
462                         stat(f2, &b2) == 0 &&
463                         b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
464 }
465 */
466
467
468 static enum token check_operator(const char *s)
469 {
470         static const struct operator_t no_op = {
471                 .op_num = -1,
472                 .op_type = -1
473         };
474         int n;
475
476         last_operator = &no_op;
477         if (s == NULL)
478                 return EOI;
479         n = index_in_strings(ops_texts, s);
480         if (n < 0)
481                 return OPERAND;
482         last_operator = &ops_table[n];
483         return ops_table[n].op_num;
484 }
485
486
487 static int binop(void)
488 {
489         const char *opnd1, *opnd2;
490         const struct operator_t *op;
491         number_t val1, val2;
492
493         opnd1 = *args;
494         check_operator(*++args);
495         op = last_operator;
496
497         opnd2 = *++args;
498         if (opnd2 == NULL)
499                 syntax(args[-1], "argument expected");
500
501         if (is_int_op(op->op_num)) {
502                 val1 = getn(opnd1);
503                 val2 = getn(opnd2);
504                 if (op->op_num == INTEQ)
505                         return val1 == val2;
506                 if (op->op_num == INTNE)
507                         return val1 != val2;
508                 if (op->op_num == INTGE)
509                         return val1 >= val2;
510                 if (op->op_num == INTGT)
511                         return val1 >  val2;
512                 if (op->op_num == INTLE)
513                         return val1 <= val2;
514                 /*if (op->op_num == INTLT)*/
515                 return val1 <  val2;
516         }
517         if (is_str_op(op->op_num)) {
518                 val1 = strcmp(opnd1, opnd2);
519                 if (op->op_num == STREQ)
520                         return val1 == 0;
521                 if (op->op_num == STRNE)
522                         return val1 != 0;
523                 if (op->op_num == STRLT)
524                         return val1 < 0;
525                 /*if (op->op_num == STRGT)*/
526                 return val1 > 0;
527         }
528         /* We are sure that these three are by now the only binops we didn't check
529          * yet, so we do not check if the class is correct:
530          */
531 /*      if (is_file_op(op->op_num)) */
532         {
533                 struct stat b1, b2;
534
535                 if (stat(opnd1, &b1) || stat(opnd2, &b2))
536                         return 0; /* false, since at least one stat failed */
537                 if (op->op_num == FILNT)
538                         return b1.st_mtime > b2.st_mtime;
539                 if (op->op_num == FILOT)
540                         return b1.st_mtime < b2.st_mtime;
541                 /*if (op->op_num == FILEQ)*/
542                 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
543         }
544         /*return 1; - NOTREACHED */
545 }
546
547
548 static void initialize_group_array(void)
549 {
550         int n;
551
552         /* getgroups may be expensive, try to use it only once */
553         ngroups = 32;
554         do {
555                 /* FIXME: ash tries so hard to not die on OOM,
556                  * and we spoil it with just one xrealloc here */
557                 /* We realloc, because test_main can be entered repeatedly by shell.
558                  * Testcase (ash): 'while true; do test -x some_file; done'
559                  * and watch top. (some_file must have owner != you) */
560                 n = ngroups;
561                 group_array = xrealloc(group_array, n * sizeof(gid_t));
562                 ngroups = getgroups(n, group_array);
563         } while (ngroups > n);
564 }
565
566
567 /* Return non-zero if GID is one that we have in our groups list. */
568 //XXX: FIXME: duplicate of existing libbb function?
569 // see toplevel TODO file:
570 // possible code duplication ingroup() and is_a_group_member()
571 static int is_a_group_member(gid_t gid)
572 {
573         int i;
574
575         /* Short-circuit if possible, maybe saving a call to getgroups(). */
576         if (gid == getgid() || gid == getegid())
577                 return 1;
578
579         if (ngroups == 0)
580                 initialize_group_array();
581
582         /* Search through the list looking for GID. */
583         for (i = 0; i < ngroups; i++)
584                 if (gid == group_array[i])
585                         return 1;
586
587         return 0;
588 }
589
590
591 /* Do the same thing access(2) does, but use the effective uid and gid,
592    and don't make the mistake of telling root that any file is
593    executable. */
594 static int test_eaccess(char *path, int mode)
595 {
596         struct stat st;
597         unsigned int euid = geteuid();
598
599         if (stat(path, &st) < 0)
600                 return -1;
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(nm, 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 (arg0[0] == '[') {
827                 --argc;
828                 if (!arg0[1]) { /* "[" ? */
829                         if (NOT_LONE_CHAR(argv[argc], ']')) {
830                                 bb_error_msg("missing ]");
831                                 return 2;
832                         }
833                 } else { /* assuming "[[" */
834                         if (strcmp(argv[argc], "]]") != 0) {
835                                 bb_error_msg("missing ]]");
836                                 return 2;
837                         }
838                 }
839                 argv[argc] = NULL;
840         }
841         /* argc is unused after this point */
842
843         /* We must do DEINIT_S() prior to returning */
844         INIT_S();
845
846         res = setjmp(leaving);
847         if (res)
848                 goto ret;
849
850         /* resetting ngroups is probably unnecessary.  it will
851          * force a new call to getgroups(), which prevents using
852          * group data fetched during a previous call.  but the
853          * only way the group data could be stale is if there's
854          * been an intervening call to setgroups(), and this
855          * isn't likely in the case of a shell.  paranoia
856          * prevails...
857          */
858         /*ngroups = 0; - done by INIT_S() */
859
860         argv++;
861         args = argv;
862
863         /* Implement special cases from POSIX.2, section 4.62.4.
864          * Testcase: "test '(' = '('"
865          * The general parser would misinterpret '(' as group start.
866          */
867         if (1) {
868                 int negate = 0;
869  again:
870                 if (!argv[0]) {
871                         /* "test" */
872                         res = 1;
873                         goto ret_special;
874                 }
875                 if (!argv[1]) {
876                         /* "test [!] arg" */
877                         res = (argv[0][0] == '\0');
878                         goto ret_special;
879                 }
880                 if (argv[2] && !argv[3]) {
881                         check_operator(argv[1]);
882                         if (last_operator->op_type == BINOP) {
883                                 /* "test [!] arg1 <binary_op> arg2" */
884                                 args = argv;
885                                 res = (binop() == 0);
886  ret_special:
887                                 /* If there was leading "!" op... */
888                                 res ^= negate;
889                                 goto ret;
890                         }
891                 }
892                 if (LONE_CHAR(argv[0], '!')) {
893                         argv++;
894                         negate ^= 1;
895                         goto again;
896                 }
897         }
898
899         res = !oexpr(check_operator(*args));
900
901         if (*args != NULL && *++args != NULL) {
902                 /* Examples:
903                  * test 3 -lt 5 6
904                  * test -t 1 2
905                  */
906                 bb_error_msg("%s: unknown operand", *args);
907                 res = 2;
908         }
909  ret:
910         DEINIT_S();
911         return res;
912 }