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