test.c revision 4171
164562Sgshapiro/*-
264562Sgshapiro * Copyright (c) 1992, 1993, 1994
364562Sgshapiro *	The Regents of the University of California.  All rights reserved.
464562Sgshapiro *
564562Sgshapiro * This code is derived from software contributed to Berkeley by
664562Sgshapiro * Kenneth Almquist.
764562Sgshapiro *
864562Sgshapiro * Redistribution and use in source and binary forms, with or without
964562Sgshapiro * modification, are permitted provided that the following conditions
1064562Sgshapiro * are met:
1164562Sgshapiro * 1. Redistributions of source code must retain the above copyright
1271345Sgshapiro *    notice, this list of conditions and the following disclaimer.
1364562Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright
1464562Sgshapiro *    notice, this list of conditions and the following disclaimer in the
1564562Sgshapiro *    documentation and/or other materials provided with the distribution.
1664562Sgshapiro * 3. All advertising materials mentioning features or use of this software
1764562Sgshapiro *    must display the following acknowledgement:
1864562Sgshapiro *	This product includes software developed by the University of
1964562Sgshapiro *	California, Berkeley and its contributors.
2064562Sgshapiro * 4. Neither the name of the University nor the names of its contributors
2164562Sgshapiro *    may be used to endorse or promote products derived from this software
2264562Sgshapiro *    without specific prior written permission.
2364562Sgshapiro *
2464562Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2564562Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2664562Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2764562Sgshapiro * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2864562Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2964562Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3071345Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3171345Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3271345Sgshapiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3364562Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3464562Sgshapiro * SUCH DAMAGE.
3564562Sgshapiro *
3664562Sgshapiro *	$Id: test.c,v 1.8 1994/11/05 20:24:49 ache Exp $
3764562Sgshapiro */
3864562Sgshapiro
3964562Sgshapiro#ifndef lint
4064562Sgshapirostatic char copyright[] =
4171345Sgshapiro"@(#) Copyright (c) 1992, 1993, 1994\n\
4271345Sgshapiro	The Regents of the University of California.  All rights reserved.\n";
4371345Sgshapiro#endif /* not lint */
4464562Sgshapiro
4564562Sgshapiro#ifndef lint
4671345Sgshapirostatic char sccsid[] = "@(#)test.c	8.3 (Berkeley) 4/2/94";
4771345Sgshapiro#endif /* not lint */
4871345Sgshapiro
4971345Sgshapiro#include <sys/types.h>
5071345Sgshapiro#include <sys/stat.h>
5171345Sgshapiro#include <sys/param.h>
5271345Sgshapiro
5371345Sgshapiro#include <ctype.h>
5464562Sgshapiro#include <err.h>
5571345Sgshapiro#include <errno.h>
5664562Sgshapiro#include <limits.h>
5771345Sgshapiro#include <stdio.h>
5871345Sgshapiro#include <stdlib.h>
5971345Sgshapiro#include <string.h>
6071345Sgshapiro#include <unistd.h>
6171345Sgshapiro
6271345Sgshapiro#include "operators.h"
6371345Sgshapiro
6471345Sgshapiro#define	STACKSIZE	12
6571345Sgshapiro#define	NESTINCR	16
6671345Sgshapiro
6771345Sgshapiro/* data types */
6864562Sgshapiro#define	STRING	0
6964562Sgshapiro#define	INTEGER	1
7064562Sgshapiro#define	BOOLEAN	2
7164562Sgshapiro
7271345Sgshapiro#define	IS_BANG(s) (s[0] == '!' && s[1] == '\0')
7371345Sgshapiro
7471345Sgshapiro/*
7571345Sgshapiro * This structure hold a value.  The type keyword specifies the type of
7671345Sgshapiro * the value, and the union u holds the value.  The value of a boolean
7771345Sgshapiro * is stored in u.num (1 = TRUE, 0 = FALSE).
7871345Sgshapiro */
7971345Sgshapirostruct value {
8071345Sgshapiro	int type;
8171345Sgshapiro	union {
8271345Sgshapiro		char *string;
8371345Sgshapiro		long num;
8471345Sgshapiro	} u;
8571345Sgshapiro};
8671345Sgshapiro
8771345Sgshapirostruct operator {
8871345Sgshapiro	short op;		/* Which operator. */
8964562Sgshapiro	short pri;		/* Priority of operator. */
9071345Sgshapiro};
9171345Sgshapiro
9271345Sgshapirostruct filestat {
9371345Sgshapiro	char *name;		/* Name of file. */
9471345Sgshapiro	int rcode;		/* Return code from stat. */
9571345Sgshapiro	struct stat stat;	/* Status info on file. */
9664562Sgshapiro};
9764562Sgshapiro
9864562Sgshapirostatic int	expr_is_false __P((struct value *));
9964562Sgshapirostatic void	expr_operator __P((int, struct value *, struct filestat *));
10064562Sgshapirostatic void	get_int __P((char *, long *));
10164562Sgshapirostatic int	lookup_op __P((char *, const char *const *));
10264562Sgshapirostatic void	overflow __P((void));
10364562Sgshapirostatic int	posix_binary_op __P((char **));
10464562Sgshapirostatic int	posix_unary_op __P((char **));
10564562Sgshapirostatic void	syntax __P((void));
10664562Sgshapiro
10764562Sgshapiroint
10864562Sgshapiromain(argc, argv)
10964562Sgshapiro	int argc;
11064562Sgshapiro	char *argv[];
11164562Sgshapiro{
11264562Sgshapiro	struct operator opstack[STACKSIZE];
11364562Sgshapiro	struct operator *opsp;
11464562Sgshapiro	struct value valstack[STACKSIZE + 1];
11564562Sgshapiro	struct value *valsp;
11664562Sgshapiro	struct filestat fs;
11764562Sgshapiro	char  c, **ap, *opname, *p;
11871345Sgshapiro	int binary, nest, op, pri, ret_val, skipping;
11964562Sgshapiro
12064562Sgshapiro	if ((p = argv[0]) == NULL)
12164562Sgshapiro		errx(2, "test: argc is zero");
12264562Sgshapiro
12364562Sgshapiro	if (*p != '\0' && p[strlen(p) - 1] == '[') {
12464562Sgshapiro		if (strcmp(argv[--argc], "]"))
12564562Sgshapiro			errx(2, "missing ]");
12664562Sgshapiro		argv[argc] = NULL;
12764562Sgshapiro	}
12864562Sgshapiro	ap = argv + 1;
12964562Sgshapiro	fs.name = NULL;
13064562Sgshapiro
13164562Sgshapiro	/*
13264562Sgshapiro	 * Test(1) implements an inherently ambiguous grammer.  In order to
13364562Sgshapiro	 * assure some degree of consistency, we special case the POSIX 1003.2
13464562Sgshapiro	 * requirements to assure correct evaluation for POSIX scripts.  The
13564562Sgshapiro	 * following special cases comply with POSIX P1003.2/D11.2 Section
13664562Sgshapiro	 * 4.62.4.
13764562Sgshapiro	 */
13864562Sgshapiro	switch(argc - 1) {
13964562Sgshapiro	case 0:				/* % test */
14064562Sgshapiro		return (1);
14164562Sgshapiro		break;
14264562Sgshapiro	case 1:				/* % test arg */
14364562Sgshapiro		return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0;
14464562Sgshapiro		break;
14564562Sgshapiro	case 2:				/* % test op arg */
14664562Sgshapiro		opname = argv[1];
14764562Sgshapiro		if (IS_BANG(opname))
14864562Sgshapiro			return (*argv[2] == '\0') ? 0 : 1;
14964562Sgshapiro		else {
15064562Sgshapiro			ret_val = posix_unary_op(&argv[1]);
15164562Sgshapiro			if (ret_val >= 0)
15264562Sgshapiro				return (ret_val);
15364562Sgshapiro		}
15464562Sgshapiro		break;
15564562Sgshapiro	case 3:				/* % test arg1 op arg2 */
15664562Sgshapiro		if (IS_BANG(argv[1])) {
15764562Sgshapiro			ret_val = posix_unary_op(&argv[1]);
15864562Sgshapiro			if (ret_val >= 0)
15964562Sgshapiro				return (!ret_val);
16064562Sgshapiro		} else {
16164562Sgshapiro			ret_val = posix_binary_op(&argv[1]);
16264562Sgshapiro			if (ret_val >= 0)
16364562Sgshapiro				return (ret_val);
16464562Sgshapiro		}
16564562Sgshapiro		break;
16664562Sgshapiro	case 4:				/* % test ! arg1 op arg2 */
16764562Sgshapiro		if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0 ) {
16864562Sgshapiro			ret_val = posix_binary_op(&argv[2]);
16964562Sgshapiro			if (ret_val >= 0)
17064562Sgshapiro				return (!ret_val);
17164562Sgshapiro		}
17264562Sgshapiro		break;
17364562Sgshapiro	default:
17464562Sgshapiro		break;
17564562Sgshapiro	}
17664562Sgshapiro
17764562Sgshapiro	/*
17864562Sgshapiro	 * We use operator precedence parsing, evaluating the expression as
17964562Sgshapiro	 * we parse it.  Parentheses are handled by bumping up the priority
18064562Sgshapiro	 * of operators using the variable "nest."  We use the variable
18164562Sgshapiro	 * "skipping" to turn off evaluation temporarily for the short
18264562Sgshapiro	 * circuit boolean operators.  (It is important do the short circuit
18364562Sgshapiro	 * evaluation because under NFS a stat operation can take infinitely
18464562Sgshapiro	 * long.)
18564562Sgshapiro	 */
18664562Sgshapiro	opsp = opstack + STACKSIZE;
18764562Sgshapiro	valsp = valstack;
18864562Sgshapiro	nest = skipping = 0;
18964562Sgshapiro	if (*ap == NULL) {
19064562Sgshapiro		valstack[0].type = BOOLEAN;
19164562Sgshapiro		valstack[0].u.num = 0;
19264562Sgshapiro		goto done;
19364562Sgshapiro	}
19464562Sgshapiro	for (;;) {
19564562Sgshapiro		opname = *ap++;
19664562Sgshapiro		if (opname == NULL)
19764562Sgshapiro			syntax();
19864562Sgshapiro		if (opname[0] == '(' && opname[1] == '\0') {
19964562Sgshapiro			nest += NESTINCR;
20064562Sgshapiro			continue;
20164562Sgshapiro		} else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
20264562Sgshapiro			if (opsp == &opstack[0])
20364562Sgshapiro				overflow();
20464562Sgshapiro			--opsp;
20564562Sgshapiro			opsp->op = op;
20664562Sgshapiro			opsp->pri = op_priority[op] + nest;
20764562Sgshapiro			continue;
20864562Sgshapiro		} else {
20964562Sgshapiro			valsp->type = STRING;
21064562Sgshapiro			valsp->u.string = opname;
21164562Sgshapiro			valsp++;
21264562Sgshapiro		}
21364562Sgshapiro		for (;;) {
21464562Sgshapiro			opname = *ap++;
21564562Sgshapiro			if (opname == NULL) {
21664562Sgshapiro				if (nest != 0)
21764562Sgshapiro					syntax();
21864562Sgshapiro				pri = 0;
21964562Sgshapiro				break;
22064562Sgshapiro			}
22164562Sgshapiro			if (opname[0] != ')' || opname[1] != '\0') {
22264562Sgshapiro				if ((op = lookup_op(opname, binary_op)) < 0)
22364562Sgshapiro					syntax();
22464562Sgshapiro				op += FIRST_BINARY_OP;
22564562Sgshapiro				pri = op_priority[op] + nest;
22664562Sgshapiro				break;
22764562Sgshapiro			}
22864562Sgshapiro			if ((nest -= NESTINCR) < 0)
22964562Sgshapiro				syntax();
23064562Sgshapiro		}
23164562Sgshapiro		while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
23264562Sgshapiro			binary = opsp->op;
23364562Sgshapiro			for (;;) {
23464562Sgshapiro				valsp--;
23564562Sgshapiro				c = op_argflag[opsp->op];
23664562Sgshapiro				if (c == OP_INT) {
23764562Sgshapiro					if (valsp->type == STRING)
23864562Sgshapiro						get_int(valsp->u.string,
23964562Sgshapiro						    &valsp->u.num);
24064562Sgshapiro					valsp->type = INTEGER;
24164562Sgshapiro				} else if (c >= OP_STRING) {
24264562Sgshapiro					            /* OP_STRING or OP_FILE */
24364562Sgshapiro					if (valsp->type == INTEGER) {
24464562Sgshapiro						if ((p = malloc(32)) == NULL)
24564562Sgshapiro							err(2, NULL);
24664562Sgshapiro#ifdef SHELL
24764562Sgshapiro						fmtstr(p, 32, "%d",
24864562Sgshapiro						    valsp->u.num);
24964562Sgshapiro#else
25064562Sgshapiro						(void)sprintf(p,
25164562Sgshapiro						    "%d", valsp->u.num);
25264562Sgshapiro#endif
25364562Sgshapiro						valsp->u.string = p;
25464562Sgshapiro					} else if (valsp->type == BOOLEAN) {
25564562Sgshapiro						if (valsp->u.num)
25664562Sgshapiro							valsp->u.string =
25764562Sgshapiro						            "true";
25864562Sgshapiro						else
25964562Sgshapiro							valsp->u.string = "";
26064562Sgshapiro					}
26164562Sgshapiro					valsp->type = STRING;
26264562Sgshapiro					if (c == OP_FILE && (fs.name == NULL ||
26364562Sgshapiro					    strcmp(fs.name, valsp->u.string))) {
26464562Sgshapiro						fs.name = valsp->u.string;
26564562Sgshapiro						fs.rcode =
26664562Sgshapiro						    stat(valsp->u.string,
26764562Sgshapiro                                                    &fs.stat);
26864562Sgshapiro					}
26964562Sgshapiro				}
27064562Sgshapiro				if (binary < FIRST_BINARY_OP)
27164562Sgshapiro					break;
27264562Sgshapiro				binary = 0;
27364562Sgshapiro			}
27464562Sgshapiro			if (!skipping)
27564562Sgshapiro				expr_operator(opsp->op, valsp, &fs);
27664562Sgshapiro			else if (opsp->op == AND1 || opsp->op == OR1)
27764562Sgshapiro				skipping--;
27864562Sgshapiro			valsp++;		/* push value */
27964562Sgshapiro			opsp++;			/* pop operator */
28064562Sgshapiro		}
28164562Sgshapiro		if (opname == NULL)
28264562Sgshapiro			break;
28364562Sgshapiro		if (opsp == &opstack[0])
28464562Sgshapiro			overflow();
28564562Sgshapiro		if (op == AND1 || op == AND2) {
28664562Sgshapiro			op = AND1;
28764562Sgshapiro			if (skipping || expr_is_false(valsp - 1))
28864562Sgshapiro				skipping++;
28964562Sgshapiro		}
29064562Sgshapiro		if (op == OR1 || op == OR2) {
29164562Sgshapiro			op = OR1;
29264562Sgshapiro			if (skipping || !expr_is_false(valsp - 1))
29364562Sgshapiro				skipping++;
29464562Sgshapiro		}
29564562Sgshapiro		opsp--;
29664562Sgshapiro		opsp->op = op;
29764562Sgshapiro		opsp->pri = pri;
29864562Sgshapiro	}
29964562Sgshapirodone:	return (expr_is_false(&valstack[0]));
30064562Sgshapiro}
30164562Sgshapiro
30264562Sgshapirostatic int
30364562Sgshapiroexpr_is_false(val)
30464562Sgshapiro	struct value *val;
30564562Sgshapiro{
30664562Sgshapiro
30764562Sgshapiro	if (val->type == STRING) {
30864562Sgshapiro		if (val->u.string[0] == '\0')
30964562Sgshapiro			return (1);
31064562Sgshapiro	} else {		/* INTEGER or BOOLEAN */
31164562Sgshapiro		if (val->u.num == 0)
31264562Sgshapiro			return (1);
31364562Sgshapiro	}
31464562Sgshapiro	return (0);
31564562Sgshapiro}
31664562Sgshapiro
31764562Sgshapiro
31864562Sgshapiro/*
31964562Sgshapiro * Execute an operator.  Op is the operator.  Sp is the stack pointer;
32064562Sgshapiro * sp[0] refers to the first operand, sp[1] refers to the second operand
32164562Sgshapiro * (if any), and the result is placed in sp[0].  The operands are converted
32264562Sgshapiro * to the type expected by the operator before expr_operator is called.
32364562Sgshapiro * Fs is a pointer to a structure which holds the value of the last call
32464562Sgshapiro * to stat, to avoid repeated stat calls on the same file.
32564562Sgshapiro */
32664562Sgshapirostatic void
32764562Sgshapiroexpr_operator(op, sp, fs)
32864562Sgshapiro	int op;
32964562Sgshapiro	struct value *sp;
33064562Sgshapiro	struct filestat *fs;
33164562Sgshapiro{
33266494Sgshapiro	int i;
33366494Sgshapiro
33464562Sgshapiro	switch (op) {
33564562Sgshapiro	case NOT:
33664562Sgshapiro		sp->u.num = expr_is_false(sp);
33764562Sgshapiro		sp->type = BOOLEAN;
33864562Sgshapiro		break;
33964562Sgshapiro	case ISEXIST:
34064562Sgshapiroexist:
34164562Sgshapiro		if (fs == NULL || fs->rcode == -1)
34264562Sgshapiro			goto false;
34364562Sgshapiro		else
34464562Sgshapiro			goto true;
34564562Sgshapiro	case ISREAD:
34664562Sgshapiro		if (geteuid() == 0)
34764562Sgshapiro			goto exist;
34864562Sgshapiro		i = S_IROTH;
34964562Sgshapiro		goto permission;
35064562Sgshapiro	case ISWRITE:
35164562Sgshapiro		if (geteuid() != 0)
35264562Sgshapiro			i = S_IWOTH;
35364562Sgshapiro		else {
35464562Sgshapiro			i = S_IWOTH|S_IWGRP|S_IWUSR;
35564562Sgshapiro			goto filebit;
35664562Sgshapiro		}
35764562Sgshapiro		goto permission;
35864562Sgshapiro	case ISEXEC:
35964562Sgshapiro		if (geteuid() != 0) {
36066494Sgshapiro			i = S_IXOTH;
36166494Sgshapiropermission:		if (fs->stat.st_uid == geteuid())
36266494Sgshapiro				i <<= 6;
36366494Sgshapiro			else {
36466494Sgshapiro				gid_t grlist[NGROUPS];
36566494Sgshapiro				int ngroups, j;
36666494Sgshapiro
36766494Sgshapiro				ngroups = getgroups(NGROUPS, grlist);
36866494Sgshapiro				for (j = 0; j < ngroups; j++)
36964562Sgshapiro					if (fs->stat.st_gid == grlist[j]) {
37064562Sgshapiro						i <<= 3;
37164562Sgshapiro						goto filebit;
37264562Sgshapiro					}
37364562Sgshapiro			}
37464562Sgshapiro		} else
37564562Sgshapiro			i = S_IXOTH|S_IXGRP|S_IXUSR;
37664562Sgshapiro		goto filebit;	/* true if (stat.st_mode & i) != 0 */
37764562Sgshapiro	case ISFILE:
37864562Sgshapiro		i = S_IFREG;
37964562Sgshapiro		goto filetype;
38064562Sgshapiro	case ISDIR:
38164562Sgshapiro		i = S_IFDIR;
38264562Sgshapiro		goto filetype;
38364562Sgshapiro	case ISCHAR:
38464562Sgshapiro		i = S_IFCHR;
38564562Sgshapiro		goto filetype;
38664562Sgshapiro	case ISBLOCK:
38764562Sgshapiro		i = S_IFBLK;
38864562Sgshapiro		goto filetype;
38964562Sgshapiro	case ISSYMLINK:
39064562Sgshapiro		i = S_IFLNK;
39164562Sgshapiro		(void)lstat(sp->u.string, &fs->stat);
39264562Sgshapiro		goto filetype;
39364562Sgshapiro	case ISFIFO:
39464562Sgshapiro		i = S_IFIFO;
39564562Sgshapiro		goto filetype;
39664562Sgshapirofiletype:	if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
39764562Sgshapirotrue:			sp->u.num = 1;
39864562Sgshapiro		else
39964562Sgshapirofalse:			sp->u.num = 0;
40064562Sgshapiro		sp->type = BOOLEAN;
40164562Sgshapiro		break;
40264562Sgshapiro	case ISSETUID:
40364562Sgshapiro		i = S_ISUID;
40464562Sgshapiro		goto filebit;
40564562Sgshapiro	case ISSETGID:
40664562Sgshapiro		i = S_ISGID;
40764562Sgshapiro		goto filebit;
40864562Sgshapiro	case ISSTICKY:
409		i = S_ISVTX;
410filebit:	if (fs->stat.st_mode & i && fs->rcode >= 0)
411			goto true;
412		goto false;
413	case ISSIZE:
414		sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
415		sp->type = INTEGER;
416		break;
417	case ISTTY:
418		sp->u.num = isatty(sp->u.num);
419		sp->type = BOOLEAN;
420		break;
421	case NULSTR:
422		if (sp->u.string[0] == '\0')
423			goto true;
424		goto false;
425	case STRLEN:
426		sp->u.num = strlen(sp->u.string);
427		sp->type = INTEGER;
428		break;
429	case OR1:
430	case AND1:
431		/*
432		 * These operators are mostly handled by the parser.  If we
433		 * get here it means that both operands were evaluated, so
434		 * the value is the value of the second operand.
435		 */
436		*sp = *(sp + 1);
437		break;
438	case STREQ:
439	case STRNE:
440		i = 0;
441		if (!strcmp(sp->u.string, (sp + 1)->u.string))
442			i++;
443		if (op == STRNE)
444			i = 1 - i;
445		sp->u.num = i;
446		sp->type = BOOLEAN;
447		break;
448	case EQ:
449		if (sp->u.num == (sp + 1)->u.num)
450			goto true;
451		goto false;
452	case NE:
453		if (sp->u.num != (sp + 1)->u.num)
454			goto true;
455		goto false;
456	case GT:
457		if (sp->u.num > (sp + 1)->u.num)
458			goto true;
459		goto false;
460	case LT:
461		if (sp->u.num < (sp + 1)->u.num)
462			goto true;
463		goto false;
464	case LE:
465		if (sp->u.num <= (sp + 1)->u.num)
466			goto true;
467		goto false;
468	case GE:
469		if (sp->u.num >= (sp + 1)->u.num)
470			goto true;
471		goto false;
472
473	}
474}
475
476static int
477lookup_op(name, table)
478	char *name;
479	const char *const * table;
480{
481	const char *const * tp;
482	const char *p;
483	char c;
484
485	c = name[1];
486	for (tp = table; (p = *tp) != NULL; tp++)
487		if (p[1] == c && !strcmp(p, name))
488			return (tp - table);
489	return (-1);
490}
491
492static int
493posix_unary_op(argv)
494	char **argv;
495{
496	struct filestat fs;
497	struct value valp;
498	int op, c;
499	char *opname;
500
501	opname = *argv;
502	if ((op = lookup_op(opname, unary_op)) < 0)
503		return (-1);
504	c = op_argflag[op];
505	opname = argv[1];
506	valp.u.string = opname;
507	if (c == OP_FILE) {
508		fs.name = opname;
509		fs.rcode = stat(opname, &fs.stat);
510	} else if (c != OP_STRING)
511		return (-1);
512
513	expr_operator(op, &valp, &fs);
514	return (valp.u.num == 0);
515}
516
517static int
518posix_binary_op(argv)
519	char  **argv;
520{
521	struct value v[2];
522	int op, c;
523	char *opname;
524
525	opname = argv[1];
526	if ((op = lookup_op(opname, binary_op)) < 0)
527		return (-1);
528	op += FIRST_BINARY_OP;
529	c = op_argflag[op];
530
531	if (c == OP_INT) {
532		get_int(argv[0], &v[0].u.num);
533		get_int(argv[2], &v[1].u.num);
534	} else {
535		v[0].u.string = argv[0];
536		v[1].u.string = argv[2];
537	}
538	expr_operator(op, v, NULL);
539	return (v[0].u.num == 0);
540}
541
542/*
543 * Integer type checking.
544 */
545static void
546get_int(v, lp)
547	char *v;
548	long *lp;
549{
550	long val;
551	char *ep;
552
553	for (; *v && isspace(*v); ++v);
554
555	if(!*v) {
556		*lp = 0;
557		return;
558	}
559
560	if (isdigit(*v) || ((*v == '-' || *v == '+') && isdigit(*(v+1)))) {
561		errno = 0;
562		val = strtol(v, &ep, 10);
563		if (*ep != '\0')
564			errx(2, "%s: trailing non-numeric characters", v);
565		if (errno == ERANGE) {
566			if (val == LONG_MIN)
567				errx(2, "%s: underflow", v);
568			if (val == LONG_MAX)
569				errx(2, "%s: overflow", v);
570		}
571		*lp = val;
572		return;
573	}
574	errx(2, "%s: expected integer", v);
575}
576
577static void
578syntax()
579{
580
581	err(2, "syntax error");
582}
583
584static void
585overflow()
586{
587
588	err(2, "expression is too complex");
589}
590