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