source: branches/3.2/mindi-busybox/coreutils/test.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
File size: 20.5 KB
Line 
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
137enum 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
197int 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)
219static 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
268enum {
269    UNOP,
270    BINOP,
271    BUNOP,
272    BBINOP,
273    PAREN
274};
275
276struct operator_t {
277    unsigned char op_num, op_type;
278};
279
280static 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 */
324static 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
370typedef int64_t number_t;
371#else
372typedef int number_t;
373#endif
374
375
376/* We try to minimize both static and stack usage. */
377struct 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 */
388extern 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
405static number_t primary(enum token n);
406
407static void syntax(const char *op, const char *msg) NORETURN;
408static 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?
420static 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
446static 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
454static 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
462static 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
473static 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
492static 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
553static 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()
576static 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. */
599static 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
630static 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
703static 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            args--;
714            unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
715            return 1;
716        }
717        res = !nexpr(n);
718        unnest_msg("<nexpr:%lld\n", res);
719        return res;
720    }
721    res = primary(n);
722    unnest_msg("<nexpr:%lld\n", res);
723    return res;
724}
725
726
727static number_t aexpr(enum token n)
728{
729    number_t res;
730
731    nest_msg(">aexpr(%s)\n", TOKSTR[n]);
732    res = nexpr(n);
733    dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
734    if (check_operator(*++args) == BAND) {
735        dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
736        res = aexpr(check_operator(*++args)) && res;
737        unnest_msg("<aexpr:%lld\n", res);
738        return res;
739    }
740    args--;
741    unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
742    return res;
743}
744
745
746static number_t oexpr(enum token n)
747{
748    number_t res;
749
750    nest_msg(">oexpr(%s)\n", TOKSTR[n]);
751    res = aexpr(n);
752    dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
753    if (check_operator(*++args) == BOR) {
754        dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
755        res = oexpr(check_operator(*++args)) || res;
756        unnest_msg("<oexpr:%lld\n", res);
757        return res;
758    }
759    args--;
760    unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
761    return res;
762}
763
764
765static number_t primary(enum token n)
766{
767#if TEST_DEBUG
768    number_t res = res; /* for compiler */
769#else
770    number_t res;
771#endif
772    const struct operator_t *args0_op;
773
774    nest_msg(">primary(%s)\n", TOKSTR[n]);
775    if (n == EOI) {
776        syntax(NULL, "argument expected");
777    }
778    if (n == LPAREN) {
779        res = oexpr(check_operator(*++args));
780        if (check_operator(*++args) != RPAREN)
781            syntax(NULL, "closing paren expected");
782        unnest_msg("<primary:%lld\n", res);
783        return res;
784    }
785
786    /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
787     * do the same */
788    args0_op = last_operator;
789    /* last_operator = operator at args[1] */
790    if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
791        if (args[2]) {
792            // coreutils also does this:
793            // if (args[3] && args[0]="-l" && args[2] is BINOP)
794            //  return binop(1 /* prepended by -l */);
795            if (last_operator->op_type == BINOP)
796                unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
797        }
798    }
799    /* check "is args[0] unop?" second */
800    if (args0_op->op_type == UNOP) {
801        /* unary expression */
802        if (args[1] == NULL)
803//          syntax(args0_op->op_text, "argument expected");
804            goto check_emptiness;
805        args++;
806        if (n == STREZ)
807            unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
808        if (n == STRNZ)
809            unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
810        if (n == FILTT)
811            unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
812        unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
813    }
814
815    /*check_operator(args[1]); - already done */
816    if (last_operator->op_type == BINOP) {
817        /* args[2] is known to be NULL, isn't it bound to fail? */
818        unnest_msg_and_return(binop(), "<primary:%lld\n");
819    }
820 check_emptiness:
821    unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
822}
823
824
825int test_main(int argc, char **argv)
826{
827    int res;
828    const char *arg0;
829//  bool negate = 0;
830
831    arg0 = bb_basename(argv[0]);
832    if (arg0[0] == '[') {
833        --argc;
834        if (!arg0[1]) { /* "[" ? */
835            if (NOT_LONE_CHAR(argv[argc], ']')) {
836                bb_error_msg("missing ]");
837                return 2;
838            }
839        } else { /* assuming "[[" */
840            if (strcmp(argv[argc], "]]") != 0) {
841                bb_error_msg("missing ]]");
842                return 2;
843            }
844        }
845        argv[argc] = NULL;
846    }
847
848    /* We must do DEINIT_S() prior to returning */
849    INIT_S();
850
851    res = setjmp(leaving);
852    if (res)
853        goto ret;
854
855    /* resetting ngroups is probably unnecessary.  it will
856     * force a new call to getgroups(), which prevents using
857     * group data fetched during a previous call.  but the
858     * only way the group data could be stale is if there's
859     * been an intervening call to setgroups(), and this
860     * isn't likely in the case of a shell.  paranoia
861     * prevails...
862     */
863    /*ngroups = 0; - done by INIT_S() */
864
865    //argc--;
866    argv++;
867
868    /* Implement special cases from POSIX.2, section 4.62.4 */
869    if (!argv[0]) { /* "test" */
870        res = 1;
871        goto ret;
872    }
873#if 0
874// Now it's fixed in the parser and should not be needed
875    if (LONE_CHAR(argv[0], '!') && argv[1]) {
876        negate = 1;
877        //argc--;
878        argv++;
879    }
880    if (!argv[1]) { /* "test [!] arg" */
881        res = (*argv[0] == '\0');
882        goto ret;
883    }
884    if (argv[2] && !argv[3]) {
885        check_operator(argv[1]);
886        if (last_operator->op_type == BINOP) {
887            /* "test [!] arg1 <binary_op> arg2" */
888            args = argv;
889            res = (binop() == 0);
890            goto ret;
891        }
892    }
893
894    /* Some complex expression. Undo '!' removal */
895    if (negate) {
896        negate = 0;
897        //argc++;
898        argv--;
899    }
900#endif
901    args = argv;
902    res = !oexpr(check_operator(*args));
903
904    if (*args != NULL && *++args != NULL) {
905        /* Examples:
906         * test 3 -lt 5 6
907         * test -t 1 2
908         */
909        bb_error_msg("%s: unknown operand", *args);
910        res = 2;
911    }
912 ret:
913    DEINIT_S();
914//  return negate ? !res : res;
915    return res;
916}
Note: See TracBrowser for help on using the repository browser.