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