test.c revision 2664
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
38static 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
44static 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 */
76struct value {
77	int type;
78	union {
79		char *string;
80		long num;
81	} u;
82};
83
84struct operator {
85	short op;		/* Which operator. */
86	short pri;		/* Priority of operator. */
87};
88
89struct filestat {
90	char *name;		/* Name of file. */
91	int rcode;		/* Return code from stat. */
92	struct stat stat;	/* Status info on file. */
93};
94
95static int	expr_is_false __P((struct value *));
96static void	expr_operator __P((int, struct value *, struct filestat *));
97static void	get_int __P((char *, long *));
98static int	lookup_op __P((char *, const char *const *));
99static void	overflow __P((void));
100static int	posix_binary_op __P((char **));
101static int	posix_unary_op __P((char **));
102static void	syntax __P((void));
103
104int
105main(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]) && lookup_op(argv[3], andor_op) < 0 ) {
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	}
296done:	return (expr_is_false(&valstack[0]));
297}
298
299static int
300expr_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 */
323static void
324expr_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;
349permission:	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;
373filetype:	if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
374true:			sp->u.num = 1;
375		else
376false:			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;
387filebit:	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
453static int
454lookup_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
469static int
470posix_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
494static int
495posix_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 */
522static void
523get_int(v, lp)
524	char *v;
525	long *lp;
526{
527	long val;
528	char *ep;
529
530	for (; *v && isspace(*v); ++v);
531
532	if (isdigit(*v) || ((*v == '-' || *v == '+') && isdigit(*(v+1)))) {
533		errno = 0;
534		val = strtol(v, &ep, 10);
535		if (*ep != '\0')
536			errx(2, "%s: trailing non-numeric characters", v);
537		if (errno == ERANGE) {
538			if (val == LONG_MIN)
539				errx(2, "%s: underflow", v);
540			if (val == LONG_MAX)
541				errx(2, "%s: overflow", v);
542		}
543		*lp = val;
544		return;
545	}
546	errx(2, "%s: expected integer", v);
547}
548
549static void
550syntax()
551{
552
553	err(2, "syntax error");
554}
555
556static void
557overflow()
558{
559
560	err(2, "expression is too complex");
561}
562