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