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