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