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