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