xref: /original-bsd/bin/test/test.c (revision 4d63cfed)
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.4 (Berkeley) 02/12/93";
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 ((p = argv[0]) == NULL) {
88 		err("test: argc is zero.\n");
89 		exit(2);
90 	}
91 
92 	if (*p != '\0' && p[strlen(p) - 1] == '[') {
93 		if (strcmp(argv[--argc], "]"))
94 			err("missing ]");
95 		argv[argc] = 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 		/* MIPS machine returns NULL of '[ ]' is called. */
113 		return (argv[1] == 0 || *argv[1] == '\0') ? 1 : 0;
114 		break;
115 	case 2:				/* % test op arg */
116 		opname = argv[1];
117 		if (IS_BANG(opname))
118 			return (*argv[2] == '\0') ? 1 : 0;
119 		else {
120 			ret_val = posix_unary_op(&argv[1]);
121 			if (ret_val >= 0)
122 				return (ret_val);
123 		}
124 		break;
125 	case 3:				/* % test arg1 op arg2 */
126 		if (IS_BANG(argv[1])) {
127 			ret_val = posix_unary_op(&argv[1]);
128 			if (ret_val >= 0)
129 				return (!ret_val);
130 		} else {
131 			ret_val = posix_binary_op(&argv[1]);
132 			if (ret_val >= 0)
133 				return (ret_val);
134 		}
135 		break;
136 	case 4:				/* % test ! arg1 op arg2 */
137 		if (IS_BANG(argv[1])) {
138 			ret_val = posix_binary_op(&argv[2]);
139 			if (ret_val >= 0)
140 				return (!ret_val);
141 		}
142 		break;
143 	default:
144 		break;
145 	}
146 
147 	/*
148 	 * We use operator precedence parsing, evaluating the expression as
149 	 * we parse it.  Parentheses are handled by bumping up the priority
150 	 * of operators using the variable "nest."  We use the variable
151 	 * "skipping" to turn off evaluation temporarily for the short
152 	 * circuit boolean operators.  (It is important do the short circuit
153 	 * evaluation because under NFS a stat operation can take infinitely
154 	 * long.)
155 	 */
156 	opsp = opstack + STACKSIZE;
157 	valsp = valstack;
158 	nest = skipping = 0;
159 	if (*ap == NULL) {
160 		valstack[0].type = BOOLEAN;
161 		valstack[0].u.num = 0;
162 		goto done;
163 	}
164 	for (;;) {
165 		opname = *ap++;
166 		if (opname == NULL)
167 			syntax();
168 		if (opname[0] == '(' && opname[1] == '\0') {
169 			nest += NESTINCR;
170 			continue;
171 		} else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
172 			if (opsp == &opstack[0])
173 				overflow();
174 			--opsp;
175 			opsp->op = op;
176 			opsp->pri = op_priority[op] + nest;
177 			continue;
178 		} else {
179 			valsp->type = STRING;
180 			valsp->u.string = opname;
181 			valsp++;
182 		}
183 		for (;;) {
184 			opname = *ap++;
185 			if (opname == NULL) {
186 				if (nest != 0)
187 					syntax();
188 				pri = 0;
189 				break;
190 			}
191 			if (opname[0] != ')' || opname[1] != '\0') {
192 				if ((op = lookup_op(opname, binary_op)) < 0)
193 					syntax();
194 				op += FIRST_BINARY_OP;
195 				pri = op_priority[op] + nest;
196 				break;
197 			}
198 			if ((nest -= NESTINCR) < 0)
199 				syntax();
200 		}
201 		while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
202 			binary = opsp->op;
203 			for (;;) {
204 				valsp--;
205 				c = op_argflag[opsp->op];
206 				if (c == OP_INT) {
207 					if (valsp->type == STRING &&
208 					    int_tcheck(valsp->u.string))
209 						valsp->u.num =
210 						    atol(valsp->u.string);
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("%s",
217 							    strerror(errno));
218 #ifdef SHELL
219 						fmtstr(p, 32, "%d",
220 						    valsp->u.num);
221 #else
222 						(void)sprintf(p,
223 						    "%d", valsp->u.num);
224 #endif
225 						valsp->u.string = p;
226 					} else if (valsp->type == BOOLEAN) {
227 						if (valsp->u.num)
228 							valsp->u.string =
229 						            "true";
230 						else
231 							valsp->u.string = "";
232 					}
233 					valsp->type = STRING;
234 					if (c == OP_FILE && (fs.name == NULL ||
235 					    strcmp(fs.name, valsp->u.string))) {
236 						fs.name = valsp->u.string;
237 						fs.rcode =
238 						    stat(valsp->u.string,
239                                                     &fs.stat);
240 					}
241 				}
242 				if (binary < FIRST_BINARY_OP)
243 					break;
244 				binary = 0;
245 			}
246 			if (!skipping)
247 				expr_operator(opsp->op, valsp, &fs);
248 			else if (opsp->op == AND1 || opsp->op == OR1)
249 				skipping--;
250 			valsp++;		/* push value */
251 			opsp++;			/* pop operator */
252 		}
253 		if (opname == NULL)
254 			break;
255 		if (opsp == &opstack[0])
256 			overflow();
257 		if (op == AND1 || op == AND2) {
258 			op = AND1;
259 			if (skipping || expr_is_false(valsp - 1))
260 				skipping++;
261 		}
262 		if (op == OR1 || op == OR2) {
263 			op = OR1;
264 			if (skipping || !expr_is_false(valsp - 1))
265 				skipping++;
266 		}
267 		opsp--;
268 		opsp->op = op;
269 		opsp->pri = pri;
270 	}
271 done:	return (expr_is_false(&valstack[0]));
272 }
273 
274 static int
275 expr_is_false(val)
276 	struct value *val;
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 ISFIFO:
341 		i = S_IFIFO;
342 		goto filetype;
343 filetype:	if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
344 true:			sp->u.num = 1;
345 		else
346 false:			sp->u.num = 0;
347 		sp->type = BOOLEAN;
348 		break;
349 	case ISSETUID:
350 		i = S_ISUID;
351 		goto filebit;
352 	case ISSETGID:
353 		i = S_ISGID;
354 		goto filebit;
355 	case ISSTICKY:
356 		i = S_ISVTX;
357 filebit:	if (fs->stat.st_mode & i && fs->rcode >= 0)
358 			goto true;
359 		goto false;
360 	case ISSIZE:
361 		sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
362 		sp->type = INTEGER;
363 		break;
364 	case ISTTY:
365 		sp->u.num = isatty(sp->u.num);
366 		sp->type = BOOLEAN;
367 		break;
368 	case NULSTR:
369 		if (sp->u.string[0] == '\0')
370 			goto true;
371 		goto false;
372 	case STRLEN:
373 		sp->u.num = strlen(sp->u.string);
374 		sp->type = INTEGER;
375 		break;
376 	case OR1:
377 	case AND1:
378 		/*
379 		 * These operators are mostly handled by the parser.  If we
380 		 * get here it means that both operands were evaluated, so
381 		 * the value is the value of the second operand.
382 		 */
383 		*sp = *(sp + 1);
384 		break;
385 	case STREQ:
386 	case STRNE:
387 		i = 0;
388 		if (!strcmp(sp->u.string, (sp + 1)->u.string))
389 			i++;
390 		if (op == STRNE)
391 			i = 1 - i;
392 		sp->u.num = i;
393 		sp->type = BOOLEAN;
394 		break;
395 	case EQ:
396 		if (sp->u.num == (sp + 1)->u.num)
397 			goto true;
398 		goto false;
399 	case NE:
400 		if (sp->u.num != (sp + 1)->u.num)
401 			goto true;
402 		goto false;
403 	case GT:
404 		if (sp->u.num > (sp + 1)->u.num)
405 			goto true;
406 		goto false;
407 	case LT:
408 		if (sp->u.num < (sp + 1)->u.num)
409 			goto true;
410 		goto false;
411 	case LE:
412 		if (sp->u.num <= (sp + 1)->u.num)
413 			goto true;
414 		goto false;
415 	case GE:
416 		if (sp->u.num >= (sp + 1)->u.num)
417 			goto true;
418 		goto false;
419 
420 	}
421 }
422 
423 static int
424 lookup_op(name, table)
425 	char   *name;
426 	char   *const * table;
427 {
428 	register char *const * tp;
429 	register char const *p;
430 	char c;
431 
432 	c = name[1];
433 	for (tp = table; (p = *tp) != NULL; tp++)
434 		if (p[1] == c && !strcmp(p, name))
435 			return (tp - table);
436 	return (-1);
437 }
438 
439 static int
440 posix_unary_op(argv)
441 	char **argv;
442 {
443 	struct filestat fs;
444 	struct value valp;
445 	int op, c;
446 	char *opname;
447 
448 	opname = *argv;
449 	if ((op = lookup_op(opname, unary_op)) < 0)
450 		return (-1);
451 	c = op_argflag[op];
452 	opname = argv[1];
453 	valp.u.string = opname;
454 	if (c == OP_FILE) {
455 		fs.name = opname;
456 		fs.rcode = stat(opname, &fs.stat);
457 	} else if (c != OP_STRING)
458 		return (-1);
459 
460 	expr_operator(op, &valp, &fs);
461 	return (valp.u.num == 0);
462 }
463 
464 static int
465 posix_binary_op(argv)
466 	char  **argv;
467 {
468 	struct value v[2];
469 	int op, c;
470 	char *opname;
471 
472 	opname = argv[1];
473 	if ((op = lookup_op(opname, binary_op)) < 0)
474 		return (-1);
475 	op += FIRST_BINARY_OP;
476 	c = op_argflag[op];
477 
478 	if (c == OP_INT && int_tcheck(argv[0]) && int_tcheck(argv[2])) {
479 		v[0].u.num = atol(argv[0]);
480 		v[1].u.num = atol(argv[2]);
481 	} else {
482 		v[0].u.string = argv[0];
483 		v[1].u.string = argv[2];
484 	}
485 	expr_operator(op, v, NULL);
486 	return (v[0].u.num == 0);
487 }
488 
489 /*
490  * Integer type checking.
491  */
492 static int
493 int_tcheck(v)
494 	char *v;
495 {
496 	char *p;
497 
498 	for (p = v; *p != '\0'; p++)
499 		if (!isdigit(*p))
500 			err("illegal operand \"%s\" -- expected integer.", v);
501 	return (1);
502 }
503 
504 static void
505 syntax()
506 {
507 	err("syntax error");
508 }
509 
510 static void
511 overflow()
512 {
513 	err("expression is too complex");
514 }
515 
516 #if __STDC__
517 #include <stdarg.h>
518 #else
519 #include <varargs.h>
520 #endif
521 
522 void
523 #if __STDC__
524 err(const char *fmt, ...)
525 #else
526 err(fmt, va_alist)
527 	char *fmt;
528         va_dcl
529 #endif
530 {
531 	va_list ap;
532 #if __STDC__
533 	va_start(ap, fmt);
534 #else
535 	va_start(ap);
536 #endif
537 	(void)fprintf(stderr, "test: ");
538 	(void)vfprintf(stderr, fmt, ap);
539 	va_end(ap);
540 	(void)fprintf(stderr, "\n");
541 	exit(2);
542 	/* NOTREACHED */
543 }
544