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 *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        (*(struct test_statics**)not_const_pp(&test_ptr_to_statics)) = xzalloc(sizeof(S)); \
 450        barrier(); \
 451} while (0)
 452#define DEINIT_S() do { \
 453        free(group_array); \
 454        free(test_ptr_to_statics); \
 455} while (0)
 456
 457static number_t primary(enum token n);
 458
 459static void syntax(const char *op, const char *msg) NORETURN;
 460static void syntax(const char *op, const char *msg)
 461{
 462        if (op && *op) {
 463                bb_error_msg("%s: %s", op, msg);
 464        } else {
 465                bb_error_msg("%s: %s"+4, msg);
 466        }
 467        longjmp(leaving, 2);
 468}
 469
 470/* atoi with error detection */
 471//XXX: FIXME: duplicate of existing libbb function?
 472static number_t getn(const char *s)
 473{
 474        char *p;
 475#if ENABLE_FEATURE_TEST_64
 476        long long r;
 477#else
 478        long r;
 479#endif
 480
 481        errno = 0;
 482#if ENABLE_FEATURE_TEST_64
 483        r = strtoll(s, &p, 10);
 484#else
 485        r = strtol(s, &p, 10);
 486#endif
 487
 488        if (errno != 0)
 489                syntax(s, "out of range");
 490
 491        if (p == s || *(skip_whitespace(p)) != '\0')
 492                syntax(s, "bad number");
 493
 494        return r;
 495}
 496
 497/* UNUSED
 498static int newerf(const char *f1, const char *f2)
 499{
 500        struct stat b1, b2;
 501
 502        return (stat(f1, &b1) == 0 &&
 503                        stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
 504}
 505
 506static int olderf(const char *f1, const char *f2)
 507{
 508        struct stat b1, b2;
 509
 510        return (stat(f1, &b1) == 0 &&
 511                        stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
 512}
 513
 514static int equalf(const char *f1, const char *f2)
 515{
 516        struct stat b1, b2;
 517
 518        return (stat(f1, &b1) == 0 &&
 519                        stat(f2, &b2) == 0 &&
 520                        b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
 521}
 522*/
 523
 524
 525static enum token check_operator(const char *s)
 526{
 527        static const struct operator_t no_op = {
 528                .op_num = -1,
 529                .op_type = -1
 530        };
 531        int n;
 532
 533        last_operator = &no_op;
 534        if (s == NULL)
 535                return EOI;
 536        n = index_in_strings(ops_texts, s);
 537        if (n < 0)
 538                return OPERAND;
 539
 540#if BASH_TEST2
 541        if (ops_table[n].op_num == REGEX && !bash_test2) {
 542                /* =~ is only for [[ ]] */
 543                return OPERAND;
 544        }
 545        if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
 546                /* [ ]   accepts -a and -o but not && and || */
 547                /* [[ ]] accepts && and || but not -a and -o */
 548                if (bash_test2 == (s[0] == '-'))
 549                        return OPERAND;
 550        }
 551#endif
 552
 553        last_operator = &ops_table[n];
 554        return ops_table[n].op_num;
 555}
 556
 557
 558static int binop(void)
 559{
 560        const char *opnd1, *opnd2;
 561        const struct operator_t *op;
 562        number_t val1, val2;
 563
 564        opnd1 = *args;
 565        check_operator(*++args);
 566        op = last_operator;
 567
 568        opnd2 = *++args;
 569        if (opnd2 == NULL)
 570                syntax(args[-1], "argument expected");
 571
 572        if (is_int_op(op->op_num)) {
 573                val1 = getn(opnd1);
 574                val2 = getn(opnd2);
 575                if (op->op_num == INTEQ)
 576                        return val1 == val2;
 577                if (op->op_num == INTNE)
 578                        return val1 != val2;
 579                if (op->op_num == INTGE)
 580                        return val1 >= val2;
 581                if (op->op_num == INTGT)
 582                        return val1 >  val2;
 583                if (op->op_num == INTLE)
 584                        return val1 <= val2;
 585                /*if (op->op_num == INTLT)*/
 586                return val1 <  val2;
 587        }
 588#if BASH_TEST2
 589        if (bash_test2) {
 590                if (op->op_num == STREQ) {
 591                        val1 = fnmatch(opnd2, opnd1, 0);
 592                        return val1 == 0;
 593                }
 594                if (op->op_num == STRNE) {
 595                        val1 = fnmatch(opnd2, opnd1, 0);
 596                        return val1 != 0;
 597                }
 598                if (op->op_num == REGEX) {
 599                        regex_t re_buffer;
 600                        memset(&re_buffer, 0, sizeof(re_buffer));
 601                        if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE?
 602                                /* Bad regex */
 603                                longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
 604                        }
 605                        val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
 606                        regfree(&re_buffer);
 607                        return val1 == 0;
 608                }
 609        }
 610#endif
 611        if (is_str_op(op->op_num)) {
 612                val1 = strcmp(opnd1, opnd2);
 613                if (op->op_num == STREQ)
 614                        return val1 == 0;
 615                if (op->op_num == STRNE)
 616                        return val1 != 0;
 617                if (op->op_num == STRLT)
 618                        return val1 < 0;
 619                /*if (op->op_num == STRGT)*/
 620                return val1 > 0;
 621        }
 622        /* We are sure that these three are by now the only binops we didn't check
 623         * yet, so we do not check if the class is correct:
 624         */
 625/*      if (is_file_op(op->op_num)) */
 626        {
 627                struct stat b1, b2;
 628
 629                if (stat(opnd1, &b1) || stat(opnd2, &b2))
 630                        return 0; /* false, since at least one stat failed */
 631                if (op->op_num == FILNT)
 632                        return b1.st_mtime > b2.st_mtime;
 633                if (op->op_num == FILOT)
 634                        return b1.st_mtime < b2.st_mtime;
 635                /*if (op->op_num == FILEQ)*/
 636                return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
 637        }
 638        /*return 1; - NOTREACHED */
 639}
 640
 641static void initialize_group_array(void)
 642{
 643        group_array = bb_getgroups(&ngroups, NULL);
 644}
 645
 646/* Return non-zero if GID is one that we have in our groups list. */
 647//XXX: FIXME: duplicate of existing libbb function?
 648// see toplevel TODO file:
 649// possible code duplication ingroup() and is_a_group_member()
 650static int is_a_group_member(gid_t gid)
 651{
 652        int i;
 653
 654        /* Short-circuit if possible, maybe saving a call to getgroups(). */
 655        if (gid == getgid() || gid == getegid())
 656                return 1;
 657
 658        if (ngroups == 0)
 659                initialize_group_array();
 660
 661        /* Search through the list looking for GID. */
 662        for (i = 0; i < ngroups; i++)
 663                if (gid == group_array[i])
 664                        return 1;
 665
 666        return 0;
 667}
 668
 669
 670/* Do the same thing access(2) does, but use the effective uid and gid,
 671   and don't make the mistake of telling root that any file is
 672   executable. */
 673static int test_eaccess(struct stat *st, int mode)
 674{
 675        unsigned int euid = geteuid();
 676
 677        if (euid == 0) {
 678                /* Root can read or write any file. */
 679                if (mode != X_OK)
 680                        return 0;
 681
 682                /* Root can execute any file that has any one of the execute
 683                 * bits set. */
 684                if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
 685                        return 0;
 686        }
 687
 688        if (st->st_uid == euid)  /* owner */
 689                mode <<= 6;
 690        else if (is_a_group_member(st->st_gid))
 691                mode <<= 3;
 692
 693        if (st->st_mode & mode)
 694                return 0;
 695
 696        return -1;
 697}
 698
 699
 700static int filstat(char *nm, enum token mode)
 701{
 702        struct stat s;
 703        unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
 704
 705        if (mode == FILSYM) {
 706#ifdef S_IFLNK
 707                if (lstat(nm, &s) == 0) {
 708                        i = S_IFLNK;
 709                        goto filetype;
 710                }
 711#endif
 712                return 0;
 713        }
 714
 715        if (stat(nm, &s) != 0)
 716                return 0;
 717        if (mode == FILEXIST)
 718                return 1;
 719        if (is_file_access(mode)) {
 720                if (mode == FILRD)
 721                        i = R_OK;
 722                if (mode == FILWR)
 723                        i = W_OK;
 724                if (mode == FILEX)
 725                        i = X_OK;
 726                return test_eaccess(&s, i) == 0;
 727        }
 728        if (is_file_type(mode)) {
 729                if (mode == FILREG)
 730                        i = S_IFREG;
 731                if (mode == FILDIR)
 732                        i = S_IFDIR;
 733                if (mode == FILCDEV)
 734                        i = S_IFCHR;
 735                if (mode == FILBDEV)
 736                        i = S_IFBLK;
 737                if (mode == FILFIFO) {
 738#ifdef S_IFIFO
 739                        i = S_IFIFO;
 740#else
 741                        return 0;
 742#endif
 743                }
 744                if (mode == FILSOCK) {
 745#ifdef S_IFSOCK
 746                        i = S_IFSOCK;
 747#else
 748                        return 0;
 749#endif
 750                }
 751 filetype:
 752                return ((s.st_mode & S_IFMT) == i);
 753        }
 754        if (is_file_bit(mode)) {
 755                if (mode == FILSUID)
 756                        i = S_ISUID;
 757                if (mode == FILSGID)
 758                        i = S_ISGID;
 759                if (mode == FILSTCK)
 760                        i = S_ISVTX;
 761                return ((s.st_mode & i) != 0);
 762        }
 763        if (mode == FILGZ)
 764                return s.st_size > 0L;
 765        if (mode == FILUID)
 766                return s.st_uid == geteuid();
 767        if (mode == FILGID)
 768                return s.st_gid == getegid();
 769        return 1; /* NOTREACHED */
 770}
 771
 772
 773static number_t nexpr(enum token n)
 774{
 775        number_t res;
 776
 777        nest_msg(">nexpr(%s)\n", TOKSTR[n]);
 778        if (n == UNOT) {
 779                n = check_operator(*++args);
 780                if (n == EOI) {
 781                        /* special case: [ ! ], [ a -a ! ] are valid */
 782                        /* IOW, "! ARG" may miss ARG */
 783                        args--;
 784                        unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
 785                        return 1;
 786                }
 787                res = !nexpr(n);
 788                unnest_msg("<nexpr:%lld\n", res);
 789                return res;
 790        }
 791        res = primary(n);
 792        unnest_msg("<nexpr:%lld\n", res);
 793        return res;
 794}
 795
 796
 797static number_t aexpr(enum token n)
 798{
 799        number_t res;
 800
 801        nest_msg(">aexpr(%s)\n", TOKSTR[n]);
 802        res = nexpr(n);
 803        dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
 804        if (check_operator(*++args) == BAND) {
 805                dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
 806                res = aexpr(check_operator(*++args)) && res;
 807                unnest_msg("<aexpr:%lld\n", res);
 808                return res;
 809        }
 810        args--;
 811        unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
 812        return res;
 813}
 814
 815
 816static number_t oexpr(enum token n)
 817{
 818        number_t res;
 819
 820        nest_msg(">oexpr(%s)\n", TOKSTR[n]);
 821        res = aexpr(n);
 822        dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
 823        if (check_operator(*++args) == BOR) {
 824                dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
 825                res = oexpr(check_operator(*++args)) || res;
 826                unnest_msg("<oexpr:%lld\n", res);
 827                return res;
 828        }
 829        args--;
 830        unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
 831        return res;
 832}
 833
 834
 835static number_t primary(enum token n)
 836{
 837#if TEST_DEBUG
 838        number_t res = res; /* for compiler */
 839#else
 840        number_t res;
 841#endif
 842        const struct operator_t *args0_op;
 843
 844        nest_msg(">primary(%s)\n", TOKSTR[n]);
 845        if (n == EOI) {
 846                syntax(NULL, "argument expected");
 847        }
 848        if (n == LPAREN) {
 849                res = oexpr(check_operator(*++args));
 850                if (check_operator(*++args) != RPAREN)
 851                        syntax(NULL, "closing paren expected");
 852                unnest_msg("<primary:%lld\n", res);
 853                return res;
 854        }
 855
 856        /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
 857         * do the same */
 858        args0_op = last_operator;
 859        /* last_operator = operator at args[1] */
 860        if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
 861                if (args[2]) {
 862                        // coreutils also does this:
 863                        // if (args[3] && args[0]="-l" && args[2] is BINOP)
 864                        //      return binop(1 /* prepended by -l */);
 865                        if (last_operator->op_type == BINOP)
 866                                unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
 867                }
 868        }
 869        /* check "is args[0] unop?" second */
 870        if (args0_op->op_type == UNOP) {
 871                /* unary expression */
 872                if (args[1] == NULL)
 873//                      syntax(args0_op->op_text, "argument expected");
 874                        goto check_emptiness;
 875                args++;
 876                if (n == STREZ)
 877                        unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
 878                if (n == STRNZ)
 879                        unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
 880                if (n == FILTT)
 881                        unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
 882                unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
 883        }
 884
 885        /*check_operator(args[1]); - already done */
 886        if (last_operator->op_type == BINOP) {
 887                /* args[2] is known to be NULL, isn't it bound to fail? */
 888                unnest_msg_and_return(binop(), "<primary:%lld\n");
 889        }
 890 check_emptiness:
 891        unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
 892}
 893
 894
 895int test_main(int argc, char **argv)
 896{
 897        int res;
 898        const char *arg0;
 899#if BASH_TEST2
 900        bool bt2 = 0;
 901#endif
 902
 903        arg0 = bb_basename(argv[0]);
 904        if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
 905         && (arg0[0] == '[')
 906        ) {
 907                --argc;
 908                if (!arg0[1]) { /* "[" ? */
 909                        if (NOT_LONE_CHAR(argv[argc], ']')) {
 910                                bb_simple_error_msg("missing ]");
 911                                return 2;
 912                        }
 913                } else { /* assuming "[[" */
 914                        if (strcmp(argv[argc], "]]") != 0) {
 915                                bb_simple_error_msg("missing ]]");
 916                                return 2;
 917                        }
 918#if BASH_TEST2
 919                        bt2 = 1;
 920#endif
 921                }
 922                argv[argc] = NULL;
 923        }
 924        /* argc is unused after this point */
 925
 926        /* We must do DEINIT_S() prior to returning */
 927        INIT_S();
 928
 929#if BASH_TEST2
 930        bash_test2 = bt2;
 931#endif
 932
 933        res = setjmp(leaving);
 934        if (res)
 935                goto ret;
 936
 937        /* resetting ngroups is probably unnecessary.  it will
 938         * force a new call to getgroups(), which prevents using
 939         * group data fetched during a previous call.  but the
 940         * only way the group data could be stale is if there's
 941         * been an intervening call to setgroups(), and this
 942         * isn't likely in the case of a shell.  paranoia
 943         * prevails...
 944         */
 945        /*ngroups = 0; - done by INIT_S() */
 946
 947        argv++;
 948        args = argv;
 949
 950        /* Implement special cases from POSIX.2, section 4.62.4.
 951         * Testcase: "test '(' = '('"
 952         * The general parser would misinterpret '(' as group start.
 953         */
 954        if (1) {
 955                int negate = 0;
 956 again:
 957                if (!argv[0]) {
 958                        /* "test" */
 959                        res = 1;
 960                        goto ret_special;
 961                }
 962                if (!argv[1]) {
 963                        /* "test [!] arg" */
 964                        res = (argv[0][0] == '\0');
 965                        goto ret_special;
 966                }
 967                if (argv[2]) {
 968                        if (!argv[3]) {
 969                                /*
 970                                 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
 971                                 * """ 3 arguments:
 972                                 * If $2 is a binary primary, perform the binary test of $1 and $3.
 973                                 * """
 974                                 */
 975                                check_operator(argv[1]);
 976                                if (last_operator->op_type == BINOP) {
 977                                        /* "test [!] arg1 <binary_op> arg2" */
 978                                        args = argv;
 979                                        res = (binop() == 0);
 980 ret_special:
 981                                        /* If there was leading "!" op... */
 982                                        res ^= negate;
 983                                        goto ret;
 984                                }
 985                                /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
 986                                 * Looks like this works without additional coding.
 987                                 */
 988                                goto check_negate;
 989                        }
 990                        /* argv[3] exists (at least 4 args), is it exactly 4 args? */
 991                        if (!argv[4]) {
 992                                /*
 993                                 * """ 4 arguments:
 994                                 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
 995                                 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
 996                                 * """
 997                                 * Example why code below is necessary: test '(' ! -e ')'
 998                                 */
 999                                if (LONE_CHAR(argv[0], '(')
1000                                 && LONE_CHAR(argv[3], ')')
1001                                ) {
1002                                        /* "test [!] ( x y )" */
1003                                        argv[3] = NULL;
1004                                        argv++;
1005                                }
1006                        }
1007                }
1008 check_negate:
1009                if (LONE_CHAR(argv[0], '!')) {
1010                        argv++;
1011                        negate ^= 1;
1012                        goto again;
1013                }
1014        }
1015
1016        res = !oexpr(check_operator(*args));
1017
1018        if (*args != NULL && *++args != NULL) {
1019                /* Examples:
1020                 * test 3 -lt 5 6
1021                 * test -t 1 2
1022                 */
1023                bb_error_msg("%s: unknown operand", *args);
1024                res = 2;
1025        }
1026 ret:
1027        DEINIT_S();
1028        return res;
1029}
1030