10f66f451Sopenharmony_ci/* test.c - evaluate expression 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2018 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html 60f66f451Sopenharmony_ci 70f66f451Sopenharmony_ciUSE_TEST(NEWTOY(test, 0, TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NOHELP|TOYFLAG_MAYFORK)) 80f66f451Sopenharmony_ciUSE_TEST(OLDTOY([, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP)) 90f66f451Sopenharmony_ci 100f66f451Sopenharmony_ciconfig TEST 110f66f451Sopenharmony_ci bool "test" 120f66f451Sopenharmony_ci default y 130f66f451Sopenharmony_ci help 140f66f451Sopenharmony_ci usage: test [-bcdefghLPrSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y] 150f66f451Sopenharmony_ci 160f66f451Sopenharmony_ci Return true or false by performing tests. (With no arguments return false.) 170f66f451Sopenharmony_ci 180f66f451Sopenharmony_ci --- Tests with a single argument (after the option): 190f66f451Sopenharmony_ci PATH is/has: 200f66f451Sopenharmony_ci -b block device -f regular file -p fifo -u setuid bit 210f66f451Sopenharmony_ci -c char device -g setgid -r read bit -w write bit 220f66f451Sopenharmony_ci -d directory -h symlink -S socket -x execute bit 230f66f451Sopenharmony_ci -e exists -L symlink -s nonzero size 240f66f451Sopenharmony_ci STRING is: 250f66f451Sopenharmony_ci -n nonzero size -z zero size (STRING by itself implies -n) 260f66f451Sopenharmony_ci FD (integer file descriptor) is: 270f66f451Sopenharmony_ci -t a TTY 280f66f451Sopenharmony_ci 290f66f451Sopenharmony_ci --- Tests with one argument on each side of an operator: 300f66f451Sopenharmony_ci Two strings: 310f66f451Sopenharmony_ci = are identical != differ 320f66f451Sopenharmony_ci Two integers: 330f66f451Sopenharmony_ci -eq equal -gt first > second -lt first < second 340f66f451Sopenharmony_ci -ne not equal -ge first >= second -le first <= second 350f66f451Sopenharmony_ci 360f66f451Sopenharmony_ci --- Modify or combine tests: 370f66f451Sopenharmony_ci ! EXPR not (swap true/false) EXPR -a EXPR and (are both true) 380f66f451Sopenharmony_ci ( EXPR ) evaluate this first EXPR -o EXPR or (is either true) 390f66f451Sopenharmony_ci*/ 400f66f451Sopenharmony_ci 410f66f451Sopenharmony_ci#include "toys.h" 420f66f451Sopenharmony_ci 430f66f451Sopenharmony_ci// Consume 3, 2, or 1 argument test, returning result and *count used. 440f66f451Sopenharmony_ciint do_test(char **args, int *count) 450f66f451Sopenharmony_ci{ 460f66f451Sopenharmony_ci char c, *s; 470f66f451Sopenharmony_ci int i; 480f66f451Sopenharmony_ci 490f66f451Sopenharmony_ci if (*count>=3) { 500f66f451Sopenharmony_ci *count = 3; 510f66f451Sopenharmony_ci char *s = args[1], *ss = "eqnegtgeltle"; 520f66f451Sopenharmony_ci if (!strcmp(s, "=") || !strcmp(s, "==")) return !strcmp(args[0], args[2]); 530f66f451Sopenharmony_ci if (!strcmp(s, "!=")) return strcmp(args[0], args[2]); 540f66f451Sopenharmony_ci if (*s=='-' && strlen(s)==3 && (s = strstr(ss, s+1)) && !((i = s-ss)&1)) { 550f66f451Sopenharmony_ci long long a = atolx(args[0]), b = atolx(args[2]); 560f66f451Sopenharmony_ci 570f66f451Sopenharmony_ci if (!i) return a == b; 580f66f451Sopenharmony_ci if (i==2) return a != b; 590f66f451Sopenharmony_ci if (i==4) return a > b; 600f66f451Sopenharmony_ci if (i==6) return a >= b; 610f66f451Sopenharmony_ci if (i==8) return a < b; 620f66f451Sopenharmony_ci if (i==10) return a<= b; 630f66f451Sopenharmony_ci } 640f66f451Sopenharmony_ci } 650f66f451Sopenharmony_ci s = *args; 660f66f451Sopenharmony_ci if (*count>=2 && *s == '-' && s[1] && !s[2]) { 670f66f451Sopenharmony_ci *count = 2; 680f66f451Sopenharmony_ci c = s[1]; 690f66f451Sopenharmony_ci if (-1 != (i = stridx("hLbcdefgpSusxwr", c))) { 700f66f451Sopenharmony_ci struct stat st; 710f66f451Sopenharmony_ci 720f66f451Sopenharmony_ci // stat or lstat, then handle rwx and s 730f66f451Sopenharmony_ci if (-1 == ((i<2) ? lstat : stat)(args[1], &st)) return 0; 740f66f451Sopenharmony_ci if (i>=12) return !!(st.st_mode&(0x111<<(i-12))); 750f66f451Sopenharmony_ci if (c == 's') return !!st.st_size; // otherwise 1<<32 == 0 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_ci // handle file type checking and SUID/SGID 780f66f451Sopenharmony_ci if ((i = (unsigned short []){80,80,48,16,32,0,64,2,8,96,4}[i]<<9)>=4096) 790f66f451Sopenharmony_ci return (st.st_mode&S_IFMT) == i; 800f66f451Sopenharmony_ci else return (st.st_mode & i) == i; 810f66f451Sopenharmony_ci } else if (c == 'z') return !*args[1]; 820f66f451Sopenharmony_ci else if (c == 'n') return *args[1]; 830f66f451Sopenharmony_ci else if (c == 't') return isatty(atolx(args[1])); 840f66f451Sopenharmony_ci } 850f66f451Sopenharmony_ci return *count = 0; 860f66f451Sopenharmony_ci} 870f66f451Sopenharmony_ci 880f66f451Sopenharmony_ci#define NOT 1 // Most recent test had an odd number of preceding ! 890f66f451Sopenharmony_ci#define AND 2 // test before -a failed since -o or ( so force false 900f66f451Sopenharmony_ci#define OR 4 // test before -o succeeded since ( so force true 910f66f451Sopenharmony_civoid test_main(void) 920f66f451Sopenharmony_ci{ 930f66f451Sopenharmony_ci char *s; 940f66f451Sopenharmony_ci int pos, paren, pstack, result = 0; 950f66f451Sopenharmony_ci 960f66f451Sopenharmony_ci toys.exitval = 2; 970f66f451Sopenharmony_ci if (!strcmp("[", toys.which->name)) 980f66f451Sopenharmony_ci if (!toys.optc || strcmp("]", toys.optargs[--toys.optc])) 990f66f451Sopenharmony_ci error_exit("Missing ']'"); 1000f66f451Sopenharmony_ci 1010f66f451Sopenharmony_ci // loop through command line arguments 1020f66f451Sopenharmony_ci if (toys.optc) for (pos = paren = pstack = 0; ; pos++) { 1030f66f451Sopenharmony_ci int len = toys.optc-pos; 1040f66f451Sopenharmony_ci 1050f66f451Sopenharmony_ci if (!toys.optargs[pos]) perror_exit("need arg @%d", pos); 1060f66f451Sopenharmony_ci 1070f66f451Sopenharmony_ci // Evaluate next test 1080f66f451Sopenharmony_ci result = do_test(toys.optargs+pos, &len); 1090f66f451Sopenharmony_ci pos += len; 1100f66f451Sopenharmony_ci // Single argument could be ! ( or nonempty 1110f66f451Sopenharmony_ci if (!len) { 1120f66f451Sopenharmony_ci if (toys.optargs[pos+1]) { 1130f66f451Sopenharmony_ci if (!strcmp("!", toys.optargs[pos])) { 1140f66f451Sopenharmony_ci pstack ^= NOT; 1150f66f451Sopenharmony_ci continue; 1160f66f451Sopenharmony_ci } 1170f66f451Sopenharmony_ci if (!strcmp("(", toys.optargs[pos])) { 1180f66f451Sopenharmony_ci if (++paren>9) perror_exit("bad ("); 1190f66f451Sopenharmony_ci pstack <<= 3; 1200f66f451Sopenharmony_ci continue; 1210f66f451Sopenharmony_ci } 1220f66f451Sopenharmony_ci } 1230f66f451Sopenharmony_ci result = *toys.optargs[pos++]; 1240f66f451Sopenharmony_ci } 1250f66f451Sopenharmony_ci s = toys.optargs[pos]; 1260f66f451Sopenharmony_ci for (;;) { 1270f66f451Sopenharmony_ci 1280f66f451Sopenharmony_ci // Handle pending ! -a -o (the else means -o beats -a) 1290f66f451Sopenharmony_ci if (pstack&NOT) result = !result; 1300f66f451Sopenharmony_ci pstack &= ~NOT; 1310f66f451Sopenharmony_ci if (pstack&OR) result = 1; 1320f66f451Sopenharmony_ci else if (pstack&AND) result = 0; 1330f66f451Sopenharmony_ci 1340f66f451Sopenharmony_ci // Do it again for every ) 1350f66f451Sopenharmony_ci if (!paren || !s || strcmp(")", s)) break; 1360f66f451Sopenharmony_ci paren--; 1370f66f451Sopenharmony_ci pstack >>= 3; 1380f66f451Sopenharmony_ci s = toys.optargs[++pos]; 1390f66f451Sopenharmony_ci } 1400f66f451Sopenharmony_ci 1410f66f451Sopenharmony_ci // Out of arguments? 1420f66f451Sopenharmony_ci if (!s) { 1430f66f451Sopenharmony_ci if (paren) perror_exit("need )"); 1440f66f451Sopenharmony_ci break; 1450f66f451Sopenharmony_ci } 1460f66f451Sopenharmony_ci 1470f66f451Sopenharmony_ci // are we followed by -a or -o? 1480f66f451Sopenharmony_ci 1490f66f451Sopenharmony_ci if (!strcmp("-a", s)) { 1500f66f451Sopenharmony_ci if (!result) pstack |= AND; 1510f66f451Sopenharmony_ci } else if (!strcmp("-o", s)) { 1520f66f451Sopenharmony_ci // -o flushes -a even if previous test was false 1530f66f451Sopenharmony_ci pstack &=~AND; 1540f66f451Sopenharmony_ci if (result) pstack |= OR; 1550f66f451Sopenharmony_ci } else error_exit("too many arguments"); 1560f66f451Sopenharmony_ci } 1570f66f451Sopenharmony_ci 1580f66f451Sopenharmony_ci // Invert C logic to get shell logic 1590f66f451Sopenharmony_ci toys.exitval = !result; 1600f66f451Sopenharmony_ci} 161