xref: /freebsd/bin/test/test.c (revision 577ad9b2)
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);
1755134c3f7SWarner Losh static int	binop(void);
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 
315577ad9b2SJilles Tjoelker 	if (TOKEN_TYPE(t_lex(nargc > 0 ? t_wp[1] : NULL)) == BINOP)
316717a08b6SSheldon Hearn 		return binop();
317717a08b6SSheldon Hearn 
318717a08b6SSheldon Hearn 	return strlen(*t_wp) > 0;
3194b88c807SRodney W. Grimes }
3204b88c807SRodney W. Grimes 
3214b88c807SRodney W. Grimes static int
3225134c3f7SWarner Losh binop(void)
3234b88c807SRodney W. Grimes {
324577ad9b2SJilles Tjoelker 	const char *opnd1, *op, *opnd2;
325577ad9b2SJilles Tjoelker 	enum token n;
3264b88c807SRodney W. Grimes 
327717a08b6SSheldon Hearn 	opnd1 = *t_wp;
328577ad9b2SJilles Tjoelker 	op = nargc > 0 ? t_wp[1] : NULL;
329577ad9b2SJilles Tjoelker 	n = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL);
3304b88c807SRodney W. Grimes 
3316c62b047SMaxim Konovalov 	if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
332577ad9b2SJilles Tjoelker 		syntax(op, "argument expected");
3334b88c807SRodney W. Grimes 
334577ad9b2SJilles Tjoelker 	switch (n) {
3354b88c807SRodney W. Grimes 	case STREQ:
336717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) == 0;
3374b88c807SRodney W. Grimes 	case STRNE:
338717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) != 0;
339717a08b6SSheldon Hearn 	case STRLT:
340717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) < 0;
341717a08b6SSheldon Hearn 	case STRGT:
342717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) > 0;
343717a08b6SSheldon Hearn 	case INTEQ:
344de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) == 0;
345717a08b6SSheldon Hearn 	case INTNE:
346de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) != 0;
347717a08b6SSheldon Hearn 	case INTGE:
348de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) >= 0;
349717a08b6SSheldon Hearn 	case INTGT:
350de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) > 0;
351717a08b6SSheldon Hearn 	case INTLE:
352de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) <= 0;
353717a08b6SSheldon Hearn 	case INTLT:
354de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) < 0;
355f19825afSJilles Tjoelker 	case FILNT:
356f19825afSJilles Tjoelker 		return newerf (opnd1, opnd2);
357f19825afSJilles Tjoelker 	case FILOT:
358f19825afSJilles Tjoelker 		return olderf (opnd1, opnd2);
359717a08b6SSheldon Hearn 	case FILEQ:
360717a08b6SSheldon Hearn 		return equalf (opnd1, opnd2);
361717a08b6SSheldon Hearn 	default:
362717a08b6SSheldon Hearn 		abort();
363717a08b6SSheldon Hearn 		/* NOTREACHED */
364717a08b6SSheldon Hearn 	}
365717a08b6SSheldon Hearn }
366717a08b6SSheldon Hearn 
367717a08b6SSheldon Hearn static int
3685134c3f7SWarner Losh filstat(char *nm, enum token mode)
369717a08b6SSheldon Hearn {
370717a08b6SSheldon Hearn 	struct stat s;
371717a08b6SSheldon Hearn 
372717a08b6SSheldon Hearn 	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
373717a08b6SSheldon Hearn 		return 0;
374717a08b6SSheldon Hearn 
375717a08b6SSheldon Hearn 	switch (mode) {
376717a08b6SSheldon Hearn 	case FILRD:
37789a3a364SMaxim Konovalov 		return (eaccess(nm, R_OK) == 0);
378717a08b6SSheldon Hearn 	case FILWR:
37989a3a364SMaxim Konovalov 		return (eaccess(nm, W_OK) == 0);
380717a08b6SSheldon Hearn 	case FILEX:
38189a3a364SMaxim Konovalov 		/* XXX work around eaccess(2) false positives for superuser */
38289a3a364SMaxim Konovalov 		if (eaccess(nm, X_OK) != 0)
383eb5e5558SBrian Feldman 			return 0;
38489a3a364SMaxim Konovalov 		if (S_ISDIR(s.st_mode) || geteuid() != 0)
385eb5e5558SBrian Feldman 			return 1;
386d2fed466SBrian Feldman 		return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
387717a08b6SSheldon Hearn 	case FILEXIST:
38889a3a364SMaxim Konovalov 		return (eaccess(nm, F_OK) == 0);
389717a08b6SSheldon Hearn 	case FILREG:
390717a08b6SSheldon Hearn 		return S_ISREG(s.st_mode);
391717a08b6SSheldon Hearn 	case FILDIR:
392717a08b6SSheldon Hearn 		return S_ISDIR(s.st_mode);
393717a08b6SSheldon Hearn 	case FILCDEV:
394717a08b6SSheldon Hearn 		return S_ISCHR(s.st_mode);
395717a08b6SSheldon Hearn 	case FILBDEV:
396717a08b6SSheldon Hearn 		return S_ISBLK(s.st_mode);
397717a08b6SSheldon Hearn 	case FILFIFO:
398717a08b6SSheldon Hearn 		return S_ISFIFO(s.st_mode);
399717a08b6SSheldon Hearn 	case FILSOCK:
400717a08b6SSheldon Hearn 		return S_ISSOCK(s.st_mode);
401717a08b6SSheldon Hearn 	case FILSYM:
402717a08b6SSheldon Hearn 		return S_ISLNK(s.st_mode);
403717a08b6SSheldon Hearn 	case FILSUID:
404717a08b6SSheldon Hearn 		return (s.st_mode & S_ISUID) != 0;
405717a08b6SSheldon Hearn 	case FILSGID:
406717a08b6SSheldon Hearn 		return (s.st_mode & S_ISGID) != 0;
407717a08b6SSheldon Hearn 	case FILSTCK:
408717a08b6SSheldon Hearn 		return (s.st_mode & S_ISVTX) != 0;
409717a08b6SSheldon Hearn 	case FILGZ:
410717a08b6SSheldon Hearn 		return s.st_size > (off_t)0;
411717a08b6SSheldon Hearn 	case FILUID:
412717a08b6SSheldon Hearn 		return s.st_uid == geteuid();
413717a08b6SSheldon Hearn 	case FILGID:
414717a08b6SSheldon Hearn 		return s.st_gid == getegid();
415717a08b6SSheldon Hearn 	default:
416717a08b6SSheldon Hearn 		return 1;
417717a08b6SSheldon Hearn 	}
418717a08b6SSheldon Hearn }
419717a08b6SSheldon Hearn 
420717a08b6SSheldon Hearn static enum token
4215134c3f7SWarner Losh t_lex(char *s)
422717a08b6SSheldon Hearn {
423717a08b6SSheldon Hearn 	struct t_op const *op = ops;
424717a08b6SSheldon Hearn 
425717a08b6SSheldon Hearn 	if (s == 0) {
426717a08b6SSheldon Hearn 		return EOI;
427717a08b6SSheldon Hearn 	}
428ba22f6c6SJilles Tjoelker 	while (*op->op_text) {
429717a08b6SSheldon Hearn 		if (strcmp(s, op->op_text) == 0) {
430577ad9b2SJilles Tjoelker 			if (((TOKEN_TYPE(op->op_num) == UNOP ||
431577ad9b2SJilles Tjoelker 			    TOKEN_TYPE(op->op_num) == BUNOP)
43200e8c94fSJilles Tjoelker 						&& isunopoperand()) ||
43300e8c94fSJilles Tjoelker 			    (op->op_num == LPAREN && islparenoperand()) ||
43400e8c94fSJilles Tjoelker 			    (op->op_num == RPAREN && isrparenoperand()))
4354b88c807SRodney W. Grimes 				break;
436717a08b6SSheldon Hearn 			return op->op_num;
4374b88c807SRodney W. Grimes 		}
438717a08b6SSheldon Hearn 		op++;
439717a08b6SSheldon Hearn 	}
440717a08b6SSheldon Hearn 	return OPERAND;
4414b88c807SRodney W. Grimes }
4424b88c807SRodney W. Grimes 
4434b88c807SRodney W. Grimes static int
44400e8c94fSJilles Tjoelker isunopoperand(void)
4454b88c807SRodney W. Grimes {
446717a08b6SSheldon Hearn 	struct t_op const *op = ops;
447717a08b6SSheldon Hearn 	char *s;
448717a08b6SSheldon Hearn 	char *t;
4494b88c807SRodney W. Grimes 
4506c62b047SMaxim Konovalov 	if (nargc == 1)
451717a08b6SSheldon Hearn 		return 1;
4526c62b047SMaxim Konovalov 	s = *(t_wp + 1);
45300e8c94fSJilles Tjoelker 	if (nargc == 2)
45400e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
4556c62b047SMaxim Konovalov 	t = *(t_wp + 2);
456ba22f6c6SJilles Tjoelker 	while (*op->op_text) {
457717a08b6SSheldon Hearn 		if (strcmp(s, op->op_text) == 0)
458577ad9b2SJilles Tjoelker 			return TOKEN_TYPE(op->op_num) == BINOP &&
45900e8c94fSJilles Tjoelker 			    (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
460717a08b6SSheldon Hearn 		op++;
461717a08b6SSheldon Hearn 	}
462717a08b6SSheldon Hearn 	return 0;
4634b88c807SRodney W. Grimes }
4644b88c807SRodney W. Grimes 
46500e8c94fSJilles Tjoelker static int
46600e8c94fSJilles Tjoelker islparenoperand(void)
46700e8c94fSJilles Tjoelker {
46800e8c94fSJilles Tjoelker 	struct t_op const *op = ops;
46900e8c94fSJilles Tjoelker 	char *s;
47000e8c94fSJilles Tjoelker 
47100e8c94fSJilles Tjoelker 	if (nargc == 1)
47200e8c94fSJilles Tjoelker 		return 1;
47300e8c94fSJilles Tjoelker 	s = *(t_wp + 1);
47400e8c94fSJilles Tjoelker 	if (nargc == 2)
47500e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
47600e8c94fSJilles Tjoelker 	if (nargc != 3)
47700e8c94fSJilles Tjoelker 		return 0;
478ba22f6c6SJilles Tjoelker 	while (*op->op_text) {
47900e8c94fSJilles Tjoelker 		if (strcmp(s, op->op_text) == 0)
480577ad9b2SJilles Tjoelker 			return TOKEN_TYPE(op->op_num) == BINOP;
48100e8c94fSJilles Tjoelker 		op++;
48200e8c94fSJilles Tjoelker 	}
48300e8c94fSJilles Tjoelker 	return 0;
48400e8c94fSJilles Tjoelker }
48500e8c94fSJilles Tjoelker 
48600e8c94fSJilles Tjoelker static int
48700e8c94fSJilles Tjoelker isrparenoperand(void)
48800e8c94fSJilles Tjoelker {
48900e8c94fSJilles Tjoelker 	char *s;
49000e8c94fSJilles Tjoelker 
49100e8c94fSJilles Tjoelker 	if (nargc == 1)
49200e8c94fSJilles Tjoelker 		return 0;
49300e8c94fSJilles Tjoelker 	s = *(t_wp + 1);
49400e8c94fSJilles Tjoelker 	if (nargc == 2)
49500e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
49600e8c94fSJilles Tjoelker 	return 0;
49700e8c94fSJilles Tjoelker }
49800e8c94fSJilles Tjoelker 
499717a08b6SSheldon Hearn /* atoi with error detection */
5004b88c807SRodney W. Grimes static int
5015134c3f7SWarner Losh getn(const char *s)
5024b88c807SRodney W. Grimes {
503717a08b6SSheldon Hearn 	char *p;
504717a08b6SSheldon Hearn 	long r;
5054b88c807SRodney W. Grimes 
5064b88c807SRodney W. Grimes 	errno = 0;
507717a08b6SSheldon Hearn 	r = strtol(s, &p, 10);
508717a08b6SSheldon Hearn 
5093d09cebfSAndrey A. Chernov 	if (s == p)
5103d09cebfSAndrey A. Chernov 		error("%s: bad number", s);
5113d09cebfSAndrey A. Chernov 
512717a08b6SSheldon Hearn 	if (errno != 0)
5139ea42c8eSAndrey A. Chernov 		error((errno == EINVAL) ? "%s: bad number" :
5149ea42c8eSAndrey A. Chernov 					  "%s: out of range", s);
515717a08b6SSheldon Hearn 
516717a08b6SSheldon Hearn 	while (isspace((unsigned char)*p))
517717a08b6SSheldon Hearn 		p++;
518717a08b6SSheldon Hearn 
519717a08b6SSheldon Hearn 	if (*p)
520d919a882SAkinori MUSHA 		error("%s: bad number", s);
521717a08b6SSheldon Hearn 
522717a08b6SSheldon Hearn 	return (int) r;
5234b88c807SRodney W. Grimes }
5244b88c807SRodney W. Grimes 
525de96f240SStefan Eßer /* atoi with error detection and 64 bit range */
52682ea3997SAndrey A. Chernov static intmax_t
5275134c3f7SWarner Losh getq(const char *s)
528de96f240SStefan Eßer {
529de96f240SStefan Eßer 	char *p;
53082ea3997SAndrey A. Chernov 	intmax_t r;
531de96f240SStefan Eßer 
532de96f240SStefan Eßer 	errno = 0;
53382ea3997SAndrey A. Chernov 	r = strtoimax(s, &p, 10);
534de96f240SStefan Eßer 
5353d09cebfSAndrey A. Chernov 	if (s == p)
5363d09cebfSAndrey A. Chernov 		error("%s: bad number", s);
5373d09cebfSAndrey A. Chernov 
538de96f240SStefan Eßer 	if (errno != 0)
5399ea42c8eSAndrey A. Chernov 		error((errno == EINVAL) ? "%s: bad number" :
5409ea42c8eSAndrey A. Chernov 					  "%s: out of range", s);
541de96f240SStefan Eßer 
542de96f240SStefan Eßer 	while (isspace((unsigned char)*p))
543de96f240SStefan Eßer 		p++;
544de96f240SStefan Eßer 
545de96f240SStefan Eßer 	if (*p)
546d919a882SAkinori MUSHA 		error("%s: bad number", s);
547de96f240SStefan Eßer 
548de96f240SStefan Eßer 	return r;
549de96f240SStefan Eßer }
550de96f240SStefan Eßer 
551de96f240SStefan Eßer static int
5525134c3f7SWarner Losh intcmp (const char *s1, const char *s2)
553de96f240SStefan Eßer {
55482ea3997SAndrey A. Chernov 	intmax_t q1, q2;
555de96f240SStefan Eßer 
556de96f240SStefan Eßer 
557de96f240SStefan Eßer 	q1 = getq(s1);
558de96f240SStefan Eßer 	q2 = getq(s2);
559de96f240SStefan Eßer 
560de96f240SStefan Eßer 	if (q1 > q2)
561de96f240SStefan Eßer 		return 1;
562de96f240SStefan Eßer 
563de96f240SStefan Eßer 	if (q1 < q2)
564de96f240SStefan Eßer 		return -1;
565de96f240SStefan Eßer 
566de96f240SStefan Eßer 	return 0;
567de96f240SStefan Eßer }
568de96f240SStefan Eßer 
569717a08b6SSheldon Hearn static int
570f19825afSJilles Tjoelker newerf (const char *f1, const char *f2)
5714b88c807SRodney W. Grimes {
572717a08b6SSheldon Hearn 	struct stat b1, b2;
5734b88c807SRodney W. Grimes 
5746576952cSDavid Malone 	if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
5756576952cSDavid Malone 		return 0;
5766576952cSDavid Malone 
577f19825afSJilles Tjoelker 	if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec)
578293beebcSPeter Jeremy 		return 1;
579f19825afSJilles Tjoelker 	if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec)
580293beebcSPeter Jeremy 		return 0;
581293beebcSPeter Jeremy 
582f19825afSJilles Tjoelker        return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec);
583f19825afSJilles Tjoelker }
584f19825afSJilles Tjoelker 
585f19825afSJilles Tjoelker static int
586f19825afSJilles Tjoelker olderf (const char *f1, const char *f2)
587f19825afSJilles Tjoelker {
588f19825afSJilles Tjoelker 	return (newerf(f2, f1));
589717a08b6SSheldon Hearn }
590717a08b6SSheldon Hearn 
591717a08b6SSheldon Hearn static int
5925134c3f7SWarner Losh equalf (const char *f1, const char *f2)
593717a08b6SSheldon Hearn {
594717a08b6SSheldon Hearn 	struct stat b1, b2;
595717a08b6SSheldon Hearn 
596717a08b6SSheldon Hearn 	return (stat (f1, &b1) == 0 &&
597717a08b6SSheldon Hearn 		stat (f2, &b2) == 0 &&
598717a08b6SSheldon Hearn 		b1.st_dev == b2.st_dev &&
599717a08b6SSheldon Hearn 		b1.st_ino == b2.st_ino);
6004b88c807SRodney W. Grimes }
601