123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- /* test.c - evaluate expression
- *
- * Copyright 2018 Rob Landley <rob@landley.net>
- *
- * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
- *
- * TODO sh [[ ]] options: < aaa<bbb > bbb>aaa ~= regex
- USE_TEST(NEWTOY(test, 0, TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NOHELP|TOYFLAG_MAYFORK))
- USE_TEST_GLUE(OLDTOY([, test, TOYFLAG_BIN|TOYFLAG_MAYFORK|TOYFLAG_NOHELP))
- config TEST
- bool "test"
- default y
- help
- usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]
- Return true or false by performing tests. (With no arguments return false.)
- --- Tests with a single argument (after the option):
- PATH is/has:
- -b block device -f regular file -p fifo -u setuid bit
- -c char device -g setgid -r read bit -w write bit
- -d directory -h symlink -S socket -x execute bit
- -e exists -L symlink -s nonzero size -k sticky bit
- STRING is:
- -n nonzero size -z zero size (STRING by itself implies -n)
- FD (integer file descriptor) is:
- -t a TTY
- --- Tests with one argument on each side of an operator:
- Two strings:
- = are identical != differ
- Two integers:
- -eq equal -gt first > second -lt first < second
- -ne not equal -ge first >= second -le first <= second
- --- Modify or combine tests:
- ! EXPR not (swap true/false) EXPR -a EXPR and (are both true)
- ( EXPR ) evaluate this first EXPR -o EXPR or (is either true)
- config TEST_GLUE
- bool
- default y
- depends on TEST || SH
- */
- #include "toys.h"
- // Consume 3, 2, or 1 argument test, returning result and *count used.
- static int do_test(char **args, int *count)
- {
- char c, *s;
- int i;
- if (*count>=3) {
- *count = 3;
- char *s = args[1], *ss = "eqnegtgeltle";
- if (!strcmp(s, "=") || !strcmp(s, "==")) return !strcmp(args[0], args[2]);
- if (!strcmp(s, "!=")) return strcmp(args[0], args[2]);
- if (*s=='-' && strlen(s)==3 && (s = strstr(ss, s+1)) && !((i = s-ss)&1)) {
- long long a = atolx(args[0]), b = atolx(args[2]);
- if (!i) return a == b;
- if (i==2) return a != b;
- if (i==4) return a > b;
- if (i==6) return a >= b;
- if (i==8) return a < b;
- if (i==10) return a<= b;
- }
- }
- s = *args;
- if (*count>=2 && *s == '-' && s[1] && !s[2]) {
- *count = 2;
- c = s[1];
- if (-1 != (i = stridx("hLbcdefgkpSusxwr", c))) {
- struct stat st;
- // stat or lstat, then handle rwx and s
- if (-1 == ((i<2) ? lstat : stat)(args[1], &st)) return 0;
- if (i>=13) return !!(st.st_mode&(0111<<(i-13)));
- if (c == 's') return !!st.st_size; // otherwise 1<<32 == 0
- // handle file type checking and SUID/SGID
- if ((i = ((char []){80,80,48,16,32,0,64,2,1,8,96,4}[i])<<9)>=4096)
- return (st.st_mode&S_IFMT) == i;
- else return (st.st_mode & i) == i;
- } else if (c == 'z') return !*args[1];
- else if (c == 'n') return *args[1];
- else if (c == 't') return isatty(atolx(args[1]));
- }
- return *count = 0;
- }
- #define NOT 1 // Most recent test had an odd number of preceding !
- #define AND 2 // test before -a failed since -o or ( so force false
- #define OR 4 // test before -o succeeded since ( so force true
- void test_main(void)
- {
- char *s;
- int pos, paren, pstack, result = 0;
- toys.exitval = 2;
- if (CFG_TOYBOX && !strcmp("[", toys.which->name))
- if (!toys.optc || strcmp("]", toys.optargs[--toys.optc]))
- error_exit("Missing ']'");
- // loop through command line arguments
- if (toys.optc) for (pos = paren = pstack = 0; ; pos++) {
- int len = toys.optc-pos;
- if (!len) perror_exit("need arg @%d", pos);
- // Evaluate next test
- result = do_test(toys.optargs+pos, &len);
- pos += len;
- // Single argument could be ! ( or nonempty
- if (!len) {
- if (toys.optargs[pos+1]) {
- if (!strcmp("!", toys.optargs[pos])) {
- pstack ^= NOT;
- continue;
- }
- if (!strcmp("(", toys.optargs[pos])) {
- if (++paren>9) perror_exit("bad (");
- pstack <<= 3;
- continue;
- }
- }
- result = *toys.optargs[pos++];
- }
- s = toys.optargs[pos];
- for (;;) {
- // Handle pending ! -a -o (the else means -o beats -a)
- if (pstack&NOT) result = !result;
- pstack &= ~NOT;
- if (pstack&OR) result = 1;
- else if (pstack&AND) result = 0;
- // Do it again for every )
- if (!paren || pos==toys.optc || strcmp(")", s)) break;
- paren--;
- pstack >>= 3;
- s = toys.optargs[++pos];
- }
- // Out of arguments?
- if (pos==toys.optc) {
- if (paren) perror_exit("need )");
- break;
- }
- // are we followed by -a or -o?
- if (!strcmp("-a", s)) {
- if (!result) pstack |= AND;
- } else if (!strcmp("-o", s)) {
- // -o flushes -a even if previous test was false
- pstack &=~AND;
- if (result) pstack |= OR;
- } else error_exit("too many arguments");
- }
- // Invert C logic to get shell logic
- toys.exitval = !result;
- }
|