X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=coreutils%2Ftest.c;h=df8387b1422c0a486b64ff35da16dc6923dd9111;hb=b6adbf1be29841501cc49917249e85f273e1df7c;hp=d7bd5a0ded597da91016e8e9b970b3a794c0471f;hpb=c1ef7bdd8d002ae0889efcf883d0e1b7faa938d4;p=oweals%2Fbusybox.git diff --git a/coreutils/test.c b/coreutils/test.c index d7bd5a0de..df8387b14 100644 --- a/coreutils/test.c +++ b/coreutils/test.c @@ -4,46 +4,33 @@ * * Copyright (c) by a whole pile of folks: * - * test(1); version 7-like -- author Erik Baalbergen - * modified by Eric Gisin to be used as built-in. - * modified by Arnold Robbins to add SVR3 compatibility - * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). - * modified by J.T. Conklin for NetBSD. - * modified by Herbert Xu to be used as built-in in ash. - * modified by Erik Andersen to be used - * in busybox. + * test(1); version 7-like -- author Erik Baalbergen + * modified by Eric Gisin to be used as built-in. + * modified by Arnold Robbins to add SVR3 compatibility + * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). + * modified by J.T. Conklin for NetBSD. + * modified by Herbert Xu to be used as built-in in ash. + * modified by Erik Andersen to be used + * in busybox. + * modified by Bernhard Fischer to be useable (i.e. a bit less bloaty). * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. * * Original copyright notice states: - * "This program is in the Public Domain." + * "This program is in the Public Domain." */ -#include -#include -#include -#include -#include -#include -#include "busybox.h" +#include "libbb.h" +#include + +/* This is a NOEXEC applet. Be very careful! */ + /* test(1) accepts the following grammar: oexpr ::= aexpr | aexpr "-o" oexpr ; aexpr ::= nexpr | nexpr "-a" aexpr ; nexpr ::= primary | "!" primary - primary ::= unary-operator operand + primary ::= unary-operator operand | operand binary-operator operand | operand | "(" oexpr ")" @@ -98,7 +85,12 @@ enum token { RPAREN, OPERAND }; - +#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5) +#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5) +#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2) +#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2) +#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) +#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) enum token_types { UNOP, BINOP, @@ -108,64 +100,67 @@ enum token_types { }; static const struct t_op { - const char *op_text; - short op_num, op_type; + char op_text[4]; + unsigned char op_num, op_type; } ops[] = { - { - "-r", FILRD, UNOP}, { - "-w", FILWR, UNOP}, { - "-x", FILEX, UNOP}, { - "-e", FILEXIST, UNOP}, { - "-f", FILREG, UNOP}, { - "-d", FILDIR, UNOP}, { - "-c", FILCDEV, UNOP}, { - "-b", FILBDEV, UNOP}, { - "-p", FILFIFO, UNOP}, { - "-u", FILSUID, UNOP}, { - "-g", FILSGID, UNOP}, { - "-k", FILSTCK, UNOP}, { - "-s", FILGZ, UNOP}, { - "-t", FILTT, UNOP}, { - "-z", STREZ, UNOP}, { - "-n", STRNZ, UNOP}, { - "-h", FILSYM, UNOP}, /* for backwards compat */ - { - "-O", FILUID, UNOP}, { - "-G", FILGID, UNOP}, { - "-L", FILSYM, UNOP}, { - "-S", FILSOCK, UNOP}, { - "=", STREQ, BINOP}, { - "==", STREQ, BINOP}, { - "!=", STRNE, BINOP}, { - "<", STRLT, BINOP}, { - ">", STRGT, BINOP}, { - "-eq", INTEQ, BINOP}, { - "-ne", INTNE, BINOP}, { - "-ge", INTGE, BINOP}, { - "-gt", INTGT, BINOP}, { - "-le", INTLE, BINOP}, { - "-lt", INTLT, BINOP}, { - "-nt", FILNT, BINOP}, { - "-ot", FILOT, BINOP}, { - "-ef", FILEQ, BINOP}, { - "!", UNOT, BUNOP}, { - "-a", BAND, BBINOP}, { - "-o", BOR, BBINOP}, { - "(", LPAREN, PAREN}, { - ")", RPAREN, PAREN}, { - 0, 0, 0} + { "-r", FILRD , UNOP }, + { "-w", FILWR , UNOP }, + { "-x", FILEX , UNOP }, + { "-e", FILEXIST, UNOP }, + { "-f", FILREG , UNOP }, + { "-d", FILDIR , UNOP }, + { "-c", FILCDEV , UNOP }, + { "-b", FILBDEV , UNOP }, + { "-p", FILFIFO , UNOP }, + { "-u", FILSUID , UNOP }, + { "-g", FILSGID , UNOP }, + { "-k", FILSTCK , UNOP }, + { "-s", FILGZ , UNOP }, + { "-t", FILTT , UNOP }, + { "-z", STREZ , UNOP }, + { "-n", STRNZ , UNOP }, + { "-h", FILSYM , UNOP }, /* for backwards compat */ + + { "-O" , FILUID , UNOP }, + { "-G" , FILGID , UNOP }, + { "-L" , FILSYM , UNOP }, + { "-S" , FILSOCK, UNOP }, + { "=" , STREQ , BINOP }, + { "==" , STREQ , BINOP }, + { "!=" , STRNE , BINOP }, + { "<" , STRLT , BINOP }, + { ">" , STRGT , BINOP }, + { "-eq", INTEQ , BINOP }, + { "-ne", INTNE , BINOP }, + { "-ge", INTGE , BINOP }, + { "-gt", INTGT , BINOP }, + { "-le", INTLE , BINOP }, + { "-lt", INTLT , BINOP }, + { "-nt", FILNT , BINOP }, + { "-ot", FILOT , BINOP }, + { "-ef", FILEQ , BINOP }, + { "!" , UNOT , BUNOP }, + { "-a" , BAND , BBINOP }, + { "-o" , BOR , BBINOP }, + { "(" , LPAREN , PAREN }, + { ")" , RPAREN , PAREN }, }; -#ifdef CONFIG_FEATURE_TEST_64 +enum { NUM_OPS = sizeof(ops) / sizeof(ops[0]) }; + +#if ENABLE_FEATURE_TEST_64 typedef int64_t arith_t; #else typedef int arith_t; #endif +/* Cannot eliminate these static data (do the G trick) + * because of bb_test usage from other applets */ static char **t_wp; static struct t_op const *t_wp_op; -static gid_t *group_array = NULL; +static gid_t *group_array; static int ngroups; +static jmp_buf leaving; static enum token t_lex(char *s); static arith_t oexpr(enum token n); @@ -175,73 +170,85 @@ static int binop(void); static arith_t primary(enum token n); static int filstat(char *nm, enum token mode); static arith_t getn(const char *s); +/* UNUSED static int newerf(const char *f1, const char *f2); static int olderf(const char *f1, const char *f2); static int equalf(const char *f1, const char *f2); -static void syntax(const char *op, const char *msg); +*/ static int test_eaccess(char *path, int mode); static int is_a_group_member(gid_t gid); static void initialize_group_array(void); -extern int test_main(int argc, char **argv) +int bb_test(int argc, char **argv) { int res; - if (strcmp(bb_applet_name, "[") == 0) { - if (strcmp(argv[--argc], "]")) - bb_error_msg_and_die("missing ]"); + if (LONE_CHAR(argv[0], '[')) { + --argc; + if (NOT_LONE_CHAR(argv[argc], ']')) { + bb_error_msg("missing ]"); + return 2; + } argv[argc] = NULL; - } - if (strcmp(bb_applet_name, "[[") == 0) { - if (strcmp(argv[--argc], "]]")) - bb_error_msg_and_die("missing ]]"); + } else if (strcmp(argv[0], "[[") == 0) { + --argc; + if (strcmp(argv[argc], "]]")) { + bb_error_msg("missing ]]"); + return 2; + } argv[argc] = NULL; } + + res = setjmp(leaving); + if (res) + return res; + + /* resetting ngroups is probably unnecessary. it will + * force a new call to getgroups(), which prevents using + * group data fetched during a previous call. but the + * only way the group data could be stale is if there's + * been an intervening call to setgroups(), and this + * isn't likely in the case of a shell. paranoia + * prevails... + */ + ngroups = 0; + /* Implement special cases from POSIX.2, section 4.62.4 */ - switch (argc) { - case 1: - exit(1); - case 2: - exit(*argv[1] == '\0'); - case 3: - if (argv[1][0] == '!' && argv[1][1] == '\0') { - exit(!(*argv[2] == '\0')); - } - break; - case 4: - if (argv[1][0] != '!' || argv[1][1] != '\0') { - if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) { - t_wp = &argv[1]; - exit(binop() == 0); - } - } - break; - case 5: - if (argv[1][0] == '!' && argv[1][1] == '\0') { - if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) { - t_wp = &argv[2]; - exit(!(binop() == 0)); - } + if (argc == 1) + return 1; + if (argc == 2) + return *argv[1] == '\0'; +//assert(argc); + if (LONE_CHAR(argv[1], '!')) { + bool _off; + if (argc == 3) + return *argv[2] != '\0'; + _off = argc - 4; + t_lex(argv[2 + _off]); + if (t_wp_op && t_wp_op->op_type == BINOP) { + t_wp = &argv[1 + _off]; + return binop() == 0; } - break; } - t_wp = &argv[1]; res = !oexpr(t_lex(*t_wp)); - if (*t_wp != NULL && *++t_wp != NULL) - syntax(*t_wp, "unknown operand"); - - return (res); + if (*t_wp != NULL && *++t_wp != NULL) { + bb_error_msg("%s: unknown operand", *t_wp); + return 2; + } + return res; } +static void syntax(const char *op, const char *msg) ATTRIBUTE_NORETURN; static void syntax(const char *op, const char *msg) { if (op && *op) { - bb_error_msg_and_die("%s: %s", op, msg); + bb_error_msg("%s: %s", op, msg); } else { - bb_error_msg_and_die("%s", msg); + bb_error_msg("%s", msg); } + longjmp(leaving, 2); } static arith_t oexpr(enum token n) @@ -291,73 +298,87 @@ static arith_t primary(enum token n) /* unary expression */ if (*++t_wp == NULL) syntax(t_wp_op->op_text, "argument expected"); - switch (n) { - case STREZ: - return strlen(*t_wp) == 0; - case STRNZ: - return strlen(*t_wp) != 0; - case FILTT: + if (n == STREZ) + return t_wp[0][0] == '\0'; + if (n == STRNZ) + return t_wp[0][0] != '\0'; + if (n == FILTT) return isatty(getn(*t_wp)); - default: - return filstat(*t_wp, n); - } + return filstat(*t_wp, n); } - if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { + t_lex(t_wp[1]); + if (t_wp_op && t_wp_op->op_type == BINOP) { return binop(); } - return strlen(*t_wp) > 0; + return t_wp[0][0] != '\0'; } static int binop(void) { const char *opnd1, *opnd2; struct t_op const *op; + smallint val1, val2; opnd1 = *t_wp; (void) t_lex(*++t_wp); op = t_wp_op; - if ((opnd2 = *++t_wp) == (char *) 0) + opnd2 = *++t_wp; + if (opnd2 == NULL) syntax(op->op_text, "argument expected"); - switch (op->op_num) { - case STREQ: - return strcmp(opnd1, opnd2) == 0; - case STRNE: - return strcmp(opnd1, opnd2) != 0; - case STRLT: - return strcmp(opnd1, opnd2) < 0; - case STRGT: - return strcmp(opnd1, opnd2) > 0; - case INTEQ: - return getn(opnd1) == getn(opnd2); - case INTNE: - return getn(opnd1) != getn(opnd2); - case INTGE: - return getn(opnd1) >= getn(opnd2); - case INTGT: - return getn(opnd1) > getn(opnd2); - case INTLE: - return getn(opnd1) <= getn(opnd2); - case INTLT: - return getn(opnd1) < getn(opnd2); - case FILNT: - return newerf(opnd1, opnd2); - case FILOT: - return olderf(opnd1, opnd2); - case FILEQ: - return equalf(opnd1, opnd2); + if (is_int_op(op->op_num)) { + val1 = getn(opnd1); + val2 = getn(opnd2); + if (op->op_num == INTEQ) + return val1 == val2; + if (op->op_num == INTNE) + return val1 != val2; + if (op->op_num == INTGE) + return val1 >= val2; + if (op->op_num == INTGT) + return val1 > val2; + if (op->op_num == INTLE) + return val1 <= val2; + if (op->op_num == INTLT) + return val1 < val2; + } + if (is_str_op(op->op_num)) { + val1 = strcmp(opnd1, opnd2); + if (op->op_num == STREQ) + return val1 == 0; + if (op->op_num == STRNE) + return val1 != 0; + if (op->op_num == STRLT) + return val1 < 0; + if (op->op_num == STRGT) + return val1 > 0; } - /* NOTREACHED */ - return 1; + /* We are sure that these three are by now the only binops we didn't check + * yet, so we do not check if the class is correct: + */ +/* if (is_file_op(op->op_num)) */ + { + struct stat b1, b2; + + if (!(!stat(opnd1, &b1) && !stat(opnd2, &b2))) + return 0; /* false, since stat failed */ + if (op->op_num == FILNT) + return b1.st_mtime > b2.st_mtime; + if (op->op_num == FILOT) + return b1.st_mtime < b2.st_mtime; + if (op->op_num == FILEQ) + return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; + } + return 1; /* NOTREACHED */ } static int filstat(char *nm, enum token mode) { struct stat s; - unsigned int i; + int i; if (mode == FILSYM) { #ifdef S_IFLNK @@ -371,114 +392,110 @@ static int filstat(char *nm, enum token mode) if (stat(nm, &s) != 0) return 0; - - switch (mode) { - case FILRD: - return test_eaccess(nm, R_OK) == 0; - case FILWR: - return test_eaccess(nm, W_OK) == 0; - case FILEX: - return test_eaccess(nm, X_OK) == 0; - case FILEXIST: + if (mode == FILEXIST) return 1; - case FILREG: - i = S_IFREG; - goto filetype; - case FILDIR: - i = S_IFDIR; - goto filetype; - case FILCDEV: - i = S_IFCHR; - goto filetype; - case FILBDEV: - i = S_IFBLK; - goto filetype; - case FILFIFO: + if (is_file_access(mode)) { + if (mode == FILRD) + i = R_OK; + if (mode == FILWR) + i = W_OK; + if (mode == FILEX) + i = X_OK; + return test_eaccess(nm, i) == 0; + } + if (is_file_type(mode)) { + if (mode == FILREG) + i = S_IFREG; + if (mode == FILDIR) + i = S_IFDIR; + if (mode == FILCDEV) + i = S_IFCHR; + if (mode == FILBDEV) + i = S_IFBLK; + if (mode == FILFIFO) { #ifdef S_IFIFO - i = S_IFIFO; - goto filetype; + i = S_IFIFO; #else - return 0; + return 0; #endif - case FILSOCK: + } + if (mode == FILSOCK) { #ifdef S_IFSOCK - i = S_IFSOCK; - goto filetype; + i = S_IFSOCK; #else - return 0; + return 0; #endif - case FILSUID: - i = S_ISUID; - goto filebit; - case FILSGID: - i = S_ISGID; - goto filebit; - case FILSTCK: - i = S_ISVTX; - goto filebit; - case FILGZ: + } + filetype: + return ((s.st_mode & S_IFMT) == i); + } + if (is_file_bit(mode)) { + if (mode == FILSUID) + i = S_ISUID; + if (mode == FILSGID) + i = S_ISGID; + if (mode == FILSTCK) + i = S_ISVTX; + return ((s.st_mode & i) != 0); + } + if (mode == FILGZ) return s.st_size > 0L; - case FILUID: + if (mode == FILUID) return s.st_uid == geteuid(); - case FILGID: + if (mode == FILGID) return s.st_gid == getegid(); - default: - return 1; - } - - filetype: - return ((s.st_mode & S_IFMT) == i); - - filebit: - return ((s.st_mode & i) != 0); + return 1; /* NOTREACHED */ } static enum token t_lex(char *s) { - struct t_op const *op = ops; + const struct t_op *op; - if (s == 0) { - t_wp_op = (struct t_op *) 0; + t_wp_op = NULL; + if (s == NULL) { return EOI; } - while (op->op_text) { + + op = ops; + do { if (strcmp(s, op->op_text) == 0) { t_wp_op = op; return op->op_num; } op++; - } - t_wp_op = (struct t_op *) 0; + } while (op < ops + NUM_OPS); + return OPERAND; } /* atoi with error detection */ +//XXX: FIXME: duplicate of existing libbb function? static arith_t getn(const char *s) { char *p; -#ifdef CONFIG_FEATURE_TEST_64 +#if ENABLE_FEATURE_TEST_64 long long r; #else long r; #endif errno = 0; -#ifdef CONFIG_FEATURE_TEST_64 +#if ENABLE_FEATURE_TEST_64 r = strtoll(s, &p, 10); #else r = strtol(s, &p, 10); #endif if (errno != 0) - bb_error_msg_and_die("%s: out of range", s); + syntax(s, "out of range"); - /* p = bb_skip_whitespace(p); avoid const warning */ - if (*(bb_skip_whitespace(p))) - bb_error_msg_and_die("%s: bad number", s); + if (*(skip_whitespace(p))) + syntax(s, "bad number"); return r; } +/* UNUSED static int newerf(const char *f1, const char *f2) { struct stat b1, b2; @@ -503,6 +520,7 @@ static int equalf(const char *f1, const char *f2) stat(f2, &b2) == 0 && b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); } +*/ /* Do the same thing access(2) does, but use the effective uid and gid, and don't make the mistake of telling root that any file is @@ -513,45 +531,50 @@ static int test_eaccess(char *path, int mode) unsigned int euid = geteuid(); if (stat(path, &st) < 0) - return (-1); + return -1; if (euid == 0) { /* Root can read or write any file. */ if (mode != X_OK) - return (0); + return 0; /* Root can execute any file that has any one of the execute bits set. */ if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) - return (0); + return 0; } - if (st.st_uid == euid) /* owner */ + if (st.st_uid == euid) /* owner */ mode <<= 6; else if (is_a_group_member(st.st_gid)) mode <<= 3; if (st.st_mode & mode) - return (0); + return 0; - return (-1); + return -1; } static void initialize_group_array(void) { ngroups = getgroups(0, NULL); - group_array = xrealloc(group_array, ngroups * sizeof(gid_t)); - getgroups(ngroups, group_array); + if (ngroups > 0) { + group_array = xmalloc(ngroups * sizeof(gid_t)); + getgroups(ngroups, group_array); + } } /* Return non-zero if GID is one that we have in our groups list. */ +//XXX: FIXME: duplicate of existing libbb function? +// see toplevel TODO file: +// possible code duplication ingroup() and is_a_group_member() static int is_a_group_member(gid_t gid) { - register int i; + int i; /* Short-circuit if possible, maybe saving a call to getgroups(). */ if (gid == getgid() || gid == getegid()) - return (1); + return 1; if (ngroups == 0) initialize_group_array(); @@ -559,7 +582,16 @@ static int is_a_group_member(gid_t gid) /* Search through the list looking for GID. */ for (i = 0; i < ngroups; i++) if (gid == group_array[i]) - return (1); + return 1; + + return 0; +} - return (0); + +/* applet entry point */ + +int test_main(int argc, char **argv); +int test_main(int argc, char **argv) +{ + return bb_test(argc, argv); }