xref: /freebsd/bin/test/test.c (revision afe61c15161c324a7af299a9b8457aba5afc92db)
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 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 static char sccsid[] = "@(#)test.c	8.3 (Berkeley) 4/2/94";
45 #endif /* not lint */
46 
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 
50 #include <ctype.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <limits.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 
59 #include "operators.h"
60 
61 #define	STACKSIZE	12
62 #define	NESTINCR	16
63 
64 /* data types */
65 #define	STRING	0
66 #define	INTEGER	1
67 #define	BOOLEAN	2
68 
69 #define	IS_BANG(s) (s[0] == '!' && s[1] == '\0')
70 
71 /*
72  * This structure hold a value.  The type keyword specifies the type of
73  * the value, and the union u holds the value.  The value of a boolean
74  * is stored in u.num (1 = TRUE, 0 = FALSE).
75  */
76 struct value {
77 	int type;
78 	union {
79 		char *string;
80 		long num;
81 	} u;
82 };
83 
84 struct operator {
85 	short op;		/* Which operator. */
86 	short pri;		/* Priority of operator. */
87 };
88 
89 struct filestat {
90 	char *name;		/* Name of file. */
91 	int rcode;		/* Return code from stat. */
92 	struct stat stat;	/* Status info on file. */
93 };
94 
95 static int	expr_is_false __P((struct value *));
96 static void	expr_operator __P((int, struct value *, struct filestat *));
97 static void	get_int __P((char *, long *));
98 static int	lookup_op __P((char *, const char *const *));
99 static void	overflow __P((void));
100 static int	posix_binary_op __P((char **));
101 static int	posix_unary_op __P((char **));
102 static void	syntax __P((void));
103 
104 int
105 main(argc, argv)
106 	int argc;
107 	char *argv[];
108 {
109 	struct operator opstack[STACKSIZE];
110 	struct operator *opsp;
111 	struct value valstack[STACKSIZE + 1];
112 	struct value *valsp;
113 	struct filestat fs;
114 	char  c, **ap, *opname, *p;
115 	int binary, nest, op, pri, ret_val, skipping;
116 
117 	if ((p = argv[0]) == NULL)
118 		errx(2, "test: argc is zero");
119 
120 	if (*p != '\0' && p[strlen(p) - 1] == '[') {
121 		if (strcmp(argv[--argc], "]"))
122 			errx(2, "missing ]");
123 		argv[argc] = NULL;
124 	}
125 	ap = argv + 1;
126 	fs.name = NULL;
127 
128 	/*
129 	 * Test(1) implements an inherently ambiguous grammer.  In order to
130 	 * assure some degree of consistency, we special case the POSIX 1003.2
131 	 * requirements to assure correct evaluation for POSIX scripts.  The
132 	 * following special cases comply with POSIX P1003.2/D11.2 Section
133 	 * 4.62.4.
134 	 */
135 	switch(argc - 1) {
136 	case 0:				/* % test */
137 		return (1);
138 		break;
139 	case 1:				/* % test arg */
140 		return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0;
141 		break;
142 	case 2:				/* % test op arg */
143 		opname = argv[1];
144 		if (IS_BANG(opname))
145 			return (*argv[2] == '\0') ? 0 : 1;
146 		else {
147 			ret_val = posix_unary_op(&argv[1]);
148 			if (ret_val >= 0)
149 				return (ret_val);
150 		}
151 		break;
152 	case 3:				/* % test arg1 op arg2 */
153 		if (IS_BANG(argv[1])) {
154 			ret_val = posix_unary_op(&argv[1]);
155 			if (ret_val >= 0)
156 				return (!ret_val);
157 		} else {
158 			ret_val = posix_binary_op(&argv[1]);
159 			if (ret_val >= 0)
160 				return (ret_val);
161 		}
162 		break;
163 	case 4:				/* % test ! arg1 op arg2 */
164 		if (IS_BANG(argv[1])) {
165 			ret_val = posix_binary_op(&argv[2]);
166 			if (ret_val >= 0)
167 				return (!ret_val);
168 		}
169 		break;
170 	default:
171 		break;
172 	}
173 
174 	/*
175 	 * We use operator precedence parsing, evaluating the expression as
176 	 * we parse it.  Parentheses are handled by bumping up the priority
177 	 * of operators using the variable "nest."  We use the variable
178 	 * "skipping" to turn off evaluation temporarily for the short
179 	 * circuit boolean operators.  (It is important do the short circuit
180 	 * evaluation because under NFS a stat operation can take infinitely
181 	 * long.)
182 	 */
183 	opsp = opstack + STACKSIZE;
184 	valsp = valstack;
185 	nest = skipping = 0;
186 	if (*ap == NULL) {
187 		valstack[0].type = BOOLEAN;
188 		valstack[0].u.num = 0;
189 		goto done;
190 	}
191 	for (;;) {
192 		opname = *ap++;
193 		if (opname == NULL)
194 			syntax();
195 		if (opname[0] == '(' && opname[1] == '\0') {
196 			nest += NESTINCR;
197 			continue;
198 		} else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
199 			if (opsp == &opstack[0])
200 				overflow();
201 			--opsp;
202 			opsp->op = op;
203 			opsp->pri = op_priority[op] + nest;
204 			continue;
205 		} else {
206 			valsp->type = STRING;
207 			valsp->u.string = opname;
208 			valsp++;
209 		}
210 		for (;;) {
211 			opname = *ap++;
212 			if (opname == NULL) {
213 				if (nest != 0)
214 					syntax();
215 				pri = 0;
216 				break;
217 			}
218 			if (opname[0] != ')' || opname[1] != '\0') {
219 				if ((op = lookup_op(opname, binary_op)) < 0)
220 					syntax();
221 				op += FIRST_BINARY_OP;
222 				pri = op_priority[op] + nest;
223 				break;
224 			}
225 			if ((nest -= NESTINCR) < 0)
226 				syntax();
227 		}
228 		while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
229 			binary = opsp->op;
230 			for (;;) {
231 				valsp--;
232 				c = op_argflag[opsp->op];
233 				if (c == OP_INT) {
234 					if (valsp->type == STRING)
235 						get_int(valsp->u.string,
236 						    &valsp->u.num);
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(2, NULL);
243 #ifdef SHELL
244 						fmtstr(p, 32, "%d",
245 						    valsp->u.num);
246 #else
247 						(void)sprintf(p,
248 						    "%d", valsp->u.num);
249 #endif
250 						valsp->u.string = p;
251 					} else if (valsp->type == BOOLEAN) {
252 						if (valsp->u.num)
253 							valsp->u.string =
254 						            "true";
255 						else
256 							valsp->u.string = "";
257 					}
258 					valsp->type = STRING;
259 					if (c == OP_FILE && (fs.name == NULL ||
260 					    strcmp(fs.name, valsp->u.string))) {
261 						fs.name = valsp->u.string;
262 						fs.rcode =
263 						    stat(valsp->u.string,
264                                                     &fs.stat);
265 					}
266 				}
267 				if (binary < FIRST_BINARY_OP)
268 					break;
269 				binary = 0;
270 			}
271 			if (!skipping)
272 				expr_operator(opsp->op, valsp, &fs);
273 			else if (opsp->op == AND1 || opsp->op == OR1)
274 				skipping--;
275 			valsp++;		/* push value */
276 			opsp++;			/* pop operator */
277 		}
278 		if (opname == NULL)
279 			break;
280 		if (opsp == &opstack[0])
281 			overflow();
282 		if (op == AND1 || op == AND2) {
283 			op = AND1;
284 			if (skipping || expr_is_false(valsp - 1))
285 				skipping++;
286 		}
287 		if (op == OR1 || op == OR2) {
288 			op = OR1;
289 			if (skipping || !expr_is_false(valsp - 1))
290 				skipping++;
291 		}
292 		opsp--;
293 		opsp->op = op;
294 		opsp->pri = pri;
295 	}
296 done:	return (expr_is_false(&valstack[0]));
297 }
298 
299 static int
300 expr_is_false(val)
301 	struct value *val;
302 {
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 ISSYMLINK:
367 		i = S_IFLNK;
368 		(void)lstat(sp->u.string, &fs->stat);
369 		goto filetype;
370 	case ISFIFO:
371 		i = S_IFIFO;
372 		goto filetype;
373 filetype:	if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
374 true:			sp->u.num = 1;
375 		else
376 false:			sp->u.num = 0;
377 		sp->type = BOOLEAN;
378 		break;
379 	case ISSETUID:
380 		i = S_ISUID;
381 		goto filebit;
382 	case ISSETGID:
383 		i = S_ISGID;
384 		goto filebit;
385 	case ISSTICKY:
386 		i = S_ISVTX;
387 filebit:	if (fs->stat.st_mode & i && fs->rcode >= 0)
388 			goto true;
389 		goto false;
390 	case ISSIZE:
391 		sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
392 		sp->type = INTEGER;
393 		break;
394 	case ISTTY:
395 		sp->u.num = isatty(sp->u.num);
396 		sp->type = BOOLEAN;
397 		break;
398 	case NULSTR:
399 		if (sp->u.string[0] == '\0')
400 			goto true;
401 		goto false;
402 	case STRLEN:
403 		sp->u.num = strlen(sp->u.string);
404 		sp->type = INTEGER;
405 		break;
406 	case OR1:
407 	case AND1:
408 		/*
409 		 * These operators are mostly handled by the parser.  If we
410 		 * get here it means that both operands were evaluated, so
411 		 * the value is the value of the second operand.
412 		 */
413 		*sp = *(sp + 1);
414 		break;
415 	case STREQ:
416 	case STRNE:
417 		i = 0;
418 		if (!strcmp(sp->u.string, (sp + 1)->u.string))
419 			i++;
420 		if (op == STRNE)
421 			i = 1 - i;
422 		sp->u.num = i;
423 		sp->type = BOOLEAN;
424 		break;
425 	case EQ:
426 		if (sp->u.num == (sp + 1)->u.num)
427 			goto true;
428 		goto false;
429 	case NE:
430 		if (sp->u.num != (sp + 1)->u.num)
431 			goto true;
432 		goto false;
433 	case GT:
434 		if (sp->u.num > (sp + 1)->u.num)
435 			goto true;
436 		goto false;
437 	case LT:
438 		if (sp->u.num < (sp + 1)->u.num)
439 			goto true;
440 		goto false;
441 	case LE:
442 		if (sp->u.num <= (sp + 1)->u.num)
443 			goto true;
444 		goto false;
445 	case GE:
446 		if (sp->u.num >= (sp + 1)->u.num)
447 			goto true;
448 		goto false;
449 
450 	}
451 }
452 
453 static int
454 lookup_op(name, table)
455 	char *name;
456 	const char *const * table;
457 {
458 	const char *const * tp;
459 	const char *p;
460 	char c;
461 
462 	c = name[1];
463 	for (tp = table; (p = *tp) != NULL; tp++)
464 		if (p[1] == c && !strcmp(p, name))
465 			return (tp - table);
466 	return (-1);
467 }
468 
469 static int
470 posix_unary_op(argv)
471 	char **argv;
472 {
473 	struct filestat fs;
474 	struct value valp;
475 	int op, c;
476 	char *opname;
477 
478 	opname = *argv;
479 	if ((op = lookup_op(opname, unary_op)) < 0)
480 		return (-1);
481 	c = op_argflag[op];
482 	opname = argv[1];
483 	valp.u.string = opname;
484 	if (c == OP_FILE) {
485 		fs.name = opname;
486 		fs.rcode = stat(opname, &fs.stat);
487 	} else if (c != OP_STRING)
488 		return (-1);
489 
490 	expr_operator(op, &valp, &fs);
491 	return (valp.u.num == 0);
492 }
493 
494 static int
495 posix_binary_op(argv)
496 	char  **argv;
497 {
498 	struct value v[2];
499 	int op, c;
500 	char *opname;
501 
502 	opname = argv[1];
503 	if ((op = lookup_op(opname, binary_op)) < 0)
504 		return (-1);
505 	op += FIRST_BINARY_OP;
506 	c = op_argflag[op];
507 
508 	if (c == OP_INT) {
509 		get_int(argv[0], &v[0].u.num);
510 		get_int(argv[2], &v[1].u.num);
511 	} else {
512 		v[0].u.string = argv[0];
513 		v[1].u.string = argv[2];
514 	}
515 	expr_operator(op, v, NULL);
516 	return (v[0].u.num == 0);
517 }
518 
519 /*
520  * Integer type checking.
521  */
522 static void
523 get_int(v, lp)
524 	char *v;
525 	long *lp;
526 {
527 	long val;
528 	char *ep;
529 
530 	for (; *v && isspace(*v); ++v);
531 	if (isdigit(*v)) {
532 		errno = 0;
533 		val = strtol(v, &ep, 10);
534 		if (*ep != '\0')
535 			errx(2, "%s: trailing non-numeric characters", v);
536 		if (errno == ERANGE) {
537 			if (val == LONG_MIN)
538 				errx(2, "%s: underflow", v);
539 			if (val == LONG_MAX)
540 				errx(2, "%s: overflow", v);
541 		}
542 		*lp = val;
543 		return;
544 	}
545 	errx(2, "%s: expected integer", v);
546 }
547 
548 static void
549 syntax()
550 {
551 
552 	err(2, "syntax error");
553 }
554 
555 static void
556 overflow()
557 {
558 
559 	err(2, "expression is too complex");
560 }
561