1*cf5a6c84SAndroid Build Coastguard Worker /* test.c - evaluate expression
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2018 Rob Landley <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker *
5*cf5a6c84SAndroid Build Coastguard Worker * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
6*cf5a6c84SAndroid Build Coastguard Worker *
7*cf5a6c84SAndroid Build Coastguard Worker * Deviations from posix: -k, [[ < > =~ ]]
8*cf5a6c84SAndroid Build Coastguard Worker
9*cf5a6c84SAndroid Build Coastguard Worker USE_TEST(NEWTOY(test, 0, TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_NOHELP|TOYFLAG_MAYFORK))
10*cf5a6c84SAndroid Build Coastguard Worker USE_TEST_GLUE(OLDTOY([, test, TOYFLAG_BIN|TOYFLAG_MAYFORK|TOYFLAG_NOHELP))
11*cf5a6c84SAndroid Build Coastguard Worker USE_SH(OLDTOY([[, test, TOYFLAG_NOFORK|TOYFLAG_NOHELP))
12*cf5a6c84SAndroid Build Coastguard Worker
13*cf5a6c84SAndroid Build Coastguard Worker config TEST
14*cf5a6c84SAndroid Build Coastguard Worker bool "test"
15*cf5a6c84SAndroid Build Coastguard Worker default y
16*cf5a6c84SAndroid Build Coastguard Worker help
17*cf5a6c84SAndroid Build Coastguard Worker usage: test [-bcdefghkLprSsuwx PATH] [-nz STRING] [-t FD] [X ?? Y]
18*cf5a6c84SAndroid Build Coastguard Worker
19*cf5a6c84SAndroid Build Coastguard Worker Return true or false by performing tests. No arguments is false, one argument
20*cf5a6c84SAndroid Build Coastguard Worker is true if not empty string.
21*cf5a6c84SAndroid Build Coastguard Worker
22*cf5a6c84SAndroid Build Coastguard Worker --- Tests with a single argument (after the option):
23*cf5a6c84SAndroid Build Coastguard Worker PATH is/has:
24*cf5a6c84SAndroid Build Coastguard Worker -b block device -f regular file -p fifo -u setuid bit
25*cf5a6c84SAndroid Build Coastguard Worker -c char device -g setgid -r readable -w writable
26*cf5a6c84SAndroid Build Coastguard Worker -d directory -h symlink -S socket -x executable
27*cf5a6c84SAndroid Build Coastguard Worker -e exists -L symlink -s nonzero size -k sticky bit
28*cf5a6c84SAndroid Build Coastguard Worker STRING is:
29*cf5a6c84SAndroid Build Coastguard Worker -n nonzero size -z zero size
30*cf5a6c84SAndroid Build Coastguard Worker FD (integer file descriptor) is:
31*cf5a6c84SAndroid Build Coastguard Worker -t a TTY
32*cf5a6c84SAndroid Build Coastguard Worker
33*cf5a6c84SAndroid Build Coastguard Worker --- Tests with one argument on each side of an operator:
34*cf5a6c84SAndroid Build Coastguard Worker Two strings:
35*cf5a6c84SAndroid Build Coastguard Worker = are identical != differ =~ string matches regex
36*cf5a6c84SAndroid Build Coastguard Worker Alphabetical sort:
37*cf5a6c84SAndroid Build Coastguard Worker < first is lower > first higher
38*cf5a6c84SAndroid Build Coastguard Worker Two integers:
39*cf5a6c84SAndroid Build Coastguard Worker -eq equal -gt first > second -lt first < second
40*cf5a6c84SAndroid Build Coastguard Worker -ne not equal -ge first >= second -le first <= second
41*cf5a6c84SAndroid Build Coastguard Worker Two files:
42*cf5a6c84SAndroid Build Coastguard Worker -ot Older mtime -nt Newer mtime -ef same dev/inode
43*cf5a6c84SAndroid Build Coastguard Worker
44*cf5a6c84SAndroid Build Coastguard Worker --- Modify or combine tests:
45*cf5a6c84SAndroid Build Coastguard Worker ! EXPR not (swap true/false) EXPR -a EXPR and (are both true)
46*cf5a6c84SAndroid Build Coastguard Worker ( EXPR ) evaluate this first EXPR -o EXPR or (is either true)
47*cf5a6c84SAndroid Build Coastguard Worker
48*cf5a6c84SAndroid Build Coastguard Worker config TEST_GLUE
49*cf5a6c84SAndroid Build Coastguard Worker bool
50*cf5a6c84SAndroid Build Coastguard Worker default y
51*cf5a6c84SAndroid Build Coastguard Worker depends on TEST || SH
52*cf5a6c84SAndroid Build Coastguard Worker */
53*cf5a6c84SAndroid Build Coastguard Worker
54*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
55*cf5a6c84SAndroid Build Coastguard Worker
56*cf5a6c84SAndroid Build Coastguard Worker // Consume 3, 2, or 1 argument test, returning result and *count used.
do_test(char ** args,int * count)57*cf5a6c84SAndroid Build Coastguard Worker static int do_test(char **args, int *count)
58*cf5a6c84SAndroid Build Coastguard Worker {
59*cf5a6c84SAndroid Build Coastguard Worker char c, *s;
60*cf5a6c84SAndroid Build Coastguard Worker int i;
61*cf5a6c84SAndroid Build Coastguard Worker
62*cf5a6c84SAndroid Build Coastguard Worker if (*count>=3) {
63*cf5a6c84SAndroid Build Coastguard Worker *count = 3;
64*cf5a6c84SAndroid Build Coastguard Worker char *s = args[1], *ss = "eqnegtgeltleefotnt";
65*cf5a6c84SAndroid Build Coastguard Worker // TODO shell integration case insensitivity
66*cf5a6c84SAndroid Build Coastguard Worker if (!strcmp(s, "=") || !strcmp(s, "==")) return !strcmp(args[0], args[2]);
67*cf5a6c84SAndroid Build Coastguard Worker if (!strcmp(s, "!=")) return strcmp(args[0], args[2]);
68*cf5a6c84SAndroid Build Coastguard Worker if (!strcmp(s, "=~")) {
69*cf5a6c84SAndroid Build Coastguard Worker regex_t reg;
70*cf5a6c84SAndroid Build Coastguard Worker
71*cf5a6c84SAndroid Build Coastguard Worker // TODO: regex needs integrated quoting support with the shell.
72*cf5a6c84SAndroid Build Coastguard Worker // Ala [[ abc =~ "1"* ]] matches but [[ abc =~ 1"*" ]] does not
73*cf5a6c84SAndroid Build Coastguard Worker xregcomp(®, args[2], REG_NOSUB); // REG_EXTENDED? REG_ICASE?
74*cf5a6c84SAndroid Build Coastguard Worker i = regexec(®, args[0], 0, 0, 0);
75*cf5a6c84SAndroid Build Coastguard Worker regfree(®);
76*cf5a6c84SAndroid Build Coastguard Worker
77*cf5a6c84SAndroid Build Coastguard Worker return !i;
78*cf5a6c84SAndroid Build Coastguard Worker }
79*cf5a6c84SAndroid Build Coastguard Worker if ((*s=='<' || *s=='>') && !s[1]) {
80*cf5a6c84SAndroid Build Coastguard Worker i = strcmp(args[0], args[2]);
81*cf5a6c84SAndroid Build Coastguard Worker return (*s=='<') ? i<0 : i>0;
82*cf5a6c84SAndroid Build Coastguard Worker }
83*cf5a6c84SAndroid Build Coastguard Worker if (*s=='-' && strlen(s)==3 && (s = strstr(ss, s+1)) && !((i = s-ss)&1)) {
84*cf5a6c84SAndroid Build Coastguard Worker struct stat st1, st2;
85*cf5a6c84SAndroid Build Coastguard Worker long long a QUIET, b QUIET;
86*cf5a6c84SAndroid Build Coastguard Worker if (i <= 10) {
87*cf5a6c84SAndroid Build Coastguard Worker a = atolx(args[0]);
88*cf5a6c84SAndroid Build Coastguard Worker b = atolx(args[2]);
89*cf5a6c84SAndroid Build Coastguard Worker } else {
90*cf5a6c84SAndroid Build Coastguard Worker if ((i == 12 ? stat : lstat)(args[0], &st1)
91*cf5a6c84SAndroid Build Coastguard Worker || (i == 12 ? stat : lstat)(args[2], &st2)) return 0;
92*cf5a6c84SAndroid Build Coastguard Worker }
93*cf5a6c84SAndroid Build Coastguard Worker
94*cf5a6c84SAndroid Build Coastguard Worker if (!i) return a == b;
95*cf5a6c84SAndroid Build Coastguard Worker if (i==2) return a != b;
96*cf5a6c84SAndroid Build Coastguard Worker if (i==4) return a > b;
97*cf5a6c84SAndroid Build Coastguard Worker if (i==6) return a >= b;
98*cf5a6c84SAndroid Build Coastguard Worker if (i==8) return a < b;
99*cf5a6c84SAndroid Build Coastguard Worker if (i==10) return a<= b;
100*cf5a6c84SAndroid Build Coastguard Worker if (i==12) return (st1.st_dev==st2.st_dev) && (st1.st_ino==st2.st_ino);
101*cf5a6c84SAndroid Build Coastguard Worker if (i==14) return (st1.st_atim.tv_sec < st2.st_atim.tv_sec) ||
102*cf5a6c84SAndroid Build Coastguard Worker (st1.st_atim.tv_nsec < st2.st_atim.tv_nsec);
103*cf5a6c84SAndroid Build Coastguard Worker if (i==16) return (st1.st_atim.tv_sec > st2.st_atim.tv_sec) ||
104*cf5a6c84SAndroid Build Coastguard Worker (st1.st_atim.tv_nsec > st2.st_atim.tv_nsec);
105*cf5a6c84SAndroid Build Coastguard Worker }
106*cf5a6c84SAndroid Build Coastguard Worker }
107*cf5a6c84SAndroid Build Coastguard Worker s = *args;
108*cf5a6c84SAndroid Build Coastguard Worker if (*count>=2 && *s == '-' && s[1] && !s[2]) {
109*cf5a6c84SAndroid Build Coastguard Worker *count = 2;
110*cf5a6c84SAndroid Build Coastguard Worker c = s[1];
111*cf5a6c84SAndroid Build Coastguard Worker if (c=='a') c = 'e';
112*cf5a6c84SAndroid Build Coastguard Worker if (-1 != (i = stridx("hLbcdefgkpSusxwr", c))) {
113*cf5a6c84SAndroid Build Coastguard Worker struct stat st;
114*cf5a6c84SAndroid Build Coastguard Worker
115*cf5a6c84SAndroid Build Coastguard Worker if (i>=13) return !access(args[1], 1<<(i-13));
116*cf5a6c84SAndroid Build Coastguard Worker // stat or lstat, check s
117*cf5a6c84SAndroid Build Coastguard Worker if (-1 == ((i<2) ? lstat : stat)(args[1], &st)) return 0;
118*cf5a6c84SAndroid Build Coastguard Worker if (c == 's') return !!st.st_size; // otherwise 1<<32 == 0
119*cf5a6c84SAndroid Build Coastguard Worker
120*cf5a6c84SAndroid Build Coastguard Worker // handle file type checking and SUID/SGID
121*cf5a6c84SAndroid Build Coastguard Worker if ((i = ((char []){80,80,48,16,32,0,64,2,1,8,96,4}[i])<<9)>=4096)
122*cf5a6c84SAndroid Build Coastguard Worker return (st.st_mode&S_IFMT) == i;
123*cf5a6c84SAndroid Build Coastguard Worker else return (st.st_mode & i) == i;
124*cf5a6c84SAndroid Build Coastguard Worker } else if (c == 'z') return !*args[1];
125*cf5a6c84SAndroid Build Coastguard Worker else if (c == 'n') return *args[1];
126*cf5a6c84SAndroid Build Coastguard Worker else if (c == 't') return isatty(atolx(args[1]));
127*cf5a6c84SAndroid Build Coastguard Worker }
128*cf5a6c84SAndroid Build Coastguard Worker return *count = 0;
129*cf5a6c84SAndroid Build Coastguard Worker }
130*cf5a6c84SAndroid Build Coastguard Worker
131*cf5a6c84SAndroid Build Coastguard Worker #define NOT 1 // Most recent test had an odd number of preceding !
132*cf5a6c84SAndroid Build Coastguard Worker #define AND 2 // test before -a failed since -o or ( so force false
133*cf5a6c84SAndroid Build Coastguard Worker #define OR 4 // test before -o succeeded since ( so force true
test_main(void)134*cf5a6c84SAndroid Build Coastguard Worker void test_main(void)
135*cf5a6c84SAndroid Build Coastguard Worker {
136*cf5a6c84SAndroid Build Coastguard Worker char *s = (void *)1;
137*cf5a6c84SAndroid Build Coastguard Worker int pos, paren, pstack, result = 0;
138*cf5a6c84SAndroid Build Coastguard Worker
139*cf5a6c84SAndroid Build Coastguard Worker toys.exitval = 2;
140*cf5a6c84SAndroid Build Coastguard Worker if (CFG_TOYBOX && *toys.which->name=='[') {
141*cf5a6c84SAndroid Build Coastguard Worker if (toys.optc) for (s = toys.optargs[--toys.optc]; *s==']'; s++);
142*cf5a6c84SAndroid Build Coastguard Worker if (*s) error_exit("Missing ']'");
143*cf5a6c84SAndroid Build Coastguard Worker }
144*cf5a6c84SAndroid Build Coastguard Worker
145*cf5a6c84SAndroid Build Coastguard Worker // loop through command line arguments
146*cf5a6c84SAndroid Build Coastguard Worker if (toys.optc) for (pos = paren = pstack = 0; ; pos++) {
147*cf5a6c84SAndroid Build Coastguard Worker int len = toys.optc-pos;
148*cf5a6c84SAndroid Build Coastguard Worker
149*cf5a6c84SAndroid Build Coastguard Worker if (!len) perror_exit("need arg @%d", pos);
150*cf5a6c84SAndroid Build Coastguard Worker
151*cf5a6c84SAndroid Build Coastguard Worker // Evaluate next test
152*cf5a6c84SAndroid Build Coastguard Worker result = do_test(toys.optargs+pos, &len);
153*cf5a6c84SAndroid Build Coastguard Worker pos += len;
154*cf5a6c84SAndroid Build Coastguard Worker // Single argument could be ! ( or nonempty
155*cf5a6c84SAndroid Build Coastguard Worker if (!len) {
156*cf5a6c84SAndroid Build Coastguard Worker if (toys.optargs[pos+1]) {
157*cf5a6c84SAndroid Build Coastguard Worker if (!strcmp("!", toys.optargs[pos])) {
158*cf5a6c84SAndroid Build Coastguard Worker pstack ^= NOT;
159*cf5a6c84SAndroid Build Coastguard Worker continue;
160*cf5a6c84SAndroid Build Coastguard Worker }
161*cf5a6c84SAndroid Build Coastguard Worker if (!strcmp("(", toys.optargs[pos])) {
162*cf5a6c84SAndroid Build Coastguard Worker if (++paren>9) perror_exit("bad (");
163*cf5a6c84SAndroid Build Coastguard Worker pstack <<= 3;
164*cf5a6c84SAndroid Build Coastguard Worker continue;
165*cf5a6c84SAndroid Build Coastguard Worker }
166*cf5a6c84SAndroid Build Coastguard Worker }
167*cf5a6c84SAndroid Build Coastguard Worker result = *toys.optargs[pos++];
168*cf5a6c84SAndroid Build Coastguard Worker }
169*cf5a6c84SAndroid Build Coastguard Worker s = toys.optargs[pos];
170*cf5a6c84SAndroid Build Coastguard Worker for (;;) {
171*cf5a6c84SAndroid Build Coastguard Worker
172*cf5a6c84SAndroid Build Coastguard Worker // Handle pending ! -a -o (the else means -o beats -a)
173*cf5a6c84SAndroid Build Coastguard Worker if (pstack&NOT) result = !result;
174*cf5a6c84SAndroid Build Coastguard Worker pstack &= ~NOT;
175*cf5a6c84SAndroid Build Coastguard Worker if (pstack&OR) result = 1;
176*cf5a6c84SAndroid Build Coastguard Worker else if (pstack&AND) result = 0;
177*cf5a6c84SAndroid Build Coastguard Worker
178*cf5a6c84SAndroid Build Coastguard Worker // Do it again for every )
179*cf5a6c84SAndroid Build Coastguard Worker if (!paren || pos==toys.optc || strcmp(")", s)) break;
180*cf5a6c84SAndroid Build Coastguard Worker paren--;
181*cf5a6c84SAndroid Build Coastguard Worker pstack >>= 3;
182*cf5a6c84SAndroid Build Coastguard Worker s = toys.optargs[++pos];
183*cf5a6c84SAndroid Build Coastguard Worker }
184*cf5a6c84SAndroid Build Coastguard Worker
185*cf5a6c84SAndroid Build Coastguard Worker // Out of arguments?
186*cf5a6c84SAndroid Build Coastguard Worker if (pos==toys.optc) {
187*cf5a6c84SAndroid Build Coastguard Worker if (paren) perror_exit("need )");
188*cf5a6c84SAndroid Build Coastguard Worker break;
189*cf5a6c84SAndroid Build Coastguard Worker }
190*cf5a6c84SAndroid Build Coastguard Worker
191*cf5a6c84SAndroid Build Coastguard Worker // are we followed by -a or -o?
192*cf5a6c84SAndroid Build Coastguard Worker
193*cf5a6c84SAndroid Build Coastguard Worker if (!strcmp("-a", s)) {
194*cf5a6c84SAndroid Build Coastguard Worker if (!result) pstack |= AND;
195*cf5a6c84SAndroid Build Coastguard Worker } else if (!strcmp("-o", s)) {
196*cf5a6c84SAndroid Build Coastguard Worker // -o flushes -a even if previous test was false
197*cf5a6c84SAndroid Build Coastguard Worker pstack &=~AND;
198*cf5a6c84SAndroid Build Coastguard Worker if (result) pstack |= OR;
199*cf5a6c84SAndroid Build Coastguard Worker } else error_exit("too many arguments");
200*cf5a6c84SAndroid Build Coastguard Worker }
201*cf5a6c84SAndroid Build Coastguard Worker
202*cf5a6c84SAndroid Build Coastguard Worker // Invert C logic to get shell logic
203*cf5a6c84SAndroid Build Coastguard Worker toys.exitval = !result;
204*cf5a6c84SAndroid Build Coastguard Worker }
205