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