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