xref: /third_party/toybox/toys/posix/test.c (revision 0f66f451)
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