test.c revision 219680
11556Srgrimes/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ 21556Srgrimes 31556Srgrimes/*- 41556Srgrimes * test(1); version 7-like -- author Erik Baalbergen 51556Srgrimes * modified by Eric Gisin to be used as built-in. 61556Srgrimes * modified by Arnold Robbins to add SVR3 compatibility 71556Srgrimes * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). 81556Srgrimes * modified by J.T. Conklin for NetBSD. 91556Srgrimes * 101556Srgrimes * This program is in the Public Domain. 111556Srgrimes */ 121556Srgrimes/* 131556Srgrimes * Important: This file is used both as a standalone program /bin/test and 141556Srgrimes * as a builtin for /bin/sh (#define SHELL). 151556Srgrimes */ 161556Srgrimes 171556Srgrimes#include <sys/cdefs.h> 181556Srgrimes__FBSDID("$FreeBSD: head/bin/test/test.c 219680 2011-03-15 22:22:11Z jilles $"); 191556Srgrimes 201556Srgrimes#include <sys/types.h> 211556Srgrimes#include <sys/stat.h> 221556Srgrimes 231556Srgrimes#include <ctype.h> 241556Srgrimes#include <err.h> 251556Srgrimes#include <errno.h> 261556Srgrimes#include <inttypes.h> 271556Srgrimes#include <limits.h> 281556Srgrimes#include <stdarg.h> 291556Srgrimes#include <stdio.h> 301556Srgrimes#include <stdlib.h> 311556Srgrimes#include <string.h> 321556Srgrimes#include <unistd.h> 331556Srgrimes 341556Srgrimes#ifdef SHELL 351556Srgrimes#define main testcmd 361556Srgrimes#include "bltin/bltin.h" 371556Srgrimes#else 381556Srgrimes#include <locale.h> 391556Srgrimes 401556Srgrimesstatic void error(const char *, ...) __dead2 __printf0like(1, 2); 411556Srgrimes 421556Srgrimesstatic void 431556Srgrimeserror(const char *msg, ...) 441556Srgrimes{ 451556Srgrimes va_list ap; 461556Srgrimes va_start(ap, msg); 471556Srgrimes verrx(2, msg, ap); 481556Srgrimes /*NOTREACHED*/ 491556Srgrimes va_end(ap); 501556Srgrimes} 511556Srgrimes#endif 521556Srgrimes 531556Srgrimes/* test(1) accepts the following grammar: 541556Srgrimes oexpr ::= aexpr | aexpr "-o" oexpr ; 551556Srgrimes aexpr ::= nexpr | nexpr "-a" aexpr ; 561556Srgrimes nexpr ::= primary | "!" primary 571556Srgrimes primary ::= unary-operator operand 581556Srgrimes | operand binary-operator operand 591556Srgrimes | operand 601556Srgrimes | "(" oexpr ")" 611556Srgrimes ; 621556Srgrimes unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 631556Srgrimes "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 641556Srgrimes 651556Srgrimes binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 661556Srgrimes "-nt"|"-ot"|"-ef"; 671556Srgrimes operand ::= <any legal UNIX file name> 681556Srgrimes*/ 691556Srgrimes 701556Srgrimesenum token { 711556Srgrimes EOI, 721556Srgrimes FILRD, 731556Srgrimes FILWR, 741556Srgrimes FILEX, 751556Srgrimes FILEXIST, 761556Srgrimes FILREG, 771556Srgrimes FILDIR, 781556Srgrimes FILCDEV, 791556Srgrimes FILBDEV, 801556Srgrimes FILFIFO, 811556Srgrimes FILSOCK, 821556Srgrimes FILSYM, 831556Srgrimes FILGZ, 841556Srgrimes FILTT, 851556Srgrimes FILSUID, 861556Srgrimes FILSGID, 871556Srgrimes FILSTCK, 881556Srgrimes FILNT, 891556Srgrimes FILOT, 901556Srgrimes FILEQ, 911556Srgrimes FILUID, 921556Srgrimes FILGID, 931556Srgrimes STREZ, 941556Srgrimes STRNZ, 951556Srgrimes STREQ, 961556Srgrimes STRNE, 971556Srgrimes STRLT, 981556Srgrimes STRGT, 991556Srgrimes INTEQ, 1001556Srgrimes INTNE, 1011556Srgrimes INTGE, 1021556Srgrimes INTGT, 1031556Srgrimes INTLE, 1041556Srgrimes INTLT, 1051556Srgrimes UNOT, 1061556Srgrimes BAND, 1071556Srgrimes BOR, 1081556Srgrimes LPAREN, 1091556Srgrimes RPAREN, 1101556Srgrimes OPERAND 1111556Srgrimes}; 1121556Srgrimes 1131556Srgrimesenum token_types { 1141556Srgrimes UNOP, 1151556Srgrimes BINOP, 1161556Srgrimes BUNOP, 1171556Srgrimes BBINOP, 1181556Srgrimes PAREN 1191556Srgrimes}; 1201556Srgrimes 1211556Srgrimesstruct t_op { 1221556Srgrimes const char *op_text; 1231556Srgrimes short op_num, op_type; 1241556Srgrimes} const ops [] = { 1251556Srgrimes {"-r", FILRD, UNOP}, 1261556Srgrimes {"-w", FILWR, UNOP}, 1271556Srgrimes {"-x", FILEX, UNOP}, 1281556Srgrimes {"-e", FILEXIST,UNOP}, 1291556Srgrimes {"-f", FILREG, UNOP}, 1301556Srgrimes {"-d", FILDIR, UNOP}, 1311556Srgrimes {"-c", FILCDEV,UNOP}, 1321556Srgrimes {"-b", FILBDEV,UNOP}, 1331556Srgrimes {"-p", FILFIFO,UNOP}, 1341556Srgrimes {"-u", FILSUID,UNOP}, 1351556Srgrimes {"-g", FILSGID,UNOP}, 1361556Srgrimes {"-k", FILSTCK,UNOP}, 1371556Srgrimes {"-s", FILGZ, UNOP}, 1381556Srgrimes {"-t", FILTT, UNOP}, 1391556Srgrimes {"-z", STREZ, UNOP}, 1401556Srgrimes {"-n", STRNZ, UNOP}, 1411556Srgrimes {"-h", FILSYM, UNOP}, /* for backwards compat */ 1421556Srgrimes {"-O", FILUID, UNOP}, 1431556Srgrimes {"-G", FILGID, UNOP}, 1441556Srgrimes {"-L", FILSYM, UNOP}, 1451556Srgrimes {"-S", FILSOCK,UNOP}, 1461556Srgrimes {"=", STREQ, BINOP}, 1471556Srgrimes {"==", STREQ, BINOP}, 1481556Srgrimes {"!=", STRNE, BINOP}, 1491556Srgrimes {"<", STRLT, BINOP}, 1501556Srgrimes {">", STRGT, BINOP}, 1511556Srgrimes {"-eq", INTEQ, BINOP}, 1521556Srgrimes {"-ne", INTNE, BINOP}, 1531556Srgrimes {"-ge", INTGE, BINOP}, 1541556Srgrimes {"-gt", INTGT, BINOP}, 1551556Srgrimes {"-le", INTLE, BINOP}, 1561556Srgrimes {"-lt", INTLT, BINOP}, 1571556Srgrimes {"-nt", FILNT, BINOP}, 1581556Srgrimes {"-ot", FILOT, BINOP}, 1591556Srgrimes {"-ef", FILEQ, BINOP}, 1601556Srgrimes {"!", UNOT, BUNOP}, 1611556Srgrimes {"-a", BAND, BBINOP}, 1621556Srgrimes {"-o", BOR, BBINOP}, 1631556Srgrimes {"(", LPAREN, PAREN}, 1641556Srgrimes {")", RPAREN, PAREN}, 1651556Srgrimes {0, 0, 0} 1661556Srgrimes}; 1671556Srgrimes 1681556Srgrimesstruct t_op const *t_wp_op; 1691556Srgrimesint nargc; 1701556Srgrimeschar **t_wp; 1711556Srgrimesint parenlevel; 1721556Srgrimes 1731556Srgrimesstatic int aexpr(enum token); 1741556Srgrimesstatic int binop(void); 1751556Srgrimesstatic int equalf(const char *, const char *); 1761556Srgrimesstatic int filstat(char *, enum token); 1771556Srgrimesstatic int getn(const char *); 1781556Srgrimesstatic intmax_t getq(const char *); 1791556Srgrimesstatic int intcmp(const char *, const char *); 1801556Srgrimesstatic int isunopoperand(void); 1811556Srgrimesstatic int islparenoperand(void); 1821556Srgrimesstatic int isrparenoperand(void); 1831556Srgrimesstatic int newerf(const char *, const char *); 1841556Srgrimesstatic int nexpr(enum token); 1851556Srgrimesstatic int oexpr(enum token); 1861556Srgrimesstatic int olderf(const char *, const char *); 1871556Srgrimesstatic int primary(enum token); 1881556Srgrimesstatic void syntax(const char *, const char *); 1891556Srgrimesstatic enum token t_lex(char *); 1901556Srgrimes 1911556Srgrimesint 1921556Srgrimesmain(int argc, char **argv) 1931556Srgrimes{ 1941556Srgrimes int res; 1951556Srgrimes char *p; 1961556Srgrimes 1971556Srgrimes if ((p = strrchr(argv[0], '/')) == NULL) 1981556Srgrimes p = argv[0]; 1991556Srgrimes else 2001556Srgrimes p++; 2011556Srgrimes if (strcmp(p, "[") == 0) { 2021556Srgrimes if (strcmp(argv[--argc], "]") != 0) 2031556Srgrimes error("missing ]"); 2041556Srgrimes argv[argc] = NULL; 2051556Srgrimes } 2061556Srgrimes 2071556Srgrimes /* no expression => false */ 2081556Srgrimes if (--argc <= 0) 2091556Srgrimes return 1; 2101556Srgrimes 2111556Srgrimes#ifndef SHELL 2121556Srgrimes (void)setlocale(LC_CTYPE, ""); 2131556Srgrimes#endif 2141556Srgrimes nargc = argc; 2151556Srgrimes t_wp = &argv[1]; 2161556Srgrimes parenlevel = 0; 2171556Srgrimes if (nargc == 4 && strcmp(*t_wp, "!") == 0) { 2181556Srgrimes /* Things like ! "" -o x do not fit in the normal grammar. */ 2191556Srgrimes --nargc; 2201556Srgrimes ++t_wp; 2211556Srgrimes res = oexpr(t_lex(*t_wp)); 2221556Srgrimes } else 2231556Srgrimes res = !oexpr(t_lex(*t_wp)); 2241556Srgrimes 2251556Srgrimes if (--nargc > 0) 2261556Srgrimes syntax(*t_wp, "unexpected operator"); 2271556Srgrimes 2281556Srgrimes return res; 2291556Srgrimes} 2301556Srgrimes 2311556Srgrimesstatic void 2321556Srgrimessyntax(const char *op, const char *msg) 2331556Srgrimes{ 2341556Srgrimes 2351556Srgrimes if (op && *op) 2361556Srgrimes error("%s: %s", op, msg); 2371556Srgrimes else 2381556Srgrimes error("%s", msg); 2391556Srgrimes} 2401556Srgrimes 2411556Srgrimesstatic int 2421556Srgrimesoexpr(enum token n) 2431556Srgrimes{ 2441556Srgrimes int res; 2451556Srgrimes 2461556Srgrimes res = aexpr(n); 2471556Srgrimes if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR) 2481556Srgrimes return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) || 2491556Srgrimes res; 2501556Srgrimes t_wp--; 2511556Srgrimes nargc++; 2521556Srgrimes return res; 2531556Srgrimes} 2541556Srgrimes 2551556Srgrimesstatic int 2561556Srgrimesaexpr(enum token n) 2571556Srgrimes{ 2581556Srgrimes int res; 2591556Srgrimes 2601556Srgrimes res = nexpr(n); 2611556Srgrimes if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND) 2621556Srgrimes return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) && 2631556Srgrimes res; 2641556Srgrimes t_wp--; 2651556Srgrimes nargc++; 2661556Srgrimes return res; 2671556Srgrimes} 2681556Srgrimes 2691556Srgrimesstatic int 2701556Srgrimesnexpr(enum token n) 2711556Srgrimes{ 2721556Srgrimes if (n == UNOT) 2731556Srgrimes return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)); 2741556Srgrimes return primary(n); 2751556Srgrimes} 2761556Srgrimes 2771556Srgrimesstatic int 2781556Srgrimesprimary(enum token n) 2791556Srgrimes{ 2801556Srgrimes enum token nn; 2811556Srgrimes int res; 2821556Srgrimes 2831556Srgrimes if (n == EOI) 2841556Srgrimes return 0; /* missing expression */ 2851556Srgrimes if (n == LPAREN) { 2861556Srgrimes parenlevel++; 2871556Srgrimes if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) == 2881556Srgrimes RPAREN) { 2891556Srgrimes parenlevel--; 2901556Srgrimes return 0; /* missing expression */ 2911556Srgrimes } 2921556Srgrimes res = oexpr(nn); 2931556Srgrimes if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN) 2941556Srgrimes syntax(NULL, "closing paren expected"); 2951556Srgrimes parenlevel--; 2961556Srgrimes return res; 2971556Srgrimes } 2981556Srgrimes if (t_wp_op && t_wp_op->op_type == UNOP) { 2991556Srgrimes /* unary expression */ 3001556Srgrimes if (--nargc == 0) 3011556Srgrimes syntax(t_wp_op->op_text, "argument expected"); 3021556Srgrimes switch (n) { 3031556Srgrimes case STREZ: 3041556Srgrimes return strlen(*++t_wp) == 0; 3051556Srgrimes case STRNZ: 3061556Srgrimes return strlen(*++t_wp) != 0; 3071556Srgrimes case FILTT: 3081556Srgrimes return isatty(getn(*++t_wp)); 3091556Srgrimes default: 3101556Srgrimes return filstat(*++t_wp, n); 3111556Srgrimes } 3121556Srgrimes } 3131556Srgrimes 3141556Srgrimes if (t_lex(nargc > 0 ? t_wp[1] : NULL), t_wp_op && t_wp_op->op_type == 3151556Srgrimes BINOP) { 3161556Srgrimes return binop(); 3171556Srgrimes } 3181556Srgrimes 3191556Srgrimes return strlen(*t_wp) > 0; 3201556Srgrimes} 3211556Srgrimes 3221556Srgrimesstatic int 3231556Srgrimesbinop(void) 3241556Srgrimes{ 3251556Srgrimes const char *opnd1, *opnd2; 3261556Srgrimes struct t_op const *op; 3271556Srgrimes 3281556Srgrimes opnd1 = *t_wp; 3291556Srgrimes (void) t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL); 3301556Srgrimes op = t_wp_op; 3311556Srgrimes 3321556Srgrimes if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL) 3331556Srgrimes syntax(op->op_text, "argument expected"); 3341556Srgrimes 3351556Srgrimes switch (op->op_num) { 3361556Srgrimes case STREQ: 3371556Srgrimes return strcmp(opnd1, opnd2) == 0; 3381556Srgrimes case STRNE: 3391556Srgrimes return strcmp(opnd1, opnd2) != 0; 3401556Srgrimes case STRLT: 3411556Srgrimes return strcmp(opnd1, opnd2) < 0; 3421556Srgrimes case STRGT: 3431556Srgrimes return strcmp(opnd1, opnd2) > 0; 3441556Srgrimes case INTEQ: 3451556Srgrimes return intcmp(opnd1, opnd2) == 0; 3461556Srgrimes case INTNE: 3471556Srgrimes return intcmp(opnd1, opnd2) != 0; 3481556Srgrimes case INTGE: 3491556Srgrimes return intcmp(opnd1, opnd2) >= 0; 3501556Srgrimes case INTGT: 3511556Srgrimes return intcmp(opnd1, opnd2) > 0; 3521556Srgrimes case INTLE: 3531556Srgrimes return intcmp(opnd1, opnd2) <= 0; 3541556Srgrimes case INTLT: 3551556Srgrimes return intcmp(opnd1, opnd2) < 0; 3561556Srgrimes case FILNT: 3571556Srgrimes return newerf (opnd1, opnd2); 3581556Srgrimes case FILOT: 3591556Srgrimes return olderf (opnd1, opnd2); 3601556Srgrimes case FILEQ: 3611556Srgrimes return equalf (opnd1, opnd2); 3621556Srgrimes default: 3631556Srgrimes abort(); 3641556Srgrimes /* NOTREACHED */ 3651556Srgrimes } 3661556Srgrimes} 3671556Srgrimes 3681556Srgrimesstatic int 3691556Srgrimesfilstat(char *nm, enum token mode) 3701556Srgrimes{ 3711556Srgrimes struct stat s; 3721556Srgrimes 3731556Srgrimes if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 3741556Srgrimes return 0; 3751556Srgrimes 3761556Srgrimes switch (mode) { 3771556Srgrimes case FILRD: 3781556Srgrimes return (eaccess(nm, R_OK) == 0); 3791556Srgrimes case FILWR: 3801556Srgrimes return (eaccess(nm, W_OK) == 0); 3811556Srgrimes case FILEX: 3821556Srgrimes /* XXX work around eaccess(2) false positives for superuser */ 3831556Srgrimes if (eaccess(nm, X_OK) != 0) 3841556Srgrimes return 0; 3851556Srgrimes if (S_ISDIR(s.st_mode) || geteuid() != 0) 3861556Srgrimes return 1; 3871556Srgrimes return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 3881556Srgrimes case FILEXIST: 3891556Srgrimes return (eaccess(nm, F_OK) == 0); 3901556Srgrimes case FILREG: 3911556Srgrimes return S_ISREG(s.st_mode); 3921556Srgrimes case FILDIR: 3931556Srgrimes return S_ISDIR(s.st_mode); 3941556Srgrimes case FILCDEV: 3951556Srgrimes return S_ISCHR(s.st_mode); 3961556Srgrimes case FILBDEV: 3971556Srgrimes return S_ISBLK(s.st_mode); 3981556Srgrimes case FILFIFO: 3991556Srgrimes return S_ISFIFO(s.st_mode); 4001556Srgrimes case FILSOCK: 4011556Srgrimes return S_ISSOCK(s.st_mode); 4021556Srgrimes case FILSYM: 4031556Srgrimes return S_ISLNK(s.st_mode); 4041556Srgrimes case FILSUID: 4051556Srgrimes return (s.st_mode & S_ISUID) != 0; 4061556Srgrimes case FILSGID: 4071556Srgrimes return (s.st_mode & S_ISGID) != 0; 4081556Srgrimes case FILSTCK: 4091556Srgrimes return (s.st_mode & S_ISVTX) != 0; 4101556Srgrimes case FILGZ: 4111556Srgrimes return s.st_size > (off_t)0; 4121556Srgrimes case FILUID: 4131556Srgrimes return s.st_uid == geteuid(); 4141556Srgrimes case FILGID: 4151556Srgrimes return s.st_gid == getegid(); 4161556Srgrimes default: 4171556Srgrimes return 1; 4181556Srgrimes } 4191556Srgrimes} 4201556Srgrimes 4211556Srgrimesstatic enum token 4221556Srgrimest_lex(char *s) 4231556Srgrimes{ 4241556Srgrimes struct t_op const *op = ops; 4251556Srgrimes 4261556Srgrimes if (s == 0) { 4271556Srgrimes t_wp_op = NULL; 4281556Srgrimes return EOI; 4291556Srgrimes } 4301556Srgrimes while (op->op_text) { 4311556Srgrimes if (strcmp(s, op->op_text) == 0) { 4321556Srgrimes if (((op->op_type == UNOP || op->op_type == BUNOP) 4331556Srgrimes && isunopoperand()) || 4341556Srgrimes (op->op_num == LPAREN && islparenoperand()) || 4351556Srgrimes (op->op_num == RPAREN && isrparenoperand())) 4361556Srgrimes break; 4371556Srgrimes t_wp_op = op; 4381556Srgrimes return op->op_num; 4391556Srgrimes } 4401556Srgrimes op++; 4411556Srgrimes } 4421556Srgrimes t_wp_op = NULL; 4431556Srgrimes return OPERAND; 4441556Srgrimes} 4451556Srgrimes 4461556Srgrimesstatic int 4471556Srgrimesisunopoperand(void) 4481556Srgrimes{ 4491556Srgrimes struct t_op const *op = ops; 4501556Srgrimes char *s; 4511556Srgrimes char *t; 4521556Srgrimes 4531556Srgrimes if (nargc == 1) 4541556Srgrimes return 1; 4551556Srgrimes s = *(t_wp + 1); 4561556Srgrimes if (nargc == 2) 4571556Srgrimes return parenlevel == 1 && strcmp(s, ")") == 0; 4581556Srgrimes t = *(t_wp + 2); 4591556Srgrimes while (op->op_text) { 4601556Srgrimes if (strcmp(s, op->op_text) == 0) 4611556Srgrimes return op->op_type == BINOP && 4621556Srgrimes (parenlevel == 0 || t[0] != ')' || t[1] != '\0'); 4631556Srgrimes op++; 4641556Srgrimes } 4651556Srgrimes return 0; 4661556Srgrimes} 4671556Srgrimes 4681556Srgrimesstatic int 4691556Srgrimesislparenoperand(void) 4701556Srgrimes{ 4711556Srgrimes struct t_op const *op = ops; 4721556Srgrimes char *s; 4731556Srgrimes 4741556Srgrimes if (nargc == 1) 4751556Srgrimes return 1; 4761556Srgrimes s = *(t_wp + 1); 4771556Srgrimes if (nargc == 2) 4781556Srgrimes return parenlevel == 1 && strcmp(s, ")") == 0; 4791556Srgrimes if (nargc != 3) 4801556Srgrimes return 0; 4811556Srgrimes while (op->op_text) { 4821556Srgrimes if (strcmp(s, op->op_text) == 0) 4831556Srgrimes return op->op_type == BINOP; 4841556Srgrimes op++; 4851556Srgrimes } 4861556Srgrimes return 0; 4871556Srgrimes} 4881556Srgrimes 4891556Srgrimesstatic int 4901556Srgrimesisrparenoperand(void) 4911556Srgrimes{ 4921556Srgrimes char *s; 4931556Srgrimes 4941556Srgrimes if (nargc == 1) 4951556Srgrimes return 0; 4961556Srgrimes s = *(t_wp + 1); 4971556Srgrimes if (nargc == 2) 4981556Srgrimes return parenlevel == 1 && strcmp(s, ")") == 0; 4991556Srgrimes return 0; 5001556Srgrimes} 5011556Srgrimes 5021556Srgrimes/* atoi with error detection */ 5031556Srgrimesstatic int 5041556Srgrimesgetn(const char *s) 5051556Srgrimes{ 5061556Srgrimes char *p; 5071556Srgrimes long r; 5081556Srgrimes 5091556Srgrimes errno = 0; 5101556Srgrimes r = strtol(s, &p, 10); 5111556Srgrimes 5121556Srgrimes if (s == p) 5131556Srgrimes error("%s: bad number", s); 5141556Srgrimes 5151556Srgrimes if (errno != 0) 5161556Srgrimes error((errno == EINVAL) ? "%s: bad number" : 5171556Srgrimes "%s: out of range", s); 5181556Srgrimes 5191556Srgrimes while (isspace((unsigned char)*p)) 5201556Srgrimes p++; 5211556Srgrimes 5221556Srgrimes if (*p) 5231556Srgrimes error("%s: bad number", s); 5241556Srgrimes 5251556Srgrimes return (int) r; 5261556Srgrimes} 5271556Srgrimes 5281556Srgrimes/* atoi with error detection and 64 bit range */ 5291556Srgrimesstatic intmax_t 5301556Srgrimesgetq(const char *s) 5311556Srgrimes{ 5321556Srgrimes char *p; 5331556Srgrimes intmax_t r; 5341556Srgrimes 5351556Srgrimes errno = 0; 5361556Srgrimes r = strtoimax(s, &p, 10); 5371556Srgrimes 5381556Srgrimes if (s == p) 5391556Srgrimes error("%s: bad number", s); 5401556Srgrimes 5411556Srgrimes if (errno != 0) 5421556Srgrimes error((errno == EINVAL) ? "%s: bad number" : 5431556Srgrimes "%s: out of range", s); 5441556Srgrimes 5451556Srgrimes while (isspace((unsigned char)*p)) 5461556Srgrimes p++; 5471556Srgrimes 5481556Srgrimes if (*p) 5491556Srgrimes error("%s: bad number", s); 5501556Srgrimes 5511556Srgrimes return r; 5521556Srgrimes} 5531556Srgrimes 5541556Srgrimesstatic int 5551556Srgrimesintcmp (const char *s1, const char *s2) 5561556Srgrimes{ 5571556Srgrimes intmax_t q1, q2; 5581556Srgrimes 5591556Srgrimes 5601556Srgrimes q1 = getq(s1); 5611556Srgrimes q2 = getq(s2); 5621556Srgrimes 5631556Srgrimes if (q1 > q2) 5641556Srgrimes return 1; 5651556Srgrimes 5661556Srgrimes if (q1 < q2) 5671556Srgrimes return -1; 5681556Srgrimes 5691556Srgrimes return 0; 5701556Srgrimes} 5711556Srgrimes 5721556Srgrimesstatic int 5731556Srgrimesnewerf (const char *f1, const char *f2) 5741556Srgrimes{ 5751556Srgrimes struct stat b1, b2; 5761556Srgrimes 5771556Srgrimes if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0) 5781556Srgrimes return 0; 5791556Srgrimes 5801556Srgrimes if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec) 5811556Srgrimes return 1; 5821556Srgrimes if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec) 5831556Srgrimes return 0; 5841556Srgrimes 5851556Srgrimes return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec); 5861556Srgrimes} 5871556Srgrimes 5881556Srgrimesstatic int 5891556Srgrimesolderf (const char *f1, const char *f2) 5901556Srgrimes{ 5911556Srgrimes return (newerf(f2, f1)); 5921556Srgrimes} 5931556Srgrimes 5941556Srgrimesstatic int 5951556Srgrimesequalf (const char *f1, const char *f2) 5961556Srgrimes{ 5971556Srgrimes struct stat b1, b2; 5981556Srgrimes 5991556Srgrimes return (stat (f1, &b1) == 0 && 6001556Srgrimes stat (f2, &b2) == 0 && 6011556Srgrimes b1.st_dev == b2.st_dev && 6021556Srgrimes b1.st_ino == b2.st_ino); 6031556Srgrimes} 6041556Srgrimes