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)
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);
- }
+ return (stat(f1, &b1) == 0 &&
+ stat(f2, &b2) == 0 &&
+ b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
+}
+*/
- t_lex(t_wp[1]);
- if (t_wp_op && t_wp_op->op_type == BINOP) {
- return binop();
+
+static enum token t_lex(char *s)
+{
+ const struct t_op *op;
+
+ t_wp_op = NULL;
+ if (s == NULL) {
+ return EOI;
}
- return t_wp[0][0] != '\0';
+ 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)
{
const char *opnd1, *opnd2;
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;
return 1; /* NOTREACHED */
}
-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;
-}
-
-/* atoi with error detection */
-//XXX: FIXME: duplicate of existing libbb function?
-static arith_t getn(const char *s)
+static arith_t nexpr(enum token n)
{
- 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;
+ if (n == UNOT)
+ return !nexpr(t_lex(*++t_wp));
+ return primary(n);
}
-/* 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)
+static arith_t aexpr(enum token n)
{
- struct stat b1, b2;
+ arith_t res;
- return (stat(f1, &b1) == 0 &&
- stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
+ res = nexpr(n);
+ if (t_lex(*++t_wp) == BAND)
+ return aexpr(t_lex(*++t_wp)) && res;
+ t_wp--;
+ return res;
}
-static int equalf(const char *f1, const char *f2)
+
+static arith_t oexpr(enum token n)
{
- struct stat b1, b2;
+ arith_t res;
- return (stat(f1, &b1) == 0 &&
- stat(f2, &b2) == 0 &&
- b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
+ res = aexpr(n);
+ if (t_lex(*++t_wp) == BOR) {
+ return oexpr(t_lex(*++t_wp)) || res;
+ }
+ t_wp--;
+ return res;
}
-*/
-/* 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;
+static arith_t primary(enum token n)
+{
+ arith_t res;
- /* 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 (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);
}
- 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;
+ t_lex(t_wp[1]);
+ if (t_wp_op && t_wp_op->op_type == BINOP) {
+ return binop();
+ }
- return -1;
+ return t_wp[0][0] != '\0';
}
-static void initialize_group_array(void)
+
+int test_main(int argc, char **argv)
{
- 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);
+ 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;
}
-}
-/* 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;
+ res = setjmp(leaving);
+ if (res)
+ return res;
- /* Short-circuit if possible, maybe saving a call to getgroups(). */
- if (gid == getgid() || gid == getegid())
- return 1;
+ /* 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;
- if (ngroups == 0)
- initialize_group_array();
+ /* 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';
- /* Search through the list looking for GID. */
- for (i = 0; i < ngroups; i++)
- if (gid == group_array[i])
- return 1;
+ 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));
- return 0;
+ if (*t_wp != NULL && *++t_wp != NULL) {
+ bb_error_msg("%s: unknown operand", *t_wp);
+ return 2;
+ }
+ return res;
}