test.c revision 86618
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 86618 2001-11-19 19:57:45Z knu $"; 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> 251556Srgrimes#include <stdio.h> 261556Srgrimes#include <stdlib.h> 271556Srgrimes#include <string.h> 281556Srgrimes#include <unistd.h> 291556Srgrimes 3086505Sknu#ifdef SHELL 3186505Sknu#define main testcmd 3286505Sknu#include "bltin/bltin.h" 3386618Sknu#else 3486618Sknustatic void error(const char *, ...) __attribute__((__noreturn__)); 3586618Sknu 3686618Sknustatic void 3786618Sknu#ifdef __STDC__ 3886618Sknuerror(const char *msg, ...) 3986618Sknu#else 4086618Sknuerror(va_alist) 4186618Sknu va_dcl 4286505Sknu#endif 4386618Sknu{ 4486618Sknu va_list ap; 4586618Sknu#ifndef __STDC__ 4686618Sknu const char *msg; 4786505Sknu 4886618Sknu va_start(ap); 4986618Sknu msg = va_arg(ap, const char *); 5086618Sknu#else 5186618Sknu va_start(ap, msg); 5286618Sknu#endif 5386618Sknu verrx(2, msg, ap); 5486618Sknu /*NOTREACHED*/ 5586618Sknu va_end(ap); 5686618Sknu} 5786618Sknu#endif 5886618Sknu 5949884Ssheldonh/* test(1) accepts the following grammar: 6049884Ssheldonh oexpr ::= aexpr | aexpr "-o" oexpr ; 6149884Ssheldonh aexpr ::= nexpr | nexpr "-a" aexpr ; 6249884Ssheldonh nexpr ::= primary | "!" primary 6349884Ssheldonh primary ::= unary-operator operand 6449884Ssheldonh | operand binary-operator operand 6549884Ssheldonh | operand 6649884Ssheldonh | "(" oexpr ")" 6749884Ssheldonh ; 6849884Ssheldonh unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 6949884Ssheldonh "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 701556Srgrimes 7149884Ssheldonh binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 7249884Ssheldonh "-nt"|"-ot"|"-ef"; 7349884Ssheldonh operand ::= <any legal UNIX file name> 7449884Ssheldonh*/ 751556Srgrimes 7649884Ssheldonhenum token { 7749884Ssheldonh EOI, 7849884Ssheldonh FILRD, 7949884Ssheldonh FILWR, 8049884Ssheldonh FILEX, 8149884Ssheldonh FILEXIST, 8249884Ssheldonh FILREG, 8349884Ssheldonh FILDIR, 8449884Ssheldonh FILCDEV, 8549884Ssheldonh FILBDEV, 8649884Ssheldonh FILFIFO, 8749884Ssheldonh FILSOCK, 8849884Ssheldonh FILSYM, 8949884Ssheldonh FILGZ, 9049884Ssheldonh FILTT, 9149884Ssheldonh FILSUID, 9249884Ssheldonh FILSGID, 9349884Ssheldonh FILSTCK, 9449884Ssheldonh FILNT, 9549884Ssheldonh FILOT, 9649884Ssheldonh FILEQ, 9749884Ssheldonh FILUID, 9849884Ssheldonh FILGID, 9949884Ssheldonh STREZ, 10049884Ssheldonh STRNZ, 10149884Ssheldonh STREQ, 10249884Ssheldonh STRNE, 10349884Ssheldonh STRLT, 10449884Ssheldonh STRGT, 10549884Ssheldonh INTEQ, 10649884Ssheldonh INTNE, 10749884Ssheldonh INTGE, 10849884Ssheldonh INTGT, 10949884Ssheldonh INTLE, 11049884Ssheldonh INTLT, 11149884Ssheldonh UNOT, 11249884Ssheldonh BAND, 11349884Ssheldonh BOR, 11449884Ssheldonh LPAREN, 11549884Ssheldonh RPAREN, 11649884Ssheldonh OPERAND 1171556Srgrimes}; 1181556Srgrimes 11949884Ssheldonhenum token_types { 12049884Ssheldonh UNOP, 12149884Ssheldonh BINOP, 12249884Ssheldonh BUNOP, 12349884Ssheldonh BBINOP, 12449884Ssheldonh PAREN 1251556Srgrimes}; 1261556Srgrimes 12749884Ssheldonhstruct t_op { 12849884Ssheldonh const char *op_text; 12949884Ssheldonh short op_num, op_type; 13049884Ssheldonh} const ops [] = { 13149884Ssheldonh {"-r", FILRD, UNOP}, 13249884Ssheldonh {"-w", FILWR, UNOP}, 13349884Ssheldonh {"-x", FILEX, UNOP}, 13449884Ssheldonh {"-e", FILEXIST,UNOP}, 13549884Ssheldonh {"-f", FILREG, UNOP}, 13649884Ssheldonh {"-d", FILDIR, UNOP}, 13749884Ssheldonh {"-c", FILCDEV,UNOP}, 13849884Ssheldonh {"-b", FILBDEV,UNOP}, 13949884Ssheldonh {"-p", FILFIFO,UNOP}, 14049884Ssheldonh {"-u", FILSUID,UNOP}, 14149884Ssheldonh {"-g", FILSGID,UNOP}, 14249884Ssheldonh {"-k", FILSTCK,UNOP}, 14349884Ssheldonh {"-s", FILGZ, UNOP}, 14449884Ssheldonh {"-t", FILTT, UNOP}, 14549884Ssheldonh {"-z", STREZ, UNOP}, 14649884Ssheldonh {"-n", STRNZ, UNOP}, 14749884Ssheldonh {"-h", FILSYM, UNOP}, /* for backwards compat */ 14849884Ssheldonh {"-O", FILUID, UNOP}, 14949884Ssheldonh {"-G", FILGID, UNOP}, 15049884Ssheldonh {"-L", FILSYM, UNOP}, 15149884Ssheldonh {"-S", FILSOCK,UNOP}, 15249884Ssheldonh {"=", STREQ, BINOP}, 15349884Ssheldonh {"!=", STRNE, BINOP}, 15449884Ssheldonh {"<", STRLT, BINOP}, 15549884Ssheldonh {">", STRGT, BINOP}, 15649884Ssheldonh {"-eq", INTEQ, BINOP}, 15749884Ssheldonh {"-ne", INTNE, BINOP}, 15849884Ssheldonh {"-ge", INTGE, BINOP}, 15949884Ssheldonh {"-gt", INTGT, BINOP}, 16049884Ssheldonh {"-le", INTLE, BINOP}, 16149884Ssheldonh {"-lt", INTLT, BINOP}, 16249884Ssheldonh {"-nt", FILNT, BINOP}, 16349884Ssheldonh {"-ot", FILOT, BINOP}, 16449884Ssheldonh {"-ef", FILEQ, BINOP}, 16549884Ssheldonh {"!", UNOT, BUNOP}, 16649884Ssheldonh {"-a", BAND, BBINOP}, 16749884Ssheldonh {"-o", BOR, BBINOP}, 16849884Ssheldonh {"(", LPAREN, PAREN}, 16949884Ssheldonh {")", RPAREN, PAREN}, 17049884Ssheldonh {0, 0, 0} 1711556Srgrimes}; 1721556Srgrimes 17349884Ssheldonhstruct t_op const *t_wp_op; 17449884Ssheldonhchar **t_wp; 1751556Srgrimes 17676883Skrisstatic int aexpr __P((enum token)); 17776883Skrisstatic int binop __P((void)); 17876883Skrisstatic int equalf __P((const char *, const char *)); 17976883Skrisstatic int filstat __P((char *, enum token)); 18076883Skrisstatic int getn __P((const char *)); 18176883Skrisstatic quad_t getq __P((const char *)); 18276883Skrisstatic int intcmp __P((const char *, const char *)); 18376883Skrisstatic int isoperand __P((void)); 18476883Skrisint main __P((int, char **)); 18576883Skrisstatic int newerf __P((const char *, const char *)); 18676883Skrisstatic int nexpr __P((enum token)); 18776883Skrisstatic int oexpr __P((enum token)); 18876883Skrisstatic int olderf __P((const char *, const char *)); 18976883Skrisstatic int primary __P((enum token)); 19076883Skrisstatic void syntax __P((const char *, const char *)); 19176883Skrisstatic enum token t_lex __P((char *)); 19249884Ssheldonh 1931556Srgrimesint 1941556Srgrimesmain(argc, argv) 1951556Srgrimes int argc; 19649884Ssheldonh char **argv; 1971556Srgrimes{ 19849884Ssheldonh int res; 19955179Ssheldonh char *p; 2001556Srgrimes 20155179Ssheldonh if ((p = rindex(argv[0], '/')) == NULL) 20255179Ssheldonh p = argv[0]; 20355179Ssheldonh else 20455179Ssheldonh p++; 20555179Ssheldonh if (strcmp(p, "[") == 0) { 2061556Srgrimes if (strcmp(argv[--argc], "]")) 20786618Sknu error("missing ]"); 2081556Srgrimes argv[argc] = NULL; 2091556Srgrimes } 2101556Srgrimes 21150302Sgreen /* XXX work around the absence of an eaccess(2) syscall */ 21250087Sgreen (void)setgid(getegid()); 21350087Sgreen (void)setuid(geteuid()); 21450087Sgreen 21586618Sknu if (--argc <= 0) 21686618Sknu return 1; 21786618Sknu 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 22849884Ssheldonhsyntax(op, msg) 22949884Ssheldonh const char *op; 23049884Ssheldonh const char *msg; 2311556Srgrimes{ 2321556Srgrimes 23349884Ssheldonh if (op && *op) 23486618Sknu error("%s: %s", op, msg); 23549884Ssheldonh else 23686618Sknu error("%s", msg); 2371556Srgrimes} 2381556Srgrimes 23949884Ssheldonhstatic int 24049884Ssheldonhoexpr(n) 24149884Ssheldonh enum token n; 2421556Srgrimes{ 24349884Ssheldonh int res; 2441556Srgrimes 24549884Ssheldonh res = aexpr(n); 24649884Ssheldonh if (t_lex(*++t_wp) == BOR) 24749884Ssheldonh return oexpr(t_lex(*++t_wp)) || res; 24849884Ssheldonh t_wp--; 24949884Ssheldonh return res; 25049884Ssheldonh} 2514171Sache 25249884Ssheldonhstatic int 25349884Ssheldonhaexpr(n) 25449884Ssheldonh enum token n; 25549884Ssheldonh{ 25649884Ssheldonh int res; 2571556Srgrimes 25849884Ssheldonh res = nexpr(n); 25949884Ssheldonh if (t_lex(*++t_wp) == BAND) 26049884Ssheldonh return aexpr(t_lex(*++t_wp)) && res; 26149884Ssheldonh t_wp--; 26249884Ssheldonh return res; 2631556Srgrimes} 2641556Srgrimes 2651556Srgrimesstatic int 26649884Ssheldonhnexpr(n) 26749884Ssheldonh enum token n; /* token */ 2681556Srgrimes{ 26949884Ssheldonh if (n == UNOT) 27049884Ssheldonh return !nexpr(t_lex(*++t_wp)); 27149884Ssheldonh return primary(n); 2721556Srgrimes} 2731556Srgrimes 2741556Srgrimesstatic int 27549884Ssheldonhprimary(n) 27649884Ssheldonh enum token n; 2771556Srgrimes{ 27849884Ssheldonh enum token nn; 27949884Ssheldonh int res; 2801556Srgrimes 28149884Ssheldonh if (n == EOI) 28249884Ssheldonh return 0; /* missing expression */ 28349884Ssheldonh if (n == LPAREN) { 28449884Ssheldonh if ((nn = t_lex(*++t_wp)) == RPAREN) 28549884Ssheldonh return 0; /* missing expression */ 28649884Ssheldonh res = oexpr(nn); 28749884Ssheldonh if (t_lex(*++t_wp) != RPAREN) 28849884Ssheldonh syntax(NULL, "closing paren expected"); 28949884Ssheldonh return res; 29049884Ssheldonh } 29149884Ssheldonh if (t_wp_op && t_wp_op->op_type == UNOP) { 29249884Ssheldonh /* unary expression */ 29349884Ssheldonh if (*++t_wp == NULL) 29449884Ssheldonh syntax(t_wp_op->op_text, "argument expected"); 29549884Ssheldonh switch (n) { 29649884Ssheldonh case STREZ: 29749884Ssheldonh return strlen(*t_wp) == 0; 29849884Ssheldonh case STRNZ: 29949884Ssheldonh return strlen(*t_wp) != 0; 30049884Ssheldonh case FILTT: 30149884Ssheldonh return isatty(getn(*t_wp)); 30249884Ssheldonh default: 30349884Ssheldonh return filstat(*t_wp, n); 30449884Ssheldonh } 30549884Ssheldonh } 3061556Srgrimes 30749884Ssheldonh if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 30849884Ssheldonh return binop(); 30949884Ssheldonh } 31049884Ssheldonh 31149884Ssheldonh return strlen(*t_wp) > 0; 3121556Srgrimes} 3131556Srgrimes 3141556Srgrimesstatic int 31549884Ssheldonhbinop() 3161556Srgrimes{ 31749884Ssheldonh const char *opnd1, *opnd2; 31849884Ssheldonh struct t_op const *op; 3191556Srgrimes 32049884Ssheldonh opnd1 = *t_wp; 32149884Ssheldonh (void) t_lex(*++t_wp); 32249884Ssheldonh op = t_wp_op; 3231556Srgrimes 32449884Ssheldonh if ((opnd2 = *++t_wp) == NULL) 32549884Ssheldonh syntax(op->op_text, "argument expected"); 32649884Ssheldonh 32749884Ssheldonh switch (op->op_num) { 32849884Ssheldonh case STREQ: 32949884Ssheldonh return strcmp(opnd1, opnd2) == 0; 33049884Ssheldonh case STRNE: 33149884Ssheldonh return strcmp(opnd1, opnd2) != 0; 33249884Ssheldonh case STRLT: 33349884Ssheldonh return strcmp(opnd1, opnd2) < 0; 33449884Ssheldonh case STRGT: 33549884Ssheldonh return strcmp(opnd1, opnd2) > 0; 33649884Ssheldonh case INTEQ: 33762925Sse return intcmp(opnd1, opnd2) == 0; 33849884Ssheldonh case INTNE: 33962925Sse return intcmp(opnd1, opnd2) != 0; 34049884Ssheldonh case INTGE: 34162925Sse return intcmp(opnd1, opnd2) >= 0; 34249884Ssheldonh case INTGT: 34362925Sse return intcmp(opnd1, opnd2) > 0; 34449884Ssheldonh case INTLE: 34562925Sse return intcmp(opnd1, opnd2) <= 0; 34649884Ssheldonh case INTLT: 34762925Sse return intcmp(opnd1, opnd2) < 0; 34849884Ssheldonh case FILNT: 34949884Ssheldonh return newerf (opnd1, opnd2); 35049884Ssheldonh case FILOT: 35149884Ssheldonh return olderf (opnd1, opnd2); 35249884Ssheldonh case FILEQ: 35349884Ssheldonh return equalf (opnd1, opnd2); 35449884Ssheldonh default: 35549884Ssheldonh abort(); 35649884Ssheldonh /* NOTREACHED */ 3571556Srgrimes } 3581556Srgrimes} 3591556Srgrimes 36049884Ssheldonhstatic int 36149884Ssheldonhfilstat(nm, mode) 36249884Ssheldonh char *nm; 36349884Ssheldonh enum token mode; 3641556Srgrimes{ 36549884Ssheldonh struct stat s; 3661556Srgrimes 36749884Ssheldonh if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 36849884Ssheldonh return 0; 3692664Scsgr 37049884Ssheldonh switch (mode) { 37149884Ssheldonh case FILRD: 37249884Ssheldonh return access(nm, R_OK) == 0; 37349884Ssheldonh case FILWR: 37449884Ssheldonh return access(nm, W_OK) == 0; 37549884Ssheldonh case FILEX: 37650302Sgreen /* XXX work around access(2) false positives for superuser */ 37750087Sgreen if (access(nm, X_OK) != 0) 37850087Sgreen return 0; 37950087Sgreen if (S_ISDIR(s.st_mode) || getuid() != 0) 38050087Sgreen return 1; 38150087Sgreen return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 38249884Ssheldonh case FILEXIST: 38349884Ssheldonh return access(nm, F_OK) == 0; 38449884Ssheldonh case FILREG: 38549884Ssheldonh return S_ISREG(s.st_mode); 38649884Ssheldonh case FILDIR: 38749884Ssheldonh return S_ISDIR(s.st_mode); 38849884Ssheldonh case FILCDEV: 38949884Ssheldonh return S_ISCHR(s.st_mode); 39049884Ssheldonh case FILBDEV: 39149884Ssheldonh return S_ISBLK(s.st_mode); 39249884Ssheldonh case FILFIFO: 39349884Ssheldonh return S_ISFIFO(s.st_mode); 39449884Ssheldonh case FILSOCK: 39549884Ssheldonh return S_ISSOCK(s.st_mode); 39649884Ssheldonh case FILSYM: 39749884Ssheldonh return S_ISLNK(s.st_mode); 39849884Ssheldonh case FILSUID: 39949884Ssheldonh return (s.st_mode & S_ISUID) != 0; 40049884Ssheldonh case FILSGID: 40149884Ssheldonh return (s.st_mode & S_ISGID) != 0; 40249884Ssheldonh case FILSTCK: 40349884Ssheldonh return (s.st_mode & S_ISVTX) != 0; 40449884Ssheldonh case FILGZ: 40549884Ssheldonh return s.st_size > (off_t)0; 40649884Ssheldonh case FILUID: 40749884Ssheldonh return s.st_uid == geteuid(); 40849884Ssheldonh case FILGID: 40949884Ssheldonh return s.st_gid == getegid(); 41049884Ssheldonh default: 41149884Ssheldonh return 1; 4122675Scsgr } 41349884Ssheldonh} 4142675Scsgr 41549884Ssheldonhstatic enum token 41649884Ssheldonht_lex(s) 41749884Ssheldonh char *s; 41849884Ssheldonh{ 41949884Ssheldonh struct t_op const *op = ops; 42049884Ssheldonh 42149884Ssheldonh if (s == 0) { 42249884Ssheldonh t_wp_op = NULL; 42349884Ssheldonh return EOI; 42449884Ssheldonh } 42549884Ssheldonh while (op->op_text) { 42649884Ssheldonh if (strcmp(s, op->op_text) == 0) { 42749884Ssheldonh if ((op->op_type == UNOP && isoperand()) || 42849884Ssheldonh (op->op_num == LPAREN && *(t_wp+1) == 0)) 42949884Ssheldonh break; 43049884Ssheldonh t_wp_op = op; 43149884Ssheldonh return op->op_num; 4321556Srgrimes } 43349884Ssheldonh op++; 4341556Srgrimes } 43549884Ssheldonh t_wp_op = NULL; 43649884Ssheldonh return OPERAND; 4371556Srgrimes} 4381556Srgrimes 43949884Ssheldonhstatic int 44049884Ssheldonhisoperand() 4411556Srgrimes{ 44249884Ssheldonh struct t_op const *op = ops; 44349884Ssheldonh char *s; 44449884Ssheldonh char *t; 4451556Srgrimes 44649884Ssheldonh if ((s = *(t_wp+1)) == 0) 44749884Ssheldonh return 1; 44849884Ssheldonh if ((t = *(t_wp+2)) == 0) 44949884Ssheldonh return 0; 45049884Ssheldonh while (op->op_text) { 45149884Ssheldonh if (strcmp(s, op->op_text) == 0) 45249884Ssheldonh return op->op_type == BINOP && 45349884Ssheldonh (t[0] != ')' || t[1] != '\0'); 45449884Ssheldonh op++; 45549884Ssheldonh } 45649884Ssheldonh return 0; 4571556Srgrimes} 4581556Srgrimes 45949884Ssheldonh/* atoi with error detection */ 46049884Ssheldonhstatic int 46149884Ssheldonhgetn(s) 46249884Ssheldonh const char *s; 4631556Srgrimes{ 46449884Ssheldonh char *p; 46549884Ssheldonh long r; 4661556Srgrimes 46749884Ssheldonh errno = 0; 46849884Ssheldonh r = strtol(s, &p, 10); 46949884Ssheldonh 47049884Ssheldonh if (errno != 0) 47186618Sknu error("%s: out of range", s); 47249884Ssheldonh 47349884Ssheldonh while (isspace((unsigned char)*p)) 47449884Ssheldonh p++; 47549884Ssheldonh 47649884Ssheldonh if (*p) 47786618Sknu error("%s: bad number", s); 47849884Ssheldonh 47949884Ssheldonh return (int) r; 4801556Srgrimes} 48149884Ssheldonh 48262925Sse/* atoi with error detection and 64 bit range */ 48362925Ssestatic quad_t 48462925Ssegetq(s) 48562925Sse const char *s; 48662925Sse{ 48762925Sse char *p; 48862925Sse quad_t r; 48962925Sse 49062925Sse errno = 0; 49162925Sse r = strtoq(s, &p, 10); 49262925Sse 49362925Sse if (errno != 0) 49486618Sknu error("%s: out of range", s); 49562925Sse 49662925Sse while (isspace((unsigned char)*p)) 49762925Sse p++; 49862925Sse 49962925Sse if (*p) 50086618Sknu error("%s: bad number", s); 50162925Sse 50262925Sse return r; 50362925Sse} 50462925Sse 50549884Ssheldonhstatic int 50662925Sseintcmp (s1, s2) 50762925Sse const char *s1, *s2; 50862925Sse{ 50962925Sse quad_t q1, q2; 51062925Sse 51162925Sse 51262925Sse q1 = getq(s1); 51362925Sse q2 = getq(s2); 51462925Sse 51562925Sse if (q1 > q2) 51662925Sse return 1; 51762925Sse 51862925Sse if (q1 < q2) 51962925Sse return -1; 52062925Sse 52162925Sse return 0; 52262925Sse} 52362925Sse 52462925Ssestatic int 52549884Ssheldonhnewerf (f1, f2) 52649884Ssheldonh const char *f1, *f2; 52749884Ssheldonh{ 52849884Ssheldonh struct stat b1, b2; 52949884Ssheldonh 53049884Ssheldonh return (stat (f1, &b1) == 0 && 53149884Ssheldonh stat (f2, &b2) == 0 && 53249884Ssheldonh b1.st_mtime > b2.st_mtime); 53349884Ssheldonh} 53449884Ssheldonh 53549884Ssheldonhstatic int 53649884Ssheldonholderf (f1, f2) 53749884Ssheldonh const char *f1, *f2; 53849884Ssheldonh{ 53949884Ssheldonh struct stat b1, b2; 54049884Ssheldonh 54149884Ssheldonh return (stat (f1, &b1) == 0 && 54249884Ssheldonh stat (f2, &b2) == 0 && 54349884Ssheldonh b1.st_mtime < b2.st_mtime); 54449884Ssheldonh} 54549884Ssheldonh 54649884Ssheldonhstatic int 54749884Ssheldonhequalf (f1, f2) 54849884Ssheldonh const char *f1, *f2; 54949884Ssheldonh{ 55049884Ssheldonh struct stat b1, b2; 55149884Ssheldonh 55249884Ssheldonh return (stat (f1, &b1) == 0 && 55349884Ssheldonh stat (f2, &b2) == 0 && 55449884Ssheldonh b1.st_dev == b2.st_dev && 55549884Ssheldonh b1.st_ino == b2.st_ino); 55649884Ssheldonh} 557