Home | History | Annotate | Line # | Download | only in test
test.c revision 1.32
      1  1.32  christos /* $NetBSD: test.c,v 1.32 2007/05/24 18:47:08 christos Exp $ */
      2  1.15       cgd 
      3  1.13       jtc /*
      4  1.13       jtc  * test(1); version 7-like  --  author Erik Baalbergen
      5  1.13       jtc  * modified by Eric Gisin to be used as built-in.
      6  1.13       jtc  * modified by Arnold Robbins to add SVR3 compatibility
      7  1.13       jtc  * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
      8  1.13       jtc  * modified by J.T. Conklin for NetBSD.
      9   1.1     glass  *
     10  1.13       jtc  * This program is in the Public Domain.
     11   1.1     glass  */
     12   1.1     glass 
     13  1.17  christos #include <sys/cdefs.h>
     14   1.1     glass #ifndef lint
     15  1.32  christos __RCSID("$NetBSD: test.c,v 1.32 2007/05/24 18:47:08 christos Exp $");
     16  1.13       jtc #endif
     17   1.1     glass 
     18  1.23       wiz #include <sys/stat.h>
     19   1.1     glass #include <sys/types.h>
     20  1.23       wiz 
     21  1.13       jtc #include <ctype.h>
     22  1.23       wiz #include <err.h>
     23   1.1     glass #include <errno.h>
     24  1.13       jtc #include <stdio.h>
     25   1.1     glass #include <stdlib.h>
     26   1.1     glass #include <string.h>
     27  1.23       wiz #include <unistd.h>
     28  1.22  christos #include <stdarg.h>
     29   1.1     glass 
     30  1.13       jtc /* test(1) accepts the following grammar:
     31  1.13       jtc 	oexpr	::= aexpr | aexpr "-o" oexpr ;
     32  1.13       jtc 	aexpr	::= nexpr | nexpr "-a" aexpr ;
     33  1.14       cgd 	nexpr	::= primary | "!" primary
     34  1.13       jtc 	primary	::= unary-operator operand
     35  1.13       jtc 		| operand binary-operator operand
     36  1.13       jtc 		| operand
     37  1.13       jtc 		| "(" oexpr ")"
     38  1.13       jtc 		;
     39  1.13       jtc 	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
     40  1.13       jtc 		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
     41  1.13       jtc 
     42  1.30   hubertf 	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
     43  1.30   hubertf 			"-nt"|"-ot"|"-ef";
     44  1.13       jtc 	operand ::= <any legal UNIX file name>
     45  1.13       jtc */
     46  1.13       jtc 
     47  1.13       jtc enum token {
     48  1.13       jtc 	EOI,
     49  1.13       jtc 	FILRD,
     50  1.13       jtc 	FILWR,
     51  1.13       jtc 	FILEX,
     52  1.13       jtc 	FILEXIST,
     53  1.13       jtc 	FILREG,
     54  1.13       jtc 	FILDIR,
     55  1.13       jtc 	FILCDEV,
     56  1.13       jtc 	FILBDEV,
     57  1.13       jtc 	FILFIFO,
     58  1.13       jtc 	FILSOCK,
     59  1.13       jtc 	FILSYM,
     60  1.13       jtc 	FILGZ,
     61  1.13       jtc 	FILTT,
     62  1.13       jtc 	FILSUID,
     63  1.13       jtc 	FILSGID,
     64  1.13       jtc 	FILSTCK,
     65  1.13       jtc 	FILNT,
     66  1.13       jtc 	FILOT,
     67  1.13       jtc 	FILEQ,
     68  1.13       jtc 	FILUID,
     69  1.13       jtc 	FILGID,
     70  1.13       jtc 	STREZ,
     71  1.13       jtc 	STRNZ,
     72  1.13       jtc 	STREQ,
     73  1.13       jtc 	STRNE,
     74  1.13       jtc 	STRLT,
     75  1.13       jtc 	STRGT,
     76  1.13       jtc 	INTEQ,
     77  1.13       jtc 	INTNE,
     78  1.13       jtc 	INTGE,
     79  1.13       jtc 	INTGT,
     80  1.13       jtc 	INTLE,
     81  1.13       jtc 	INTLT,
     82  1.13       jtc 	UNOT,
     83  1.13       jtc 	BAND,
     84  1.13       jtc 	BOR,
     85  1.13       jtc 	LPAREN,
     86  1.13       jtc 	RPAREN,
     87  1.13       jtc 	OPERAND
     88  1.13       jtc };
     89   1.1     glass 
     90  1.13       jtc enum token_types {
     91  1.13       jtc 	UNOP,
     92  1.13       jtc 	BINOP,
     93  1.13       jtc 	BUNOP,
     94  1.13       jtc 	BBINOP,
     95  1.13       jtc 	PAREN
     96   1.1     glass };
     97   1.1     glass 
     98  1.31  christos struct t_op {
     99  1.13       jtc 	const char *op_text;
    100  1.13       jtc 	short op_num, op_type;
    101  1.31  christos };
    102  1.31  christos 
    103  1.31  christos static const struct t_op cop[] = {
    104  1.13       jtc 	{"!",	UNOT,	BUNOP},
    105  1.13       jtc 	{"(",	LPAREN,	PAREN},
    106  1.13       jtc 	{")",	RPAREN,	PAREN},
    107  1.31  christos 	{"<",	STRLT,	BINOP},
    108  1.31  christos 	{"=",	STREQ,	BINOP},
    109  1.31  christos 	{">",	STRGT,	BINOP},
    110  1.31  christos };
    111  1.31  christos 
    112  1.31  christos static const struct t_op cop2[] = {
    113  1.31  christos 	{"!=",	STRNE,	BINOP},
    114  1.31  christos };
    115  1.31  christos 
    116  1.31  christos static const struct t_op mop3[] = {
    117  1.31  christos 	{"ef",	FILEQ,	BINOP},
    118  1.31  christos 	{"eq",	INTEQ,	BINOP},
    119  1.31  christos 	{"ge",	INTGE,	BINOP},
    120  1.31  christos 	{"gt",	INTGT,	BINOP},
    121  1.31  christos 	{"le",	INTLE,	BINOP},
    122  1.31  christos 	{"lt",	INTLT,	BINOP},
    123  1.31  christos 	{"ne",	INTNE,	BINOP},
    124  1.31  christos 	{"nt",	FILNT,	BINOP},
    125  1.31  christos 	{"ot",	FILOT,	BINOP},
    126  1.31  christos };
    127  1.31  christos 
    128  1.31  christos static const struct t_op mop2[] = {
    129  1.31  christos 	{"G",	FILGID,	UNOP},
    130  1.31  christos 	{"L",	FILSYM,	UNOP},
    131  1.31  christos 	{"O",	FILUID,	UNOP},
    132  1.31  christos 	{"S",	FILSOCK,UNOP},
    133  1.31  christos 	{"a",	BAND,	BBINOP},
    134  1.31  christos 	{"b",	FILBDEV,UNOP},
    135  1.31  christos 	{"c",	FILCDEV,UNOP},
    136  1.31  christos 	{"d",	FILDIR,	UNOP},
    137  1.31  christos 	{"e",	FILEXIST,UNOP},
    138  1.31  christos 	{"f",	FILREG,	UNOP},
    139  1.31  christos 	{"g",	FILSGID,UNOP},
    140  1.31  christos 	{"h",	FILSYM,	UNOP},		/* for backwards compat */
    141  1.31  christos 	{"k",	FILSTCK,UNOP},
    142  1.31  christos 	{"n",	STRNZ,	UNOP},
    143  1.31  christos 	{"o",	BOR,	BBINOP},
    144  1.31  christos 	{"p",	FILFIFO,UNOP},
    145  1.31  christos 	{"r",	FILRD,	UNOP},
    146  1.31  christos 	{"s",	FILGZ,	UNOP},
    147  1.31  christos 	{"t",	FILTT,	UNOP},
    148  1.31  christos 	{"u",	FILSUID,UNOP},
    149  1.31  christos 	{"w",	FILWR,	UNOP},
    150  1.31  christos 	{"x",	FILEX,	UNOP},
    151  1.31  christos 	{"z",	STREZ,	UNOP},
    152   1.1     glass };
    153   1.1     glass 
    154  1.22  christos static char **t_wp;
    155  1.22  christos static struct t_op const *t_wp_op;
    156   1.1     glass 
    157  1.23       wiz static void syntax(const char *, const char *);
    158  1.23       wiz static int oexpr(enum token);
    159  1.23       wiz static int aexpr(enum token);
    160  1.23       wiz static int nexpr(enum token);
    161  1.23       wiz static int primary(enum token);
    162  1.23       wiz static int binop(void);
    163  1.23       wiz static int filstat(char *, enum token);
    164  1.23       wiz static enum token t_lex(char *);
    165  1.23       wiz static int isoperand(void);
    166  1.23       wiz static int getn(const char *);
    167  1.23       wiz static int newerf(const char *, const char *);
    168  1.23       wiz static int olderf(const char *, const char *);
    169  1.23       wiz static int equalf(const char *, const char *);
    170  1.17  christos 
    171  1.22  christos #if defined(SHELL)
    172  1.23       wiz extern void error(const char *, ...) __attribute__((__noreturn__));
    173  1.22  christos #else
    174  1.23       wiz static void error(const char *, ...) __attribute__((__noreturn__));
    175  1.22  christos 
    176  1.22  christos static void
    177  1.22  christos error(const char *msg, ...)
    178  1.22  christos {
    179  1.22  christos 	va_list ap;
    180  1.22  christos 
    181  1.22  christos 	va_start(ap, msg);
    182  1.22  christos 	verrx(2, msg, ap);
    183  1.22  christos 	/*NOTREACHED*/
    184  1.22  christos 	va_end(ap);
    185  1.22  christos }
    186  1.22  christos #endif
    187  1.22  christos 
    188  1.22  christos #ifdef SHELL
    189  1.23       wiz int testcmd(int, char **);
    190  1.22  christos 
    191  1.22  christos int
    192  1.23       wiz testcmd(int argc, char **argv)
    193  1.22  christos #else
    194  1.24       wiz int main(int, char *[]);
    195   1.1     glass 
    196   1.1     glass int
    197  1.24       wiz main(int argc, char *argv[])
    198  1.22  christos #endif
    199   1.1     glass {
    200  1.24       wiz 	int res;
    201  1.28  christos 	const char *argv0;
    202  1.22  christos 
    203  1.28  christos #ifdef SHELL
    204  1.28  christos 	argv0 = argv[0];
    205  1.28  christos #else
    206  1.24       wiz 	setprogname(argv[0]);
    207  1.28  christos 	argv0 = getprogname();
    208  1.28  christos #endif
    209  1.28  christos 	if (strcmp(argv0, "[") == 0) {
    210   1.1     glass 		if (strcmp(argv[--argc], "]"))
    211  1.22  christos 			error("missing ]");
    212   1.1     glass 		argv[argc] = NULL;
    213   1.1     glass 	}
    214   1.1     glass 
    215  1.22  christos 	if (argc < 2)
    216  1.22  christos 		return 1;
    217  1.22  christos 
    218  1.14       cgd 	t_wp = &argv[1];
    219  1.13       jtc 	res = !oexpr(t_lex(*t_wp));
    220  1.13       jtc 
    221  1.13       jtc 	if (*t_wp != NULL && *++t_wp != NULL)
    222  1.21    kleink 		syntax(*t_wp, "unexpected operator");
    223  1.13       jtc 
    224  1.13       jtc 	return res;
    225  1.13       jtc }
    226  1.13       jtc 
    227  1.13       jtc static void
    228  1.23       wiz syntax(const char *op, const char *msg)
    229  1.13       jtc {
    230  1.26    simonb 
    231  1.13       jtc 	if (op && *op)
    232  1.22  christos 		error("%s: %s", op, msg);
    233  1.13       jtc 	else
    234  1.22  christos 		error("%s", msg);
    235  1.13       jtc }
    236  1.13       jtc 
    237  1.13       jtc static int
    238  1.23       wiz oexpr(enum token n)
    239  1.13       jtc {
    240  1.13       jtc 	int res;
    241  1.13       jtc 
    242  1.13       jtc 	res = aexpr(n);
    243  1.32  christos 	if (*t_wp == NULL)
    244  1.32  christos 		return res;
    245  1.13       jtc 	if (t_lex(*++t_wp) == BOR)
    246  1.13       jtc 		return oexpr(t_lex(*++t_wp)) || res;
    247  1.13       jtc 	t_wp--;
    248  1.13       jtc 	return res;
    249  1.13       jtc }
    250  1.13       jtc 
    251  1.13       jtc static int
    252  1.23       wiz aexpr(enum token n)
    253  1.13       jtc {
    254  1.13       jtc 	int res;
    255  1.13       jtc 
    256  1.13       jtc 	res = nexpr(n);
    257  1.32  christos 	if (*t_wp == NULL)
    258  1.32  christos 		return res;
    259  1.13       jtc 	if (t_lex(*++t_wp) == BAND)
    260  1.13       jtc 		return aexpr(t_lex(*++t_wp)) && res;
    261  1.13       jtc 	t_wp--;
    262  1.13       jtc 	return res;
    263  1.13       jtc }
    264  1.13       jtc 
    265  1.13       jtc static int
    266  1.23       wiz nexpr(enum token n)
    267  1.13       jtc {
    268  1.26    simonb 
    269  1.13       jtc 	if (n == UNOT)
    270  1.13       jtc 		return !nexpr(t_lex(*++t_wp));
    271  1.13       jtc 	return primary(n);
    272  1.13       jtc }
    273  1.13       jtc 
    274  1.13       jtc static int
    275  1.23       wiz primary(enum token n)
    276  1.13       jtc {
    277  1.21    kleink 	enum token nn;
    278  1.13       jtc 	int res;
    279  1.13       jtc 
    280  1.13       jtc 	if (n == EOI)
    281  1.21    kleink 		return 0;		/* missing expression */
    282  1.13       jtc 	if (n == LPAREN) {
    283  1.21    kleink 		if ((nn = t_lex(*++t_wp)) == RPAREN)
    284  1.21    kleink 			return 0;	/* missing expression */
    285  1.21    kleink 		res = oexpr(nn);
    286  1.13       jtc 		if (t_lex(*++t_wp) != RPAREN)
    287  1.13       jtc 			syntax(NULL, "closing paren expected");
    288  1.13       jtc 		return res;
    289   1.1     glass 	}
    290  1.13       jtc 	if (t_wp_op && t_wp_op->op_type == UNOP) {
    291  1.13       jtc 		/* unary expression */
    292  1.13       jtc 		if (*++t_wp == NULL)
    293  1.13       jtc 			syntax(t_wp_op->op_text, "argument expected");
    294  1.13       jtc 		switch (n) {
    295  1.13       jtc 		case STREZ:
    296  1.13       jtc 			return strlen(*t_wp) == 0;
    297  1.13       jtc 		case STRNZ:
    298  1.13       jtc 			return strlen(*t_wp) != 0;
    299  1.13       jtc 		case FILTT:
    300  1.13       jtc 			return isatty(getn(*t_wp));
    301  1.13       jtc 		default:
    302  1.13       jtc 			return filstat(*t_wp, n);
    303   1.1     glass 		}
    304  1.13       jtc 	}
    305  1.14       cgd 
    306  1.14       cgd 	if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
    307  1.14       cgd 		return binop();
    308  1.14       cgd 	}
    309  1.14       cgd 
    310  1.14       cgd 	return strlen(*t_wp) > 0;
    311  1.14       cgd }
    312  1.14       cgd 
    313  1.14       cgd static int
    314  1.23       wiz binop(void)
    315  1.14       cgd {
    316  1.16       tls 	const char *opnd1, *opnd2;
    317  1.14       cgd 	struct t_op const *op;
    318  1.14       cgd 
    319  1.13       jtc 	opnd1 = *t_wp;
    320  1.13       jtc 	(void) t_lex(*++t_wp);
    321  1.14       cgd 	op = t_wp_op;
    322  1.13       jtc 
    323  1.26    simonb 	if ((opnd2 = *++t_wp) == NULL)
    324  1.14       cgd 		syntax(op->op_text, "argument expected");
    325  1.13       jtc 
    326  1.14       cgd 	switch (op->op_num) {
    327  1.14       cgd 	case STREQ:
    328  1.14       cgd 		return strcmp(opnd1, opnd2) == 0;
    329  1.14       cgd 	case STRNE:
    330  1.14       cgd 		return strcmp(opnd1, opnd2) != 0;
    331  1.14       cgd 	case STRLT:
    332  1.14       cgd 		return strcmp(opnd1, opnd2) < 0;
    333  1.14       cgd 	case STRGT:
    334  1.14       cgd 		return strcmp(opnd1, opnd2) > 0;
    335  1.14       cgd 	case INTEQ:
    336  1.14       cgd 		return getn(opnd1) == getn(opnd2);
    337  1.14       cgd 	case INTNE:
    338  1.14       cgd 		return getn(opnd1) != getn(opnd2);
    339  1.14       cgd 	case INTGE:
    340  1.14       cgd 		return getn(opnd1) >= getn(opnd2);
    341  1.14       cgd 	case INTGT:
    342  1.14       cgd 		return getn(opnd1) > getn(opnd2);
    343  1.14       cgd 	case INTLE:
    344  1.14       cgd 		return getn(opnd1) <= getn(opnd2);
    345  1.14       cgd 	case INTLT:
    346  1.14       cgd 		return getn(opnd1) < getn(opnd2);
    347  1.14       cgd 	case FILNT:
    348  1.26    simonb 		return newerf(opnd1, opnd2);
    349  1.14       cgd 	case FILOT:
    350  1.26    simonb 		return olderf(opnd1, opnd2);
    351  1.14       cgd 	case FILEQ:
    352  1.26    simonb 		return equalf(opnd1, opnd2);
    353  1.17  christos 	default:
    354  1.19   mycroft 		abort();
    355  1.17  christos 		/* NOTREACHED */
    356   1.1     glass 	}
    357   1.1     glass }
    358   1.1     glass 
    359   1.1     glass static int
    360  1.23       wiz filstat(char *nm, enum token mode)
    361   1.1     glass {
    362  1.13       jtc 	struct stat s;
    363  1.13       jtc 
    364  1.18   mycroft 	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
    365  1.13       jtc 		return 0;
    366   1.1     glass 
    367  1.13       jtc 	switch (mode) {
    368  1.13       jtc 	case FILRD:
    369  1.13       jtc 		return access(nm, R_OK) == 0;
    370  1.13       jtc 	case FILWR:
    371  1.13       jtc 		return access(nm, W_OK) == 0;
    372  1.13       jtc 	case FILEX:
    373  1.13       jtc 		return access(nm, X_OK) == 0;
    374  1.13       jtc 	case FILEXIST:
    375  1.13       jtc 		return access(nm, F_OK) == 0;
    376  1.13       jtc 	case FILREG:
    377  1.18   mycroft 		return S_ISREG(s.st_mode);
    378  1.13       jtc 	case FILDIR:
    379  1.18   mycroft 		return S_ISDIR(s.st_mode);
    380  1.13       jtc 	case FILCDEV:
    381  1.18   mycroft 		return S_ISCHR(s.st_mode);
    382  1.13       jtc 	case FILBDEV:
    383  1.18   mycroft 		return S_ISBLK(s.st_mode);
    384  1.13       jtc 	case FILFIFO:
    385  1.18   mycroft 		return S_ISFIFO(s.st_mode);
    386  1.13       jtc 	case FILSOCK:
    387  1.18   mycroft 		return S_ISSOCK(s.st_mode);
    388  1.18   mycroft 	case FILSYM:
    389  1.18   mycroft 		return S_ISLNK(s.st_mode);
    390  1.13       jtc 	case FILSUID:
    391  1.18   mycroft 		return (s.st_mode & S_ISUID) != 0;
    392  1.13       jtc 	case FILSGID:
    393  1.18   mycroft 		return (s.st_mode & S_ISGID) != 0;
    394  1.13       jtc 	case FILSTCK:
    395  1.18   mycroft 		return (s.st_mode & S_ISVTX) != 0;
    396  1.13       jtc 	case FILGZ:
    397  1.18   mycroft 		return s.st_size > (off_t)0;
    398  1.13       jtc 	case FILUID:
    399  1.13       jtc 		return s.st_uid == geteuid();
    400  1.13       jtc 	case FILGID:
    401  1.13       jtc 		return s.st_gid == getegid();
    402  1.13       jtc 	default:
    403  1.13       jtc 		return 1;
    404  1.13       jtc 	}
    405   1.1     glass }
    406   1.1     glass 
    407  1.31  christos #define VTOC(x)	(const unsigned char *)((const struct t_op *)x)->op_text
    408  1.31  christos 
    409  1.31  christos static int
    410  1.31  christos compare1(const void *va, const void *vb)
    411  1.31  christos {
    412  1.31  christos 	const unsigned char *a = va;
    413  1.31  christos 	const unsigned char *b = VTOC(vb);
    414  1.31  christos 
    415  1.31  christos 	return a[0] - b[0];
    416  1.31  christos }
    417  1.31  christos 
    418  1.31  christos static int
    419  1.31  christos compare2(const void *va, const void *vb)
    420  1.31  christos {
    421  1.31  christos 	const unsigned char *a = va;
    422  1.31  christos 	const unsigned char *b = VTOC(vb);
    423  1.31  christos 	int z = a[0] - b[0];
    424  1.31  christos 
    425  1.31  christos 	return z ? z : (a[1] - b[1]);
    426  1.31  christos }
    427  1.31  christos 
    428  1.31  christos static struct t_op const *
    429  1.31  christos findop(const char *s)
    430  1.31  christos {
    431  1.31  christos 	if (s[0] == '-') {
    432  1.31  christos 		if (s[1] == '\0')
    433  1.31  christos 			return NULL;
    434  1.31  christos 		if (s[2] == '\0')
    435  1.31  christos 			return bsearch(s + 1, mop2, __arraycount(mop2),
    436  1.31  christos 			    sizeof(*mop2), compare1);
    437  1.31  christos 		else if (s[3] != '\0')
    438  1.31  christos 			return NULL;
    439  1.31  christos 		else
    440  1.31  christos 			return bsearch(s + 1, mop3, __arraycount(mop3),
    441  1.31  christos 			    sizeof(*mop3), compare2);
    442  1.31  christos 	} else {
    443  1.31  christos 		if (s[1] == '\0')
    444  1.31  christos 			return bsearch(s, cop, __arraycount(cop), sizeof(*cop),
    445  1.31  christos 			    compare1);
    446  1.31  christos 		else if (strcmp(s, cop2[0].op_text) == 0)
    447  1.31  christos 			return cop2;
    448  1.31  christos 		else
    449  1.31  christos 			return NULL;
    450  1.31  christos 	}
    451  1.31  christos }
    452  1.31  christos 
    453  1.13       jtc static enum token
    454  1.23       wiz t_lex(char *s)
    455   1.1     glass {
    456  1.24       wiz 	struct t_op const *op;
    457  1.24       wiz 
    458  1.31  christos 	if (s == NULL) {
    459  1.26    simonb 		t_wp_op = NULL;
    460  1.13       jtc 		return EOI;
    461  1.13       jtc 	}
    462  1.31  christos 
    463  1.31  christos 	if ((op = findop(s)) != NULL) {
    464  1.31  christos 		if (!((op->op_type == UNOP && isoperand()) ||
    465  1.31  christos 		    (op->op_num == LPAREN && *(t_wp+1) == 0))) {
    466  1.13       jtc 			t_wp_op = op;
    467  1.13       jtc 			return op->op_num;
    468  1.13       jtc 		}
    469  1.13       jtc 	}
    470  1.26    simonb 	t_wp_op = NULL;
    471  1.13       jtc 	return OPERAND;
    472  1.21    kleink }
    473  1.21    kleink 
    474  1.21    kleink static int
    475  1.23       wiz isoperand(void)
    476  1.21    kleink {
    477  1.24       wiz 	struct t_op const *op;
    478  1.24       wiz 	char *s, *t;
    479  1.21    kleink 
    480  1.21    kleink 	if ((s  = *(t_wp+1)) == 0)
    481  1.21    kleink 		return 1;
    482  1.21    kleink 	if ((t = *(t_wp+2)) == 0)
    483  1.21    kleink 		return 0;
    484  1.31  christos 	if ((op = findop(s)) != NULL)
    485  1.31  christos 		return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
    486  1.21    kleink 	return 0;
    487   1.1     glass }
    488   1.1     glass 
    489  1.13       jtc /* atoi with error detection */
    490   1.1     glass static int
    491  1.23       wiz getn(const char *s)
    492   1.1     glass {
    493   1.6       alm 	char *p;
    494   1.6       alm 	long r;
    495   1.1     glass 
    496   1.6       alm 	errno = 0;
    497  1.13       jtc 	r = strtol(s, &p, 10);
    498  1.13       jtc 
    499   1.6       alm 	if (errno != 0)
    500  1.22  christos 	      error("%s: out of range", s);
    501  1.13       jtc 
    502  1.20  christos 	while (isspace((unsigned char)*p))
    503  1.22  christos 	      p++;
    504  1.13       jtc 
    505  1.13       jtc 	if (*p)
    506  1.22  christos 	      error("%s: bad number", s);
    507  1.13       jtc 
    508  1.13       jtc 	return (int) r;
    509   1.1     glass }
    510   1.1     glass 
    511  1.13       jtc static int
    512  1.26    simonb newerf(const char *f1, const char *f2)
    513   1.1     glass {
    514  1.13       jtc 	struct stat b1, b2;
    515  1.13       jtc 
    516  1.26    simonb 	return (stat(f1, &b1) == 0 &&
    517  1.26    simonb 		stat(f2, &b2) == 0 &&
    518  1.13       jtc 		b1.st_mtime > b2.st_mtime);
    519   1.1     glass }
    520   1.1     glass 
    521  1.13       jtc static int
    522  1.26    simonb olderf(const char *f1, const char *f2)
    523   1.1     glass {
    524  1.13       jtc 	struct stat b1, b2;
    525  1.13       jtc 
    526  1.26    simonb 	return (stat(f1, &b1) == 0 &&
    527  1.26    simonb 		stat(f2, &b2) == 0 &&
    528  1.13       jtc 		b1.st_mtime < b2.st_mtime);
    529   1.1     glass }
    530   1.1     glass 
    531  1.13       jtc static int
    532  1.26    simonb equalf(const char *f1, const char *f2)
    533  1.13       jtc {
    534  1.13       jtc 	struct stat b1, b2;
    535   1.1     glass 
    536  1.26    simonb 	return (stat(f1, &b1) == 0 &&
    537  1.26    simonb 		stat(f2, &b2) == 0 &&
    538  1.13       jtc 		b1.st_dev == b2.st_dev &&
    539  1.13       jtc 		b1.st_ino == b2.st_ino);
    540   1.1     glass }
    541