xref: /original-bsd/bin/test/test.c (revision 3b6250d9)
1 /*
2  * Copyright (c) 1988 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 static char copyright[] =
13 "@(#) Copyright (c) 1988 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	1.3 (Berkeley) 06/03/92";
19 #endif				/* not lint */
20 
21 #include <stdio.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include "operators.h"
26 #include "extern.h"
27 
28 #define STACKSIZE 12
29 #define NESTINCR 16
30 
31 /* data types */
32 #define STRING 0
33 #define INTEGER 1
34 #define BOOLEAN 2
35 
36 #define INITARGS(argv)	if (argv[0] == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
37 
38 #define IS_BANG(s) (s[0] == '!' && s[1] == '\0')
39 #define equal(s1, s2)   (strcmp(s1, s2) == 0)
40 
41 /*
42  * This structure hold a value.  The type keyword specifies the type of
43  * the value, and the union u holds the value.  The value of a boolean
44  * is stored in u.num (1 = TRUE, 0 = FALSE).
45  */
46 
47 struct value {
48 	int     type;
49 	union {
50 		char   *string;
51 		long    num;
52 	}       u;
53 };
54 
55 struct operator {
56 	short   op;		/* which operator */
57 	short   pri;		/* priority of operator */
58 };
59 
60 struct filestat {
61 	char   *name;		/* name of file */
62 	int     rcode;		/* return code from stat */
63 	struct stat stat;	/* status info on file */
64 };
65 
66 static int expr_is_false __P((struct value *));
67 static void expr_operator __P((int, struct value *, struct filestat *));
68 static int lookup_op __P((char *, char *const *));
69 static long atol __P((const char *));
70 static int posix_binary_op __P((char **));
71 static int posix_unary_op __P((char **));
72 static int int_tcheck __P((char *));
73 
74 int
75 main(argc, argv)
76 	int     argc;
77 	char  **argv;
78 {
79 	char  **ap;
80 	char   *opname;
81 	char    c;
82 	char   *p;
83 	int     nest;		/* parentheses nesting */
84 	int     op;
85 	int     pri;
86 	int     skipping;
87 	int     binary;
88 	struct operator opstack[STACKSIZE];
89 	struct operator *opsp;
90 	struct value valstack[STACKSIZE + 1];
91 	struct value *valsp;
92 	struct filestat fs;
93 	int     ret_val;
94 
95 	INITARGS(argv);
96 	if (**argv == '[') {
97 		if (!equal(argv[argc - 1], "]"))
98 			error("missing ]");
99 		argv[argc - 1] = NULL;
100 	}
101 	ap = argv + 1;
102 	fs.name = NULL;
103 
104 	/* Test(1) implements an inherently ambiguous grammer.  In order to
105 	 * assure some degree of consistency, we special case the POSIX
106 	 * requirements to assure correct evaluation for POSIX following
107 	 * scripts.  The following special cases comply with POSIX
108 	 * P1003.2/D11.2 Section 4.62.4. */
109 	switch (argc - 1) {
110 	case 0:		/* % test */
111 		return 1;
112 		break;
113 	case 1:		/* % test arg */
114 		return (*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') ? 1 : 0;
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 	/* 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 = 0;
159 	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 			goto 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 				goto overflow;
175 			--opsp;
176 			opsp->op = op;
177 			opsp->pri = op_priority[op] + nest;
178 			continue;
179 
180 		} else {
181 			valsp->type = STRING;
182 			valsp->u.string = opname;
183 			valsp++;
184 		}
185 		for (;;) {
186 			opname = *ap++;
187 			if (opname == NULL) {
188 				if (nest != 0)
189 					goto syntax;
190 				pri = 0;
191 				break;
192 			}
193 			if (opname[0] != ')' || opname[1] != '\0') {
194 				if ((op = lookup_op(opname, binary_op)) < 0)
195 					goto syntax;
196 				op += FIRST_BINARY_OP;
197 				pri = op_priority[op] + nest;
198 				break;
199 			}
200 			if ((nest -= NESTINCR) < 0)
201 				goto syntax;
202 		}
203 		while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
204 			binary = opsp->op;
205 			for (;;) {
206 				valsp--;
207 				c = op_argflag[opsp->op];
208 				if (c == OP_INT) {
209 					if (valsp->type == STRING &&
210 					    int_tcheck(valsp->u.string))
211 						valsp->u.num =
212 						    atol(valsp->u.string);
213 					valsp->type = INTEGER;
214 				} else if (c >= OP_STRING) {
215 					            /* OP_STRING or OP_FILE */
216 					if (valsp->type == INTEGER) {
217 						p = stalloc(32);
218 #ifdef SHELL
219 						fmtstr(p, 32, "%d",
220 						    valsp->u.num);
221 #else
222 						sprintf(p, "%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
234 					    && (fs.name == NULL
235 						|| !equal(fs.name,
236                                                     valsp->u.string))) {
237 						fs.name = valsp->u.string;
238 						fs.rcode =
239 						    stat(valsp->u.string,
240                                                     &fs.stat);
241 					}
242 				}
243 				if (binary < FIRST_BINARY_OP)
244 					break;
245 				binary = 0;
246 			}
247 			if (!skipping)
248 				expr_operator(opsp->op, valsp, &fs);
249 			else if (opsp->op == AND1 || opsp->op == OR1)
250 				skipping--;
251 			valsp++;/* push value */
252 			opsp++;	/* pop operator */
253 		}
254 		if (opname == NULL)
255 			break;
256 		if (opsp == &opstack[0])
257 			goto overflow;
258 		if (op == AND1 || op == AND2) {
259 			op = AND1;
260 			if (skipping || expr_is_false(valsp - 1))
261 				skipping++;
262 		}
263 		if (op == OR1 || op == OR2) {
264 			op = OR1;
265 			if (skipping || !expr_is_false(valsp - 1))
266 				skipping++;
267 		}
268 		opsp--;
269 		opsp->op = op;
270 		opsp->pri = pri;
271 	}
272 done:
273 	return expr_is_false(&valstack[0]);
274 
275 syntax:   error("syntax error");
276 overflow: error("Expression too complex");
277 
278 }
279 
280 
281 static int
282 expr_is_false(val)
283 	struct value *val;
284 {
285 	if (val->type == STRING) {
286 		if (val->u.string[0] == '\0')
287 			return 1;
288 	} else {		/* INTEGER or BOOLEAN */
289 		if (val->u.num == 0)
290 			return 1;
291 	}
292 	return 0;
293 }
294 
295 
296 /*
297  * Execute an operator.  Op is the operator.  Sp is the stack pointer;
298  * sp[0] refers to the first operand, sp[1] refers to the second operand
299  * (if any), and the result is placed in sp[0].  The operands are converted
300  * to the type expected by the operator before expr_operator is called.
301  * Fs is a pointer to a structure which holds the value of the last call
302  * to stat, to avoid repeated stat calls on the same file.
303  */
304 
305 static void
306 expr_operator(op, sp, fs)
307 	int     op;
308 	struct value *sp;
309 	struct filestat *fs;
310 {
311 	int     i;
312 
313 	switch (op) {
314 	case NOT:
315 		sp->u.num = expr_is_false(sp);
316 		sp->type = BOOLEAN;
317 		break;
318 	case ISEXIST:
319 		if (fs == NULL || fs->rcode == -1)
320 			goto false;
321 		else
322 			goto true;
323 	case ISREAD:
324 		i = 04;
325 		goto permission;
326 	case ISWRITE:
327 		i = 02;
328 		goto permission;
329 	case ISEXEC:
330 		i = 01;
331 permission:
332 		if (fs->stat.st_uid == geteuid())
333 			i <<= 6;
334 		else if (fs->stat.st_gid == getegid())
335 			i <<= 3;
336 		goto filebit;	/* true if (stat.st_mode & i) != 0 */
337 	case ISFILE:
338 		i = S_IFREG;
339 		goto filetype;
340 	case ISDIR:
341 		i = S_IFDIR;
342 		goto filetype;
343 	case ISCHAR:
344 		i = S_IFCHR;
345 		goto filetype;
346 	case ISBLOCK:
347 		i = S_IFBLK;
348 		goto filetype;
349 	case ISFIFO:
350 #ifdef S_IFIFO
351 		i = S_IFIFO;
352 		goto filetype;
353 #else
354 		goto false;
355 #endif
356 filetype:
357 		if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) {
358 	true:
359 			sp->u.num = 1;
360 		} else {
361 	false:
362 			sp->u.num = 0;
363 		}
364 		sp->type = BOOLEAN;
365 		break;
366 	case ISSETUID:
367 		i = S_ISUID;
368 		goto filebit;
369 	case ISSETGID:
370 		i = S_ISGID;
371 		goto filebit;
372 	case ISSTICKY:
373 		i = S_ISVTX;
374 filebit:
375 		if (fs->stat.st_mode & i && fs->rcode >= 0)
376 			goto true;
377 		goto false;
378 	case ISSIZE:
379 		sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
380 		sp->type = INTEGER;
381 		break;
382 	case ISTTY:
383 		sp->u.num = isatty(sp->u.num);
384 		sp->type = BOOLEAN;
385 		break;
386 	case NULSTR:
387 		if (sp->u.string[0] == '\0')
388 			goto true;
389 		goto false;
390 	case STRLEN:
391 		sp->u.num = strlen(sp->u.string);
392 		sp->type = INTEGER;
393 		break;
394 	case OR1:
395 	case AND1:
396 		/* These operators are mostly handled by the parser.  If we
397 		 * get here it means that both operands were evaluated, so
398 		 * the value is the value of the second operand. */
399 		*sp = *(sp + 1);
400 		break;
401 	case STREQ:
402 	case STRNE:
403 		i = 0;
404 		if (equal(sp->u.string, (sp + 1)->u.string))
405 			i++;
406 		if (op == STRNE)
407 			i = 1 - i;
408 		sp->u.num = i;
409 		sp->type = BOOLEAN;
410 		break;
411 	case EQ:
412 		if (sp->u.num == (sp + 1)->u.num)
413 			goto true;
414 		goto false;
415 	case NE:
416 		if (sp->u.num != (sp + 1)->u.num)
417 			goto true;
418 		goto false;
419 	case GT:
420 		if (sp->u.num > (sp + 1)->u.num)
421 			goto true;
422 		goto false;
423 	case LT:
424 		if (sp->u.num < (sp + 1)->u.num)
425 			goto true;
426 		goto false;
427 	case LE:
428 		if (sp->u.num <= (sp + 1)->u.num)
429 			goto true;
430 		goto false;
431 	case GE:
432 		if (sp->u.num >= (sp + 1)->u.num)
433 			goto true;
434 		goto false;
435 
436 	}
437 }
438 
439 
440 static int
441 lookup_op(name, table)
442 	char   *name;
443 	char   *const * table;
444 {
445 	register char *const * tp;
446 	register char const *p;
447 	char    c = name[1];
448 
449 	for (tp = table; (p = *tp) != NULL; tp++) {
450 		if (p[1] == c && equal(p, name))
451 			return tp - table;
452 	}
453 	return -1;
454 }
455 
456 static int
457 posix_unary_op(argv)
458 	char  **argv;
459 {
460 	int     op, c;
461 	char   *opname;
462 	struct filestat fs;
463 	struct value valp;
464 
465 	opname = *argv;
466 	if ((op = lookup_op(opname, unary_op)) < 0)
467 		return -1;
468 	c = op_argflag[op];
469 	opname = argv[1];
470 	valp.u.string = opname;
471 	if (c == OP_FILE) {
472 		fs.name = opname;
473 		fs.rcode = stat(opname, &fs.stat);
474 	} else if (c != OP_STRING)
475 		return -1;
476 
477 	expr_operator(op, &valp, &fs);
478 	return (valp.u.num == 0);
479 }
480 
481 
482 static int
483 posix_binary_op(argv)
484 	char  **argv;
485 {
486 	int     op, c;
487 	char   *opname;
488 	struct value v[2];
489 
490 	opname = argv[1];
491 	if ((op = lookup_op(opname, binary_op)) < 0)
492 		return -1;
493 	op += FIRST_BINARY_OP;
494 	c = op_argflag[op];
495 
496 	if (c == OP_INT && int_tcheck(argv[0]) && int_tcheck(argv[2])) {
497 		v[0].u.num = atol(argv[0]);
498 		v[1].u.num = atol(argv[2]);
499 	}
500 	expr_operator(op, v, NULL);
501 	return (v[0].u.num == 0);
502 }
503 
504 /*
505  * Integer type checking.
506  */
507 static int
508 int_tcheck(v)
509 	char   *v;
510 {
511 	char   *p;
512 	char    outbuf[512];
513 
514 	for (p = v; *p != '\0'; p++)
515 		if (*p < '0' || *p > '9') {
516 			snprintf(outbuf, sizeof(outbuf),
517 			  "Illegal operand \"%s\" -- expected integer.", v);
518 			error(outbuf);
519 		}
520 	return 1;
521 }
522