149884Ssheldonh/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ 249884Ssheldonh 3139969Simp/*- 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 */ 12218724Sjilles/* 13218724Sjilles * Important: This file is used both as a standalone program /bin/test and 14218724Sjilles * as a builtin for /bin/sh (#define SHELL). 15218724Sjilles */ 161556Srgrimes 1799110Sobrien#include <sys/cdefs.h> 1899110Sobrien__FBSDID("$FreeBSD$"); 191556Srgrimes 2049884Ssheldonh#include <sys/types.h> 211556Srgrimes#include <sys/stat.h> 221556Srgrimes 231556Srgrimes#include <ctype.h> 241556Srgrimes#include <err.h> 251556Srgrimes#include <errno.h> 2693345Sache#include <inttypes.h> 2776883Skris#include <limits.h> 2886619Sknu#include <stdarg.h> 291556Srgrimes#include <stdio.h> 301556Srgrimes#include <stdlib.h> 311556Srgrimes#include <string.h> 321556Srgrimes#include <unistd.h> 331556Srgrimes 3486505Sknu#ifdef SHELL 3586505Sknu#define main testcmd 3686505Sknu#include "bltin/bltin.h" 3786618Sknu#else 3888084Sache#include <locale.h> 3988084Sache 4096376Salfredstatic void error(const char *, ...) __dead2 __printf0like(1, 2); 4186618Sknu 4286618Sknustatic void 4386618Sknuerror(const char *msg, ...) 4486618Sknu{ 4586618Sknu va_list ap; 4686618Sknu va_start(ap, msg); 4786618Sknu verrx(2, msg, ap); 4886618Sknu /*NOTREACHED*/ 4986618Sknu va_end(ap); 5086618Sknu} 5186618Sknu#endif 5286618Sknu 5349884Ssheldonh/* test(1) accepts the following grammar: 5449884Ssheldonh oexpr ::= aexpr | aexpr "-o" oexpr ; 5549884Ssheldonh aexpr ::= nexpr | nexpr "-a" aexpr ; 5649884Ssheldonh nexpr ::= primary | "!" primary 5749884Ssheldonh primary ::= unary-operator operand 5849884Ssheldonh | operand binary-operator operand 5949884Ssheldonh | operand 6049884Ssheldonh | "(" oexpr ")" 6149884Ssheldonh ; 6249884Ssheldonh unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 6349884Ssheldonh "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 641556Srgrimes 6549884Ssheldonh binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 66251208Sjilles "-nt"|"-ot"|"-ef"; 6749884Ssheldonh operand ::= <any legal UNIX file name> 6849884Ssheldonh*/ 691556Srgrimes 7049884Ssheldonhenum token { 7149884Ssheldonh EOI, 7249884Ssheldonh FILRD, 7349884Ssheldonh FILWR, 7449884Ssheldonh FILEX, 7549884Ssheldonh FILEXIST, 7649884Ssheldonh FILREG, 7749884Ssheldonh FILDIR, 7849884Ssheldonh FILCDEV, 7949884Ssheldonh FILBDEV, 8049884Ssheldonh FILFIFO, 8149884Ssheldonh FILSOCK, 8249884Ssheldonh FILSYM, 8349884Ssheldonh FILGZ, 8449884Ssheldonh FILTT, 8549884Ssheldonh FILSUID, 8649884Ssheldonh FILSGID, 8749884Ssheldonh FILSTCK, 88251208Sjilles FILNT, 89251208Sjilles FILOT, 9049884Ssheldonh FILEQ, 9149884Ssheldonh FILUID, 9249884Ssheldonh FILGID, 9349884Ssheldonh STREZ, 9449884Ssheldonh STRNZ, 9549884Ssheldonh STREQ, 9649884Ssheldonh STRNE, 9749884Ssheldonh STRLT, 9849884Ssheldonh STRGT, 9949884Ssheldonh INTEQ, 10049884Ssheldonh INTNE, 10149884Ssheldonh INTGE, 10249884Ssheldonh INTGT, 10349884Ssheldonh INTLE, 10449884Ssheldonh INTLT, 10549884Ssheldonh UNOT, 10649884Ssheldonh BAND, 10749884Ssheldonh BOR, 10849884Ssheldonh LPAREN, 10949884Ssheldonh RPAREN, 11049884Ssheldonh OPERAND 1111556Srgrimes}; 1121556Srgrimes 11349884Ssheldonhenum token_types { 11449884Ssheldonh UNOP, 11549884Ssheldonh BINOP, 11649884Ssheldonh BUNOP, 11749884Ssheldonh BBINOP, 11849884Ssheldonh PAREN 1191556Srgrimes}; 1201556Srgrimes 121226961Sedstatic struct t_op { 122251208Sjilles char op_text[4]; 123251208Sjilles short op_num, op_type; 12449884Ssheldonh} const ops [] = { 12549884Ssheldonh {"-r", FILRD, UNOP}, 12649884Ssheldonh {"-w", FILWR, UNOP}, 12749884Ssheldonh {"-x", FILEX, UNOP}, 12849884Ssheldonh {"-e", FILEXIST,UNOP}, 12949884Ssheldonh {"-f", FILREG, UNOP}, 13049884Ssheldonh {"-d", FILDIR, UNOP}, 13149884Ssheldonh {"-c", FILCDEV,UNOP}, 13249884Ssheldonh {"-b", FILBDEV,UNOP}, 13349884Ssheldonh {"-p", FILFIFO,UNOP}, 13449884Ssheldonh {"-u", FILSUID,UNOP}, 13549884Ssheldonh {"-g", FILSGID,UNOP}, 13649884Ssheldonh {"-k", FILSTCK,UNOP}, 13749884Ssheldonh {"-s", FILGZ, UNOP}, 13849884Ssheldonh {"-t", FILTT, UNOP}, 13949884Ssheldonh {"-z", STREZ, UNOP}, 14049884Ssheldonh {"-n", STRNZ, UNOP}, 14149884Ssheldonh {"-h", FILSYM, UNOP}, /* for backwards compat */ 14249884Ssheldonh {"-O", FILUID, UNOP}, 14349884Ssheldonh {"-G", FILGID, UNOP}, 14449884Ssheldonh {"-L", FILSYM, UNOP}, 14549884Ssheldonh {"-S", FILSOCK,UNOP}, 14649884Ssheldonh {"=", STREQ, BINOP}, 147219084Sdelphij {"==", STREQ, BINOP}, 14849884Ssheldonh {"!=", STRNE, BINOP}, 14949884Ssheldonh {"<", STRLT, BINOP}, 15049884Ssheldonh {">", STRGT, BINOP}, 15149884Ssheldonh {"-eq", INTEQ, BINOP}, 15249884Ssheldonh {"-ne", INTNE, BINOP}, 15349884Ssheldonh {"-ge", INTGE, BINOP}, 15449884Ssheldonh {"-gt", INTGT, BINOP}, 15549884Ssheldonh {"-le", INTLE, BINOP}, 15649884Ssheldonh {"-lt", INTLT, BINOP}, 157251208Sjilles {"-nt", FILNT, BINOP}, 158251208Sjilles {"-ot", FILOT, BINOP}, 15949884Ssheldonh {"-ef", FILEQ, BINOP}, 16049884Ssheldonh {"!", UNOT, BUNOP}, 16149884Ssheldonh {"-a", BAND, BBINOP}, 16249884Ssheldonh {"-o", BOR, BBINOP}, 16349884Ssheldonh {"(", LPAREN, PAREN}, 16449884Ssheldonh {")", RPAREN, PAREN}, 165227984Sjilles {"", 0, 0} 1661556Srgrimes}; 1671556Srgrimes 168226961Sedstatic struct t_op const *t_wp_op; 169226961Sedstatic int nargc; 170226961Sedstatic char **t_wp; 171226961Sedstatic int parenlevel; 1721556Srgrimes 17390111Simpstatic int aexpr(enum token); 17490111Simpstatic int binop(void); 17590111Simpstatic int equalf(const char *, const char *); 17690111Simpstatic int filstat(char *, enum token); 17790111Simpstatic int getn(const char *); 17893345Sachestatic intmax_t getq(const char *); 17990111Simpstatic int intcmp(const char *, const char *); 180192862Sjillesstatic int isunopoperand(void); 181192862Sjillesstatic int islparenoperand(void); 182192862Sjillesstatic int isrparenoperand(void); 183251208Sjillesstatic int newerf(const char *, const char *); 18490111Simpstatic int nexpr(enum token); 18590111Simpstatic int oexpr(enum token); 186251208Sjillesstatic int olderf(const char *, const char *); 18790111Simpstatic int primary(enum token); 18890111Simpstatic void syntax(const char *, const char *); 18990111Simpstatic enum token t_lex(char *); 19049884Ssheldonh 1911556Srgrimesint 19290111Simpmain(int argc, char **argv) 1931556Srgrimes{ 194101923Smaxim int res; 19555179Ssheldonh char *p; 1961556Srgrimes 197219680Sjilles if ((p = strrchr(argv[0], '/')) == NULL) 19855179Ssheldonh p = argv[0]; 19955179Ssheldonh else 20055179Ssheldonh p++; 20155179Ssheldonh if (strcmp(p, "[") == 0) { 20286622Sknu if (strcmp(argv[--argc], "]") != 0) 20386618Sknu error("missing ]"); 2041556Srgrimes argv[argc] = NULL; 2051556Srgrimes } 2061556Srgrimes 207101923Smaxim /* no expression => false */ 208101923Smaxim if (--argc <= 0) 209101923Smaxim return 1; 210101923Smaxim 21188084Sache#ifndef SHELL 21288084Sache (void)setlocale(LC_CTYPE, ""); 21388084Sache#endif 214101923Smaxim nargc = argc; 21549884Ssheldonh t_wp = &argv[1]; 216192862Sjilles parenlevel = 0; 217192862Sjilles if (nargc == 4 && strcmp(*t_wp, "!") == 0) { 218192862Sjilles /* Things like ! "" -o x do not fit in the normal grammar. */ 219192862Sjilles --nargc; 220192862Sjilles ++t_wp; 221192862Sjilles res = oexpr(t_lex(*t_wp)); 222192862Sjilles } else 223192862Sjilles res = !oexpr(t_lex(*t_wp)); 2241556Srgrimes 225101923Smaxim if (--nargc > 0) 22649884Ssheldonh syntax(*t_wp, "unexpected operator"); 22749884Ssheldonh 22849884Ssheldonh return res; 2291556Srgrimes} 2301556Srgrimes 23149884Ssheldonhstatic void 23290111Simpsyntax(const char *op, const char *msg) 2331556Srgrimes{ 2341556Srgrimes 23549884Ssheldonh if (op && *op) 23686618Sknu error("%s: %s", op, msg); 23749884Ssheldonh else 23886618Sknu error("%s", msg); 2391556Srgrimes} 2401556Srgrimes 24149884Ssheldonhstatic int 24290111Simpoexpr(enum token n) 2431556Srgrimes{ 24449884Ssheldonh int res; 2451556Srgrimes 24649884Ssheldonh res = aexpr(n); 247101923Smaxim if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR) 248101923Smaxim return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) || 249101923Smaxim res; 25049884Ssheldonh t_wp--; 251101923Smaxim nargc++; 25249884Ssheldonh return res; 25349884Ssheldonh} 2544171Sache 25549884Ssheldonhstatic int 25690111Simpaexpr(enum token n) 25749884Ssheldonh{ 25849884Ssheldonh int res; 2591556Srgrimes 26049884Ssheldonh res = nexpr(n); 261101923Smaxim if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND) 262101923Smaxim return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) && 263101923Smaxim res; 26449884Ssheldonh t_wp--; 265101923Smaxim nargc++; 26649884Ssheldonh return res; 2671556Srgrimes} 2681556Srgrimes 2691556Srgrimesstatic int 27090111Simpnexpr(enum token n) 2711556Srgrimes{ 27249884Ssheldonh if (n == UNOT) 273101923Smaxim return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)); 27449884Ssheldonh return primary(n); 2751556Srgrimes} 2761556Srgrimes 2771556Srgrimesstatic int 27890111Simpprimary(enum token n) 2791556Srgrimes{ 28049884Ssheldonh enum token nn; 28149884Ssheldonh int res; 2821556Srgrimes 28349884Ssheldonh if (n == EOI) 28449884Ssheldonh return 0; /* missing expression */ 28549884Ssheldonh if (n == LPAREN) { 286192862Sjilles parenlevel++; 287101923Smaxim if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) == 288192862Sjilles RPAREN) { 289192862Sjilles parenlevel--; 29049884Ssheldonh return 0; /* missing expression */ 291192862Sjilles } 29249884Ssheldonh res = oexpr(nn); 293101923Smaxim if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN) 29449884Ssheldonh syntax(NULL, "closing paren expected"); 295192862Sjilles parenlevel--; 29649884Ssheldonh return res; 29749884Ssheldonh } 29849884Ssheldonh if (t_wp_op && t_wp_op->op_type == UNOP) { 29949884Ssheldonh /* unary expression */ 300101923Smaxim if (--nargc == 0) 30149884Ssheldonh syntax(t_wp_op->op_text, "argument expected"); 30249884Ssheldonh switch (n) { 30349884Ssheldonh case STREZ: 304101923Smaxim return strlen(*++t_wp) == 0; 30549884Ssheldonh case STRNZ: 306101923Smaxim return strlen(*++t_wp) != 0; 30749884Ssheldonh case FILTT: 308101923Smaxim return isatty(getn(*++t_wp)); 30949884Ssheldonh default: 310101923Smaxim return filstat(*++t_wp, n); 31149884Ssheldonh } 31249884Ssheldonh } 3131556Srgrimes 314101923Smaxim if (t_lex(nargc > 0 ? t_wp[1] : NULL), t_wp_op && t_wp_op->op_type == 315101923Smaxim BINOP) { 31649884Ssheldonh return binop(); 31749884Ssheldonh } 31849884Ssheldonh 31949884Ssheldonh return strlen(*t_wp) > 0; 3201556Srgrimes} 3211556Srgrimes 3221556Srgrimesstatic int 32390111Simpbinop(void) 3241556Srgrimes{ 32549884Ssheldonh const char *opnd1, *opnd2; 32649884Ssheldonh struct t_op const *op; 3271556Srgrimes 32849884Ssheldonh opnd1 = *t_wp; 329101923Smaxim (void) t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL); 33049884Ssheldonh op = t_wp_op; 3311556Srgrimes 332101923Smaxim if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL) 33349884Ssheldonh syntax(op->op_text, "argument expected"); 33449884Ssheldonh 33549884Ssheldonh switch (op->op_num) { 33649884Ssheldonh case STREQ: 33749884Ssheldonh return strcmp(opnd1, opnd2) == 0; 33849884Ssheldonh case STRNE: 33949884Ssheldonh return strcmp(opnd1, opnd2) != 0; 34049884Ssheldonh case STRLT: 34149884Ssheldonh return strcmp(opnd1, opnd2) < 0; 34249884Ssheldonh case STRGT: 34349884Ssheldonh return strcmp(opnd1, opnd2) > 0; 34449884Ssheldonh case INTEQ: 34562925Sse return intcmp(opnd1, opnd2) == 0; 34649884Ssheldonh case INTNE: 34762925Sse return intcmp(opnd1, opnd2) != 0; 34849884Ssheldonh case INTGE: 34962925Sse return intcmp(opnd1, opnd2) >= 0; 35049884Ssheldonh case INTGT: 35162925Sse return intcmp(opnd1, opnd2) > 0; 35249884Ssheldonh case INTLE: 35362925Sse return intcmp(opnd1, opnd2) <= 0; 35449884Ssheldonh case INTLT: 35562925Sse return intcmp(opnd1, opnd2) < 0; 356251208Sjilles case FILNT: 357251208Sjilles return newerf (opnd1, opnd2); 358251208Sjilles case FILOT: 359251208Sjilles return olderf (opnd1, opnd2); 36049884Ssheldonh case FILEQ: 36149884Ssheldonh return equalf (opnd1, opnd2); 36249884Ssheldonh default: 36349884Ssheldonh abort(); 36449884Ssheldonh /* NOTREACHED */ 3651556Srgrimes } 3661556Srgrimes} 3671556Srgrimes 36849884Ssheldonhstatic int 36990111Simpfilstat(char *nm, enum token mode) 3701556Srgrimes{ 37149884Ssheldonh struct stat s; 3721556Srgrimes 37349884Ssheldonh if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 37449884Ssheldonh return 0; 3752664Scsgr 37649884Ssheldonh switch (mode) { 37749884Ssheldonh case FILRD: 37891737Smaxim return (eaccess(nm, R_OK) == 0); 37949884Ssheldonh case FILWR: 38091737Smaxim return (eaccess(nm, W_OK) == 0); 38149884Ssheldonh case FILEX: 38291737Smaxim /* XXX work around eaccess(2) false positives for superuser */ 38391737Smaxim if (eaccess(nm, X_OK) != 0) 38450087Sgreen return 0; 38591737Smaxim if (S_ISDIR(s.st_mode) || geteuid() != 0) 38650087Sgreen return 1; 38750087Sgreen return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 38849884Ssheldonh case FILEXIST: 38991737Smaxim return (eaccess(nm, F_OK) == 0); 39049884Ssheldonh case FILREG: 39149884Ssheldonh return S_ISREG(s.st_mode); 39249884Ssheldonh case FILDIR: 39349884Ssheldonh return S_ISDIR(s.st_mode); 39449884Ssheldonh case FILCDEV: 39549884Ssheldonh return S_ISCHR(s.st_mode); 39649884Ssheldonh case FILBDEV: 39749884Ssheldonh return S_ISBLK(s.st_mode); 39849884Ssheldonh case FILFIFO: 39949884Ssheldonh return S_ISFIFO(s.st_mode); 40049884Ssheldonh case FILSOCK: 40149884Ssheldonh return S_ISSOCK(s.st_mode); 40249884Ssheldonh case FILSYM: 40349884Ssheldonh return S_ISLNK(s.st_mode); 40449884Ssheldonh case FILSUID: 40549884Ssheldonh return (s.st_mode & S_ISUID) != 0; 40649884Ssheldonh case FILSGID: 40749884Ssheldonh return (s.st_mode & S_ISGID) != 0; 40849884Ssheldonh case FILSTCK: 40949884Ssheldonh return (s.st_mode & S_ISVTX) != 0; 41049884Ssheldonh case FILGZ: 41149884Ssheldonh return s.st_size > (off_t)0; 41249884Ssheldonh case FILUID: 41349884Ssheldonh return s.st_uid == geteuid(); 41449884Ssheldonh case FILGID: 41549884Ssheldonh return s.st_gid == getegid(); 41649884Ssheldonh default: 41749884Ssheldonh return 1; 4182675Scsgr } 41949884Ssheldonh} 4202675Scsgr 42149884Ssheldonhstatic enum token 42290111Simpt_lex(char *s) 42349884Ssheldonh{ 42449884Ssheldonh struct t_op const *op = ops; 42549884Ssheldonh 42649884Ssheldonh if (s == 0) { 42749884Ssheldonh t_wp_op = NULL; 42849884Ssheldonh return EOI; 42949884Ssheldonh } 430227984Sjilles while (*op->op_text) { 43149884Ssheldonh if (strcmp(s, op->op_text) == 0) { 432192862Sjilles if (((op->op_type == UNOP || op->op_type == BUNOP) 433192862Sjilles && isunopoperand()) || 434192862Sjilles (op->op_num == LPAREN && islparenoperand()) || 435192862Sjilles (op->op_num == RPAREN && isrparenoperand())) 43649884Ssheldonh break; 43749884Ssheldonh t_wp_op = op; 43849884Ssheldonh return op->op_num; 4391556Srgrimes } 44049884Ssheldonh op++; 4411556Srgrimes } 44249884Ssheldonh t_wp_op = NULL; 44349884Ssheldonh return OPERAND; 4441556Srgrimes} 4451556Srgrimes 44649884Ssheldonhstatic int 447192862Sjillesisunopoperand(void) 4481556Srgrimes{ 44949884Ssheldonh struct t_op const *op = ops; 45049884Ssheldonh char *s; 45149884Ssheldonh char *t; 4521556Srgrimes 453101923Smaxim if (nargc == 1) 45449884Ssheldonh return 1; 455192862Sjilles s = *(t_wp + 1); 456101923Smaxim if (nargc == 2) 457192862Sjilles return parenlevel == 1 && strcmp(s, ")") == 0; 458101923Smaxim t = *(t_wp + 2); 459227984Sjilles while (*op->op_text) { 46049884Ssheldonh if (strcmp(s, op->op_text) == 0) 46149884Ssheldonh return op->op_type == BINOP && 462192862Sjilles (parenlevel == 0 || t[0] != ')' || t[1] != '\0'); 46349884Ssheldonh op++; 46449884Ssheldonh } 46549884Ssheldonh return 0; 4661556Srgrimes} 4671556Srgrimes 468192862Sjillesstatic int 469192862Sjillesislparenoperand(void) 470192862Sjilles{ 471192862Sjilles struct t_op const *op = ops; 472192862Sjilles char *s; 473192862Sjilles 474192862Sjilles if (nargc == 1) 475192862Sjilles return 1; 476192862Sjilles s = *(t_wp + 1); 477192862Sjilles if (nargc == 2) 478192862Sjilles return parenlevel == 1 && strcmp(s, ")") == 0; 479192862Sjilles if (nargc != 3) 480192862Sjilles return 0; 481227984Sjilles while (*op->op_text) { 482192862Sjilles if (strcmp(s, op->op_text) == 0) 483192862Sjilles return op->op_type == BINOP; 484192862Sjilles op++; 485192862Sjilles } 486192862Sjilles return 0; 487192862Sjilles} 488192862Sjilles 489192862Sjillesstatic int 490192862Sjillesisrparenoperand(void) 491192862Sjilles{ 492192862Sjilles char *s; 493192862Sjilles 494192862Sjilles if (nargc == 1) 495192862Sjilles return 0; 496192862Sjilles s = *(t_wp + 1); 497192862Sjilles if (nargc == 2) 498192862Sjilles return parenlevel == 1 && strcmp(s, ")") == 0; 499192862Sjilles return 0; 500192862Sjilles} 501192862Sjilles 50249884Ssheldonh/* atoi with error detection */ 50349884Ssheldonhstatic int 50490111Simpgetn(const char *s) 5051556Srgrimes{ 50649884Ssheldonh char *p; 50749884Ssheldonh long r; 5081556Srgrimes 50949884Ssheldonh errno = 0; 51049884Ssheldonh r = strtol(s, &p, 10); 51149884Ssheldonh 51288084Sache if (s == p) 51388084Sache error("%s: bad number", s); 51488084Sache 51549884Ssheldonh if (errno != 0) 51687961Sache error((errno == EINVAL) ? "%s: bad number" : 51787961Sache "%s: out of range", s); 51849884Ssheldonh 51949884Ssheldonh while (isspace((unsigned char)*p)) 52086622Sknu p++; 52149884Ssheldonh 52249884Ssheldonh if (*p) 52386622Sknu error("%s: bad number", s); 52449884Ssheldonh 52549884Ssheldonh return (int) r; 5261556Srgrimes} 52749884Ssheldonh 52862925Sse/* atoi with error detection and 64 bit range */ 52993345Sachestatic intmax_t 53090111Simpgetq(const char *s) 53162925Sse{ 53262925Sse char *p; 53393345Sache intmax_t r; 53462925Sse 53562925Sse errno = 0; 53693345Sache r = strtoimax(s, &p, 10); 53762925Sse 53888084Sache if (s == p) 53988084Sache error("%s: bad number", s); 54088084Sache 54162925Sse if (errno != 0) 54287961Sache error((errno == EINVAL) ? "%s: bad number" : 54387961Sache "%s: out of range", s); 54462925Sse 54562925Sse while (isspace((unsigned char)*p)) 54686622Sknu p++; 54762925Sse 54862925Sse if (*p) 54986622Sknu error("%s: bad number", s); 55062925Sse 55162925Sse return r; 55262925Sse} 55362925Sse 55449884Ssheldonhstatic int 55590111Simpintcmp (const char *s1, const char *s2) 55662925Sse{ 55793345Sache intmax_t q1, q2; 55862925Sse 55962925Sse 56062925Sse q1 = getq(s1); 56162925Sse q2 = getq(s2); 56262925Sse 56362925Sse if (q1 > q2) 56462925Sse return 1; 56562925Sse 56662925Sse if (q1 < q2) 56762925Sse return -1; 56862925Sse 56962925Sse return 0; 57062925Sse} 57162925Sse 57262925Ssestatic int 573251208Sjillesnewerf (const char *f1, const char *f2) 57449884Ssheldonh{ 57549884Ssheldonh struct stat b1, b2; 57649884Ssheldonh 577100774Sdwmalone if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0) 578100774Sdwmalone return 0; 579100774Sdwmalone 580251208Sjilles if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec) 581100774Sdwmalone return 1; 582251208Sjilles if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec) 583100774Sdwmalone return 0; 584100774Sdwmalone 585251208Sjilles return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec); 58649884Ssheldonh} 58749884Ssheldonh 58849884Ssheldonhstatic int 589251208Sjillesolderf (const char *f1, const char *f2) 590251208Sjilles{ 591251208Sjilles return (newerf(f2, f1)); 592251208Sjilles} 593251208Sjilles 594251208Sjillesstatic int 59590111Simpequalf (const char *f1, const char *f2) 59649884Ssheldonh{ 59749884Ssheldonh struct stat b1, b2; 59849884Ssheldonh 59949884Ssheldonh return (stat (f1, &b1) == 0 && 60049884Ssheldonh stat (f2, &b2) == 0 && 60149884Ssheldonh b1.st_dev == b2.st_dev && 60249884Ssheldonh b1.st_ino == b2.st_ino); 60349884Ssheldonh} 604