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