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