xref: /freebsd/bin/test/test.c (revision c80ad859)
1717a08b6SSheldon Hearn /*	$NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $	*/
2717a08b6SSheldon Hearn 
39ddb49cbSWarner Losh /*-
4717a08b6SSheldon Hearn  * test(1); version 7-like  --  author Erik Baalbergen
5717a08b6SSheldon Hearn  * modified by Eric Gisin to be used as built-in.
6717a08b6SSheldon Hearn  * modified by Arnold Robbins to add SVR3 compatibility
7717a08b6SSheldon Hearn  * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8717a08b6SSheldon Hearn  * modified by J.T. Conklin for NetBSD.
94b88c807SRodney W. Grimes  *
10717a08b6SSheldon Hearn  * This program is in the Public Domain.
114b88c807SRodney W. Grimes  */
122fd7d6aaSJilles Tjoelker /*
132fd7d6aaSJilles Tjoelker  * Important: This file is used both as a standalone program /bin/test and
142fd7d6aaSJilles Tjoelker  * as a builtin for /bin/sh (#define SHELL).
152fd7d6aaSJilles Tjoelker  */
164b88c807SRodney W. Grimes 
172749b141SDavid E. O'Brien #include <sys/cdefs.h>
182749b141SDavid E. O'Brien __FBSDID("$FreeBSD$");
194b88c807SRodney W. Grimes 
20717a08b6SSheldon Hearn #include <sys/types.h>
21007d3350SEivind Eklund #include <sys/stat.h>
224b88c807SRodney W. Grimes 
234b88c807SRodney W. Grimes #include <ctype.h>
244b88c807SRodney W. Grimes #include <err.h>
254b88c807SRodney W. Grimes #include <errno.h>
2682ea3997SAndrey A. Chernov #include <inttypes.h>
276dca6515SKris Kennaway #include <limits.h>
2825e04004SAkinori MUSHA #include <stdarg.h>
294b88c807SRodney W. Grimes #include <stdio.h>
304b88c807SRodney W. Grimes #include <stdlib.h>
314b88c807SRodney W. Grimes #include <string.h>
324b88c807SRodney W. Grimes #include <unistd.h>
334b88c807SRodney W. Grimes 
34d90c5c4aSAkinori MUSHA #ifdef SHELL
35d90c5c4aSAkinori MUSHA #define main testcmd
36d90c5c4aSAkinori MUSHA #include "bltin/bltin.h"
37d919a882SAkinori MUSHA #else
383d09cebfSAndrey A. Chernov #include <locale.h>
393d09cebfSAndrey A. Chernov 
40033be9aeSAlfred Perlstein static void error(const char *, ...) __dead2 __printf0like(1, 2);
41d919a882SAkinori MUSHA 
42d919a882SAkinori MUSHA static void
43d919a882SAkinori MUSHA error(const char *msg, ...)
44d919a882SAkinori MUSHA {
45d919a882SAkinori MUSHA 	va_list ap;
46d919a882SAkinori MUSHA 	va_start(ap, msg);
47d919a882SAkinori MUSHA 	verrx(2, msg, ap);
48d919a882SAkinori MUSHA 	/*NOTREACHED*/
49d919a882SAkinori MUSHA 	va_end(ap);
50d919a882SAkinori MUSHA }
51d90c5c4aSAkinori MUSHA #endif
52d90c5c4aSAkinori MUSHA 
53717a08b6SSheldon Hearn /* test(1) accepts the following grammar:
54717a08b6SSheldon Hearn 	oexpr	::= aexpr | aexpr "-o" oexpr ;
55717a08b6SSheldon Hearn 	aexpr	::= nexpr | nexpr "-a" aexpr ;
56717a08b6SSheldon Hearn 	nexpr	::= primary | "!" primary
57717a08b6SSheldon Hearn 	primary	::= unary-operator operand
58717a08b6SSheldon Hearn 		| operand binary-operator operand
59717a08b6SSheldon Hearn 		| operand
60717a08b6SSheldon Hearn 		| "(" oexpr ")"
61717a08b6SSheldon Hearn 		;
62717a08b6SSheldon Hearn 	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
63717a08b6SSheldon Hearn 		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
644b88c807SRodney W. Grimes 
65717a08b6SSheldon Hearn 	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
66f19825afSJilles Tjoelker 			"-nt"|"-ot"|"-ef";
67717a08b6SSheldon Hearn 	operand ::= <any legal UNIX file name>
684b88c807SRodney W. Grimes */
69717a08b6SSheldon Hearn 
70577ad9b2SJilles Tjoelker enum token_types {
71577ad9b2SJilles Tjoelker 	UNOP = 0x100,
72577ad9b2SJilles Tjoelker 	BINOP = 0x200,
73577ad9b2SJilles Tjoelker 	BUNOP = 0x300,
74577ad9b2SJilles Tjoelker 	BBINOP = 0x400,
75577ad9b2SJilles Tjoelker 	PAREN = 0x500
76577ad9b2SJilles Tjoelker };
77577ad9b2SJilles Tjoelker 
78717a08b6SSheldon Hearn enum token {
79717a08b6SSheldon Hearn 	EOI,
80577ad9b2SJilles Tjoelker 	OPERAND,
81577ad9b2SJilles Tjoelker 	FILRD = UNOP + 1,
82717a08b6SSheldon Hearn 	FILWR,
83717a08b6SSheldon Hearn 	FILEX,
84717a08b6SSheldon Hearn 	FILEXIST,
85717a08b6SSheldon Hearn 	FILREG,
86717a08b6SSheldon Hearn 	FILDIR,
87717a08b6SSheldon Hearn 	FILCDEV,
88717a08b6SSheldon Hearn 	FILBDEV,
89717a08b6SSheldon Hearn 	FILFIFO,
90717a08b6SSheldon Hearn 	FILSOCK,
91717a08b6SSheldon Hearn 	FILSYM,
92717a08b6SSheldon Hearn 	FILGZ,
93717a08b6SSheldon Hearn 	FILTT,
94717a08b6SSheldon Hearn 	FILSUID,
95717a08b6SSheldon Hearn 	FILSGID,
96717a08b6SSheldon Hearn 	FILSTCK,
97717a08b6SSheldon Hearn 	STREZ,
98717a08b6SSheldon Hearn 	STRNZ,
99577ad9b2SJilles Tjoelker 	FILUID,
100577ad9b2SJilles Tjoelker 	FILGID,
101577ad9b2SJilles Tjoelker 	FILNT = BINOP + 1,
102577ad9b2SJilles Tjoelker 	FILOT,
103577ad9b2SJilles Tjoelker 	FILEQ,
104717a08b6SSheldon Hearn 	STREQ,
105717a08b6SSheldon Hearn 	STRNE,
106717a08b6SSheldon Hearn 	STRLT,
107717a08b6SSheldon Hearn 	STRGT,
108717a08b6SSheldon Hearn 	INTEQ,
109717a08b6SSheldon Hearn 	INTNE,
110717a08b6SSheldon Hearn 	INTGE,
111717a08b6SSheldon Hearn 	INTGT,
112717a08b6SSheldon Hearn 	INTLE,
113717a08b6SSheldon Hearn 	INTLT,
114577ad9b2SJilles Tjoelker 	UNOT = BUNOP + 1,
115577ad9b2SJilles Tjoelker 	BAND = BBINOP + 1,
116717a08b6SSheldon Hearn 	BOR,
117577ad9b2SJilles Tjoelker 	LPAREN = PAREN + 1,
118577ad9b2SJilles Tjoelker 	RPAREN
1194b88c807SRodney W. Grimes };
1204b88c807SRodney W. Grimes 
121577ad9b2SJilles Tjoelker #define TOKEN_TYPE(token) ((token) & 0xff00)
1224b88c807SRodney W. Grimes 
123f9d4afb4SEd Schouten static struct t_op {
124f19825afSJilles Tjoelker 	char op_text[4];
125577ad9b2SJilles Tjoelker 	short op_num;
126717a08b6SSheldon Hearn } const ops [] = {
127577ad9b2SJilles Tjoelker 	{"-r",	FILRD},
128577ad9b2SJilles Tjoelker 	{"-w",	FILWR},
129577ad9b2SJilles Tjoelker 	{"-x",	FILEX},
130577ad9b2SJilles Tjoelker 	{"-e",	FILEXIST},
131577ad9b2SJilles Tjoelker 	{"-f",	FILREG},
132577ad9b2SJilles Tjoelker 	{"-d",	FILDIR},
133577ad9b2SJilles Tjoelker 	{"-c",	FILCDEV},
134577ad9b2SJilles Tjoelker 	{"-b",	FILBDEV},
135577ad9b2SJilles Tjoelker 	{"-p",	FILFIFO},
136577ad9b2SJilles Tjoelker 	{"-u",	FILSUID},
137577ad9b2SJilles Tjoelker 	{"-g",	FILSGID},
138577ad9b2SJilles Tjoelker 	{"-k",	FILSTCK},
139577ad9b2SJilles Tjoelker 	{"-s",	FILGZ},
140577ad9b2SJilles Tjoelker 	{"-t",	FILTT},
141577ad9b2SJilles Tjoelker 	{"-z",	STREZ},
142577ad9b2SJilles Tjoelker 	{"-n",	STRNZ},
143577ad9b2SJilles Tjoelker 	{"-h",	FILSYM},		/* for backwards compat */
144577ad9b2SJilles Tjoelker 	{"-O",	FILUID},
145577ad9b2SJilles Tjoelker 	{"-G",	FILGID},
146577ad9b2SJilles Tjoelker 	{"-L",	FILSYM},
147577ad9b2SJilles Tjoelker 	{"-S",	FILSOCK},
148577ad9b2SJilles Tjoelker 	{"=",	STREQ},
149577ad9b2SJilles Tjoelker 	{"==",	STREQ},
150577ad9b2SJilles Tjoelker 	{"!=",	STRNE},
151577ad9b2SJilles Tjoelker 	{"<",	STRLT},
152577ad9b2SJilles Tjoelker 	{">",	STRGT},
153577ad9b2SJilles Tjoelker 	{"-eq",	INTEQ},
154577ad9b2SJilles Tjoelker 	{"-ne",	INTNE},
155577ad9b2SJilles Tjoelker 	{"-ge",	INTGE},
156577ad9b2SJilles Tjoelker 	{"-gt",	INTGT},
157577ad9b2SJilles Tjoelker 	{"-le",	INTLE},
158577ad9b2SJilles Tjoelker 	{"-lt",	INTLT},
159577ad9b2SJilles Tjoelker 	{"-nt",	FILNT},
160577ad9b2SJilles Tjoelker 	{"-ot",	FILOT},
161577ad9b2SJilles Tjoelker 	{"-ef",	FILEQ},
162577ad9b2SJilles Tjoelker 	{"!",	UNOT},
163577ad9b2SJilles Tjoelker 	{"-a",	BAND},
164577ad9b2SJilles Tjoelker 	{"-o",	BOR},
165577ad9b2SJilles Tjoelker 	{"(",	LPAREN},
166577ad9b2SJilles Tjoelker 	{")",	RPAREN},
167577ad9b2SJilles Tjoelker 	{"",	0}
1684b88c807SRodney W. Grimes };
1694b88c807SRodney W. Grimes 
170f9d4afb4SEd Schouten static int nargc;
171f9d4afb4SEd Schouten static char **t_wp;
172f9d4afb4SEd Schouten static int parenlevel;
173717a08b6SSheldon Hearn 
1745134c3f7SWarner Losh static int	aexpr(enum token);
175c80ad859SJilles Tjoelker static int	binop(enum token);
1765134c3f7SWarner Losh static int	equalf(const char *, const char *);
1775134c3f7SWarner Losh static int	filstat(char *, enum token);
1785134c3f7SWarner Losh static int	getn(const char *);
17982ea3997SAndrey A. Chernov static intmax_t	getq(const char *);
1805134c3f7SWarner Losh static int	intcmp(const char *, const char *);
18100e8c94fSJilles Tjoelker static int	isunopoperand(void);
18200e8c94fSJilles Tjoelker static int	islparenoperand(void);
18300e8c94fSJilles Tjoelker static int	isrparenoperand(void);
184f19825afSJilles Tjoelker static int	newerf(const char *, const char *);
1855134c3f7SWarner Losh static int	nexpr(enum token);
1865134c3f7SWarner Losh static int	oexpr(enum token);
187f19825afSJilles Tjoelker static int	olderf(const char *, const char *);
1885134c3f7SWarner Losh static int	primary(enum token);
1895134c3f7SWarner Losh static void	syntax(const char *, const char *);
1905134c3f7SWarner Losh static enum	token t_lex(char *);
1914b88c807SRodney W. Grimes 
1924b88c807SRodney W. Grimes int
1935134c3f7SWarner Losh main(int argc, char **argv)
1944b88c807SRodney W. Grimes {
1956c62b047SMaxim Konovalov 	int	res;
1969abf3043SSheldon Hearn 	char	*p;
1974b88c807SRodney W. Grimes 
1980cf90cd1SJilles Tjoelker 	if ((p = strrchr(argv[0], '/')) == NULL)
1999abf3043SSheldon Hearn 		p = argv[0];
2009abf3043SSheldon Hearn 	else
2019abf3043SSheldon Hearn 		p++;
2029abf3043SSheldon Hearn 	if (strcmp(p, "[") == 0) {
203bd90b9c7SAkinori MUSHA 		if (strcmp(argv[--argc], "]") != 0)
204d919a882SAkinori MUSHA 			error("missing ]");
2054b88c807SRodney W. Grimes 		argv[argc] = NULL;
2064b88c807SRodney W. Grimes 	}
2074b88c807SRodney W. Grimes 
2086c62b047SMaxim Konovalov 	/* no expression => false */
2096c62b047SMaxim Konovalov 	if (--argc <= 0)
2106c62b047SMaxim Konovalov 		return 1;
2116c62b047SMaxim Konovalov 
2123d09cebfSAndrey A. Chernov #ifndef SHELL
2133d09cebfSAndrey A. Chernov 	(void)setlocale(LC_CTYPE, "");
2143d09cebfSAndrey A. Chernov #endif
2156c62b047SMaxim Konovalov 	nargc = argc;
216717a08b6SSheldon Hearn 	t_wp = &argv[1];
21700e8c94fSJilles Tjoelker 	parenlevel = 0;
21800e8c94fSJilles Tjoelker 	if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
21900e8c94fSJilles Tjoelker 		/* Things like ! "" -o x do not fit in the normal grammar. */
22000e8c94fSJilles Tjoelker 		--nargc;
22100e8c94fSJilles Tjoelker 		++t_wp;
22200e8c94fSJilles Tjoelker 		res = oexpr(t_lex(*t_wp));
22300e8c94fSJilles Tjoelker 	} else
224717a08b6SSheldon Hearn 		res = !oexpr(t_lex(*t_wp));
225717a08b6SSheldon Hearn 
2266c62b047SMaxim Konovalov 	if (--nargc > 0)
227717a08b6SSheldon Hearn 		syntax(*t_wp, "unexpected operator");
228717a08b6SSheldon Hearn 
229717a08b6SSheldon Hearn 	return res;
2304b88c807SRodney W. Grimes }
231717a08b6SSheldon Hearn 
232717a08b6SSheldon Hearn static void
2335134c3f7SWarner Losh syntax(const char *op, const char *msg)
234717a08b6SSheldon Hearn {
235717a08b6SSheldon Hearn 
236717a08b6SSheldon Hearn 	if (op && *op)
237d919a882SAkinori MUSHA 		error("%s: %s", op, msg);
238717a08b6SSheldon Hearn 	else
239d919a882SAkinori MUSHA 		error("%s", msg);
2404b88c807SRodney W. Grimes }
241717a08b6SSheldon Hearn 
242717a08b6SSheldon Hearn static int
2435134c3f7SWarner Losh oexpr(enum token n)
244717a08b6SSheldon Hearn {
245717a08b6SSheldon Hearn 	int res;
246717a08b6SSheldon Hearn 
247717a08b6SSheldon Hearn 	res = aexpr(n);
2486c62b047SMaxim Konovalov 	if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
2496c62b047SMaxim Konovalov 		return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
2506c62b047SMaxim Konovalov 		    res;
251717a08b6SSheldon Hearn 	t_wp--;
2526c62b047SMaxim Konovalov 	nargc++;
253717a08b6SSheldon Hearn 	return res;
2544b88c807SRodney W. Grimes }
255717a08b6SSheldon Hearn 
256717a08b6SSheldon Hearn static int
2575134c3f7SWarner Losh aexpr(enum token n)
258717a08b6SSheldon Hearn {
259717a08b6SSheldon Hearn 	int res;
260717a08b6SSheldon Hearn 
261717a08b6SSheldon Hearn 	res = nexpr(n);
2626c62b047SMaxim Konovalov 	if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
2636c62b047SMaxim Konovalov 		return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
2646c62b047SMaxim Konovalov 		    res;
265717a08b6SSheldon Hearn 	t_wp--;
2666c62b047SMaxim Konovalov 	nargc++;
267717a08b6SSheldon Hearn 	return res;
268717a08b6SSheldon Hearn }
269717a08b6SSheldon Hearn 
270717a08b6SSheldon Hearn static int
2715134c3f7SWarner Losh nexpr(enum token n)
272717a08b6SSheldon Hearn {
273717a08b6SSheldon Hearn 	if (n == UNOT)
2746c62b047SMaxim Konovalov 		return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
275717a08b6SSheldon Hearn 	return primary(n);
276717a08b6SSheldon Hearn }
277717a08b6SSheldon Hearn 
278717a08b6SSheldon Hearn static int
2795134c3f7SWarner Losh primary(enum token n)
280717a08b6SSheldon Hearn {
281717a08b6SSheldon Hearn 	enum token nn;
282717a08b6SSheldon Hearn 	int res;
283717a08b6SSheldon Hearn 
284717a08b6SSheldon Hearn 	if (n == EOI)
285717a08b6SSheldon Hearn 		return 0;		/* missing expression */
286717a08b6SSheldon Hearn 	if (n == LPAREN) {
28700e8c94fSJilles Tjoelker 		parenlevel++;
2886c62b047SMaxim Konovalov 		if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
28900e8c94fSJilles Tjoelker 		    RPAREN) {
29000e8c94fSJilles Tjoelker 			parenlevel--;
291717a08b6SSheldon Hearn 			return 0;	/* missing expression */
29200e8c94fSJilles Tjoelker 		}
293717a08b6SSheldon Hearn 		res = oexpr(nn);
2946c62b047SMaxim Konovalov 		if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
295717a08b6SSheldon Hearn 			syntax(NULL, "closing paren expected");
29600e8c94fSJilles Tjoelker 		parenlevel--;
297717a08b6SSheldon Hearn 		return res;
298717a08b6SSheldon Hearn 	}
299577ad9b2SJilles Tjoelker 	if (TOKEN_TYPE(n) == UNOP) {
300717a08b6SSheldon Hearn 		/* unary expression */
3016c62b047SMaxim Konovalov 		if (--nargc == 0)
302577ad9b2SJilles Tjoelker 			syntax(NULL, "argument expected"); /* impossible */
303717a08b6SSheldon Hearn 		switch (n) {
304717a08b6SSheldon Hearn 		case STREZ:
3056c62b047SMaxim Konovalov 			return strlen(*++t_wp) == 0;
306717a08b6SSheldon Hearn 		case STRNZ:
3076c62b047SMaxim Konovalov 			return strlen(*++t_wp) != 0;
308717a08b6SSheldon Hearn 		case FILTT:
3096c62b047SMaxim Konovalov 			return isatty(getn(*++t_wp));
3104b88c807SRodney W. Grimes 		default:
3116c62b047SMaxim Konovalov 			return filstat(*++t_wp, n);
312717a08b6SSheldon Hearn 		}
3134b88c807SRodney W. Grimes 	}
3144b88c807SRodney W. Grimes 
315c80ad859SJilles Tjoelker 	nn = t_lex(nargc > 0 ? t_wp[1] : NULL);
316c80ad859SJilles Tjoelker 	if (TOKEN_TYPE(nn) == BINOP)
317c80ad859SJilles Tjoelker 		return binop(nn);
318717a08b6SSheldon Hearn 
319717a08b6SSheldon Hearn 	return strlen(*t_wp) > 0;
3204b88c807SRodney W. Grimes }
3214b88c807SRodney W. Grimes 
3224b88c807SRodney W. Grimes static int
323c80ad859SJilles Tjoelker binop(enum token n)
3244b88c807SRodney W. Grimes {
325577ad9b2SJilles Tjoelker 	const char *opnd1, *op, *opnd2;
3264b88c807SRodney W. Grimes 
327717a08b6SSheldon Hearn 	opnd1 = *t_wp;
328c80ad859SJilles Tjoelker 	op = nargc > 0 ? (--nargc, *++t_wp) : NULL;
3294b88c807SRodney W. Grimes 
3306c62b047SMaxim Konovalov 	if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
331577ad9b2SJilles Tjoelker 		syntax(op, "argument expected");
3324b88c807SRodney W. Grimes 
333577ad9b2SJilles Tjoelker 	switch (n) {
3344b88c807SRodney W. Grimes 	case STREQ:
335717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) == 0;
3364b88c807SRodney W. Grimes 	case STRNE:
337717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) != 0;
338717a08b6SSheldon Hearn 	case STRLT:
339717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) < 0;
340717a08b6SSheldon Hearn 	case STRGT:
341717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) > 0;
342717a08b6SSheldon Hearn 	case INTEQ:
343de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) == 0;
344717a08b6SSheldon Hearn 	case INTNE:
345de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) != 0;
346717a08b6SSheldon Hearn 	case INTGE:
347de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) >= 0;
348717a08b6SSheldon Hearn 	case INTGT:
349de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) > 0;
350717a08b6SSheldon Hearn 	case INTLE:
351de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) <= 0;
352717a08b6SSheldon Hearn 	case INTLT:
353de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) < 0;
354f19825afSJilles Tjoelker 	case FILNT:
355f19825afSJilles Tjoelker 		return newerf (opnd1, opnd2);
356f19825afSJilles Tjoelker 	case FILOT:
357f19825afSJilles Tjoelker 		return olderf (opnd1, opnd2);
358717a08b6SSheldon Hearn 	case FILEQ:
359717a08b6SSheldon Hearn 		return equalf (opnd1, opnd2);
360717a08b6SSheldon Hearn 	default:
361717a08b6SSheldon Hearn 		abort();
362717a08b6SSheldon Hearn 		/* NOTREACHED */
363717a08b6SSheldon Hearn 	}
364717a08b6SSheldon Hearn }
365717a08b6SSheldon Hearn 
366717a08b6SSheldon Hearn static int
3675134c3f7SWarner Losh filstat(char *nm, enum token mode)
368717a08b6SSheldon Hearn {
369717a08b6SSheldon Hearn 	struct stat s;
370717a08b6SSheldon Hearn 
371717a08b6SSheldon Hearn 	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
372717a08b6SSheldon Hearn 		return 0;
373717a08b6SSheldon Hearn 
374717a08b6SSheldon Hearn 	switch (mode) {
375717a08b6SSheldon Hearn 	case FILRD:
37689a3a364SMaxim Konovalov 		return (eaccess(nm, R_OK) == 0);
377717a08b6SSheldon Hearn 	case FILWR:
37889a3a364SMaxim Konovalov 		return (eaccess(nm, W_OK) == 0);
379717a08b6SSheldon Hearn 	case FILEX:
38089a3a364SMaxim Konovalov 		/* XXX work around eaccess(2) false positives for superuser */
38189a3a364SMaxim Konovalov 		if (eaccess(nm, X_OK) != 0)
382eb5e5558SBrian Feldman 			return 0;
38389a3a364SMaxim Konovalov 		if (S_ISDIR(s.st_mode) || geteuid() != 0)
384eb5e5558SBrian Feldman 			return 1;
385d2fed466SBrian Feldman 		return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
386717a08b6SSheldon Hearn 	case FILEXIST:
38789a3a364SMaxim Konovalov 		return (eaccess(nm, F_OK) == 0);
388717a08b6SSheldon Hearn 	case FILREG:
389717a08b6SSheldon Hearn 		return S_ISREG(s.st_mode);
390717a08b6SSheldon Hearn 	case FILDIR:
391717a08b6SSheldon Hearn 		return S_ISDIR(s.st_mode);
392717a08b6SSheldon Hearn 	case FILCDEV:
393717a08b6SSheldon Hearn 		return S_ISCHR(s.st_mode);
394717a08b6SSheldon Hearn 	case FILBDEV:
395717a08b6SSheldon Hearn 		return S_ISBLK(s.st_mode);
396717a08b6SSheldon Hearn 	case FILFIFO:
397717a08b6SSheldon Hearn 		return S_ISFIFO(s.st_mode);
398717a08b6SSheldon Hearn 	case FILSOCK:
399717a08b6SSheldon Hearn 		return S_ISSOCK(s.st_mode);
400717a08b6SSheldon Hearn 	case FILSYM:
401717a08b6SSheldon Hearn 		return S_ISLNK(s.st_mode);
402717a08b6SSheldon Hearn 	case FILSUID:
403717a08b6SSheldon Hearn 		return (s.st_mode & S_ISUID) != 0;
404717a08b6SSheldon Hearn 	case FILSGID:
405717a08b6SSheldon Hearn 		return (s.st_mode & S_ISGID) != 0;
406717a08b6SSheldon Hearn 	case FILSTCK:
407717a08b6SSheldon Hearn 		return (s.st_mode & S_ISVTX) != 0;
408717a08b6SSheldon Hearn 	case FILGZ:
409717a08b6SSheldon Hearn 		return s.st_size > (off_t)0;
410717a08b6SSheldon Hearn 	case FILUID:
411717a08b6SSheldon Hearn 		return s.st_uid == geteuid();
412717a08b6SSheldon Hearn 	case FILGID:
413717a08b6SSheldon Hearn 		return s.st_gid == getegid();
414717a08b6SSheldon Hearn 	default:
415717a08b6SSheldon Hearn 		return 1;
416717a08b6SSheldon Hearn 	}
417717a08b6SSheldon Hearn }
418717a08b6SSheldon Hearn 
419717a08b6SSheldon Hearn static enum token
4205134c3f7SWarner Losh t_lex(char *s)
421717a08b6SSheldon Hearn {
422717a08b6SSheldon Hearn 	struct t_op const *op = ops;
423717a08b6SSheldon Hearn 
424717a08b6SSheldon Hearn 	if (s == 0) {
425717a08b6SSheldon Hearn 		return EOI;
426717a08b6SSheldon Hearn 	}
427ba22f6c6SJilles Tjoelker 	while (*op->op_text) {
428717a08b6SSheldon Hearn 		if (strcmp(s, op->op_text) == 0) {
429577ad9b2SJilles Tjoelker 			if (((TOKEN_TYPE(op->op_num) == UNOP ||
430577ad9b2SJilles Tjoelker 			    TOKEN_TYPE(op->op_num) == BUNOP)
43100e8c94fSJilles Tjoelker 						&& isunopoperand()) ||
43200e8c94fSJilles Tjoelker 			    (op->op_num == LPAREN && islparenoperand()) ||
43300e8c94fSJilles Tjoelker 			    (op->op_num == RPAREN && isrparenoperand()))
4344b88c807SRodney W. Grimes 				break;
435717a08b6SSheldon Hearn 			return op->op_num;
4364b88c807SRodney W. Grimes 		}
437717a08b6SSheldon Hearn 		op++;
438717a08b6SSheldon Hearn 	}
439717a08b6SSheldon Hearn 	return OPERAND;
4404b88c807SRodney W. Grimes }
4414b88c807SRodney W. Grimes 
4424b88c807SRodney W. Grimes static int
44300e8c94fSJilles Tjoelker isunopoperand(void)
4444b88c807SRodney W. Grimes {
445717a08b6SSheldon Hearn 	struct t_op const *op = ops;
446717a08b6SSheldon Hearn 	char *s;
447717a08b6SSheldon Hearn 	char *t;
4484b88c807SRodney W. Grimes 
4496c62b047SMaxim Konovalov 	if (nargc == 1)
450717a08b6SSheldon Hearn 		return 1;
4516c62b047SMaxim Konovalov 	s = *(t_wp + 1);
45200e8c94fSJilles Tjoelker 	if (nargc == 2)
45300e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
4546c62b047SMaxim Konovalov 	t = *(t_wp + 2);
455ba22f6c6SJilles Tjoelker 	while (*op->op_text) {
456717a08b6SSheldon Hearn 		if (strcmp(s, op->op_text) == 0)
457577ad9b2SJilles Tjoelker 			return TOKEN_TYPE(op->op_num) == BINOP &&
45800e8c94fSJilles Tjoelker 			    (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
459717a08b6SSheldon Hearn 		op++;
460717a08b6SSheldon Hearn 	}
461717a08b6SSheldon Hearn 	return 0;
4624b88c807SRodney W. Grimes }
4634b88c807SRodney W. Grimes 
46400e8c94fSJilles Tjoelker static int
46500e8c94fSJilles Tjoelker islparenoperand(void)
46600e8c94fSJilles Tjoelker {
46700e8c94fSJilles Tjoelker 	struct t_op const *op = ops;
46800e8c94fSJilles Tjoelker 	char *s;
46900e8c94fSJilles Tjoelker 
47000e8c94fSJilles Tjoelker 	if (nargc == 1)
47100e8c94fSJilles Tjoelker 		return 1;
47200e8c94fSJilles Tjoelker 	s = *(t_wp + 1);
47300e8c94fSJilles Tjoelker 	if (nargc == 2)
47400e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
47500e8c94fSJilles Tjoelker 	if (nargc != 3)
47600e8c94fSJilles Tjoelker 		return 0;
477ba22f6c6SJilles Tjoelker 	while (*op->op_text) {
47800e8c94fSJilles Tjoelker 		if (strcmp(s, op->op_text) == 0)
479577ad9b2SJilles Tjoelker 			return TOKEN_TYPE(op->op_num) == BINOP;
48000e8c94fSJilles Tjoelker 		op++;
48100e8c94fSJilles Tjoelker 	}
48200e8c94fSJilles Tjoelker 	return 0;
48300e8c94fSJilles Tjoelker }
48400e8c94fSJilles Tjoelker 
48500e8c94fSJilles Tjoelker static int
48600e8c94fSJilles Tjoelker isrparenoperand(void)
48700e8c94fSJilles Tjoelker {
48800e8c94fSJilles Tjoelker 	char *s;
48900e8c94fSJilles Tjoelker 
49000e8c94fSJilles Tjoelker 	if (nargc == 1)
49100e8c94fSJilles Tjoelker 		return 0;
49200e8c94fSJilles Tjoelker 	s = *(t_wp + 1);
49300e8c94fSJilles Tjoelker 	if (nargc == 2)
49400e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
49500e8c94fSJilles Tjoelker 	return 0;
49600e8c94fSJilles Tjoelker }
49700e8c94fSJilles Tjoelker 
498717a08b6SSheldon Hearn /* atoi with error detection */
4994b88c807SRodney W. Grimes static int
5005134c3f7SWarner Losh getn(const char *s)
5014b88c807SRodney W. Grimes {
502717a08b6SSheldon Hearn 	char *p;
503717a08b6SSheldon Hearn 	long r;
5044b88c807SRodney W. Grimes 
5054b88c807SRodney W. Grimes 	errno = 0;
506717a08b6SSheldon Hearn 	r = strtol(s, &p, 10);
507717a08b6SSheldon Hearn 
5083d09cebfSAndrey A. Chernov 	if (s == p)
5093d09cebfSAndrey A. Chernov 		error("%s: bad number", s);
5103d09cebfSAndrey A. Chernov 
511717a08b6SSheldon Hearn 	if (errno != 0)
5129ea42c8eSAndrey A. Chernov 		error((errno == EINVAL) ? "%s: bad number" :
5139ea42c8eSAndrey A. Chernov 					  "%s: out of range", s);
514717a08b6SSheldon Hearn 
515717a08b6SSheldon Hearn 	while (isspace((unsigned char)*p))
516717a08b6SSheldon Hearn 		p++;
517717a08b6SSheldon Hearn 
518717a08b6SSheldon Hearn 	if (*p)
519d919a882SAkinori MUSHA 		error("%s: bad number", s);
520717a08b6SSheldon Hearn 
521717a08b6SSheldon Hearn 	return (int) r;
5224b88c807SRodney W. Grimes }
5234b88c807SRodney W. Grimes 
524de96f240SStefan Eßer /* atoi with error detection and 64 bit range */
52582ea3997SAndrey A. Chernov static intmax_t
5265134c3f7SWarner Losh getq(const char *s)
527de96f240SStefan Eßer {
528de96f240SStefan Eßer 	char *p;
52982ea3997SAndrey A. Chernov 	intmax_t r;
530de96f240SStefan Eßer 
531de96f240SStefan Eßer 	errno = 0;
53282ea3997SAndrey A. Chernov 	r = strtoimax(s, &p, 10);
533de96f240SStefan Eßer 
5343d09cebfSAndrey A. Chernov 	if (s == p)
5353d09cebfSAndrey A. Chernov 		error("%s: bad number", s);
5363d09cebfSAndrey A. Chernov 
537de96f240SStefan Eßer 	if (errno != 0)
5389ea42c8eSAndrey A. Chernov 		error((errno == EINVAL) ? "%s: bad number" :
5399ea42c8eSAndrey A. Chernov 					  "%s: out of range", s);
540de96f240SStefan Eßer 
541de96f240SStefan Eßer 	while (isspace((unsigned char)*p))
542de96f240SStefan Eßer 		p++;
543de96f240SStefan Eßer 
544de96f240SStefan Eßer 	if (*p)
545d919a882SAkinori MUSHA 		error("%s: bad number", s);
546de96f240SStefan Eßer 
547de96f240SStefan Eßer 	return r;
548de96f240SStefan Eßer }
549de96f240SStefan Eßer 
550de96f240SStefan Eßer static int
5515134c3f7SWarner Losh intcmp (const char *s1, const char *s2)
552de96f240SStefan Eßer {
55382ea3997SAndrey A. Chernov 	intmax_t q1, q2;
554de96f240SStefan Eßer 
555de96f240SStefan Eßer 
556de96f240SStefan Eßer 	q1 = getq(s1);
557de96f240SStefan Eßer 	q2 = getq(s2);
558de96f240SStefan Eßer 
559de96f240SStefan Eßer 	if (q1 > q2)
560de96f240SStefan Eßer 		return 1;
561de96f240SStefan Eßer 
562de96f240SStefan Eßer 	if (q1 < q2)
563de96f240SStefan Eßer 		return -1;
564de96f240SStefan Eßer 
565de96f240SStefan Eßer 	return 0;
566de96f240SStefan Eßer }
567de96f240SStefan Eßer 
568717a08b6SSheldon Hearn static int
569f19825afSJilles Tjoelker newerf (const char *f1, const char *f2)
5704b88c807SRodney W. Grimes {
571717a08b6SSheldon Hearn 	struct stat b1, b2;
5724b88c807SRodney W. Grimes 
5736576952cSDavid Malone 	if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
5746576952cSDavid Malone 		return 0;
5756576952cSDavid Malone 
576f19825afSJilles Tjoelker 	if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec)
577293beebcSPeter Jeremy 		return 1;
578f19825afSJilles Tjoelker 	if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec)
579293beebcSPeter Jeremy 		return 0;
580293beebcSPeter Jeremy 
581f19825afSJilles Tjoelker        return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec);
582f19825afSJilles Tjoelker }
583f19825afSJilles Tjoelker 
584f19825afSJilles Tjoelker static int
585f19825afSJilles Tjoelker olderf (const char *f1, const char *f2)
586f19825afSJilles Tjoelker {
587f19825afSJilles Tjoelker 	return (newerf(f2, f1));
588717a08b6SSheldon Hearn }
589717a08b6SSheldon Hearn 
590717a08b6SSheldon Hearn static int
5915134c3f7SWarner Losh equalf (const char *f1, const char *f2)
592717a08b6SSheldon Hearn {
593717a08b6SSheldon Hearn 	struct stat b1, b2;
594717a08b6SSheldon Hearn 
595717a08b6SSheldon Hearn 	return (stat (f1, &b1) == 0 &&
596717a08b6SSheldon Hearn 		stat (f2, &b2) == 0 &&
597717a08b6SSheldon Hearn 		b1.st_dev == b2.st_dev &&
598717a08b6SSheldon Hearn 		b1.st_ino == b2.st_ino);
5994b88c807SRodney W. Grimes }
600