xref: /netbsd-src/bin/test/test.c (revision ae1bfcddc410612bc8c58b807e1830becb69a24c)
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