Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • 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:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/coreutils/test.c

    r1765 r2725  
    1313 *     modified by Erik Andersen <andersen@codepoet.org> to be used
    1414 *     in busybox.
    15  *     modified by Bernhard Fischer to be useable (i.e. a bit less bloaty).
     15 *     modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty).
    1616 *
    17  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     17 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    1818 *
    1919 * Original copyright notice states:
     
    2121 */
    2222
     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
    2342#include "libbb.h"
    2443#include <setjmp.h>
    2544
    26 /* This is a NOEXEC applet. Be very careful! */
    27 
     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. */
    2850
    2951/* test(1) accepts the following grammar:
    30     oexpr   ::= aexpr | aexpr "-o" oexpr ;
    31     aexpr   ::= nexpr | nexpr "-a" aexpr ;
    32     nexpr   ::= primary | "!" primary
     52    oexpr   ::= aexpr | aexpr "-o" oexpr ;
     53    aexpr   ::= nexpr | nexpr "-a" aexpr ;
     54    nexpr   ::= primary | "!" primary
    3355    primary ::= unary-operator operand
    3456        | operand binary-operator operand
     
    4466*/
    4567
     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
    46114enum token {
    47115    EOI,
    48     FILRD,
     116
     117    FILRD, /* file access */
    49118    FILWR,
    50119    FILEX,
     120
    51121    FILEXIST,
    52     FILREG,
     122
     123    FILREG, /* file type */
    53124    FILDIR,
    54125    FILCDEV,
     
    56127    FILFIFO,
    57128    FILSOCK,
     129
    58130    FILSYM,
    59131    FILGZ,
    60132    FILTT,
    61     FILSUID,
     133
     134    FILSUID, /* file bit */
    62135    FILSGID,
    63136    FILSTCK,
    64     FILNT,
     137
     138    FILNT, /* file ops */
    65139    FILOT,
    66140    FILEQ,
     141
    67142    FILUID,
    68143    FILGID,
    69     STREZ,
     144
     145    STREZ, /* str ops */
    70146    STRNZ,
    71147    STREQ,
     
    73149    STRLT,
    74150    STRGT,
    75     INTEQ,
     151
     152    INTEQ, /* int ops */
    76153    INTNE,
    77154    INTGE,
     
    79156    INTLE,
    80157    INTLT,
     158
    81159    UNOT,
    82160    BAND,
     
    86164    OPERAND
    87165};
    88 #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
    89 #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
    90 #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
     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)
    91169#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
    92 #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
    93 #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
    94 enum token_types {
     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 {
    95246    UNOP,
    96247    BINOP,
     
    100251};
    101252
    102 static const struct t_op {
    103     char op_text[4];
     253struct operator_t {
    104254    unsigned char op_num, op_type;
    105 } ops[] = {
    106     { "-r", FILRD   , UNOP   },
    107     { "-w", FILWR   , UNOP   },
    108     { "-x", FILEX   , UNOP   },
    109     { "-e", FILEXIST, UNOP   },
    110     { "-f", FILREG  , UNOP   },
    111     { "-d", FILDIR  , UNOP   },
    112     { "-c", FILCDEV , UNOP   },
    113     { "-b", FILBDEV , UNOP   },
    114     { "-p", FILFIFO , UNOP   },
    115     { "-u", FILSUID , UNOP   },
    116     { "-g", FILSGID , UNOP   },
    117     { "-k", FILSTCK , UNOP   },
    118     { "-s", FILGZ   , UNOP   },
    119     { "-t", FILTT   , UNOP   },
    120     { "-z", STREZ   , UNOP   },
    121     { "-n", STRNZ   , UNOP   },
    122     { "-h", FILSYM  , UNOP   },    /* for backwards compat */
    123 
    124     { "-O" , FILUID , UNOP   },
    125     { "-G" , FILGID , UNOP   },
    126     { "-L" , FILSYM , UNOP   },
    127     { "-S" , FILSOCK, UNOP   },
    128     { "="  , STREQ  , BINOP  },
    129     { "==" , STREQ  , BINOP  },
    130     { "!=" , STRNE  , BINOP  },
    131     { "<"  , STRLT  , BINOP  },
    132     { ">"  , STRGT  , BINOP  },
    133     { "-eq", INTEQ  , BINOP  },
    134     { "-ne", INTNE  , BINOP  },
    135     { "-ge", INTGE  , BINOP  },
    136     { "-gt", INTGT  , BINOP  },
    137     { "-le", INTLE  , BINOP  },
    138     { "-lt", INTLT  , BINOP  },
    139     { "-nt", FILNT  , BINOP  },
    140     { "-ot", FILOT  , BINOP  },
    141     { "-ef", FILEQ  , BINOP  },
    142     { "!"  , UNOT   , BUNOP  },
    143     { "-a" , BAND   , BBINOP },
    144     { "-o" , BOR    , BBINOP },
    145     { "("  , LPAREN , PAREN  },
    146     { ")"  , RPAREN , PAREN  },
    147255};
    148256
     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
    149345
    150346#if ENABLE_FEATURE_TEST_64
    151 typedef int64_t arith_t;
     347typedef int64_t number_t;
    152348#else
    153 typedef int arith_t;
     349typedef int number_t;
    154350#endif
    155351
    156 /* Cannot eliminate these static data (do the G trick)
    157  * because of test_main usage from other applets */
    158 static char **t_wp;
    159 static struct t_op const *t_wp_op;
    160 static gid_t *group_array;
    161 static int ngroups;
    162 static jmp_buf leaving;
    163 
    164 static enum token t_lex(char *s);
    165 static arith_t oexpr(enum token n);
    166 static arith_t aexpr(enum token n);
    167 static arith_t nexpr(enum token n);
    168 static int binop(void);
    169 static arith_t primary(enum token n);
    170 static int filstat(char *nm, enum token mode);
    171 static arith_t getn(const char *s);
    172 /* UNUSED
    173 static int newerf(const char *f1, const char *f2);
    174 static int olderf(const char *f1, const char *f2);
    175 static int equalf(const char *f1, const char *f2);
    176 */
    177 static int test_eaccess(char *path, int mode);
    178 static int is_a_group_member(gid_t gid);
    179 static void initialize_group_array(void);
    180 
    181 int test_main(int argc, char **argv)
    182 {
    183     int res;
    184     const char *arg0;
    185     bool _off;
    186 
    187     arg0 = bb_basename(argv[0]);
    188     if (arg0[0] == '[') {
    189         --argc;
    190         if (!arg0[1]) { /* "[" ? */
    191             if (NOT_LONE_CHAR(argv[argc], ']')) {
    192                 bb_error_msg("missing ]");
    193                 return 2;
    194             }
    195         } else { /* assuming "[[" */
    196             if (strcmp(argv[argc], "]]") != 0) {
    197                 bb_error_msg("missing ]]");
    198                 return 2;
    199             }
    200         }
    201         argv[argc] = NULL;
    202     }
    203 
    204     res = setjmp(leaving);
    205     if (res)
    206         return res;
    207 
    208     /* resetting ngroups is probably unnecessary.  it will
    209      * force a new call to getgroups(), which prevents using
    210      * group data fetched during a previous call.  but the
    211      * only way the group data could be stale is if there's
    212      * been an intervening call to setgroups(), and this
    213      * isn't likely in the case of a shell.  paranoia
    214      * prevails...
    215      */
    216     ngroups = 0;
    217 
    218     /* Implement special cases from POSIX.2, section 4.62.4 */
    219     if (argc == 1)
    220         return 1;
    221     if (argc == 2)
    222         return *argv[1] == '\0';
    223 //assert(argc);
    224     /* remember if we saw argc==4 which wants *no* '!' test */
    225     _off = argc - 4;
    226     if (_off ?
    227         (LONE_CHAR(argv[1], '!'))
    228         : (argv[1][0] != '!' || argv[1][1] != '\0'))
    229     {
    230         if (argc == 3)
    231             return *argv[2] != '\0';
    232 
    233         t_lex(argv[2 + _off]);
    234         if (t_wp_op && t_wp_op->op_type == BINOP) {
    235             t_wp = &argv[1 + _off];
    236             return binop() == _off;
    237         }
    238     }
    239     t_wp = &argv[1];
    240     res = !oexpr(t_lex(*t_wp));
    241 
    242     if (*t_wp != NULL && *++t_wp != NULL) {
    243         bb_error_msg("%s: unknown operand", *t_wp);
    244         return 2;
    245     }
    246     return res;
    247 }
    248 
    249 static void syntax(const char *op, const char *msg) ATTRIBUTE_NORETURN;
     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;
    250385static void syntax(const char *op, const char *msg)
    251386{
     
    258393}
    259394
    260 static arith_t oexpr(enum token n)
    261 {
    262     arith_t res;
    263 
    264     res = aexpr(n);
    265     if (t_lex(*++t_wp) == BOR) {
    266         return oexpr(t_lex(*++t_wp)) || res;
    267     }
    268     t_wp--;
    269     return res;
    270 }
    271 
    272 static arith_t aexpr(enum token n)
    273 {
    274     arith_t res;
    275 
    276     res = nexpr(n);
    277     if (t_lex(*++t_wp) == BAND)
    278         return aexpr(t_lex(*++t_wp)) && res;
    279     t_wp--;
    280     return res;
    281 }
    282 
    283 static arith_t nexpr(enum token n)
    284 {
    285     if (n == UNOT)
    286         return !nexpr(t_lex(*++t_wp));
    287     return primary(n);
    288 }
    289 
    290 static arith_t primary(enum token n)
    291 {
    292     arith_t res;
    293 
    294     if (n == EOI) {
    295         syntax(NULL, "argument expected");
    296     }
    297     if (n == LPAREN) {
    298         res = oexpr(t_lex(*++t_wp));
    299         if (t_lex(*++t_wp) != RPAREN)
    300             syntax(NULL, "closing paren expected");
    301         return res;
    302     }
    303     if (t_wp_op && t_wp_op->op_type == UNOP) {
    304         /* unary expression */
    305         if (*++t_wp == NULL)
    306             syntax(t_wp_op->op_text, "argument expected");
    307         if (n == STREZ)
    308             return t_wp[0][0] == '\0';
    309         if (n == STRNZ)
    310             return t_wp[0][0] != '\0';
    311         if (n == FILTT)
    312             return isatty(getn(*t_wp));
    313         return filstat(*t_wp, n);
    314     }
    315 
    316     t_lex(t_wp[1]);
    317     if (t_wp_op && t_wp_op->op_type == BINOP) {
    318         return binop();
    319     }
    320 
    321     return t_wp[0][0] != '\0';
    322 }
     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
    323468
    324469static int binop(void)
    325470{
    326471    const char *opnd1, *opnd2;
    327     struct t_op const *op;
    328     arith_t val1, val2;
    329 
    330     opnd1 = *t_wp;
    331     (void) t_lex(*++t_wp);
    332     op = t_wp_op;
    333 
    334     opnd2 = *++t_wp;
     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;
    335480    if (opnd2 == NULL)
    336         syntax(op->op_text, "argument expected");
     481        syntax(args[-1], "argument expected");
    337482
    338483    if (is_int_op(op->op_num)) {
     
    349494        if (op->op_num == INTLE)
    350495            return val1 <= val2;
    351         if (op->op_num == INTLT)
    352             return val1 <  val2;
     496        /*if (op->op_num == INTLT)*/
     497        return val1 <  val2;
    353498    }
    354499    if (is_str_op(op->op_num)) {
     
    360505        if (op->op_num == STRLT)
    361506            return val1 < 0;
    362         if (op->op_num == STRGT)
    363             return val1 > 0;
     507        /*if (op->op_num == STRGT)*/
     508        return val1 > 0;
    364509    }
    365510    /* We are sure that these three are by now the only binops we didn't check
     
    376521        if (op->op_num == FILOT)
    377522            return b1.st_mtime < b2.st_mtime;
    378         if (op->op_num == FILEQ)
    379             return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
    380     }
    381     return 1; /* NOTREACHED */
    382 }
     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
    383606
    384607static int filstat(char *nm, enum token mode)
    385608{
    386609    struct stat s;
    387     int i = i; /* gcc 3.x thinks it can be used uninitialized */
     610    unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
    388611
    389612    if (mode == FILSYM) {
     
    454677}
    455678
    456 static enum token t_lex(char *s)
    457 {
    458     const struct t_op *op;
    459 
    460     t_wp_op = NULL;
    461     if (s == NULL) {
    462         return EOI;
    463     }
    464 
    465     op = ops;
    466     do {
    467         if (strcmp(s, op->op_text) == 0) {
    468             t_wp_op = op;
    469             return op->op_num;
     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;
    470692        }
    471         op++;
    472     } while (op < ops + ARRAY_SIZE(ops));
    473 
    474     return OPERAND;
    475 }
    476 
    477 /* atoi with error detection */
    478 //XXX: FIXME: duplicate of existing libbb function?
    479 static arith_t getn(const char *s)
    480 {
    481     char *p;
    482 #if ENABLE_FEATURE_TEST_64
    483     long long r;
     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 */
    484745#else
    485     long r;
     746    number_t res;
    486747#endif
    487 
    488     errno = 0;
    489 #if ENABLE_FEATURE_TEST_64
    490     r = strtoll(s, &p, 10);
    491 #else
    492     r = strtol(s, &p, 10);
     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    }
    493876#endif
    494 
    495     if (errno != 0)
    496         syntax(s, "out of range");
    497 
    498     if (*(skip_whitespace(p)))
    499         syntax(s, "bad number");
    500 
    501     return r;
    502 }
    503 
    504 /* UNUSED
    505 static int newerf(const char *f1, const char *f2)
    506 {
    507     struct stat b1, b2;
    508 
    509     return (stat(f1, &b1) == 0 &&
    510             stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
    511 }
    512 
    513 static int olderf(const char *f1, const char *f2)
    514 {
    515     struct stat b1, b2;
    516 
    517     return (stat(f1, &b1) == 0 &&
    518             stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
    519 }
    520 
    521 static int equalf(const char *f1, const char *f2)
    522 {
    523     struct stat b1, b2;
    524 
    525     return (stat(f1, &b1) == 0 &&
    526             stat(f2, &b2) == 0 &&
    527             b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
    528 }
    529 */
    530 
    531 /* Do the same thing access(2) does, but use the effective uid and gid,
    532    and don't make the mistake of telling root that any file is
    533    executable. */
    534 static int test_eaccess(char *path, int mode)
    535 {
    536     struct stat st;
    537     unsigned int euid = geteuid();
    538 
    539     if (stat(path, &st) < 0)
    540         return -1;
    541 
    542     if (euid == 0) {
    543         /* Root can read or write any file. */
    544         if (mode != X_OK)
    545             return 0;
    546 
    547         /* Root can execute any file that has any one of the execute
    548            bits set. */
    549         if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
    550             return 0;
    551     }
    552 
    553     if (st.st_uid == euid)  /* owner */
    554         mode <<= 6;
    555     else if (is_a_group_member(st.st_gid))
    556         mode <<= 3;
    557 
    558     if (st.st_mode & mode)
    559         return 0;
    560 
    561     return -1;
    562 }
    563 
    564 static void initialize_group_array(void)
    565 {
    566     ngroups = getgroups(0, NULL);
    567     if (ngroups > 0) {
    568         /* FIXME: ash tries so hard to not die on OOM,
    569          * and we spoil it with just one xrealloc here */
    570         /* We realloc, because test_main can be entered repeatedly by shell.
    571          * Testcase (ash): 'while true; do test -x some_file; done'
    572          * and watch top. (some_file must have owner != you) */
    573         group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
    574         getgroups(ngroups, group_array);
    575     }
    576 }
    577 
    578 /* Return non-zero if GID is one that we have in our groups list. */
    579 //XXX: FIXME: duplicate of existing libbb function?
    580 // see toplevel TODO file:
    581 // possible code duplication ingroup() and is_a_group_member()
    582 static int is_a_group_member(gid_t gid)
    583 {
    584     int i;
    585 
    586     /* Short-circuit if possible, maybe saving a call to getgroups(). */
    587     if (gid == getgid() || gid == getegid())
    588         return 1;
    589 
    590     if (ngroups == 0)
    591         initialize_group_array();
    592 
    593     /* Search through the list looking for GID. */
    594     for (i = 0; i < ngroups; i++)
    595         if (gid == group_array[i])
    596             return 1;
    597 
    598     return 0;
    599 }
     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 TracChangeset for help on using the changeset viewer.