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