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