test.c revision 4167
1271947Sdes/*-
2271947Sdes * Copyright (c) 1992, 1993, 1994
3271947Sdes *	The Regents of the University of California.  All rights reserved.
4271947Sdes *
5271947Sdes * This code is derived from software contributed to Berkeley by
6271947Sdes * Kenneth Almquist.
7271947Sdes *
8271947Sdes * Redistribution and use in source and binary forms, with or without
9271947Sdes * modification, are permitted provided that the following conditions
10271947Sdes * are met:
11271947Sdes * 1. Redistributions of source code must retain the above copyright
12271947Sdes *    notice, this list of conditions and the following disclaimer.
13271947Sdes * 2. Redistributions in binary form must reproduce the above copyright
14271947Sdes *    notice, this list of conditions and the following disclaimer in the
15271947Sdes *    documentation and/or other materials provided with the distribution.
16271947Sdes * 3. All advertising materials mentioning features or use of this software
17271947Sdes *    must display the following acknowledgement:
18271947Sdes *	This product includes software developed by the University of
19271947Sdes *	California, Berkeley and its contributors.
20271947Sdes * 4. Neither the name of the University nor the names of its contributors
21271947Sdes *    may be used to endorse or promote products derived from this software
22255376Sdes *    without specific prior written permission.
23255376Sdes *
24255376Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25255376Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26255376Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27255376Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28255376Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29255376Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30255376Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31255376Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32255376Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33255376Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34255376Sdes * SUCH DAMAGE.
35255376Sdes *
36255376Sdes *	$Id: test.c,v 1.5 1994/11/05 17:07:14 ache Exp $
37255376Sdes */
38255376Sdes
39255376Sdes#ifndef lint
40255376Sdesstatic char copyright[] =
41255376Sdes"@(#) Copyright (c) 1992, 1993, 1994\n\
42255376Sdes	The Regents of the University of California.  All rights reserved.\n";
43255376Sdes#endif /* not lint */
44255376Sdes
45255376Sdes#ifndef lint
46255376Sdesstatic char sccsid[] = "@(#)test.c	8.3 (Berkeley) 4/2/94";
47255376Sdes#endif /* not lint */
48255376Sdes
49236109Sdes#include <sys/types.h>
50236109Sdes#include <sys/stat.h>
51236109Sdes
52236109Sdes#include <ctype.h>
53236109Sdes#include <err.h>
54236109Sdes#include <errno.h>
55236109Sdes#include <limits.h>
56236109Sdes#include <stdio.h>
57236109Sdes#include <stdlib.h>
58236109Sdes#include <string.h>
59236109Sdes#include <unistd.h>
60236109Sdes
61236109Sdes#include "operators.h"
62236109Sdes
63236109Sdes#define	STACKSIZE	12
64236109Sdes#define	NESTINCR	16
65236109Sdes
66236109Sdes/* data types */
67236109Sdes#define	STRING	0
68236109Sdes#define	INTEGER	1
69236109Sdes#define	BOOLEAN	2
70236109Sdes
71236109Sdes#define	IS_BANG(s) (s[0] == '!' && s[1] == '\0')
72236109Sdes
73236109Sdes/*
74236109Sdes * This structure hold a value.  The type keyword specifies the type of
75236109Sdes * the value, and the union u holds the value.  The value of a boolean
76236109Sdes * is stored in u.num (1 = TRUE, 0 = FALSE).
77236109Sdes */
78236109Sdesstruct value {
79236109Sdes	int type;
80236109Sdes	union {
81236109Sdes		char *string;
82236109Sdes		long num;
83236109Sdes	} u;
84236109Sdes};
85236109Sdes
86236109Sdesstruct operator {
87236109Sdes	short op;		/* Which operator. */
88236109Sdes	short pri;		/* Priority of operator. */
89236109Sdes};
90236109Sdes
91236109Sdesstruct filestat {
92236109Sdes	char *name;		/* Name of file. */
93236109Sdes	int rcode;		/* Return code from stat. */
94236109Sdes	struct stat stat;	/* Status info on file. */
95236109Sdes};
96236109Sdes
97228692Sdesstatic int	expr_is_false __P((struct value *));
98228692Sdesstatic void	expr_operator __P((int, struct value *, struct filestat *));
99228692Sdesstatic void	get_int __P((char *, long *));
100228692Sdesstatic int	lookup_op __P((char *, const char *const *));
101228692Sdesstatic void	overflow __P((void));
102228692Sdesstatic int	posix_binary_op __P((char **));
103228692Sdesstatic int	posix_unary_op __P((char **));
104228692Sdesstatic void	syntax __P((void));
105228692Sdes
106228692Sdesint
107228692Sdesmain(argc, argv)
108228692Sdes	int argc;
109228692Sdes	char *argv[];
110228692Sdes{
111228692Sdes	struct operator opstack[STACKSIZE];
112228692Sdes	struct operator *opsp;
113228692Sdes	struct value valstack[STACKSIZE + 1];
114228692Sdes	struct value *valsp;
115228692Sdes	struct filestat fs;
116228692Sdes	char  c, **ap, *opname, *p;
117228692Sdes	int binary, nest, op, pri, ret_val, skipping;
118228692Sdes
119228692Sdes	if ((p = argv[0]) == NULL)
120228692Sdes		errx(2, "test: argc is zero");
121271947Sdes
122228692Sdes	if (*p != '\0' && p[strlen(p) - 1] == '[') {
123174832Sdes		if (strcmp(argv[--argc], "]"))
124147455Sdes			errx(2, "missing ]");
125174832Sdes		argv[argc] = NULL;
126174832Sdes	}
127174832Sdes	ap = argv + 1;
128174832Sdes	fs.name = NULL;
129174832Sdes
130174832Sdes	/*
131174832Sdes	 * Test(1) implements an inherently ambiguous grammer.  In order to
132174832Sdes	 * assure some degree of consistency, we special case the POSIX 1003.2
133174832Sdes	 * requirements to assure correct evaluation for POSIX scripts.  The
134174832Sdes	 * following special cases comply with POSIX P1003.2/D11.2 Section
135174832Sdes	 * 4.62.4.
136174832Sdes	 */
137174832Sdes	switch(argc - 1) {
138174832Sdes	case 0:				/* % test */
139174832Sdes		return (1);
140174832Sdes		break;
141174832Sdes	case 1:				/* % test arg */
142174832Sdes		return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0;
143174832Sdes		break;
144174832Sdes	case 2:				/* % test op arg */
145174832Sdes		opname = argv[1];
146228692Sdes		if (IS_BANG(opname))
147174832Sdes			return (*argv[2] == '\0') ? 0 : 1;
148147455Sdes		else {
149147455Sdes			ret_val = posix_unary_op(&argv[1]);
150147455Sdes			if (ret_val >= 0)
151147455Sdes				return (ret_val);
152147455Sdes		}
153147455Sdes		break;
154147455Sdes	case 3:				/* % test arg1 op arg2 */
155147455Sdes		if (IS_BANG(argv[1])) {
156147455Sdes			ret_val = posix_unary_op(&argv[1]);
157147455Sdes			if (ret_val >= 0)
158147455Sdes				return (!ret_val);
159141098Sdes		} else {
160141098Sdes			ret_val = posix_binary_op(&argv[1]);
161141098Sdes			if (ret_val >= 0)
162141098Sdes				return (ret_val);
163141098Sdes		}
164141098Sdes		break;
165141098Sdes	case 4:				/* % test ! arg1 op arg2 */
166141098Sdes		if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0 ) {
167141098Sdes			ret_val = posix_binary_op(&argv[2]);
168141098Sdes			if (ret_val >= 0)
169141098Sdes				return (!ret_val);
170141098Sdes		}
171141098Sdes		break;
172141098Sdes	default:
173141098Sdes		break;
174141098Sdes	}
175141098Sdes
176141098Sdes	/*
177141098Sdes	 * We use operator precedence parsing, evaluating the expression as
178125647Sdes	 * we parse it.  Parentheses are handled by bumping up the priority
179125647Sdes	 * of operators using the variable "nest."  We use the variable
180125647Sdes	 * "skipping" to turn off evaluation temporarily for the short
181125647Sdes	 * circuit boolean operators.  (It is important do the short circuit
182125647Sdes	 * evaluation because under NFS a stat operation can take infinitely
183125647Sdes	 * long.)
184125647Sdes	 */
185125647Sdes	opsp = opstack + STACKSIZE;
186125647Sdes	valsp = valstack;
187117610Sdes	nest = skipping = 0;
188117610Sdes	if (*ap == NULL) {
189117610Sdes		valstack[0].type = BOOLEAN;
190117610Sdes		valstack[0].u.num = 0;
191117610Sdes		goto done;
192117610Sdes	}
193117610Sdes	for (;;) {
194117610Sdes		opname = *ap++;
195117610Sdes		if (opname == NULL)
196117610Sdes			syntax();
197117610Sdes		if (opname[0] == '(' && opname[1] == '\0') {
198117610Sdes			nest += NESTINCR;
199117610Sdes			continue;
200117610Sdes		} else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
201117610Sdes			if (opsp == &opstack[0])
202117610Sdes				overflow();
203117610Sdes			--opsp;
204117610Sdes			opsp->op = op;
205117610Sdes			opsp->pri = op_priority[op] + nest;
206117610Sdes			continue;
207117610Sdes		} else {
208115619Sdes			valsp->type = STRING;
209115619Sdes			valsp->u.string = opname;
210115619Sdes			valsp++;
211115619Sdes		}
212115619Sdes		for (;;) {
213115619Sdes			opname = *ap++;
214115619Sdes			if (opname == NULL) {
215115619Sdes				if (nest != 0)
216115619Sdes					syntax();
217115619Sdes				pri = 0;
218115619Sdes				break;
219115619Sdes			}
220115619Sdes			if (opname[0] != ')' || opname[1] != '\0') {
221115619Sdes				if ((op = lookup_op(opname, binary_op)) < 0)
222115619Sdes					syntax();
223115619Sdes				op += FIRST_BINARY_OP;
224115619Sdes				pri = op_priority[op] + nest;
225115619Sdes				break;
226115619Sdes			}
227114536Sdes			if ((nest -= NESTINCR) < 0)
228114536Sdes				syntax();
229114536Sdes		}
230114536Sdes		while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
231114536Sdes			binary = opsp->op;
232114536Sdes			for (;;) {
233114536Sdes				valsp--;
234114536Sdes				c = op_argflag[opsp->op];
235114536Sdes				if (c == OP_INT) {
236114536Sdes					if (valsp->type == STRING)
237114536Sdes						get_int(valsp->u.string,
238114536Sdes						    &valsp->u.num);
239114536Sdes					valsp->type = INTEGER;
240114536Sdes				} else if (c >= OP_STRING) {
241114536Sdes					            /* OP_STRING or OP_FILE */
242114536Sdes					if (valsp->type == INTEGER) {
243114536Sdes						if ((p = malloc(32)) == NULL)
244108794Sdes							err(2, NULL);
245108794Sdes#ifdef SHELL
246108794Sdes						fmtstr(p, 32, "%d",
247108794Sdes						    valsp->u.num);
248108794Sdes#else
249108794Sdes						(void)sprintf(p,
250108794Sdes						    "%d", valsp->u.num);
251108794Sdes#endif
252107937Sdes						valsp->u.string = p;
253107937Sdes					} else if (valsp->type == BOOLEAN) {
254107937Sdes						if (valsp->u.num)
255107937Sdes							valsp->u.string =
256107937Sdes						            "true";
257107937Sdes						else
258107937Sdes							valsp->u.string = "";
259107937Sdes					}
260107937Sdes					valsp->type = STRING;
261107937Sdes					if (c == OP_FILE && (fs.name == NULL ||
262107937Sdes					    strcmp(fs.name, valsp->u.string))) {
263107937Sdes						fs.name = valsp->u.string;
264107937Sdes						fs.rcode =
265107937Sdes						    stat(valsp->u.string,
266107937Sdes                                                    &fs.stat);
26791094Sdes					}
26899158Sdes				}
26999158Sdes				if (binary < FIRST_BINARY_OP)
27099158Sdes					break;
27199158Sdes				binary = 0;
27299158Sdes			}
27399158Sdes			if (!skipping)
27499158Sdes				expr_operator(opsp->op, valsp, &fs);
275107937Sdes			else if (opsp->op == AND1 || opsp->op == OR1)
27699158Sdes				skipping--;
27799158Sdes			valsp++;		/* push value */
27899158Sdes			opsp++;			/* pop operator */
27999158Sdes		}
28099158Sdes		if (opname == NULL)
28199158Sdes			break;
28299158Sdes		if (opsp == &opstack[0])
28399158Sdes			overflow();
28499158Sdes		if (op == AND1 || op == AND2) {
28599158Sdes			op = AND1;
28699158Sdes			if (skipping || expr_is_false(valsp - 1))
28799158Sdes				skipping++;
28899158Sdes		}
28997241Sdes		if (op == OR1 || op == OR2) {
29097241Sdes			op = OR1;
29197241Sdes			if (skipping || !expr_is_false(valsp - 1))
29297241Sdes				skipping++;
29397241Sdes		}
29497241Sdes		opsp--;
29597241Sdes		opsp->op = op;
29697241Sdes		opsp->pri = pri;
29797241Sdes	}
29897241Sdesdone:	return (expr_is_false(&valstack[0]));
29995908Sdes}
30095908Sdes
30195908Sdesstatic int
30295908Sdesexpr_is_false(val)
30395908Sdes	struct value *val;
30495908Sdes{
30595908Sdes
30695908Sdes	if (val->type == STRING) {
30795908Sdes		if (val->u.string[0] == '\0')
30895908Sdes			return (1);
30995908Sdes	} else {		/* INTEGER or BOOLEAN */
31095908Sdes		if (val->u.num == 0)
31195908Sdes			return (1);
31295908Sdes	}
31395908Sdes	return (0);
31495908Sdes}
31595908Sdes
31695908Sdes
31795908Sdes/*
31895908Sdes * Execute an operator.  Op is the operator.  Sp is the stack pointer;
31995908Sdes * sp[0] refers to the first operand, sp[1] refers to the second operand
32094670Sdes * (if any), and the result is placed in sp[0].  The operands are converted
32194670Sdes * to the type expected by the operator before expr_operator is called.
32295908Sdes * Fs is a pointer to a structure which holds the value of the last call
32395908Sdes * to stat, to avoid repeated stat calls on the same file.
32495908Sdes */
32594670Sdesstatic void
32694670Sdesexpr_operator(op, sp, fs)
32794670Sdes	int op;
32894670Sdes	struct value *sp;
32994670Sdes	struct filestat *fs;
33094670Sdes{
33195908Sdes	int i;
33294670Sdes
33394670Sdes	switch (op) {
33494670Sdes	case NOT:
33594670Sdes		sp->u.num = expr_is_false(sp);
33694670Sdes		sp->type = BOOLEAN;
33794209Sdes		break;
33894209Sdes	case ISEXIST:
33994209Sdesexist:
34094209Sdes		if (fs == NULL || fs->rcode == -1)
34194209Sdes			goto false;
34294209Sdes		else
34394209Sdes			goto true;
34494209Sdes	case ISREAD:
34594209Sdes		if (geteuid() == 0)
34694209Sdes			goto exist;
34794209Sdes		i = S_IROTH;
34894209Sdes		goto permission;
34994209Sdes	case ISWRITE:
35094209Sdes		if (geteuid() == 0)
35194209Sdes			goto exist;
35294209Sdes		i = S_IWOTH;
35394209Sdes		goto permission;
35494209Sdes	case ISEXEC:
35594209Sdes		if (geteuid() != 0) {
35694209Sdes		i = S_IXOTH;
35794209Sdespermission:             if (fs->stat.st_uid == geteuid())
35894209Sdes			i <<= 6;
35994209Sdes		else if (fs->stat.st_gid == getegid())
36094209Sdes			i <<= 3;
36194209Sdes		} else
36294209Sdes			i = S_IXOTH|S_IXGRP|S_IXUSR;
36394209Sdes		goto filebit;	/* true if (stat.st_mode & i) != 0 */
36494209Sdes	case ISFILE:
36594209Sdes		i = S_IFREG;
36694209Sdes		goto filetype;
36794209Sdes	case ISDIR:
368236109Sdes		i = S_IFDIR;
36991684Sdes		goto filetype;
37092289Sdes	case ISCHAR:
37192289Sdes		i = S_IFCHR;
37292289Sdes		goto filetype;
37392289Sdes	case ISBLOCK:
37492289Sdes		i = S_IFBLK;
37592289Sdes		goto filetype;
37692289Sdes	case ISSYMLINK:
37792289Sdes		i = S_IFLNK;
37892289Sdes		(void)lstat(sp->u.string, &fs->stat);
37992289Sdes		goto filetype;
38092289Sdes	case ISFIFO:
38192289Sdes		i = S_IFIFO;
38292289Sdes		goto filetype;
38392289Sdesfiletype:	if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
38492289Sdestrue:			sp->u.num = 1;
38594209Sdes		else
38692289Sdesfalse:			sp->u.num = 0;
38791684Sdes		sp->type = BOOLEAN;
38891684Sdes		break;
38991684Sdes	case ISSETUID:
39091684Sdes		i = S_ISUID;
39191684Sdes		goto filebit;
39291684Sdes	case ISSETGID:
39391684Sdes		i = S_ISGID;
39491684Sdes		goto filebit;
39591684Sdes	case ISSTICKY:
39691684Sdes		i = S_ISVTX;
39791684Sdesfilebit:	if (fs->stat.st_mode & i && fs->rcode >= 0)
39891684Sdes			goto true;
39991684Sdes		goto false;
40091684Sdes	case ISSIZE:
40191684Sdes		sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
40291684Sdes		sp->type = INTEGER;
40391684Sdes		break;
40491684Sdes	case ISTTY:
40591684Sdes		sp->u.num = isatty(sp->u.num);
40691684Sdes		sp->type = BOOLEAN;
407236109Sdes		break;
40891100Sdes	case NULSTR:
40991100Sdes		if (sp->u.string[0] == '\0')
41091100Sdes			goto true;
41191100Sdes		goto false;
41291100Sdes	case STRLEN:
41391100Sdes		sp->u.num = strlen(sp->u.string);
41491100Sdes		sp->type = INTEGER;
41591100Sdes		break;
41691100Sdes	case OR1:
41791100Sdes	case AND1:
41891100Sdes		/*
41991100Sdes		 * These operators are mostly handled by the parser.  If we
42091100Sdes		 * get here it means that both operands were evaluated, so
42191100Sdes		 * the value is the value of the second operand.
42291100Sdes		 */
42391100Sdes		*sp = *(sp + 1);
42491100Sdes		break;
42591100Sdes	case STREQ:
42691100Sdes	case STRNE:
42791100Sdes		i = 0;
42891100Sdes		if (!strcmp(sp->u.string, (sp + 1)->u.string))
42991100Sdes			i++;
43091100Sdes		if (op == STRNE)
43191100Sdes			i = 1 - i;
43291100Sdes		sp->u.num = i;
43391100Sdes		sp->type = BOOLEAN;
43491100Sdes		break;
43591100Sdes	case EQ:
43691100Sdes		if (sp->u.num == (sp + 1)->u.num)
437236109Sdes			goto true;
43891097Sdes		goto false;
43991097Sdes	case NE:
44091097Sdes		if (sp->u.num != (sp + 1)->u.num)
44191097Sdes			goto true;
44291097Sdes		goto false;
44391097Sdes	case GT:
44491097Sdes		if (sp->u.num > (sp + 1)->u.num)
44591097Sdes			goto true;
44691097Sdes		goto false;
44791097Sdes	case LT:
448236109Sdes		if (sp->u.num < (sp + 1)->u.num)
44991094Sdes			goto true;
45091094Sdes		goto false;
451	case LE:
452		if (sp->u.num <= (sp + 1)->u.num)
453			goto true;
454		goto false;
455	case GE:
456		if (sp->u.num >= (sp + 1)->u.num)
457			goto true;
458		goto false;
459
460	}
461}
462
463static int
464lookup_op(name, table)
465	char *name;
466	const char *const * table;
467{
468	const char *const * tp;
469	const char *p;
470	char c;
471
472	c = name[1];
473	for (tp = table; (p = *tp) != NULL; tp++)
474		if (p[1] == c && !strcmp(p, name))
475			return (tp - table);
476	return (-1);
477}
478
479static int
480posix_unary_op(argv)
481	char **argv;
482{
483	struct filestat fs;
484	struct value valp;
485	int op, c;
486	char *opname;
487
488	opname = *argv;
489	if ((op = lookup_op(opname, unary_op)) < 0)
490		return (-1);
491	c = op_argflag[op];
492	opname = argv[1];
493	valp.u.string = opname;
494	if (c == OP_FILE) {
495		fs.name = opname;
496		fs.rcode = stat(opname, &fs.stat);
497	} else if (c != OP_STRING)
498		return (-1);
499
500	expr_operator(op, &valp, &fs);
501	return (valp.u.num == 0);
502}
503
504static int
505posix_binary_op(argv)
506	char  **argv;
507{
508	struct value v[2];
509	int op, c;
510	char *opname;
511
512	opname = argv[1];
513	if ((op = lookup_op(opname, binary_op)) < 0)
514		return (-1);
515	op += FIRST_BINARY_OP;
516	c = op_argflag[op];
517
518	if (c == OP_INT) {
519		get_int(argv[0], &v[0].u.num);
520		get_int(argv[2], &v[1].u.num);
521	} else {
522		v[0].u.string = argv[0];
523		v[1].u.string = argv[2];
524	}
525	expr_operator(op, v, NULL);
526	return (v[0].u.num == 0);
527}
528
529/*
530 * Integer type checking.
531 */
532static void
533get_int(v, lp)
534	char *v;
535	long *lp;
536{
537	long val;
538	char *ep;
539
540	for (; *v && isspace(*v); ++v);
541
542	if(!*v) {
543		*lp = 0;
544		return;
545	}
546
547	if (isdigit(*v) || ((*v == '-' || *v == '+') && isdigit(*(v+1)))) {
548		errno = 0;
549		val = strtol(v, &ep, 10);
550		if (*ep != '\0')
551			errx(2, "%s: trailing non-numeric characters", v);
552		if (errno == ERANGE) {
553			if (val == LONG_MIN)
554				errx(2, "%s: underflow", v);
555			if (val == LONG_MAX)
556				errx(2, "%s: overflow", v);
557		}
558		*lp = val;
559		return;
560	}
561	errx(2, "%s: expected integer", v);
562}
563
564static void
565syntax()
566{
567
568	err(2, "syntax error");
569}
570
571static void
572overflow()
573{
574
575	err(2, "expression is too complex");
576}
577