source: branches/2.2.9/mindi-busybox/coreutils/test.c @ 2725

Last change on this file since 2725 was 2725, checked in by Bruno Cornec, 9 years ago
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File size: 19.6 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#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 STANDALONE_SHELL
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
114enum 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
174int 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)
196static 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
245enum {
246    UNOP,
247    BINOP,
248    BUNOP,
249    BBINOP,
250    PAREN
251};
252
253struct operator_t {
254    unsigned char op_num, op_type;
255};
256
257static 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 */
301static 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
347typedef int64_t number_t;
348#else
349typedef int number_t;
350#endif
351
352
353/* We try to minimize both static and stack usage. */
354struct 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 */
365extern 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
382static number_t primary(enum token n);
383
384static void syntax(const char *op, const char *msg) NORETURN;
385static 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?
397static 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
423static 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
431static 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
439static 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
450static 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
469static 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
530static 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()
553static 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. */
576static 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
607static 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
680static 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
703static 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
722static 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
741static 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
801int 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        /* TODO: example when this happens? */
882        bb_error_msg("%s: unknown operand", *args);
883        res = 2;
884    }
885 ret:
886    DEINIT_S();
887//  return negate ? !res : res;
888    return res;
889}
Note: See TracBrowser for help on using the repository browser.