test.c revision 96376
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 96376 2002-05-11 01:25:54Z alfred $"; 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> 2493345Sache#include <inttypes.h> 2576883Skris#include <limits.h> 2686619Sknu#include <stdarg.h> 271556Srgrimes#include <stdio.h> 281556Srgrimes#include <stdlib.h> 291556Srgrimes#include <string.h> 301556Srgrimes#include <unistd.h> 311556Srgrimes 3286505Sknu#ifdef SHELL 3386505Sknu#define main testcmd 3486505Sknu#include "bltin/bltin.h" 3586618Sknu#else 3688084Sache#include <locale.h> 3788084Sache 3896376Salfredstatic void error(const char *, ...) __dead2 __printf0like(1, 2); 3986618Sknu 4086618Sknustatic void 4186618Sknuerror(const char *msg, ...) 4286618Sknu{ 4386618Sknu va_list ap; 4486618Sknu va_start(ap, msg); 4586618Sknu verrx(2, msg, ap); 4686618Sknu /*NOTREACHED*/ 4786618Sknu va_end(ap); 4886618Sknu} 4986618Sknu#endif 5086618Sknu 5149884Ssheldonh/* test(1) accepts the following grammar: 5249884Ssheldonh oexpr ::= aexpr | aexpr "-o" oexpr ; 5349884Ssheldonh aexpr ::= nexpr | nexpr "-a" aexpr ; 5449884Ssheldonh nexpr ::= primary | "!" primary 5549884Ssheldonh primary ::= unary-operator operand 5649884Ssheldonh | operand binary-operator operand 5749884Ssheldonh | operand 5849884Ssheldonh | "(" oexpr ")" 5949884Ssheldonh ; 6049884Ssheldonh unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 6149884Ssheldonh "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 621556Srgrimes 6349884Ssheldonh binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 6449884Ssheldonh "-nt"|"-ot"|"-ef"; 6549884Ssheldonh operand ::= <any legal UNIX file name> 6649884Ssheldonh*/ 671556Srgrimes 6849884Ssheldonhenum token { 6949884Ssheldonh EOI, 7049884Ssheldonh FILRD, 7149884Ssheldonh FILWR, 7249884Ssheldonh FILEX, 7349884Ssheldonh FILEXIST, 7449884Ssheldonh FILREG, 7549884Ssheldonh FILDIR, 7649884Ssheldonh FILCDEV, 7749884Ssheldonh FILBDEV, 7849884Ssheldonh FILFIFO, 7949884Ssheldonh FILSOCK, 8049884Ssheldonh FILSYM, 8149884Ssheldonh FILGZ, 8249884Ssheldonh FILTT, 8349884Ssheldonh FILSUID, 8449884Ssheldonh FILSGID, 8549884Ssheldonh FILSTCK, 8649884Ssheldonh FILNT, 8749884Ssheldonh FILOT, 8849884Ssheldonh FILEQ, 8949884Ssheldonh FILUID, 9049884Ssheldonh FILGID, 9149884Ssheldonh STREZ, 9249884Ssheldonh STRNZ, 9349884Ssheldonh STREQ, 9449884Ssheldonh STRNE, 9549884Ssheldonh STRLT, 9649884Ssheldonh STRGT, 9749884Ssheldonh INTEQ, 9849884Ssheldonh INTNE, 9949884Ssheldonh INTGE, 10049884Ssheldonh INTGT, 10149884Ssheldonh INTLE, 10249884Ssheldonh INTLT, 10349884Ssheldonh UNOT, 10449884Ssheldonh BAND, 10549884Ssheldonh BOR, 10649884Ssheldonh LPAREN, 10749884Ssheldonh RPAREN, 10849884Ssheldonh OPERAND 1091556Srgrimes}; 1101556Srgrimes 11149884Ssheldonhenum token_types { 11249884Ssheldonh UNOP, 11349884Ssheldonh BINOP, 11449884Ssheldonh BUNOP, 11549884Ssheldonh BBINOP, 11649884Ssheldonh PAREN 1171556Srgrimes}; 1181556Srgrimes 11949884Ssheldonhstruct t_op { 12049884Ssheldonh const char *op_text; 12149884Ssheldonh short op_num, op_type; 12249884Ssheldonh} const ops [] = { 12349884Ssheldonh {"-r", FILRD, UNOP}, 12449884Ssheldonh {"-w", FILWR, UNOP}, 12549884Ssheldonh {"-x", FILEX, UNOP}, 12649884Ssheldonh {"-e", FILEXIST,UNOP}, 12749884Ssheldonh {"-f", FILREG, UNOP}, 12849884Ssheldonh {"-d", FILDIR, UNOP}, 12949884Ssheldonh {"-c", FILCDEV,UNOP}, 13049884Ssheldonh {"-b", FILBDEV,UNOP}, 13149884Ssheldonh {"-p", FILFIFO,UNOP}, 13249884Ssheldonh {"-u", FILSUID,UNOP}, 13349884Ssheldonh {"-g", FILSGID,UNOP}, 13449884Ssheldonh {"-k", FILSTCK,UNOP}, 13549884Ssheldonh {"-s", FILGZ, UNOP}, 13649884Ssheldonh {"-t", FILTT, UNOP}, 13749884Ssheldonh {"-z", STREZ, UNOP}, 13849884Ssheldonh {"-n", STRNZ, UNOP}, 13949884Ssheldonh {"-h", FILSYM, UNOP}, /* for backwards compat */ 14049884Ssheldonh {"-O", FILUID, UNOP}, 14149884Ssheldonh {"-G", FILGID, UNOP}, 14249884Ssheldonh {"-L", FILSYM, UNOP}, 14349884Ssheldonh {"-S", FILSOCK,UNOP}, 14449884Ssheldonh {"=", STREQ, BINOP}, 14549884Ssheldonh {"!=", STRNE, BINOP}, 14649884Ssheldonh {"<", STRLT, BINOP}, 14749884Ssheldonh {">", STRGT, BINOP}, 14849884Ssheldonh {"-eq", INTEQ, BINOP}, 14949884Ssheldonh {"-ne", INTNE, BINOP}, 15049884Ssheldonh {"-ge", INTGE, BINOP}, 15149884Ssheldonh {"-gt", INTGT, BINOP}, 15249884Ssheldonh {"-le", INTLE, BINOP}, 15349884Ssheldonh {"-lt", INTLT, BINOP}, 15449884Ssheldonh {"-nt", FILNT, BINOP}, 15549884Ssheldonh {"-ot", FILOT, BINOP}, 15649884Ssheldonh {"-ef", FILEQ, BINOP}, 15749884Ssheldonh {"!", UNOT, BUNOP}, 15849884Ssheldonh {"-a", BAND, BBINOP}, 15949884Ssheldonh {"-o", BOR, BBINOP}, 16049884Ssheldonh {"(", LPAREN, PAREN}, 16149884Ssheldonh {")", RPAREN, PAREN}, 16249884Ssheldonh {0, 0, 0} 1631556Srgrimes}; 1641556Srgrimes 16549884Ssheldonhstruct t_op const *t_wp_op; 16649884Ssheldonhchar **t_wp; 1671556Srgrimes 16890111Simpstatic int aexpr(enum token); 16990111Simpstatic int binop(void); 17090111Simpstatic int equalf(const char *, const char *); 17190111Simpstatic int filstat(char *, enum token); 17290111Simpstatic int getn(const char *); 17393345Sachestatic intmax_t getq(const char *); 17490111Simpstatic int intcmp(const char *, const char *); 17590111Simpstatic int isoperand(void); 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{ 18790133Sknu int i, res; 18855179Ssheldonh char *p; 18990133Sknu char **nargv; 1901556Srgrimes 19190133Sknu /* 19290133Sknu * XXX copy the whole contents of argv to a newly allocated 19390133Sknu * space with two extra cells filled with NULL's - this source 19490133Sknu * code totally depends on their presence. 19590133Sknu */ 19690133Sknu if ((nargv = (char **)malloc((argc + 2) * sizeof(char *))) == NULL) 19790133Sknu error("Out of space"); 19890133Sknu 19990133Sknu for (i = 0; i < argc; i++) 20090133Sknu nargv[i] = argv[i]; 20190133Sknu 20290133Sknu nargv[i] = nargv[i + 1] = NULL; 20390133Sknu argv = nargv; 20490133Sknu 20555179Ssheldonh if ((p = rindex(argv[0], '/')) == NULL) 20655179Ssheldonh p = argv[0]; 20755179Ssheldonh else 20855179Ssheldonh p++; 20955179Ssheldonh if (strcmp(p, "[") == 0) { 21086622Sknu if (strcmp(argv[--argc], "]") != 0) 21186618Sknu error("missing ]"); 2121556Srgrimes argv[argc] = NULL; 2131556Srgrimes } 2141556Srgrimes 21588084Sache#ifndef SHELL 21688084Sache (void)setlocale(LC_CTYPE, ""); 21788084Sache#endif 21849884Ssheldonh t_wp = &argv[1]; 21949884Ssheldonh res = !oexpr(t_lex(*t_wp)); 2201556Srgrimes 22149884Ssheldonh if (*t_wp != NULL && *++t_wp != NULL) 22249884Ssheldonh syntax(*t_wp, "unexpected operator"); 22349884Ssheldonh 22449884Ssheldonh return res; 2251556Srgrimes} 2261556Srgrimes 22749884Ssheldonhstatic void 22890111Simpsyntax(const char *op, const char *msg) 2291556Srgrimes{ 2301556Srgrimes 23149884Ssheldonh if (op && *op) 23286618Sknu error("%s: %s", op, msg); 23349884Ssheldonh else 23486618Sknu error("%s", msg); 2351556Srgrimes} 2361556Srgrimes 23749884Ssheldonhstatic int 23890111Simpoexpr(enum token n) 2391556Srgrimes{ 24049884Ssheldonh int res; 2411556Srgrimes 24249884Ssheldonh res = aexpr(n); 24349884Ssheldonh if (t_lex(*++t_wp) == BOR) 24449884Ssheldonh return oexpr(t_lex(*++t_wp)) || res; 24549884Ssheldonh t_wp--; 24649884Ssheldonh return res; 24749884Ssheldonh} 2484171Sache 24949884Ssheldonhstatic int 25090111Simpaexpr(enum token n) 25149884Ssheldonh{ 25249884Ssheldonh int res; 2531556Srgrimes 25449884Ssheldonh res = nexpr(n); 25549884Ssheldonh if (t_lex(*++t_wp) == BAND) 25649884Ssheldonh return aexpr(t_lex(*++t_wp)) && res; 25749884Ssheldonh t_wp--; 25849884Ssheldonh return res; 2591556Srgrimes} 2601556Srgrimes 2611556Srgrimesstatic int 26290111Simpnexpr(enum token n) 2631556Srgrimes{ 26449884Ssheldonh if (n == UNOT) 26549884Ssheldonh return !nexpr(t_lex(*++t_wp)); 26649884Ssheldonh return primary(n); 2671556Srgrimes} 2681556Srgrimes 2691556Srgrimesstatic int 27090111Simpprimary(enum token n) 2711556Srgrimes{ 27249884Ssheldonh enum token nn; 27349884Ssheldonh int res; 2741556Srgrimes 27549884Ssheldonh if (n == EOI) 27649884Ssheldonh return 0; /* missing expression */ 27749884Ssheldonh if (n == LPAREN) { 27849884Ssheldonh if ((nn = t_lex(*++t_wp)) == RPAREN) 27949884Ssheldonh return 0; /* missing expression */ 28049884Ssheldonh res = oexpr(nn); 28149884Ssheldonh if (t_lex(*++t_wp) != RPAREN) 28249884Ssheldonh syntax(NULL, "closing paren expected"); 28349884Ssheldonh return res; 28449884Ssheldonh } 28549884Ssheldonh if (t_wp_op && t_wp_op->op_type == UNOP) { 28649884Ssheldonh /* unary expression */ 28749884Ssheldonh if (*++t_wp == NULL) 28849884Ssheldonh syntax(t_wp_op->op_text, "argument expected"); 28949884Ssheldonh switch (n) { 29049884Ssheldonh case STREZ: 29149884Ssheldonh return strlen(*t_wp) == 0; 29249884Ssheldonh case STRNZ: 29349884Ssheldonh return strlen(*t_wp) != 0; 29449884Ssheldonh case FILTT: 29549884Ssheldonh return isatty(getn(*t_wp)); 29649884Ssheldonh default: 29749884Ssheldonh return filstat(*t_wp, n); 29849884Ssheldonh } 29949884Ssheldonh } 3001556Srgrimes 30149884Ssheldonh if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 30249884Ssheldonh return binop(); 30349884Ssheldonh } 30449884Ssheldonh 30549884Ssheldonh return strlen(*t_wp) > 0; 3061556Srgrimes} 3071556Srgrimes 3081556Srgrimesstatic int 30990111Simpbinop(void) 3101556Srgrimes{ 31149884Ssheldonh const char *opnd1, *opnd2; 31249884Ssheldonh struct t_op const *op; 3131556Srgrimes 31449884Ssheldonh opnd1 = *t_wp; 31549884Ssheldonh (void) t_lex(*++t_wp); 31649884Ssheldonh op = t_wp_op; 3171556Srgrimes 31849884Ssheldonh if ((opnd2 = *++t_wp) == NULL) 31949884Ssheldonh syntax(op->op_text, "argument expected"); 32049884Ssheldonh 32149884Ssheldonh switch (op->op_num) { 32249884Ssheldonh case STREQ: 32349884Ssheldonh return strcmp(opnd1, opnd2) == 0; 32449884Ssheldonh case STRNE: 32549884Ssheldonh return strcmp(opnd1, opnd2) != 0; 32649884Ssheldonh case STRLT: 32749884Ssheldonh return strcmp(opnd1, opnd2) < 0; 32849884Ssheldonh case STRGT: 32949884Ssheldonh return strcmp(opnd1, opnd2) > 0; 33049884Ssheldonh case INTEQ: 33162925Sse return intcmp(opnd1, opnd2) == 0; 33249884Ssheldonh case INTNE: 33362925Sse return intcmp(opnd1, opnd2) != 0; 33449884Ssheldonh case INTGE: 33562925Sse return intcmp(opnd1, opnd2) >= 0; 33649884Ssheldonh case INTGT: 33762925Sse return intcmp(opnd1, opnd2) > 0; 33849884Ssheldonh case INTLE: 33962925Sse return intcmp(opnd1, opnd2) <= 0; 34049884Ssheldonh case INTLT: 34162925Sse return intcmp(opnd1, opnd2) < 0; 34249884Ssheldonh case FILNT: 34349884Ssheldonh return newerf (opnd1, opnd2); 34449884Ssheldonh case FILOT: 34549884Ssheldonh return olderf (opnd1, opnd2); 34649884Ssheldonh case FILEQ: 34749884Ssheldonh return equalf (opnd1, opnd2); 34849884Ssheldonh default: 34949884Ssheldonh abort(); 35049884Ssheldonh /* NOTREACHED */ 3511556Srgrimes } 3521556Srgrimes} 3531556Srgrimes 35449884Ssheldonhstatic int 35590111Simpfilstat(char *nm, enum token mode) 3561556Srgrimes{ 35749884Ssheldonh struct stat s; 3581556Srgrimes 35949884Ssheldonh if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 36049884Ssheldonh return 0; 3612664Scsgr 36249884Ssheldonh switch (mode) { 36349884Ssheldonh case FILRD: 36491737Smaxim return (eaccess(nm, R_OK) == 0); 36549884Ssheldonh case FILWR: 36691737Smaxim return (eaccess(nm, W_OK) == 0); 36749884Ssheldonh case FILEX: 36891737Smaxim /* XXX work around eaccess(2) false positives for superuser */ 36991737Smaxim if (eaccess(nm, X_OK) != 0) 37050087Sgreen return 0; 37191737Smaxim if (S_ISDIR(s.st_mode) || geteuid() != 0) 37250087Sgreen return 1; 37350087Sgreen return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 37449884Ssheldonh case FILEXIST: 37591737Smaxim return (eaccess(nm, F_OK) == 0); 37649884Ssheldonh case FILREG: 37749884Ssheldonh return S_ISREG(s.st_mode); 37849884Ssheldonh case FILDIR: 37949884Ssheldonh return S_ISDIR(s.st_mode); 38049884Ssheldonh case FILCDEV: 38149884Ssheldonh return S_ISCHR(s.st_mode); 38249884Ssheldonh case FILBDEV: 38349884Ssheldonh return S_ISBLK(s.st_mode); 38449884Ssheldonh case FILFIFO: 38549884Ssheldonh return S_ISFIFO(s.st_mode); 38649884Ssheldonh case FILSOCK: 38749884Ssheldonh return S_ISSOCK(s.st_mode); 38849884Ssheldonh case FILSYM: 38949884Ssheldonh return S_ISLNK(s.st_mode); 39049884Ssheldonh case FILSUID: 39149884Ssheldonh return (s.st_mode & S_ISUID) != 0; 39249884Ssheldonh case FILSGID: 39349884Ssheldonh return (s.st_mode & S_ISGID) != 0; 39449884Ssheldonh case FILSTCK: 39549884Ssheldonh return (s.st_mode & S_ISVTX) != 0; 39649884Ssheldonh case FILGZ: 39749884Ssheldonh return s.st_size > (off_t)0; 39849884Ssheldonh case FILUID: 39949884Ssheldonh return s.st_uid == geteuid(); 40049884Ssheldonh case FILGID: 40149884Ssheldonh return s.st_gid == getegid(); 40249884Ssheldonh default: 40349884Ssheldonh return 1; 4042675Scsgr } 40549884Ssheldonh} 4062675Scsgr 40749884Ssheldonhstatic enum token 40890111Simpt_lex(char *s) 40949884Ssheldonh{ 41049884Ssheldonh struct t_op const *op = ops; 41149884Ssheldonh 41249884Ssheldonh if (s == 0) { 41349884Ssheldonh t_wp_op = NULL; 41449884Ssheldonh return EOI; 41549884Ssheldonh } 41649884Ssheldonh while (op->op_text) { 41749884Ssheldonh if (strcmp(s, op->op_text) == 0) { 41849884Ssheldonh if ((op->op_type == UNOP && isoperand()) || 41949884Ssheldonh (op->op_num == LPAREN && *(t_wp+1) == 0)) 42049884Ssheldonh break; 42149884Ssheldonh t_wp_op = op; 42249884Ssheldonh return op->op_num; 4231556Srgrimes } 42449884Ssheldonh op++; 4251556Srgrimes } 42649884Ssheldonh t_wp_op = NULL; 42749884Ssheldonh return OPERAND; 4281556Srgrimes} 4291556Srgrimes 43049884Ssheldonhstatic int 43190111Simpisoperand(void) 4321556Srgrimes{ 43349884Ssheldonh struct t_op const *op = ops; 43449884Ssheldonh char *s; 43549884Ssheldonh char *t; 4361556Srgrimes 43749884Ssheldonh if ((s = *(t_wp+1)) == 0) 43849884Ssheldonh return 1; 43949884Ssheldonh if ((t = *(t_wp+2)) == 0) 44049884Ssheldonh return 0; 44149884Ssheldonh while (op->op_text) { 44249884Ssheldonh if (strcmp(s, op->op_text) == 0) 44349884Ssheldonh return op->op_type == BINOP && 44449884Ssheldonh (t[0] != ')' || t[1] != '\0'); 44549884Ssheldonh op++; 44649884Ssheldonh } 44749884Ssheldonh return 0; 4481556Srgrimes} 4491556Srgrimes 45049884Ssheldonh/* atoi with error detection */ 45149884Ssheldonhstatic int 45290111Simpgetn(const char *s) 4531556Srgrimes{ 45449884Ssheldonh char *p; 45549884Ssheldonh long r; 4561556Srgrimes 45749884Ssheldonh errno = 0; 45849884Ssheldonh r = strtol(s, &p, 10); 45949884Ssheldonh 46088084Sache if (s == p) 46188084Sache error("%s: bad number", s); 46288084Sache 46349884Ssheldonh if (errno != 0) 46487961Sache error((errno == EINVAL) ? "%s: bad number" : 46587961Sache "%s: out of range", s); 46649884Ssheldonh 46749884Ssheldonh while (isspace((unsigned char)*p)) 46886622Sknu p++; 46949884Ssheldonh 47049884Ssheldonh if (*p) 47186622Sknu error("%s: bad number", s); 47249884Ssheldonh 47349884Ssheldonh return (int) r; 4741556Srgrimes} 47549884Ssheldonh 47662925Sse/* atoi with error detection and 64 bit range */ 47793345Sachestatic intmax_t 47890111Simpgetq(const char *s) 47962925Sse{ 48062925Sse char *p; 48193345Sache intmax_t r; 48262925Sse 48362925Sse errno = 0; 48493345Sache r = strtoimax(s, &p, 10); 48562925Sse 48688084Sache if (s == p) 48788084Sache error("%s: bad number", s); 48888084Sache 48962925Sse if (errno != 0) 49087961Sache error((errno == EINVAL) ? "%s: bad number" : 49187961Sache "%s: out of range", s); 49262925Sse 49362925Sse while (isspace((unsigned char)*p)) 49486622Sknu p++; 49562925Sse 49662925Sse if (*p) 49786622Sknu error("%s: bad number", s); 49862925Sse 49962925Sse return r; 50062925Sse} 50162925Sse 50249884Ssheldonhstatic int 50390111Simpintcmp (const char *s1, const char *s2) 50462925Sse{ 50593345Sache intmax_t q1, q2; 50662925Sse 50762925Sse 50862925Sse q1 = getq(s1); 50962925Sse q2 = getq(s2); 51062925Sse 51162925Sse if (q1 > q2) 51262925Sse return 1; 51362925Sse 51462925Sse if (q1 < q2) 51562925Sse return -1; 51662925Sse 51762925Sse return 0; 51862925Sse} 51962925Sse 52062925Ssestatic int 52190111Simpnewerf (const char *f1, const char *f2) 52249884Ssheldonh{ 52349884Ssheldonh struct stat b1, b2; 52449884Ssheldonh 52549884Ssheldonh return (stat (f1, &b1) == 0 && 52649884Ssheldonh stat (f2, &b2) == 0 && 52749884Ssheldonh b1.st_mtime > b2.st_mtime); 52849884Ssheldonh} 52949884Ssheldonh 53049884Ssheldonhstatic int 53190111Simpolderf (const char *f1, const char *f2) 53249884Ssheldonh{ 53349884Ssheldonh struct stat b1, b2; 53449884Ssheldonh 53549884Ssheldonh return (stat (f1, &b1) == 0 && 53649884Ssheldonh stat (f2, &b2) == 0 && 53749884Ssheldonh b1.st_mtime < b2.st_mtime); 53849884Ssheldonh} 53949884Ssheldonh 54049884Ssheldonhstatic int 54190111Simpequalf (const char *f1, const char *f2) 54249884Ssheldonh{ 54349884Ssheldonh struct stat b1, b2; 54449884Ssheldonh 54549884Ssheldonh return (stat (f1, &b1) == 0 && 54649884Ssheldonh stat (f2, &b2) == 0 && 54749884Ssheldonh b1.st_dev == b2.st_dev && 54849884Ssheldonh b1.st_ino == b2.st_ino); 54949884Ssheldonh} 550