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