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
70297766Sjillesenum token_types {
71297766Sjilles	UNOP = 0x100,
72297766Sjilles	BINOP = 0x200,
73297766Sjilles	BUNOP = 0x300,
74297766Sjilles	BBINOP = 0x400,
75297766Sjilles	PAREN = 0x500
76297766Sjilles};
77297766Sjilles
7849884Ssheldonhenum token {
7949884Ssheldonh	EOI,
80297766Sjilles	OPERAND,
81297766Sjilles	FILRD = UNOP + 1,
8249884Ssheldonh	FILWR,
8349884Ssheldonh	FILEX,
8449884Ssheldonh	FILEXIST,
8549884Ssheldonh	FILREG,
8649884Ssheldonh	FILDIR,
8749884Ssheldonh	FILCDEV,
8849884Ssheldonh	FILBDEV,
8949884Ssheldonh	FILFIFO,
9049884Ssheldonh	FILSOCK,
9149884Ssheldonh	FILSYM,
9249884Ssheldonh	FILGZ,
9349884Ssheldonh	FILTT,
9449884Ssheldonh	FILSUID,
9549884Ssheldonh	FILSGID,
9649884Ssheldonh	FILSTCK,
97297766Sjilles	STREZ,
98297766Sjilles	STRNZ,
99297766Sjilles	FILUID,
100297766Sjilles	FILGID,
101297766Sjilles	FILNT = BINOP + 1,
102251208Sjilles	FILOT,
10349884Ssheldonh	FILEQ,
10449884Ssheldonh	STREQ,
10549884Ssheldonh	STRNE,
10649884Ssheldonh	STRLT,
10749884Ssheldonh	STRGT,
10849884Ssheldonh	INTEQ,
10949884Ssheldonh	INTNE,
11049884Ssheldonh	INTGE,
11149884Ssheldonh	INTGT,
11249884Ssheldonh	INTLE,
11349884Ssheldonh	INTLT,
114297766Sjilles	UNOT = BUNOP + 1,
115297766Sjilles	BAND = BBINOP + 1,
11649884Ssheldonh	BOR,
117297766Sjilles	LPAREN = PAREN + 1,
118297766Sjilles	RPAREN
1191556Srgrimes};
1201556Srgrimes
121297766Sjilles#define TOKEN_TYPE(token) ((token) & 0xff00)
1221556Srgrimes
123226961Sedstatic struct t_op {
124251208Sjilles	char op_text[4];
125297766Sjilles	short op_num;
12649884Ssheldonh} const ops [] = {
127297766Sjilles	{"-r",	FILRD},
128297766Sjilles	{"-w",	FILWR},
129297766Sjilles	{"-x",	FILEX},
130297766Sjilles	{"-e",	FILEXIST},
131297766Sjilles	{"-f",	FILREG},
132297766Sjilles	{"-d",	FILDIR},
133297766Sjilles	{"-c",	FILCDEV},
134297766Sjilles	{"-b",	FILBDEV},
135297766Sjilles	{"-p",	FILFIFO},
136297766Sjilles	{"-u",	FILSUID},
137297766Sjilles	{"-g",	FILSGID},
138297766Sjilles	{"-k",	FILSTCK},
139297766Sjilles	{"-s",	FILGZ},
140297766Sjilles	{"-t",	FILTT},
141297766Sjilles	{"-z",	STREZ},
142297766Sjilles	{"-n",	STRNZ},
143297766Sjilles	{"-h",	FILSYM},		/* for backwards compat */
144297766Sjilles	{"-O",	FILUID},
145297766Sjilles	{"-G",	FILGID},
146297766Sjilles	{"-L",	FILSYM},
147297766Sjilles	{"-S",	FILSOCK},
148297766Sjilles	{"=",	STREQ},
149297766Sjilles	{"==",	STREQ},
150297766Sjilles	{"!=",	STRNE},
151297766Sjilles	{"<",	STRLT},
152297766Sjilles	{">",	STRGT},
153297766Sjilles	{"-eq",	INTEQ},
154297766Sjilles	{"-ne",	INTNE},
155297766Sjilles	{"-ge",	INTGE},
156297766Sjilles	{"-gt",	INTGT},
157297766Sjilles	{"-le",	INTLE},
158297766Sjilles	{"-lt",	INTLT},
159297766Sjilles	{"-nt",	FILNT},
160297766Sjilles	{"-ot",	FILOT},
161297766Sjilles	{"-ef",	FILEQ},
162297766Sjilles	{"!",	UNOT},
163297766Sjilles	{"-a",	BAND},
164297766Sjilles	{"-o",	BOR},
165297766Sjilles	{"(",	LPAREN},
166297766Sjilles	{")",	RPAREN},
167297766Sjilles	{"",	0}
1681556Srgrimes};
1691556Srgrimes
170226961Sedstatic int nargc;
171226961Sedstatic char **t_wp;
172226961Sedstatic int parenlevel;
1731556Srgrimes
17490111Simpstatic int	aexpr(enum token);
175297767Sjillesstatic int	binop(enum token);
17690111Simpstatic int	equalf(const char *, const char *);
17790111Simpstatic int	filstat(char *, enum token);
17890111Simpstatic int	getn(const char *);
17993345Sachestatic intmax_t	getq(const char *);
18090111Simpstatic int	intcmp(const char *, const char *);
181192862Sjillesstatic int	isunopoperand(void);
182192862Sjillesstatic int	islparenoperand(void);
183192862Sjillesstatic int	isrparenoperand(void);
184251208Sjillesstatic int	newerf(const char *, const char *);
18590111Simpstatic int	nexpr(enum token);
18690111Simpstatic int	oexpr(enum token);
187251208Sjillesstatic int	olderf(const char *, const char *);
18890111Simpstatic int	primary(enum token);
18990111Simpstatic void	syntax(const char *, const char *);
19090111Simpstatic enum	token t_lex(char *);
19149884Ssheldonh
1921556Srgrimesint
19390111Simpmain(int argc, char **argv)
1941556Srgrimes{
195101923Smaxim	int	res;
19655179Ssheldonh	char	*p;
1971556Srgrimes
198219680Sjilles	if ((p = strrchr(argv[0], '/')) == NULL)
19955179Ssheldonh		p = argv[0];
20055179Ssheldonh	else
20155179Ssheldonh		p++;
20255179Ssheldonh	if (strcmp(p, "[") == 0) {
20386622Sknu		if (strcmp(argv[--argc], "]") != 0)
20486618Sknu			error("missing ]");
2051556Srgrimes		argv[argc] = NULL;
2061556Srgrimes	}
2071556Srgrimes
208101923Smaxim	/* no expression => false */
209101923Smaxim	if (--argc <= 0)
210101923Smaxim		return 1;
211101923Smaxim
21288084Sache#ifndef SHELL
21388084Sache	(void)setlocale(LC_CTYPE, "");
21488084Sache#endif
215101923Smaxim	nargc = argc;
21649884Ssheldonh	t_wp = &argv[1];
217192862Sjilles	parenlevel = 0;
218192862Sjilles	if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
219192862Sjilles		/* Things like ! "" -o x do not fit in the normal grammar. */
220192862Sjilles		--nargc;
221192862Sjilles		++t_wp;
222192862Sjilles		res = oexpr(t_lex(*t_wp));
223192862Sjilles	} else
224192862Sjilles		res = !oexpr(t_lex(*t_wp));
2251556Srgrimes
226101923Smaxim	if (--nargc > 0)
22749884Ssheldonh		syntax(*t_wp, "unexpected operator");
22849884Ssheldonh
22949884Ssheldonh	return res;
2301556Srgrimes}
2311556Srgrimes
23249884Ssheldonhstatic void
23390111Simpsyntax(const char *op, const char *msg)
2341556Srgrimes{
2351556Srgrimes
23649884Ssheldonh	if (op && *op)
23786618Sknu		error("%s: %s", op, msg);
23849884Ssheldonh	else
23986618Sknu		error("%s", msg);
2401556Srgrimes}
2411556Srgrimes
24249884Ssheldonhstatic int
24390111Simpoexpr(enum token n)
2441556Srgrimes{
24549884Ssheldonh	int res;
2461556Srgrimes
24749884Ssheldonh	res = aexpr(n);
248101923Smaxim	if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
249101923Smaxim		return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
250101923Smaxim		    res;
25149884Ssheldonh	t_wp--;
252101923Smaxim	nargc++;
25349884Ssheldonh	return res;
25449884Ssheldonh}
2554171Sache
25649884Ssheldonhstatic int
25790111Simpaexpr(enum token n)
25849884Ssheldonh{
25949884Ssheldonh	int res;
2601556Srgrimes
26149884Ssheldonh	res = nexpr(n);
262101923Smaxim	if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
263101923Smaxim		return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
264101923Smaxim		    res;
26549884Ssheldonh	t_wp--;
266101923Smaxim	nargc++;
26749884Ssheldonh	return res;
2681556Srgrimes}
2691556Srgrimes
2701556Srgrimesstatic int
27190111Simpnexpr(enum token n)
2721556Srgrimes{
27349884Ssheldonh	if (n == UNOT)
274101923Smaxim		return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
27549884Ssheldonh	return primary(n);
2761556Srgrimes}
2771556Srgrimes
2781556Srgrimesstatic int
27990111Simpprimary(enum token n)
2801556Srgrimes{
28149884Ssheldonh	enum token nn;
28249884Ssheldonh	int res;
2831556Srgrimes
28449884Ssheldonh	if (n == EOI)
28549884Ssheldonh		return 0;		/* missing expression */
28649884Ssheldonh	if (n == LPAREN) {
287192862Sjilles		parenlevel++;
288101923Smaxim		if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
289192862Sjilles		    RPAREN) {
290192862Sjilles			parenlevel--;
29149884Ssheldonh			return 0;	/* missing expression */
292192862Sjilles		}
29349884Ssheldonh		res = oexpr(nn);
294101923Smaxim		if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
29549884Ssheldonh			syntax(NULL, "closing paren expected");
296192862Sjilles		parenlevel--;
29749884Ssheldonh		return res;
29849884Ssheldonh	}
299297766Sjilles	if (TOKEN_TYPE(n) == UNOP) {
30049884Ssheldonh		/* unary expression */
301101923Smaxim		if (--nargc == 0)
302297766Sjilles			syntax(NULL, "argument expected"); /* impossible */
30349884Ssheldonh		switch (n) {
30449884Ssheldonh		case STREZ:
305101923Smaxim			return strlen(*++t_wp) == 0;
30649884Ssheldonh		case STRNZ:
307101923Smaxim			return strlen(*++t_wp) != 0;
30849884Ssheldonh		case FILTT:
309101923Smaxim			return isatty(getn(*++t_wp));
31049884Ssheldonh		default:
311101923Smaxim			return filstat(*++t_wp, n);
31249884Ssheldonh		}
31349884Ssheldonh	}
3141556Srgrimes
315297767Sjilles	nn = t_lex(nargc > 0 ? t_wp[1] : NULL);
316297767Sjilles	if (TOKEN_TYPE(nn) == BINOP)
317297767Sjilles		return binop(nn);
31849884Ssheldonh
31949884Ssheldonh	return strlen(*t_wp) > 0;
3201556Srgrimes}
3211556Srgrimes
3221556Srgrimesstatic int
323297767Sjillesbinop(enum token n)
3241556Srgrimes{
325297766Sjilles	const char *opnd1, *op, *opnd2;
3261556Srgrimes
32749884Ssheldonh	opnd1 = *t_wp;
328297767Sjilles	op = nargc > 0 ? (--nargc, *++t_wp) : NULL;
3291556Srgrimes
330101923Smaxim	if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
331297766Sjilles		syntax(op, "argument expected");
33249884Ssheldonh
333297766Sjilles	switch (n) {
33449884Ssheldonh	case STREQ:
33549884Ssheldonh		return strcmp(opnd1, opnd2) == 0;
33649884Ssheldonh	case STRNE:
33749884Ssheldonh		return strcmp(opnd1, opnd2) != 0;
33849884Ssheldonh	case STRLT:
33949884Ssheldonh		return strcmp(opnd1, opnd2) < 0;
34049884Ssheldonh	case STRGT:
34149884Ssheldonh		return strcmp(opnd1, opnd2) > 0;
34249884Ssheldonh	case INTEQ:
34362925Sse		return intcmp(opnd1, opnd2) == 0;
34449884Ssheldonh	case INTNE:
34562925Sse		return intcmp(opnd1, opnd2) != 0;
34649884Ssheldonh	case INTGE:
34762925Sse		return intcmp(opnd1, opnd2) >= 0;
34849884Ssheldonh	case INTGT:
34962925Sse		return intcmp(opnd1, opnd2) > 0;
35049884Ssheldonh	case INTLE:
35162925Sse		return intcmp(opnd1, opnd2) <= 0;
35249884Ssheldonh	case INTLT:
35362925Sse		return intcmp(opnd1, opnd2) < 0;
354251208Sjilles	case FILNT:
355251208Sjilles		return newerf (opnd1, opnd2);
356251208Sjilles	case FILOT:
357251208Sjilles		return olderf (opnd1, opnd2);
35849884Ssheldonh	case FILEQ:
35949884Ssheldonh		return equalf (opnd1, opnd2);
36049884Ssheldonh	default:
36149884Ssheldonh		abort();
36249884Ssheldonh		/* NOTREACHED */
3631556Srgrimes	}
3641556Srgrimes}
3651556Srgrimes
36649884Ssheldonhstatic int
36790111Simpfilstat(char *nm, enum token mode)
3681556Srgrimes{
36949884Ssheldonh	struct stat s;
3701556Srgrimes
37149884Ssheldonh	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
37249884Ssheldonh		return 0;
3732664Scsgr
37449884Ssheldonh	switch (mode) {
37549884Ssheldonh	case FILRD:
37691737Smaxim		return (eaccess(nm, R_OK) == 0);
37749884Ssheldonh	case FILWR:
37891737Smaxim		return (eaccess(nm, W_OK) == 0);
37949884Ssheldonh	case FILEX:
38091737Smaxim		/* XXX work around eaccess(2) false positives for superuser */
38191737Smaxim		if (eaccess(nm, X_OK) != 0)
38250087Sgreen			return 0;
38391737Smaxim		if (S_ISDIR(s.st_mode) || geteuid() != 0)
38450087Sgreen			return 1;
38550087Sgreen		return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
38649884Ssheldonh	case FILEXIST:
38791737Smaxim		return (eaccess(nm, F_OK) == 0);
38849884Ssheldonh	case FILREG:
38949884Ssheldonh		return S_ISREG(s.st_mode);
39049884Ssheldonh	case FILDIR:
39149884Ssheldonh		return S_ISDIR(s.st_mode);
39249884Ssheldonh	case FILCDEV:
39349884Ssheldonh		return S_ISCHR(s.st_mode);
39449884Ssheldonh	case FILBDEV:
39549884Ssheldonh		return S_ISBLK(s.st_mode);
39649884Ssheldonh	case FILFIFO:
39749884Ssheldonh		return S_ISFIFO(s.st_mode);
39849884Ssheldonh	case FILSOCK:
39949884Ssheldonh		return S_ISSOCK(s.st_mode);
40049884Ssheldonh	case FILSYM:
40149884Ssheldonh		return S_ISLNK(s.st_mode);
40249884Ssheldonh	case FILSUID:
40349884Ssheldonh		return (s.st_mode & S_ISUID) != 0;
40449884Ssheldonh	case FILSGID:
40549884Ssheldonh		return (s.st_mode & S_ISGID) != 0;
40649884Ssheldonh	case FILSTCK:
40749884Ssheldonh		return (s.st_mode & S_ISVTX) != 0;
40849884Ssheldonh	case FILGZ:
40949884Ssheldonh		return s.st_size > (off_t)0;
41049884Ssheldonh	case FILUID:
41149884Ssheldonh		return s.st_uid == geteuid();
41249884Ssheldonh	case FILGID:
41349884Ssheldonh		return s.st_gid == getegid();
41449884Ssheldonh	default:
41549884Ssheldonh		return 1;
4162675Scsgr	}
41749884Ssheldonh}
4182675Scsgr
41949884Ssheldonhstatic enum token
42090111Simpt_lex(char *s)
42149884Ssheldonh{
42249884Ssheldonh	struct t_op const *op = ops;
42349884Ssheldonh
42449884Ssheldonh	if (s == 0) {
42549884Ssheldonh		return EOI;
42649884Ssheldonh	}
427227984Sjilles	while (*op->op_text) {
42849884Ssheldonh		if (strcmp(s, op->op_text) == 0) {
429297766Sjilles			if (((TOKEN_TYPE(op->op_num) == UNOP ||
430297766Sjilles			    TOKEN_TYPE(op->op_num) == BUNOP)
431192862Sjilles						&& isunopoperand()) ||
432192862Sjilles			    (op->op_num == LPAREN && islparenoperand()) ||
433192862Sjilles			    (op->op_num == RPAREN && isrparenoperand()))
43449884Ssheldonh				break;
43549884Ssheldonh			return op->op_num;
4361556Srgrimes		}
43749884Ssheldonh		op++;
4381556Srgrimes	}
43949884Ssheldonh	return OPERAND;
4401556Srgrimes}
4411556Srgrimes
44249884Ssheldonhstatic int
443192862Sjillesisunopoperand(void)
4441556Srgrimes{
44549884Ssheldonh	struct t_op const *op = ops;
44649884Ssheldonh	char *s;
44749884Ssheldonh	char *t;
4481556Srgrimes
449101923Smaxim	if (nargc == 1)
45049884Ssheldonh		return 1;
451192862Sjilles	s = *(t_wp + 1);
452101923Smaxim	if (nargc == 2)
453192862Sjilles		return parenlevel == 1 && strcmp(s, ")") == 0;
454101923Smaxim	t = *(t_wp + 2);
455227984Sjilles	while (*op->op_text) {
45649884Ssheldonh		if (strcmp(s, op->op_text) == 0)
457297766Sjilles			return TOKEN_TYPE(op->op_num) == BINOP &&
458192862Sjilles			    (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
45949884Ssheldonh		op++;
46049884Ssheldonh	}
46149884Ssheldonh	return 0;
4621556Srgrimes}
4631556Srgrimes
464192862Sjillesstatic int
465192862Sjillesislparenoperand(void)
466192862Sjilles{
467192862Sjilles	struct t_op const *op = ops;
468192862Sjilles	char *s;
469192862Sjilles
470192862Sjilles	if (nargc == 1)
471192862Sjilles		return 1;
472192862Sjilles	s = *(t_wp + 1);
473192862Sjilles	if (nargc == 2)
474192862Sjilles		return parenlevel == 1 && strcmp(s, ")") == 0;
475192862Sjilles	if (nargc != 3)
476192862Sjilles		return 0;
477227984Sjilles	while (*op->op_text) {
478192862Sjilles		if (strcmp(s, op->op_text) == 0)
479297766Sjilles			return TOKEN_TYPE(op->op_num) == BINOP;
480192862Sjilles		op++;
481192862Sjilles	}
482192862Sjilles	return 0;
483192862Sjilles}
484192862Sjilles
485192862Sjillesstatic int
486192862Sjillesisrparenoperand(void)
487192862Sjilles{
488192862Sjilles	char *s;
489192862Sjilles
490192862Sjilles	if (nargc == 1)
491192862Sjilles		return 0;
492192862Sjilles	s = *(t_wp + 1);
493192862Sjilles	if (nargc == 2)
494192862Sjilles		return parenlevel == 1 && strcmp(s, ")") == 0;
495192862Sjilles	return 0;
496192862Sjilles}
497192862Sjilles
49849884Ssheldonh/* atoi with error detection */
49949884Ssheldonhstatic int
50090111Simpgetn(const char *s)
5011556Srgrimes{
50249884Ssheldonh	char *p;
50349884Ssheldonh	long r;
5041556Srgrimes
50549884Ssheldonh	errno = 0;
50649884Ssheldonh	r = strtol(s, &p, 10);
50749884Ssheldonh
50888084Sache	if (s == p)
50988084Sache		error("%s: bad number", s);
51088084Sache
51149884Ssheldonh	if (errno != 0)
51287961Sache		error((errno == EINVAL) ? "%s: bad number" :
51387961Sache					  "%s: out of range", s);
51449884Ssheldonh
51549884Ssheldonh	while (isspace((unsigned char)*p))
51686622Sknu		p++;
51749884Ssheldonh
51849884Ssheldonh	if (*p)
51986622Sknu		error("%s: bad number", s);
52049884Ssheldonh
52149884Ssheldonh	return (int) r;
5221556Srgrimes}
52349884Ssheldonh
52462925Sse/* atoi with error detection and 64 bit range */
52593345Sachestatic intmax_t
52690111Simpgetq(const char *s)
52762925Sse{
52862925Sse	char *p;
52993345Sache	intmax_t r;
53062925Sse
53162925Sse	errno = 0;
53293345Sache	r = strtoimax(s, &p, 10);
53362925Sse
53488084Sache	if (s == p)
53588084Sache		error("%s: bad number", s);
53688084Sache
53762925Sse	if (errno != 0)
53887961Sache		error((errno == EINVAL) ? "%s: bad number" :
53987961Sache					  "%s: out of range", s);
54062925Sse
54162925Sse	while (isspace((unsigned char)*p))
54286622Sknu		p++;
54362925Sse
54462925Sse	if (*p)
54586622Sknu		error("%s: bad number", s);
54662925Sse
54762925Sse	return r;
54862925Sse}
54962925Sse
55049884Ssheldonhstatic int
55190111Simpintcmp (const char *s1, const char *s2)
55262925Sse{
55393345Sache	intmax_t q1, q2;
55462925Sse
55562925Sse
55662925Sse	q1 = getq(s1);
55762925Sse	q2 = getq(s2);
55862925Sse
55962925Sse	if (q1 > q2)
56062925Sse		return 1;
56162925Sse
56262925Sse	if (q1 < q2)
56362925Sse		return -1;
56462925Sse
56562925Sse	return 0;
56662925Sse}
56762925Sse
56862925Ssestatic int
569251208Sjillesnewerf (const char *f1, const char *f2)
57049884Ssheldonh{
57149884Ssheldonh	struct stat b1, b2;
57249884Ssheldonh
573100774Sdwmalone	if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
574100774Sdwmalone		return 0;
575100774Sdwmalone
576251208Sjilles	if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec)
577100774Sdwmalone		return 1;
578251208Sjilles	if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec)
579100774Sdwmalone		return 0;
580100774Sdwmalone
581251208Sjilles       return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec);
58249884Ssheldonh}
58349884Ssheldonh
58449884Ssheldonhstatic int
585251208Sjillesolderf (const char *f1, const char *f2)
586251208Sjilles{
587251208Sjilles	return (newerf(f2, f1));
588251208Sjilles}
589251208Sjilles
590251208Sjillesstatic int
59190111Simpequalf (const char *f1, const char *f2)
59249884Ssheldonh{
59349884Ssheldonh	struct stat b1, b2;
59449884Ssheldonh
59549884Ssheldonh	return (stat (f1, &b1) == 0 &&
59649884Ssheldonh		stat (f2, &b2) == 0 &&
59749884Ssheldonh		b1.st_dev == b2.st_dev &&
59849884Ssheldonh		b1.st_ino == b2.st_ino);
59949884Ssheldonh}
600