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