diff --git a/coreutils/test.c b/coreutils/test.c index 3c57cf418..0b94100c1 100644 --- a/coreutils/test.c +++ b/coreutils/test.c @@ -161,90 +161,7 @@ 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); -static arith_t aexpr(enum token n); -static arith_t nexpr(enum token n); -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 int test_eaccess(char *path, int mode); -static int is_a_group_member(gid_t gid); -static void initialize_group_array(void); - -int test_main(int argc, char **argv) -{ - int res; - const char *arg0; - bool _off; - - arg0 = bb_basename(argv[0]); - if (arg0[0] == '[') { - --argc; - if (!arg0[1]) { /* "[" ? */ - if (NOT_LONE_CHAR(argv[argc], ']')) { - bb_error_msg("missing ]"); - return 2; - } - } else { /* assuming "[[" */ - if (strcmp(argv[argc], "]]") != 0) { - 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 */ - if (argc == 1) - return 1; - if (argc == 2) - return *argv[1] == '\0'; -//assert(argc); - /* remember if we saw argc==4 which wants *no* '!' test */ - _off = argc - 4; - if (_off ? - (LONE_CHAR(argv[1], '!')) - : (argv[1][0] != '!' || argv[1][1] != '\0')) - { - if (argc == 3) - return *argv[2] != '\0'; - - t_lex(argv[2 + _off]); - if (t_wp_op && t_wp_op->op_type == BINOP) { - t_wp = &argv[1 + _off]; - return binop() == _off; - } - } - t_wp = &argv[1]; - res = !oexpr(t_lex(*t_wp)); - - 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) @@ -257,69 +174,82 @@ static void syntax(const char *op, const char *msg) longjmp(leaving, 2); } -static arith_t oexpr(enum token n) +/* atoi with error detection */ +//XXX: FIXME: duplicate of existing libbb function? +static arith_t getn(const char *s) { - arith_t res; + char *p; +#if ENABLE_FEATURE_TEST_64 + long long r; +#else + long r; +#endif - res = aexpr(n); - if (t_lex(*++t_wp) == BOR) { - return oexpr(t_lex(*++t_wp)) || res; - } - t_wp--; - return res; + errno = 0; +#if ENABLE_FEATURE_TEST_64 + r = strtoll(s, &p, 10); +#else + r = strtol(s, &p, 10); +#endif + + if (errno != 0) + syntax(s, "out of range"); + + if (*(skip_whitespace(p))) + syntax(s, "bad number"); + + return r; } -static arith_t aexpr(enum token n) +/* UNUSED +static int newerf(const char *f1, const char *f2) { - arith_t res; + struct stat b1, b2; - res = nexpr(n); - if (t_lex(*++t_wp) == BAND) - return aexpr(t_lex(*++t_wp)) && res; - t_wp--; - return res; + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); } -static arith_t nexpr(enum token n) +static int olderf(const char *f1, const char *f2) { - if (n == UNOT) - return !nexpr(t_lex(*++t_wp)); - return primary(n); + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); } -static arith_t primary(enum token n) +static int equalf(const char *f1, const char *f2) { - arith_t res; + struct stat b1, b2; - if (n == EOI) { - syntax(NULL, "argument expected"); - } - if (n == LPAREN) { - res = oexpr(t_lex(*++t_wp)); - if (t_lex(*++t_wp) != RPAREN) - syntax(NULL, "closing paren expected"); - return res; - } - if (t_wp_op && t_wp_op->op_type == UNOP) { - /* unary expression */ - if (*++t_wp == NULL) - syntax(t_wp_op->op_text, "argument expected"); - 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)); - return filstat(*t_wp, n); - } - - t_lex(t_wp[1]); - if (t_wp_op && t_wp_op->op_type == BINOP) { - return binop(); - } - - return t_wp[0][0] != '\0'; + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && + b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); } +*/ + + +static enum token t_lex(char *s) +{ + const struct t_op *op; + + t_wp_op = NULL; + if (s == NULL) { + return EOI; + } + + op = ops; + do { + if (strcmp(s, op->op_text) == 0) { + t_wp_op = op; + return op->op_num; + } + op++; + } while (op < ops + ARRAY_SIZE(ops)); + + return OPERAND; +} + static int binop(void) { @@ -381,6 +311,80 @@ static int binop(void) return 1; /* NOTREACHED */ } + +static void initialize_group_array(void) +{ + ngroups = getgroups(0, NULL); + if (ngroups > 0) { + /* FIXME: ash tries so hard to not die on OOM, + * and we spoil it with just one xrealloc here */ + /* We realloc, because test_main can be entered repeatedly by shell. + * Testcase (ash): 'while true; do test -x some_file; done' + * and watch top. (some_file must have owner != you) */ + group_array = xrealloc(group_array, 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) +{ + int i; + + /* Short-circuit if possible, maybe saving a call to getgroups(). */ + if (gid == getgid() || gid == getegid()) + return 1; + + if (ngroups == 0) + initialize_group_array(); + + /* Search through the list looking for GID. */ + for (i = 0; i < ngroups; i++) + if (gid == group_array[i]) + return 1; + + return 0; +} + + +/* 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 + executable. */ +static int test_eaccess(char *path, int mode) +{ + struct stat st; + unsigned int euid = geteuid(); + + if (stat(path, &st) < 0) + return -1; + + if (euid == 0) { + /* Root can read or write any file. */ + if (mode != X_OK) + 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; + } + + 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 -1; +} + + static int filstat(char *nm, enum token mode) { struct stat s; @@ -453,147 +457,140 @@ static int filstat(char *nm, enum token mode) return 1; /* NOTREACHED */ } -static enum token t_lex(char *s) -{ - const struct t_op *op; - t_wp_op = NULL; - if (s == NULL) { - return EOI; +static arith_t nexpr(enum token n) +{ + if (n == UNOT) + return !nexpr(t_lex(*++t_wp)); + return primary(n); +} + + +static arith_t aexpr(enum token n) +{ + arith_t res; + + res = nexpr(n); + if (t_lex(*++t_wp) == BAND) + return aexpr(t_lex(*++t_wp)) && res; + t_wp--; + return res; +} + + +static arith_t oexpr(enum token n) +{ + arith_t res; + + res = aexpr(n); + if (t_lex(*++t_wp) == BOR) { + return oexpr(t_lex(*++t_wp)) || res; + } + t_wp--; + return res; +} + + + +static arith_t primary(enum token n) +{ + arith_t res; + + if (n == EOI) { + syntax(NULL, "argument expected"); + } + if (n == LPAREN) { + res = oexpr(t_lex(*++t_wp)); + if (t_lex(*++t_wp) != RPAREN) + syntax(NULL, "closing paren expected"); + return res; + } + if (t_wp_op && t_wp_op->op_type == UNOP) { + /* unary expression */ + if (*++t_wp == NULL) + syntax(t_wp_op->op_text, "argument expected"); + 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)); + return filstat(*t_wp, n); } - op = ops; - do { - if (strcmp(s, op->op_text) == 0) { - t_wp_op = op; - return op->op_num; + t_lex(t_wp[1]); + if (t_wp_op && t_wp_op->op_type == BINOP) { + return binop(); + } + + return t_wp[0][0] != '\0'; +} + + +int test_main(int argc, char **argv) +{ + int res; + const char *arg0; + bool _off; + + arg0 = bb_basename(argv[0]); + if (arg0[0] == '[') { + --argc; + if (!arg0[1]) { /* "[" ? */ + if (NOT_LONE_CHAR(argv[argc], ']')) { + bb_error_msg("missing ]"); + return 2; + } + } else { /* assuming "[[" */ + if (strcmp(argv[argc], "]]") != 0) { + bb_error_msg("missing ]]"); + return 2; + } } - op++; - } while (op < ops + ARRAY_SIZE(ops)); - - return OPERAND; -} - -/* atoi with error detection */ -//XXX: FIXME: duplicate of existing libbb function? -static arith_t getn(const char *s) -{ - char *p; -#if ENABLE_FEATURE_TEST_64 - long long r; -#else - long r; -#endif - - errno = 0; -#if ENABLE_FEATURE_TEST_64 - r = strtoll(s, &p, 10); -#else - r = strtol(s, &p, 10); -#endif - - if (errno != 0) - syntax(s, "out of range"); - - if (*(skip_whitespace(p))) - syntax(s, "bad number"); - - return r; -} - -/* UNUSED -static int newerf(const char *f1, const char *f2) -{ - struct stat b1, b2; - - return (stat(f1, &b1) == 0 && - stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); -} - -static int olderf(const char *f1, const char *f2) -{ - struct stat b1, b2; - - return (stat(f1, &b1) == 0 && - stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); -} - -static int equalf(const char *f1, const char *f2) -{ - struct stat b1, b2; - - return (stat(f1, &b1) == 0 && - 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 - executable. */ -static int test_eaccess(char *path, int mode) -{ - struct stat st; - unsigned int euid = geteuid(); - - if (stat(path, &st) < 0) - return -1; - - if (euid == 0) { - /* Root can read or write any file. */ - if (mode != X_OK) - 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; + argv[argc] = NULL; } - if (st.st_uid == euid) /* owner */ - mode <<= 6; - else if (is_a_group_member(st.st_gid)) - mode <<= 3; + res = setjmp(leaving); + if (res) + return res; - if (st.st_mode & mode) - return 0; + /* 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; - return -1; -} - -static void initialize_group_array(void) -{ - ngroups = getgroups(0, NULL); - if (ngroups > 0) { - /* FIXME: ash tries so hard to not die on OOM, - * and we spoil it with just one xrealloc here */ - /* We realloc, because test_main can be entered repeatedly by shell. - * Testcase (ash): 'while true; do test -x some_file; done' - * and watch top. (some_file must have owner != you) */ - group_array = xrealloc(group_array, 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) -{ - int i; - - /* Short-circuit if possible, maybe saving a call to getgroups(). */ - if (gid == getgid() || gid == getegid()) + /* Implement special cases from POSIX.2, section 4.62.4 */ + if (argc == 1) return 1; + if (argc == 2) + return *argv[1] == '\0'; +//assert(argc); + /* remember if we saw argc==4 which wants *no* '!' test */ + _off = argc - 4; + if (_off ? + (LONE_CHAR(argv[1], '!')) + : (argv[1][0] != '!' || argv[1][1] != '\0')) + { + if (argc == 3) + return *argv[2] != '\0'; - if (ngroups == 0) - initialize_group_array(); + t_lex(argv[2 + _off]); + if (t_wp_op && t_wp_op->op_type == BINOP) { + t_wp = &argv[1 + _off]; + return binop() == _off; + } + } + t_wp = &argv[1]; + res = !oexpr(t_lex(*t_wp)); - /* Search through the list looking for GID. */ - for (i = 0; i < ngroups; i++) - if (gid == group_array[i]) - return 1; - - return 0; + if (*t_wp != NULL && *++t_wp != NULL) { + bb_error_msg("%s: unknown operand", *t_wp); + return 2; + } + return res; }