test.c revision 90111
149884Ssheldonh/*	$NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $	*/
249884Ssheldonh
349884Ssheldonh/*
449884Ssheldonh * test(1); version 7-like  --  author Erik Baalbergen
549884Ssheldonh * modified by Eric Gisin to be used as built-in.
649884Ssheldonh * modified by Arnold Robbins to add SVR3 compatibility
749884Ssheldonh * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
849884Ssheldonh * modified by J.T. Conklin for NetBSD.
91556Srgrimes *
1049884Ssheldonh * This program is in the Public Domain.
111556Srgrimes */
121556Srgrimes
131556Srgrimes#ifndef lint
1436152Scharnierstatic const char rcsid[] =
1550471Speter  "$FreeBSD: head/bin/test/test.c 90111 2002-02-02 06:50:57Z imp $";
161556Srgrimes#endif /* not lint */
171556Srgrimes
1849884Ssheldonh#include <sys/types.h>
191556Srgrimes#include <sys/stat.h>
201556Srgrimes
211556Srgrimes#include <ctype.h>
221556Srgrimes#include <err.h>
231556Srgrimes#include <errno.h>
2476883Skris#include <limits.h>
2586619Sknu#include <stdarg.h>
261556Srgrimes#include <stdio.h>
271556Srgrimes#include <stdlib.h>
281556Srgrimes#include <string.h>
291556Srgrimes#include <unistd.h>
301556Srgrimes
3186505Sknu#ifdef SHELL
3286505Sknu#define main testcmd
3386505Sknu#include "bltin/bltin.h"
3486618Sknu#else
3588084Sache#include <locale.h>
3688084Sache
3786618Sknustatic void error(const char *, ...) __attribute__((__noreturn__));
3886618Sknu
3986618Sknustatic void
4086618Sknuerror(const char *msg, ...)
4186618Sknu{
4286618Sknu	va_list ap;
4386618Sknu	va_start(ap, msg);
4486618Sknu	verrx(2, msg, ap);
4586618Sknu	/*NOTREACHED*/
4686618Sknu	va_end(ap);
4786618Sknu}
4886618Sknu#endif
4986618Sknu
5049884Ssheldonh/* test(1) accepts the following grammar:
5149884Ssheldonh	oexpr	::= aexpr | aexpr "-o" oexpr ;
5249884Ssheldonh	aexpr	::= nexpr | nexpr "-a" aexpr ;
5349884Ssheldonh	nexpr	::= primary | "!" primary
5449884Ssheldonh	primary	::= unary-operator operand
5549884Ssheldonh		| operand binary-operator operand
5649884Ssheldonh		| operand
5749884Ssheldonh		| "(" oexpr ")"
5849884Ssheldonh		;
5949884Ssheldonh	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
6049884Ssheldonh		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
611556Srgrimes
6249884Ssheldonh	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
6349884Ssheldonh			"-nt"|"-ot"|"-ef";
6449884Ssheldonh	operand ::= <any legal UNIX file name>
6549884Ssheldonh*/
661556Srgrimes
6749884Ssheldonhenum token {
6849884Ssheldonh	EOI,
6949884Ssheldonh	FILRD,
7049884Ssheldonh	FILWR,
7149884Ssheldonh	FILEX,
7249884Ssheldonh	FILEXIST,
7349884Ssheldonh	FILREG,
7449884Ssheldonh	FILDIR,
7549884Ssheldonh	FILCDEV,
7649884Ssheldonh	FILBDEV,
7749884Ssheldonh	FILFIFO,
7849884Ssheldonh	FILSOCK,
7949884Ssheldonh	FILSYM,
8049884Ssheldonh	FILGZ,
8149884Ssheldonh	FILTT,
8249884Ssheldonh	FILSUID,
8349884Ssheldonh	FILSGID,
8449884Ssheldonh	FILSTCK,
8549884Ssheldonh	FILNT,
8649884Ssheldonh	FILOT,
8749884Ssheldonh	FILEQ,
8849884Ssheldonh	FILUID,
8949884Ssheldonh	FILGID,
9049884Ssheldonh	STREZ,
9149884Ssheldonh	STRNZ,
9249884Ssheldonh	STREQ,
9349884Ssheldonh	STRNE,
9449884Ssheldonh	STRLT,
9549884Ssheldonh	STRGT,
9649884Ssheldonh	INTEQ,
9749884Ssheldonh	INTNE,
9849884Ssheldonh	INTGE,
9949884Ssheldonh	INTGT,
10049884Ssheldonh	INTLE,
10149884Ssheldonh	INTLT,
10249884Ssheldonh	UNOT,
10349884Ssheldonh	BAND,
10449884Ssheldonh	BOR,
10549884Ssheldonh	LPAREN,
10649884Ssheldonh	RPAREN,
10749884Ssheldonh	OPERAND
1081556Srgrimes};
1091556Srgrimes
11049884Ssheldonhenum token_types {
11149884Ssheldonh	UNOP,
11249884Ssheldonh	BINOP,
11349884Ssheldonh	BUNOP,
11449884Ssheldonh	BBINOP,
11549884Ssheldonh	PAREN
1161556Srgrimes};
1171556Srgrimes
11849884Ssheldonhstruct t_op {
11949884Ssheldonh	const char *op_text;
12049884Ssheldonh	short op_num, op_type;
12149884Ssheldonh} const ops [] = {
12249884Ssheldonh	{"-r",	FILRD,	UNOP},
12349884Ssheldonh	{"-w",	FILWR,	UNOP},
12449884Ssheldonh	{"-x",	FILEX,	UNOP},
12549884Ssheldonh	{"-e",	FILEXIST,UNOP},
12649884Ssheldonh	{"-f",	FILREG,	UNOP},
12749884Ssheldonh	{"-d",	FILDIR,	UNOP},
12849884Ssheldonh	{"-c",	FILCDEV,UNOP},
12949884Ssheldonh	{"-b",	FILBDEV,UNOP},
13049884Ssheldonh	{"-p",	FILFIFO,UNOP},
13149884Ssheldonh	{"-u",	FILSUID,UNOP},
13249884Ssheldonh	{"-g",	FILSGID,UNOP},
13349884Ssheldonh	{"-k",	FILSTCK,UNOP},
13449884Ssheldonh	{"-s",	FILGZ,	UNOP},
13549884Ssheldonh	{"-t",	FILTT,	UNOP},
13649884Ssheldonh	{"-z",	STREZ,	UNOP},
13749884Ssheldonh	{"-n",	STRNZ,	UNOP},
13849884Ssheldonh	{"-h",	FILSYM,	UNOP},		/* for backwards compat */
13949884Ssheldonh	{"-O",	FILUID,	UNOP},
14049884Ssheldonh	{"-G",	FILGID,	UNOP},
14149884Ssheldonh	{"-L",	FILSYM,	UNOP},
14249884Ssheldonh	{"-S",	FILSOCK,UNOP},
14349884Ssheldonh	{"=",	STREQ,	BINOP},
14449884Ssheldonh	{"!=",	STRNE,	BINOP},
14549884Ssheldonh	{"<",	STRLT,	BINOP},
14649884Ssheldonh	{">",	STRGT,	BINOP},
14749884Ssheldonh	{"-eq",	INTEQ,	BINOP},
14849884Ssheldonh	{"-ne",	INTNE,	BINOP},
14949884Ssheldonh	{"-ge",	INTGE,	BINOP},
15049884Ssheldonh	{"-gt",	INTGT,	BINOP},
15149884Ssheldonh	{"-le",	INTLE,	BINOP},
15249884Ssheldonh	{"-lt",	INTLT,	BINOP},
15349884Ssheldonh	{"-nt",	FILNT,	BINOP},
15449884Ssheldonh	{"-ot",	FILOT,	BINOP},
15549884Ssheldonh	{"-ef",	FILEQ,	BINOP},
15649884Ssheldonh	{"!",	UNOT,	BUNOP},
15749884Ssheldonh	{"-a",	BAND,	BBINOP},
15849884Ssheldonh	{"-o",	BOR,	BBINOP},
15949884Ssheldonh	{"(",	LPAREN,	PAREN},
16049884Ssheldonh	{")",	RPAREN,	PAREN},
16149884Ssheldonh	{0,	0,	0}
1621556Srgrimes};
1631556Srgrimes
16449884Ssheldonhstruct t_op const *t_wp_op;
16549884Ssheldonhchar **t_wp;
1661556Srgrimes
16790111Simpstatic int	aexpr(enum token);
16890111Simpstatic int	binop(void);
16990111Simpstatic int	equalf(const char *, const char *);
17090111Simpstatic int	filstat(char *, enum token);
17190111Simpstatic int	getn(const char *);
17290111Simpstatic long long	getq(const char *);
17390111Simpstatic int	intcmp(const char *, const char *);
17490111Simpstatic int	isoperand(void);
17590111Simpint		main(int, char **);
17690111Simpstatic int	newerf(const char *, const char *);
17790111Simpstatic int	nexpr(enum token);
17890111Simpstatic int	oexpr(enum token);
17990111Simpstatic int	olderf(const char *, const char *);
18090111Simpstatic int	primary(enum token);
18190111Simpstatic void	syntax(const char *, const char *);
18290111Simpstatic enum	token t_lex(char *);
18349884Ssheldonh
1841556Srgrimesint
18590111Simpmain(int argc, char **argv)
1861556Srgrimes{
18749884Ssheldonh	int	res;
18855179Ssheldonh	char	*p;
1891556Srgrimes
19055179Ssheldonh	if ((p = rindex(argv[0], '/')) == NULL)
19155179Ssheldonh		p = argv[0];
19255179Ssheldonh	else
19355179Ssheldonh		p++;
19455179Ssheldonh	if (strcmp(p, "[") == 0) {
19586622Sknu		if (strcmp(argv[--argc], "]") != 0)
19686618Sknu			error("missing ]");
1971556Srgrimes		argv[argc] = NULL;
1981556Srgrimes	}
1991556Srgrimes
20086622Sknu	/* no expression => false */
20186622Sknu	if (--argc <= 0)
20286622Sknu		return 1;
20386622Sknu
20488084Sache#ifndef SHELL
20588084Sache	(void)setlocale(LC_CTYPE, "");
20688084Sache#endif
20750302Sgreen	/* XXX work around the absence of an eaccess(2) syscall */
20850087Sgreen	(void)setgid(getegid());
20950087Sgreen	(void)setuid(geteuid());
21050087Sgreen
21149884Ssheldonh	t_wp = &argv[1];
21249884Ssheldonh	res = !oexpr(t_lex(*t_wp));
2131556Srgrimes
21449884Ssheldonh	if (*t_wp != NULL && *++t_wp != NULL)
21549884Ssheldonh		syntax(*t_wp, "unexpected operator");
21649884Ssheldonh
21749884Ssheldonh	return res;
2181556Srgrimes}
2191556Srgrimes
22049884Ssheldonhstatic void
22190111Simpsyntax(const char *op, const char *msg)
2221556Srgrimes{
2231556Srgrimes
22449884Ssheldonh	if (op && *op)
22586618Sknu		error("%s: %s", op, msg);
22649884Ssheldonh	else
22786618Sknu		error("%s", msg);
2281556Srgrimes}
2291556Srgrimes
23049884Ssheldonhstatic int
23190111Simpoexpr(enum token n)
2321556Srgrimes{
23349884Ssheldonh	int res;
2341556Srgrimes
23549884Ssheldonh	res = aexpr(n);
23649884Ssheldonh	if (t_lex(*++t_wp) == BOR)
23749884Ssheldonh		return oexpr(t_lex(*++t_wp)) || res;
23849884Ssheldonh	t_wp--;
23949884Ssheldonh	return res;
24049884Ssheldonh}
2414171Sache
24249884Ssheldonhstatic int
24390111Simpaexpr(enum token n)
24449884Ssheldonh{
24549884Ssheldonh	int res;
2461556Srgrimes
24749884Ssheldonh	res = nexpr(n);
24849884Ssheldonh	if (t_lex(*++t_wp) == BAND)
24949884Ssheldonh		return aexpr(t_lex(*++t_wp)) && res;
25049884Ssheldonh	t_wp--;
25149884Ssheldonh	return res;
2521556Srgrimes}
2531556Srgrimes
2541556Srgrimesstatic int
25590111Simpnexpr(enum token n)
2561556Srgrimes{
25749884Ssheldonh	if (n == UNOT)
25849884Ssheldonh		return !nexpr(t_lex(*++t_wp));
25949884Ssheldonh	return primary(n);
2601556Srgrimes}
2611556Srgrimes
2621556Srgrimesstatic int
26390111Simpprimary(enum token n)
2641556Srgrimes{
26549884Ssheldonh	enum token nn;
26649884Ssheldonh	int res;
2671556Srgrimes
26849884Ssheldonh	if (n == EOI)
26949884Ssheldonh		return 0;		/* missing expression */
27049884Ssheldonh	if (n == LPAREN) {
27149884Ssheldonh		if ((nn = t_lex(*++t_wp)) == RPAREN)
27249884Ssheldonh			return 0;	/* missing expression */
27349884Ssheldonh		res = oexpr(nn);
27449884Ssheldonh		if (t_lex(*++t_wp) != RPAREN)
27549884Ssheldonh			syntax(NULL, "closing paren expected");
27649884Ssheldonh		return res;
27749884Ssheldonh	}
27849884Ssheldonh	if (t_wp_op && t_wp_op->op_type == UNOP) {
27949884Ssheldonh		/* unary expression */
28049884Ssheldonh		if (*++t_wp == NULL)
28149884Ssheldonh			syntax(t_wp_op->op_text, "argument expected");
28249884Ssheldonh		switch (n) {
28349884Ssheldonh		case STREZ:
28449884Ssheldonh			return strlen(*t_wp) == 0;
28549884Ssheldonh		case STRNZ:
28649884Ssheldonh			return strlen(*t_wp) != 0;
28749884Ssheldonh		case FILTT:
28849884Ssheldonh			return isatty(getn(*t_wp));
28949884Ssheldonh		default:
29049884Ssheldonh			return filstat(*t_wp, n);
29149884Ssheldonh		}
29249884Ssheldonh	}
2931556Srgrimes
29449884Ssheldonh	if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
29549884Ssheldonh		return binop();
29649884Ssheldonh	}
29749884Ssheldonh
29849884Ssheldonh	return strlen(*t_wp) > 0;
2991556Srgrimes}
3001556Srgrimes
3011556Srgrimesstatic int
30290111Simpbinop(void)
3031556Srgrimes{
30449884Ssheldonh	const char *opnd1, *opnd2;
30549884Ssheldonh	struct t_op const *op;
3061556Srgrimes
30749884Ssheldonh	opnd1 = *t_wp;
30849884Ssheldonh	(void) t_lex(*++t_wp);
30949884Ssheldonh	op = t_wp_op;
3101556Srgrimes
31149884Ssheldonh	if ((opnd2 = *++t_wp) == NULL)
31249884Ssheldonh		syntax(op->op_text, "argument expected");
31349884Ssheldonh
31449884Ssheldonh	switch (op->op_num) {
31549884Ssheldonh	case STREQ:
31649884Ssheldonh		return strcmp(opnd1, opnd2) == 0;
31749884Ssheldonh	case STRNE:
31849884Ssheldonh		return strcmp(opnd1, opnd2) != 0;
31949884Ssheldonh	case STRLT:
32049884Ssheldonh		return strcmp(opnd1, opnd2) < 0;
32149884Ssheldonh	case STRGT:
32249884Ssheldonh		return strcmp(opnd1, opnd2) > 0;
32349884Ssheldonh	case INTEQ:
32462925Sse		return intcmp(opnd1, opnd2) == 0;
32549884Ssheldonh	case INTNE:
32662925Sse		return intcmp(opnd1, opnd2) != 0;
32749884Ssheldonh	case INTGE:
32862925Sse		return intcmp(opnd1, opnd2) >= 0;
32949884Ssheldonh	case INTGT:
33062925Sse		return intcmp(opnd1, opnd2) > 0;
33149884Ssheldonh	case INTLE:
33262925Sse		return intcmp(opnd1, opnd2) <= 0;
33349884Ssheldonh	case INTLT:
33462925Sse		return intcmp(opnd1, opnd2) < 0;
33549884Ssheldonh	case FILNT:
33649884Ssheldonh		return newerf (opnd1, opnd2);
33749884Ssheldonh	case FILOT:
33849884Ssheldonh		return olderf (opnd1, opnd2);
33949884Ssheldonh	case FILEQ:
34049884Ssheldonh		return equalf (opnd1, opnd2);
34149884Ssheldonh	default:
34249884Ssheldonh		abort();
34349884Ssheldonh		/* NOTREACHED */
3441556Srgrimes	}
3451556Srgrimes}
3461556Srgrimes
34749884Ssheldonhstatic int
34890111Simpfilstat(char *nm, enum token mode)
3491556Srgrimes{
35049884Ssheldonh	struct stat s;
3511556Srgrimes
35249884Ssheldonh	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
35349884Ssheldonh		return 0;
3542664Scsgr
35549884Ssheldonh	switch (mode) {
35649884Ssheldonh	case FILRD:
35749884Ssheldonh		return access(nm, R_OK) == 0;
35849884Ssheldonh	case FILWR:
35949884Ssheldonh		return access(nm, W_OK) == 0;
36049884Ssheldonh	case FILEX:
36150302Sgreen		/* XXX work around access(2) false positives for superuser */
36250087Sgreen		if (access(nm, X_OK) != 0)
36350087Sgreen			return 0;
36450087Sgreen		if (S_ISDIR(s.st_mode) || getuid() != 0)
36550087Sgreen			return 1;
36650087Sgreen		return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
36749884Ssheldonh	case FILEXIST:
36849884Ssheldonh		return access(nm, F_OK) == 0;
36949884Ssheldonh	case FILREG:
37049884Ssheldonh		return S_ISREG(s.st_mode);
37149884Ssheldonh	case FILDIR:
37249884Ssheldonh		return S_ISDIR(s.st_mode);
37349884Ssheldonh	case FILCDEV:
37449884Ssheldonh		return S_ISCHR(s.st_mode);
37549884Ssheldonh	case FILBDEV:
37649884Ssheldonh		return S_ISBLK(s.st_mode);
37749884Ssheldonh	case FILFIFO:
37849884Ssheldonh		return S_ISFIFO(s.st_mode);
37949884Ssheldonh	case FILSOCK:
38049884Ssheldonh		return S_ISSOCK(s.st_mode);
38149884Ssheldonh	case FILSYM:
38249884Ssheldonh		return S_ISLNK(s.st_mode);
38349884Ssheldonh	case FILSUID:
38449884Ssheldonh		return (s.st_mode & S_ISUID) != 0;
38549884Ssheldonh	case FILSGID:
38649884Ssheldonh		return (s.st_mode & S_ISGID) != 0;
38749884Ssheldonh	case FILSTCK:
38849884Ssheldonh		return (s.st_mode & S_ISVTX) != 0;
38949884Ssheldonh	case FILGZ:
39049884Ssheldonh		return s.st_size > (off_t)0;
39149884Ssheldonh	case FILUID:
39249884Ssheldonh		return s.st_uid == geteuid();
39349884Ssheldonh	case FILGID:
39449884Ssheldonh		return s.st_gid == getegid();
39549884Ssheldonh	default:
39649884Ssheldonh		return 1;
3972675Scsgr	}
39849884Ssheldonh}
3992675Scsgr
40049884Ssheldonhstatic enum token
40190111Simpt_lex(char *s)
40249884Ssheldonh{
40349884Ssheldonh	struct t_op const *op = ops;
40449884Ssheldonh
40549884Ssheldonh	if (s == 0) {
40649884Ssheldonh		t_wp_op = NULL;
40749884Ssheldonh		return EOI;
40849884Ssheldonh	}
40949884Ssheldonh	while (op->op_text) {
41049884Ssheldonh		if (strcmp(s, op->op_text) == 0) {
41149884Ssheldonh			if ((op->op_type == UNOP && isoperand()) ||
41249884Ssheldonh			    (op->op_num == LPAREN && *(t_wp+1) == 0))
41349884Ssheldonh				break;
41449884Ssheldonh			t_wp_op = op;
41549884Ssheldonh			return op->op_num;
4161556Srgrimes		}
41749884Ssheldonh		op++;
4181556Srgrimes	}
41949884Ssheldonh	t_wp_op = NULL;
42049884Ssheldonh	return OPERAND;
4211556Srgrimes}
4221556Srgrimes
42349884Ssheldonhstatic int
42490111Simpisoperand(void)
4251556Srgrimes{
42649884Ssheldonh	struct t_op const *op = ops;
42749884Ssheldonh	char *s;
42849884Ssheldonh	char *t;
4291556Srgrimes
43049884Ssheldonh	if ((s  = *(t_wp+1)) == 0)
43149884Ssheldonh		return 1;
43249884Ssheldonh	if ((t = *(t_wp+2)) == 0)
43349884Ssheldonh		return 0;
43449884Ssheldonh	while (op->op_text) {
43549884Ssheldonh		if (strcmp(s, op->op_text) == 0)
43649884Ssheldonh			return op->op_type == BINOP &&
43749884Ssheldonh			    (t[0] != ')' || t[1] != '\0');
43849884Ssheldonh		op++;
43949884Ssheldonh	}
44049884Ssheldonh	return 0;
4411556Srgrimes}
4421556Srgrimes
44349884Ssheldonh/* atoi with error detection */
44449884Ssheldonhstatic int
44590111Simpgetn(const char *s)
4461556Srgrimes{
44749884Ssheldonh	char *p;
44849884Ssheldonh	long r;
4491556Srgrimes
45049884Ssheldonh	errno = 0;
45149884Ssheldonh	r = strtol(s, &p, 10);
45249884Ssheldonh
45388084Sache	if (s == p)
45488084Sache		error("%s: bad number", s);
45588084Sache
45649884Ssheldonh	if (errno != 0)
45787961Sache		error((errno == EINVAL) ? "%s: bad number" :
45887961Sache					  "%s: out of range", s);
45949884Ssheldonh
46049884Ssheldonh	while (isspace((unsigned char)*p))
46186622Sknu		p++;
46249884Ssheldonh
46349884Ssheldonh	if (*p)
46486622Sknu		error("%s: bad number", s);
46549884Ssheldonh
46649884Ssheldonh	return (int) r;
4671556Srgrimes}
46849884Ssheldonh
46962925Sse/* atoi with error detection and 64 bit range */
47088471Sachestatic long long
47190111Simpgetq(const char *s)
47262925Sse{
47362925Sse	char *p;
47488471Sache	long long r;
47562925Sse
47662925Sse	errno = 0;
47788471Sache	r = strtoll(s, &p, 10);
47862925Sse
47988084Sache	if (s == p)
48088084Sache		error("%s: bad number", s);
48188084Sache
48262925Sse	if (errno != 0)
48387961Sache		error((errno == EINVAL) ? "%s: bad number" :
48487961Sache					  "%s: out of range", s);
48562925Sse
48662925Sse	while (isspace((unsigned char)*p))
48786622Sknu		p++;
48862925Sse
48962925Sse	if (*p)
49086622Sknu		error("%s: bad number", s);
49162925Sse
49262925Sse	return r;
49362925Sse}
49462925Sse
49549884Ssheldonhstatic int
49690111Simpintcmp (const char *s1, const char *s2)
49762925Sse{
49888471Sache	long long q1, q2;
49962925Sse
50062925Sse
50162925Sse	q1 = getq(s1);
50262925Sse	q2 = getq(s2);
50362925Sse
50462925Sse	if (q1 > q2)
50562925Sse		return 1;
50662925Sse
50762925Sse	if (q1 < q2)
50862925Sse		return -1;
50962925Sse
51062925Sse	return 0;
51162925Sse}
51262925Sse
51362925Ssestatic int
51490111Simpnewerf (const char *f1, const char *f2)
51549884Ssheldonh{
51649884Ssheldonh	struct stat b1, b2;
51749884Ssheldonh
51849884Ssheldonh	return (stat (f1, &b1) == 0 &&
51949884Ssheldonh		stat (f2, &b2) == 0 &&
52049884Ssheldonh		b1.st_mtime > b2.st_mtime);
52149884Ssheldonh}
52249884Ssheldonh
52349884Ssheldonhstatic int
52490111Simpolderf (const char *f1, const char *f2)
52549884Ssheldonh{
52649884Ssheldonh	struct stat b1, b2;
52749884Ssheldonh
52849884Ssheldonh	return (stat (f1, &b1) == 0 &&
52949884Ssheldonh		stat (f2, &b2) == 0 &&
53049884Ssheldonh		b1.st_mtime < b2.st_mtime);
53149884Ssheldonh}
53249884Ssheldonh
53349884Ssheldonhstatic int
53490111Simpequalf (const char *f1, const char *f2)
53549884Ssheldonh{
53649884Ssheldonh	struct stat b1, b2;
53749884Ssheldonh
53849884Ssheldonh	return (stat (f1, &b1) == 0 &&
53949884Ssheldonh		stat (f2, &b2) == 0 &&
54049884Ssheldonh		b1.st_dev == b2.st_dev &&
54149884Ssheldonh		b1.st_ino == b2.st_ino);
54249884Ssheldonh}
543