1 /*-
2 * Copyright (c) 1992, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * %sccs.include.redist.c%
9 */
10
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1992, 1993, 1994\n\
14 The Regents of the University of California. All rights reserved.\n";
15 #endif /* not lint */
16
17 #ifndef lint
18 static char sccsid[] = "@(#)test.c 8.3 (Berkeley) 04/02/94";
19 #endif /* not lint */
20
21 #include <sys/types.h>
22 #include <sys/stat.h>
23
24 #include <ctype.h>
25 #include <err.h>
26 #include <errno.h>
27 #include <limits.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "operators.h"
34
35 #define STACKSIZE 12
36 #define NESTINCR 16
37
38 /* data types */
39 #define STRING 0
40 #define INTEGER 1
41 #define BOOLEAN 2
42
43 #define IS_BANG(s) (s[0] == '!' && s[1] == '\0')
44
45 /*
46 * This structure hold a value. The type keyword specifies the type of
47 * the value, and the union u holds the value. The value of a boolean
48 * is stored in u.num (1 = TRUE, 0 = FALSE).
49 */
50 struct value {
51 int type;
52 union {
53 char *string;
54 long num;
55 } u;
56 };
57
58 struct operator {
59 short op; /* Which operator. */
60 short pri; /* Priority of operator. */
61 };
62
63 struct filestat {
64 char *name; /* Name of file. */
65 int rcode; /* Return code from stat. */
66 struct stat stat; /* Status info on file. */
67 };
68
69 static int expr_is_false __P((struct value *));
70 static void expr_operator __P((int, struct value *, struct filestat *));
71 static void get_int __P((char *, long *));
72 static int lookup_op __P((char *, const char *const *));
73 static void overflow __P((void));
74 static int posix_binary_op __P((char **));
75 static int posix_unary_op __P((char **));
76 static void syntax __P((void));
77
78 int
main(argc,argv)79 main(argc, argv)
80 int argc;
81 char *argv[];
82 {
83 struct operator opstack[STACKSIZE];
84 struct operator *opsp;
85 struct value valstack[STACKSIZE + 1];
86 struct value *valsp;
87 struct filestat fs;
88 char c, **ap, *opname, *p;
89 int binary, nest, op, pri, ret_val, skipping;
90
91 if ((p = argv[0]) == NULL)
92 errx(2, "test: argc is zero");
93
94 if (*p != '\0' && p[strlen(p) - 1] == '[') {
95 if (strcmp(argv[--argc], "]"))
96 errx(2, "missing ]");
97 argv[argc] = NULL;
98 }
99 ap = argv + 1;
100 fs.name = NULL;
101
102 /*
103 * Test(1) implements an inherently ambiguous grammer. In order to
104 * assure some degree of consistency, we special case the POSIX 1003.2
105 * requirements to assure correct evaluation for POSIX scripts. The
106 * following special cases comply with POSIX P1003.2/D11.2 Section
107 * 4.62.4.
108 */
109 switch(argc - 1) {
110 case 0: /* % test */
111 return (1);
112 break;
113 case 1: /* % test arg */
114 return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0;
115 break;
116 case 2: /* % test op arg */
117 opname = argv[1];
118 if (IS_BANG(opname))
119 return (*argv[2] == '\0') ? 0 : 1;
120 else {
121 ret_val = posix_unary_op(&argv[1]);
122 if (ret_val >= 0)
123 return (ret_val);
124 }
125 break;
126 case 3: /* % test arg1 op arg2 */
127 if (IS_BANG(argv[1])) {
128 ret_val = posix_unary_op(&argv[1]);
129 if (ret_val >= 0)
130 return (!ret_val);
131 } else {
132 ret_val = posix_binary_op(&argv[1]);
133 if (ret_val >= 0)
134 return (ret_val);
135 }
136 break;
137 case 4: /* % test ! arg1 op arg2 */
138 if (IS_BANG(argv[1])) {
139 ret_val = posix_binary_op(&argv[2]);
140 if (ret_val >= 0)
141 return (!ret_val);
142 }
143 break;
144 default:
145 break;
146 }
147
148 /*
149 * We use operator precedence parsing, evaluating the expression as
150 * we parse it. Parentheses are handled by bumping up the priority
151 * of operators using the variable "nest." We use the variable
152 * "skipping" to turn off evaluation temporarily for the short
153 * circuit boolean operators. (It is important do the short circuit
154 * evaluation because under NFS a stat operation can take infinitely
155 * long.)
156 */
157 opsp = opstack + STACKSIZE;
158 valsp = valstack;
159 nest = skipping = 0;
160 if (*ap == NULL) {
161 valstack[0].type = BOOLEAN;
162 valstack[0].u.num = 0;
163 goto done;
164 }
165 for (;;) {
166 opname = *ap++;
167 if (opname == NULL)
168 syntax();
169 if (opname[0] == '(' && opname[1] == '\0') {
170 nest += NESTINCR;
171 continue;
172 } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
173 if (opsp == &opstack[0])
174 overflow();
175 --opsp;
176 opsp->op = op;
177 opsp->pri = op_priority[op] + nest;
178 continue;
179 } else {
180 valsp->type = STRING;
181 valsp->u.string = opname;
182 valsp++;
183 }
184 for (;;) {
185 opname = *ap++;
186 if (opname == NULL) {
187 if (nest != 0)
188 syntax();
189 pri = 0;
190 break;
191 }
192 if (opname[0] != ')' || opname[1] != '\0') {
193 if ((op = lookup_op(opname, binary_op)) < 0)
194 syntax();
195 op += FIRST_BINARY_OP;
196 pri = op_priority[op] + nest;
197 break;
198 }
199 if ((nest -= NESTINCR) < 0)
200 syntax();
201 }
202 while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
203 binary = opsp->op;
204 for (;;) {
205 valsp--;
206 c = op_argflag[opsp->op];
207 if (c == OP_INT) {
208 if (valsp->type == STRING)
209 get_int(valsp->u.string,
210 &valsp->u.num);
211 valsp->type = INTEGER;
212 } else if (c >= OP_STRING) {
213 /* OP_STRING or OP_FILE */
214 if (valsp->type == INTEGER) {
215 if ((p = malloc(32)) == NULL)
216 err(2, NULL);
217 #ifdef SHELL
218 fmtstr(p, 32, "%d",
219 valsp->u.num);
220 #else
221 (void)sprintf(p,
222 "%d", valsp->u.num);
223 #endif
224 valsp->u.string = p;
225 } else if (valsp->type == BOOLEAN) {
226 if (valsp->u.num)
227 valsp->u.string =
228 "true";
229 else
230 valsp->u.string = "";
231 }
232 valsp->type = STRING;
233 if (c == OP_FILE && (fs.name == NULL ||
234 strcmp(fs.name, valsp->u.string))) {
235 fs.name = valsp->u.string;
236 fs.rcode =
237 stat(valsp->u.string,
238 &fs.stat);
239 }
240 }
241 if (binary < FIRST_BINARY_OP)
242 break;
243 binary = 0;
244 }
245 if (!skipping)
246 expr_operator(opsp->op, valsp, &fs);
247 else if (opsp->op == AND1 || opsp->op == OR1)
248 skipping--;
249 valsp++; /* push value */
250 opsp++; /* pop operator */
251 }
252 if (opname == NULL)
253 break;
254 if (opsp == &opstack[0])
255 overflow();
256 if (op == AND1 || op == AND2) {
257 op = AND1;
258 if (skipping || expr_is_false(valsp - 1))
259 skipping++;
260 }
261 if (op == OR1 || op == OR2) {
262 op = OR1;
263 if (skipping || !expr_is_false(valsp - 1))
264 skipping++;
265 }
266 opsp--;
267 opsp->op = op;
268 opsp->pri = pri;
269 }
270 done: return (expr_is_false(&valstack[0]));
271 }
272
273 static int
expr_is_false(val)274 expr_is_false(val)
275 struct value *val;
276 {
277
278 if (val->type == STRING) {
279 if (val->u.string[0] == '\0')
280 return (1);
281 } else { /* INTEGER or BOOLEAN */
282 if (val->u.num == 0)
283 return (1);
284 }
285 return (0);
286 }
287
288
289 /*
290 * Execute an operator. Op is the operator. Sp is the stack pointer;
291 * sp[0] refers to the first operand, sp[1] refers to the second operand
292 * (if any), and the result is placed in sp[0]. The operands are converted
293 * to the type expected by the operator before expr_operator is called.
294 * Fs is a pointer to a structure which holds the value of the last call
295 * to stat, to avoid repeated stat calls on the same file.
296 */
297 static void
expr_operator(op,sp,fs)298 expr_operator(op, sp, fs)
299 int op;
300 struct value *sp;
301 struct filestat *fs;
302 {
303 int i;
304
305 switch (op) {
306 case NOT:
307 sp->u.num = expr_is_false(sp);
308 sp->type = BOOLEAN;
309 break;
310 case ISEXIST:
311 if (fs == NULL || fs->rcode == -1)
312 goto false;
313 else
314 goto true;
315 case ISREAD:
316 i = S_IROTH;
317 goto permission;
318 case ISWRITE:
319 i = S_IWOTH;
320 goto permission;
321 case ISEXEC:
322 i = S_IXOTH;
323 permission: if (fs->stat.st_uid == geteuid())
324 i <<= 6;
325 else if (fs->stat.st_gid == getegid())
326 i <<= 3;
327 goto filebit; /* true if (stat.st_mode & i) != 0 */
328 case ISFILE:
329 i = S_IFREG;
330 goto filetype;
331 case ISDIR:
332 i = S_IFDIR;
333 goto filetype;
334 case ISCHAR:
335 i = S_IFCHR;
336 goto filetype;
337 case ISBLOCK:
338 i = S_IFBLK;
339 goto filetype;
340 case ISSYMLINK:
341 i = S_IFLNK;
342 (void)lstat(sp->u.string, &fs->stat);
343 goto filetype;
344 case ISFIFO:
345 i = S_IFIFO;
346 goto filetype;
347 filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
348 true: sp->u.num = 1;
349 else
350 false: sp->u.num = 0;
351 sp->type = BOOLEAN;
352 break;
353 case ISSETUID:
354 i = S_ISUID;
355 goto filebit;
356 case ISSETGID:
357 i = S_ISGID;
358 goto filebit;
359 case ISSTICKY:
360 i = S_ISVTX;
361 filebit: if (fs->stat.st_mode & i && fs->rcode >= 0)
362 goto true;
363 goto false;
364 case ISSIZE:
365 sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
366 sp->type = INTEGER;
367 break;
368 case ISTTY:
369 sp->u.num = isatty(sp->u.num);
370 sp->type = BOOLEAN;
371 break;
372 case NULSTR:
373 if (sp->u.string[0] == '\0')
374 goto true;
375 goto false;
376 case STRLEN:
377 sp->u.num = strlen(sp->u.string);
378 sp->type = INTEGER;
379 break;
380 case OR1:
381 case AND1:
382 /*
383 * These operators are mostly handled by the parser. If we
384 * get here it means that both operands were evaluated, so
385 * the value is the value of the second operand.
386 */
387 *sp = *(sp + 1);
388 break;
389 case STREQ:
390 case STRNE:
391 i = 0;
392 if (!strcmp(sp->u.string, (sp + 1)->u.string))
393 i++;
394 if (op == STRNE)
395 i = 1 - i;
396 sp->u.num = i;
397 sp->type = BOOLEAN;
398 break;
399 case EQ:
400 if (sp->u.num == (sp + 1)->u.num)
401 goto true;
402 goto false;
403 case NE:
404 if (sp->u.num != (sp + 1)->u.num)
405 goto true;
406 goto false;
407 case GT:
408 if (sp->u.num > (sp + 1)->u.num)
409 goto true;
410 goto false;
411 case LT:
412 if (sp->u.num < (sp + 1)->u.num)
413 goto true;
414 goto false;
415 case LE:
416 if (sp->u.num <= (sp + 1)->u.num)
417 goto true;
418 goto false;
419 case GE:
420 if (sp->u.num >= (sp + 1)->u.num)
421 goto true;
422 goto false;
423
424 }
425 }
426
427 static int
lookup_op(name,table)428 lookup_op(name, table)
429 char *name;
430 const char *const * table;
431 {
432 const char *const * tp;
433 const char *p;
434 char c;
435
436 c = name[1];
437 for (tp = table; (p = *tp) != NULL; tp++)
438 if (p[1] == c && !strcmp(p, name))
439 return (tp - table);
440 return (-1);
441 }
442
443 static int
posix_unary_op(argv)444 posix_unary_op(argv)
445 char **argv;
446 {
447 struct filestat fs;
448 struct value valp;
449 int op, c;
450 char *opname;
451
452 opname = *argv;
453 if ((op = lookup_op(opname, unary_op)) < 0)
454 return (-1);
455 c = op_argflag[op];
456 opname = argv[1];
457 valp.u.string = opname;
458 if (c == OP_FILE) {
459 fs.name = opname;
460 fs.rcode = stat(opname, &fs.stat);
461 } else if (c != OP_STRING)
462 return (-1);
463
464 expr_operator(op, &valp, &fs);
465 return (valp.u.num == 0);
466 }
467
468 static int
posix_binary_op(argv)469 posix_binary_op(argv)
470 char **argv;
471 {
472 struct value v[2];
473 int op, c;
474 char *opname;
475
476 opname = argv[1];
477 if ((op = lookup_op(opname, binary_op)) < 0)
478 return (-1);
479 op += FIRST_BINARY_OP;
480 c = op_argflag[op];
481
482 if (c == OP_INT) {
483 get_int(argv[0], &v[0].u.num);
484 get_int(argv[2], &v[1].u.num);
485 } else {
486 v[0].u.string = argv[0];
487 v[1].u.string = argv[2];
488 }
489 expr_operator(op, v, NULL);
490 return (v[0].u.num == 0);
491 }
492
493 /*
494 * Integer type checking.
495 */
496 static void
get_int(v,lp)497 get_int(v, lp)
498 char *v;
499 long *lp;
500 {
501 long val;
502 char *ep;
503
504 for (; *v && isspace(*v); ++v);
505 if (isdigit(*v)) {
506 errno = 0;
507 val = strtol(v, &ep, 10);
508 if (*ep != '\0')
509 errx(2, "%s: trailing non-numeric characters", v);
510 if (errno == ERANGE) {
511 if (val == LONG_MIN)
512 errx(2, "%s: underflow", v);
513 if (val == LONG_MAX)
514 errx(2, "%s: overflow", v);
515 }
516 *lp = val;
517 return;
518 }
519 errx(2, "%s: expected integer", v);
520 }
521
522 static void
syntax()523 syntax()
524 {
525
526 err(2, "syntax error");
527 }
528
529 static void
overflow()530 overflow()
531 {
532
533 err(2, "expression is too complex");
534 }
535