hust testsuite: fix a false positive
[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(group_array); \
398         free(test_ptr_to_statics); \
399 } while (0)
400
401 static number_t primary(enum token n);
402
403 static void syntax(const char *op, const char *msg) NORETURN;
404 static void syntax(const char *op, const char *msg)
405 {
406         if (op && *op) {
407                 bb_error_msg("%s: %s", op, msg);
408         } else {
409                 bb_error_msg("%s: %s"+4, msg);
410         }
411         longjmp(leaving, 2);
412 }
413
414 /* atoi with error detection */
415 //XXX: FIXME: duplicate of existing libbb function?
416 static number_t getn(const char *s)
417 {
418         char *p;
419 #if ENABLE_FEATURE_TEST_64
420         long long r;
421 #else
422         long r;
423 #endif
424
425         errno = 0;
426 #if ENABLE_FEATURE_TEST_64
427         r = strtoll(s, &p, 10);
428 #else
429         r = strtol(s, &p, 10);
430 #endif
431
432         if (errno != 0)
433                 syntax(s, "out of range");
434
435         if (p == s || *(skip_whitespace(p)) != '\0')
436                 syntax(s, "bad number");
437
438         return r;
439 }
440
441 /* UNUSED
442 static int newerf(const char *f1, const char *f2)
443 {
444         struct stat b1, b2;
445
446         return (stat(f1, &b1) == 0 &&
447                         stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
448 }
449
450 static int olderf(const char *f1, const char *f2)
451 {
452         struct stat b1, b2;
453
454         return (stat(f1, &b1) == 0 &&
455                         stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
456 }
457
458 static int equalf(const char *f1, const char *f2)
459 {
460         struct stat b1, b2;
461
462         return (stat(f1, &b1) == 0 &&
463                         stat(f2, &b2) == 0 &&
464                         b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
465 }
466 */
467
468
469 static enum token check_operator(const char *s)
470 {
471         static const struct operator_t no_op = {
472                 .op_num = -1,
473                 .op_type = -1
474         };
475         int n;
476
477         last_operator = &no_op;
478         if (s == NULL)
479                 return EOI;
480         n = index_in_strings(ops_texts, s);
481         if (n < 0)
482                 return OPERAND;
483         last_operator = &ops_table[n];
484         return ops_table[n].op_num;
485 }
486
487
488 static int binop(void)
489 {
490         const char *opnd1, *opnd2;
491         const struct operator_t *op;
492         number_t val1, val2;
493
494         opnd1 = *args;
495         check_operator(*++args);
496         op = last_operator;
497
498         opnd2 = *++args;
499         if (opnd2 == NULL)
500                 syntax(args[-1], "argument expected");
501
502         if (is_int_op(op->op_num)) {
503                 val1 = getn(opnd1);
504                 val2 = getn(opnd2);
505                 if (op->op_num == INTEQ)
506                         return val1 == val2;
507                 if (op->op_num == INTNE)
508                         return val1 != val2;
509                 if (op->op_num == INTGE)
510                         return val1 >= val2;
511                 if (op->op_num == INTGT)
512                         return val1 >  val2;
513                 if (op->op_num == INTLE)
514                         return val1 <= val2;
515                 /*if (op->op_num == INTLT)*/
516                 return val1 <  val2;
517         }
518         if (is_str_op(op->op_num)) {
519                 val1 = strcmp(opnd1, opnd2);
520                 if (op->op_num == STREQ)
521                         return val1 == 0;
522                 if (op->op_num == STRNE)
523                         return val1 != 0;
524                 if (op->op_num == STRLT)
525                         return val1 < 0;
526                 /*if (op->op_num == STRGT)*/
527                 return val1 > 0;
528         }
529         /* We are sure that these three are by now the only binops we didn't check
530          * yet, so we do not check if the class is correct:
531          */
532 /*      if (is_file_op(op->op_num)) */
533         {
534                 struct stat b1, b2;
535
536                 if (stat(opnd1, &b1) || stat(opnd2, &b2))
537                         return 0; /* false, since at least one stat failed */
538                 if (op->op_num == FILNT)
539                         return b1.st_mtime > b2.st_mtime;
540                 if (op->op_num == FILOT)
541                         return b1.st_mtime < b2.st_mtime;
542                 /*if (op->op_num == FILEQ)*/
543                 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
544         }
545         /*return 1; - NOTREACHED */
546 }
547
548
549 static void initialize_group_array(void)
550 {
551         int n;
552
553         /* getgroups may be expensive, try to use it only once */
554         ngroups = 32;
555         do {
556                 /* FIXME: ash tries so hard to not die on OOM,
557                  * and we spoil it with just one xrealloc here */
558                 /* We realloc, because test_main can be entered repeatedly by shell.
559                  * Testcase (ash): 'while true; do test -x some_file; done'
560                  * and watch top. (some_file must have owner != you) */
561                 n = ngroups;
562                 group_array = xrealloc(group_array, n * sizeof(gid_t));
563                 ngroups = getgroups(n, group_array);
564         } while (ngroups > n);
565 }
566
567
568 /* Return non-zero if GID is one that we have in our groups list. */
569 //XXX: FIXME: duplicate of existing libbb function?
570 // see toplevel TODO file:
571 // possible code duplication ingroup() and is_a_group_member()
572 static int is_a_group_member(gid_t gid)
573 {
574         int i;
575
576         /* Short-circuit if possible, maybe saving a call to getgroups(). */
577         if (gid == getgid() || gid == getegid())
578                 return 1;
579
580         if (ngroups == 0)
581                 initialize_group_array();
582
583         /* Search through the list looking for GID. */
584         for (i = 0; i < ngroups; i++)
585                 if (gid == group_array[i])
586                         return 1;
587
588         return 0;
589 }
590
591
592 /* Do the same thing access(2) does, but use the effective uid and gid,
593    and don't make the mistake of telling root that any file is
594    executable. */
595 static int test_eaccess(char *path, int mode)
596 {
597         struct stat st;
598         unsigned int euid = geteuid();
599
600         if (stat(path, &st) < 0)
601                 return -1;
602
603         if (euid == 0) {
604                 /* Root can read or write any file. */
605                 if (mode != X_OK)
606                         return 0;
607
608                 /* Root can execute any file that has any one of the execute
609                  * bits set. */
610                 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
611                         return 0;
612         }
613
614         if (st.st_uid == euid)  /* owner */
615                 mode <<= 6;
616         else if (is_a_group_member(st.st_gid))
617                 mode <<= 3;
618
619         if (st.st_mode & mode)
620                 return 0;
621
622         return -1;
623 }
624
625
626 static int filstat(char *nm, enum token mode)
627 {
628         struct stat s;
629         unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
630
631         if (mode == FILSYM) {
632 #ifdef S_IFLNK
633                 if (lstat(nm, &s) == 0) {
634                         i = S_IFLNK;
635                         goto filetype;
636                 }
637 #endif
638                 return 0;
639         }
640
641         if (stat(nm, &s) != 0)
642                 return 0;
643         if (mode == FILEXIST)
644                 return 1;
645         if (is_file_access(mode)) {
646                 if (mode == FILRD)
647                         i = R_OK;
648                 if (mode == FILWR)
649                         i = W_OK;
650                 if (mode == FILEX)
651                         i = X_OK;
652                 return test_eaccess(nm, i) == 0;
653         }
654         if (is_file_type(mode)) {
655                 if (mode == FILREG)
656                         i = S_IFREG;
657                 if (mode == FILDIR)
658                         i = S_IFDIR;
659                 if (mode == FILCDEV)
660                         i = S_IFCHR;
661                 if (mode == FILBDEV)
662                         i = S_IFBLK;
663                 if (mode == FILFIFO) {
664 #ifdef S_IFIFO
665                         i = S_IFIFO;
666 #else
667                         return 0;
668 #endif
669                 }
670                 if (mode == FILSOCK) {
671 #ifdef S_IFSOCK
672                         i = S_IFSOCK;
673 #else
674                         return 0;
675 #endif
676                 }
677  filetype:
678                 return ((s.st_mode & S_IFMT) == i);
679         }
680         if (is_file_bit(mode)) {
681                 if (mode == FILSUID)
682                         i = S_ISUID;
683                 if (mode == FILSGID)
684                         i = S_ISGID;
685                 if (mode == FILSTCK)
686                         i = S_ISVTX;
687                 return ((s.st_mode & i) != 0);
688         }
689         if (mode == FILGZ)
690                 return s.st_size > 0L;
691         if (mode == FILUID)
692                 return s.st_uid == geteuid();
693         if (mode == FILGID)
694                 return s.st_gid == getegid();
695         return 1; /* NOTREACHED */
696 }
697
698
699 static number_t nexpr(enum token n)
700 {
701         number_t res;
702
703         nest_msg(">nexpr(%s)\n", TOKSTR[n]);
704         if (n == UNOT) {
705                 n = check_operator(*++args);
706                 if (n == EOI) {
707                         /* special case: [ ! ], [ a -a ! ] are valid */
708                         /* IOW, "! ARG" may miss ARG */
709                         args--;
710                         unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
711                         return 1;
712                 }
713                 res = !nexpr(n);
714                 unnest_msg("<nexpr:%lld\n", res);
715                 return res;
716         }
717         res = primary(n);
718         unnest_msg("<nexpr:%lld\n", res);
719         return res;
720 }
721
722
723 static number_t aexpr(enum token n)
724 {
725         number_t res;
726
727         nest_msg(">aexpr(%s)\n", TOKSTR[n]);
728         res = nexpr(n);
729         dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
730         if (check_operator(*++args) == BAND) {
731                 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
732                 res = aexpr(check_operator(*++args)) && res;
733                 unnest_msg("<aexpr:%lld\n", res);
734                 return res;
735         }
736         args--;
737         unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
738         return res;
739 }
740
741
742 static number_t oexpr(enum token n)
743 {
744         number_t res;
745
746         nest_msg(">oexpr(%s)\n", TOKSTR[n]);
747         res = aexpr(n);
748         dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
749         if (check_operator(*++args) == BOR) {
750                 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
751                 res = oexpr(check_operator(*++args)) || res;
752                 unnest_msg("<oexpr:%lld\n", res);
753                 return res;
754         }
755         args--;
756         unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
757         return res;
758 }
759
760
761 static number_t primary(enum token n)
762 {
763 #if TEST_DEBUG
764         number_t res = res; /* for compiler */
765 #else
766         number_t res;
767 #endif
768         const struct operator_t *args0_op;
769
770         nest_msg(">primary(%s)\n", TOKSTR[n]);
771         if (n == EOI) {
772                 syntax(NULL, "argument expected");
773         }
774         if (n == LPAREN) {
775                 res = oexpr(check_operator(*++args));
776                 if (check_operator(*++args) != RPAREN)
777                         syntax(NULL, "closing paren expected");
778                 unnest_msg("<primary:%lld\n", res);
779                 return res;
780         }
781
782         /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
783          * do the same */
784         args0_op = last_operator;
785         /* last_operator = operator at args[1] */
786         if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
787                 if (args[2]) {
788                         // coreutils also does this:
789                         // if (args[3] && args[0]="-l" && args[2] is BINOP)
790                         //      return binop(1 /* prepended by -l */);
791                         if (last_operator->op_type == BINOP)
792                                 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
793                 }
794         }
795         /* check "is args[0] unop?" second */
796         if (args0_op->op_type == UNOP) {
797                 /* unary expression */
798                 if (args[1] == NULL)
799 //                      syntax(args0_op->op_text, "argument expected");
800                         goto check_emptiness;
801                 args++;
802                 if (n == STREZ)
803                         unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
804                 if (n == STRNZ)
805                         unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
806                 if (n == FILTT)
807                         unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
808                 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
809         }
810
811         /*check_operator(args[1]); - already done */
812         if (last_operator->op_type == BINOP) {
813                 /* args[2] is known to be NULL, isn't it bound to fail? */
814                 unnest_msg_and_return(binop(), "<primary:%lld\n");
815         }
816  check_emptiness:
817         unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
818 }
819
820
821 int test_main(int argc, char **argv)
822 {
823         int res;
824         const char *arg0;
825
826         arg0 = bb_basename(argv[0]);
827         if (arg0[0] == '[') {
828                 --argc;
829                 if (!arg0[1]) { /* "[" ? */
830                         if (NOT_LONE_CHAR(argv[argc], ']')) {
831                                 bb_error_msg("missing ]");
832                                 return 2;
833                         }
834                 } else { /* assuming "[[" */
835                         if (strcmp(argv[argc], "]]") != 0) {
836                                 bb_error_msg("missing ]]");
837                                 return 2;
838                         }
839                 }
840                 argv[argc] = NULL;
841         }
842         /* argc is unused after this point */
843
844         /* We must do DEINIT_S() prior to returning */
845         INIT_S();
846
847         res = setjmp(leaving);
848         if (res)
849                 goto ret;
850
851         /* resetting ngroups is probably unnecessary.  it will
852          * force a new call to getgroups(), which prevents using
853          * group data fetched during a previous call.  but the
854          * only way the group data could be stale is if there's
855          * been an intervening call to setgroups(), and this
856          * isn't likely in the case of a shell.  paranoia
857          * prevails...
858          */
859         /*ngroups = 0; - done by INIT_S() */
860
861         argv++;
862         args = argv;
863
864         /* Implement special cases from POSIX.2, section 4.62.4.
865          * Testcase: "test '(' = '('"
866          * The general parser would misinterpret '(' as group start.
867          */
868         if (1) {
869                 int negate = 0;
870  again:
871                 if (!argv[0]) {
872                         /* "test" */
873                         res = 1;
874                         goto ret_special;
875                 }
876                 if (!argv[1]) {
877                         /* "test [!] arg" */
878                         res = (argv[0][0] == '\0');
879                         goto ret_special;
880                 }
881                 if (argv[2] && !argv[3]) {
882                         check_operator(argv[1]);
883                         if (last_operator->op_type == BINOP) {
884                                 /* "test [!] arg1 <binary_op> arg2" */
885                                 args = argv;
886                                 res = (binop() == 0);
887  ret_special:
888                                 /* If there was leading "!" op... */
889                                 res ^= negate;
890                                 goto ret;
891                         }
892                 }
893                 if (LONE_CHAR(argv[0], '!')) {
894                         argv++;
895                         negate ^= 1;
896                         goto again;
897                 }
898         }
899
900         res = !oexpr(check_operator(*args));
901
902         if (*args != NULL && *++args != NULL) {
903                 /* Examples:
904                  * test 3 -lt 5 6
905                  * test -t 1 2
906                  */
907                 bb_error_msg("%s: unknown operand", *args);
908                 res = 2;
909         }
910  ret:
911         DEINIT_S();
912         return res;
913 }