xref: /freebsd/bin/test/test.c (revision e043f372)
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 
17717a08b6SSheldon Hearn #include <sys/types.h>
18007d3350SEivind Eklund #include <sys/stat.h>
194b88c807SRodney W. Grimes 
204b88c807SRodney W. Grimes #include <ctype.h>
214b88c807SRodney W. Grimes #include <err.h>
224b88c807SRodney W. Grimes #include <errno.h>
2382ea3997SAndrey A. Chernov #include <inttypes.h>
2425e04004SAkinori MUSHA #include <stdarg.h>
254b88c807SRodney W. Grimes #include <stdlib.h>
264b88c807SRodney W. Grimes #include <string.h>
274b88c807SRodney W. Grimes #include <unistd.h>
284b88c807SRodney W. Grimes 
29d90c5c4aSAkinori MUSHA #ifdef SHELL
30d90c5c4aSAkinori MUSHA #define main testcmd
31d90c5c4aSAkinori MUSHA #include "bltin/bltin.h"
32d919a882SAkinori MUSHA #else
333d09cebfSAndrey A. Chernov #include <locale.h>
343d09cebfSAndrey A. Chernov 
35033be9aeSAlfred Perlstein static void error(const char *, ...) __dead2 __printf0like(1, 2);
36d919a882SAkinori MUSHA 
37d919a882SAkinori MUSHA static void
error(const char * msg,...)38d919a882SAkinori MUSHA error(const char *msg, ...)
39d919a882SAkinori MUSHA {
40d919a882SAkinori MUSHA 	va_list ap;
41d919a882SAkinori MUSHA 	va_start(ap, msg);
42d919a882SAkinori MUSHA 	verrx(2, msg, ap);
43d919a882SAkinori MUSHA 	/*NOTREACHED*/
44d919a882SAkinori MUSHA 	va_end(ap);
45d919a882SAkinori MUSHA }
46d90c5c4aSAkinori MUSHA #endif
47d90c5c4aSAkinori MUSHA 
48717a08b6SSheldon Hearn /* test(1) accepts the following grammar:
49717a08b6SSheldon Hearn 	oexpr	::= aexpr | aexpr "-o" oexpr ;
50717a08b6SSheldon Hearn 	aexpr	::= nexpr | nexpr "-a" aexpr ;
51717a08b6SSheldon Hearn 	nexpr	::= primary | "!" primary
52717a08b6SSheldon Hearn 	primary	::= unary-operator operand
53717a08b6SSheldon Hearn 		| operand binary-operator operand
54717a08b6SSheldon Hearn 		| operand
55717a08b6SSheldon Hearn 		| "(" oexpr ")"
56717a08b6SSheldon Hearn 		;
57717a08b6SSheldon Hearn 	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
58717a08b6SSheldon Hearn 		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
594b88c807SRodney W. Grimes 
60717a08b6SSheldon Hearn 	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
61f19825afSJilles Tjoelker 			"-nt"|"-ot"|"-ef";
62717a08b6SSheldon Hearn 	operand ::= <any legal UNIX file name>
634b88c807SRodney W. Grimes */
64717a08b6SSheldon Hearn 
65577ad9b2SJilles Tjoelker enum token_types {
66577ad9b2SJilles Tjoelker 	UNOP = 0x100,
67577ad9b2SJilles Tjoelker 	BINOP = 0x200,
68577ad9b2SJilles Tjoelker 	BUNOP = 0x300,
69577ad9b2SJilles Tjoelker 	BBINOP = 0x400,
70577ad9b2SJilles Tjoelker 	PAREN = 0x500
71577ad9b2SJilles Tjoelker };
72577ad9b2SJilles Tjoelker 
73717a08b6SSheldon Hearn enum token {
74717a08b6SSheldon Hearn 	EOI,
75577ad9b2SJilles Tjoelker 	OPERAND,
76577ad9b2SJilles Tjoelker 	FILRD = UNOP + 1,
77717a08b6SSheldon Hearn 	FILWR,
78717a08b6SSheldon Hearn 	FILEX,
79717a08b6SSheldon Hearn 	FILEXIST,
80717a08b6SSheldon Hearn 	FILREG,
81717a08b6SSheldon Hearn 	FILDIR,
82717a08b6SSheldon Hearn 	FILCDEV,
83717a08b6SSheldon Hearn 	FILBDEV,
84717a08b6SSheldon Hearn 	FILFIFO,
85717a08b6SSheldon Hearn 	FILSOCK,
86717a08b6SSheldon Hearn 	FILSYM,
87717a08b6SSheldon Hearn 	FILGZ,
88717a08b6SSheldon Hearn 	FILTT,
89717a08b6SSheldon Hearn 	FILSUID,
90717a08b6SSheldon Hearn 	FILSGID,
91717a08b6SSheldon Hearn 	FILSTCK,
92717a08b6SSheldon Hearn 	STREZ,
93717a08b6SSheldon Hearn 	STRNZ,
94577ad9b2SJilles Tjoelker 	FILUID,
95577ad9b2SJilles Tjoelker 	FILGID,
96577ad9b2SJilles Tjoelker 	FILNT = BINOP + 1,
97577ad9b2SJilles Tjoelker 	FILOT,
98577ad9b2SJilles Tjoelker 	FILEQ,
99717a08b6SSheldon Hearn 	STREQ,
100717a08b6SSheldon Hearn 	STRNE,
101717a08b6SSheldon Hearn 	STRLT,
102717a08b6SSheldon Hearn 	STRGT,
103717a08b6SSheldon Hearn 	INTEQ,
104717a08b6SSheldon Hearn 	INTNE,
105717a08b6SSheldon Hearn 	INTGE,
106717a08b6SSheldon Hearn 	INTGT,
107717a08b6SSheldon Hearn 	INTLE,
108717a08b6SSheldon Hearn 	INTLT,
109577ad9b2SJilles Tjoelker 	UNOT = BUNOP + 1,
110577ad9b2SJilles Tjoelker 	BAND = BBINOP + 1,
111717a08b6SSheldon Hearn 	BOR,
112577ad9b2SJilles Tjoelker 	LPAREN = PAREN + 1,
113577ad9b2SJilles Tjoelker 	RPAREN
1144b88c807SRodney W. Grimes };
1154b88c807SRodney W. Grimes 
116577ad9b2SJilles Tjoelker #define TOKEN_TYPE(token) ((token) & 0xff00)
1174b88c807SRodney W. Grimes 
118f1602fa5SJilles Tjoelker static const struct t_op {
119f1602fa5SJilles Tjoelker 	char op_text[2];
120577ad9b2SJilles Tjoelker 	short op_num;
121f1602fa5SJilles Tjoelker } ops1[] = {
122577ad9b2SJilles Tjoelker 	{"=",	STREQ},
123577ad9b2SJilles Tjoelker 	{"<",	STRLT},
124577ad9b2SJilles Tjoelker 	{">",	STRGT},
125577ad9b2SJilles Tjoelker 	{"!",	UNOT},
126577ad9b2SJilles Tjoelker 	{"(",	LPAREN},
127577ad9b2SJilles Tjoelker 	{")",	RPAREN},
128f1602fa5SJilles Tjoelker }, opsm1[] = {
129f1602fa5SJilles Tjoelker 	{"r",	FILRD},
130f1602fa5SJilles Tjoelker 	{"w",	FILWR},
131f1602fa5SJilles Tjoelker 	{"x",	FILEX},
132f1602fa5SJilles Tjoelker 	{"e",	FILEXIST},
133f1602fa5SJilles Tjoelker 	{"f",	FILREG},
134f1602fa5SJilles Tjoelker 	{"d",	FILDIR},
135f1602fa5SJilles Tjoelker 	{"c",	FILCDEV},
136f1602fa5SJilles Tjoelker 	{"b",	FILBDEV},
137f1602fa5SJilles Tjoelker 	{"p",	FILFIFO},
138f1602fa5SJilles Tjoelker 	{"u",	FILSUID},
139f1602fa5SJilles Tjoelker 	{"g",	FILSGID},
140f1602fa5SJilles Tjoelker 	{"k",	FILSTCK},
141f1602fa5SJilles Tjoelker 	{"s",	FILGZ},
142f1602fa5SJilles Tjoelker 	{"t",	FILTT},
143f1602fa5SJilles Tjoelker 	{"z",	STREZ},
144f1602fa5SJilles Tjoelker 	{"n",	STRNZ},
145f1602fa5SJilles Tjoelker 	{"h",	FILSYM},		/* for backwards compat */
146f1602fa5SJilles Tjoelker 	{"O",	FILUID},
147f1602fa5SJilles Tjoelker 	{"G",	FILGID},
148f1602fa5SJilles Tjoelker 	{"L",	FILSYM},
149f1602fa5SJilles Tjoelker 	{"S",	FILSOCK},
150f1602fa5SJilles Tjoelker 	{"a",	BAND},
151f1602fa5SJilles Tjoelker 	{"o",	BOR},
152f1602fa5SJilles Tjoelker }, ops2[] = {
153f1602fa5SJilles Tjoelker 	{"==",	STREQ},
154f1602fa5SJilles Tjoelker 	{"!=",	STRNE},
155f1602fa5SJilles Tjoelker }, opsm2[] = {
156f1602fa5SJilles Tjoelker 	{"eq",	INTEQ},
157f1602fa5SJilles Tjoelker 	{"ne",	INTNE},
158f1602fa5SJilles Tjoelker 	{"ge",	INTGE},
159f1602fa5SJilles Tjoelker 	{"gt",	INTGT},
160f1602fa5SJilles Tjoelker 	{"le",	INTLE},
161f1602fa5SJilles Tjoelker 	{"lt",	INTLT},
162f1602fa5SJilles Tjoelker 	{"nt",	FILNT},
163f1602fa5SJilles Tjoelker 	{"ot",	FILOT},
164f1602fa5SJilles Tjoelker 	{"ef",	FILEQ},
1654b88c807SRodney W. Grimes };
1664b88c807SRodney W. Grimes 
167f9d4afb4SEd Schouten static int nargc;
168f9d4afb4SEd Schouten static char **t_wp;
169f9d4afb4SEd Schouten static int parenlevel;
170717a08b6SSheldon Hearn 
1715134c3f7SWarner Losh static int	aexpr(enum token);
172c80ad859SJilles Tjoelker static int	binop(enum token);
1735134c3f7SWarner Losh static int	equalf(const char *, const char *);
1745134c3f7SWarner Losh static int	filstat(char *, enum token);
1755134c3f7SWarner Losh static int	getn(const char *);
17682ea3997SAndrey A. Chernov static intmax_t	getq(const char *);
1775134c3f7SWarner Losh static int	intcmp(const char *, const char *);
17800e8c94fSJilles Tjoelker static int	isunopoperand(void);
17900e8c94fSJilles Tjoelker static int	islparenoperand(void);
18000e8c94fSJilles Tjoelker static int	isrparenoperand(void);
181f19825afSJilles Tjoelker static int	newerf(const char *, const char *);
1825134c3f7SWarner Losh static int	nexpr(enum token);
1835134c3f7SWarner Losh static int	oexpr(enum token);
184f19825afSJilles Tjoelker static int	olderf(const char *, const char *);
1855134c3f7SWarner Losh static int	primary(enum token);
1865134c3f7SWarner Losh static void	syntax(const char *, const char *);
1875134c3f7SWarner Losh static enum	token t_lex(char *);
1884b88c807SRodney W. Grimes 
1894b88c807SRodney W. Grimes int
main(int argc,char ** argv)1905134c3f7SWarner Losh main(int argc, char **argv)
1914b88c807SRodney W. Grimes {
1926c62b047SMaxim Konovalov 	int	res;
1939abf3043SSheldon Hearn 	char	*p;
1944b88c807SRodney W. Grimes 
1950cf90cd1SJilles Tjoelker 	if ((p = strrchr(argv[0], '/')) == NULL)
1969abf3043SSheldon Hearn 		p = argv[0];
1979abf3043SSheldon Hearn 	else
1989abf3043SSheldon Hearn 		p++;
1999abf3043SSheldon Hearn 	if (strcmp(p, "[") == 0) {
200bd90b9c7SAkinori MUSHA 		if (strcmp(argv[--argc], "]") != 0)
201d919a882SAkinori MUSHA 			error("missing ]");
2024b88c807SRodney W. Grimes 		argv[argc] = NULL;
2034b88c807SRodney W. Grimes 	}
2044b88c807SRodney W. Grimes 
2056c62b047SMaxim Konovalov 	/* no expression => false */
2066c62b047SMaxim Konovalov 	if (--argc <= 0)
2076c62b047SMaxim Konovalov 		return 1;
2086c62b047SMaxim Konovalov 
2093d09cebfSAndrey A. Chernov #ifndef SHELL
2103d09cebfSAndrey A. Chernov 	(void)setlocale(LC_CTYPE, "");
2113d09cebfSAndrey A. Chernov #endif
2126c62b047SMaxim Konovalov 	nargc = argc;
213717a08b6SSheldon Hearn 	t_wp = &argv[1];
21400e8c94fSJilles Tjoelker 	parenlevel = 0;
21500e8c94fSJilles Tjoelker 	if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
21600e8c94fSJilles Tjoelker 		/* Things like ! "" -o x do not fit in the normal grammar. */
21700e8c94fSJilles Tjoelker 		--nargc;
21800e8c94fSJilles Tjoelker 		++t_wp;
21900e8c94fSJilles Tjoelker 		res = oexpr(t_lex(*t_wp));
22000e8c94fSJilles Tjoelker 	} else
221717a08b6SSheldon Hearn 		res = !oexpr(t_lex(*t_wp));
222717a08b6SSheldon Hearn 
2236c62b047SMaxim Konovalov 	if (--nargc > 0)
224717a08b6SSheldon Hearn 		syntax(*t_wp, "unexpected operator");
225717a08b6SSheldon Hearn 
226717a08b6SSheldon Hearn 	return res;
2274b88c807SRodney W. Grimes }
228717a08b6SSheldon Hearn 
229717a08b6SSheldon Hearn static void
syntax(const char * op,const char * msg)2305134c3f7SWarner Losh syntax(const char *op, const char *msg)
231717a08b6SSheldon Hearn {
232717a08b6SSheldon Hearn 
233717a08b6SSheldon Hearn 	if (op && *op)
234d919a882SAkinori MUSHA 		error("%s: %s", op, msg);
235717a08b6SSheldon Hearn 	else
236d919a882SAkinori MUSHA 		error("%s", msg);
2374b88c807SRodney W. Grimes }
238717a08b6SSheldon Hearn 
239717a08b6SSheldon Hearn static int
oexpr(enum token n)2405134c3f7SWarner Losh oexpr(enum token n)
241717a08b6SSheldon Hearn {
242717a08b6SSheldon Hearn 	int res;
243717a08b6SSheldon Hearn 
244717a08b6SSheldon Hearn 	res = aexpr(n);
2456c62b047SMaxim Konovalov 	if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
2466c62b047SMaxim Konovalov 		return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
2476c62b047SMaxim Konovalov 		    res;
248717a08b6SSheldon Hearn 	t_wp--;
2496c62b047SMaxim Konovalov 	nargc++;
250717a08b6SSheldon Hearn 	return res;
2514b88c807SRodney W. Grimes }
252717a08b6SSheldon Hearn 
253717a08b6SSheldon Hearn static int
aexpr(enum token n)2545134c3f7SWarner Losh aexpr(enum token n)
255717a08b6SSheldon Hearn {
256717a08b6SSheldon Hearn 	int res;
257717a08b6SSheldon Hearn 
258717a08b6SSheldon Hearn 	res = nexpr(n);
2596c62b047SMaxim Konovalov 	if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
2606c62b047SMaxim Konovalov 		return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
2616c62b047SMaxim Konovalov 		    res;
262717a08b6SSheldon Hearn 	t_wp--;
2636c62b047SMaxim Konovalov 	nargc++;
264717a08b6SSheldon Hearn 	return res;
265717a08b6SSheldon Hearn }
266717a08b6SSheldon Hearn 
267717a08b6SSheldon Hearn static int
nexpr(enum token n)2685134c3f7SWarner Losh nexpr(enum token n)
269717a08b6SSheldon Hearn {
270717a08b6SSheldon Hearn 	if (n == UNOT)
2716c62b047SMaxim Konovalov 		return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
272717a08b6SSheldon Hearn 	return primary(n);
273717a08b6SSheldon Hearn }
274717a08b6SSheldon Hearn 
275717a08b6SSheldon Hearn static int
primary(enum token n)2765134c3f7SWarner Losh primary(enum token n)
277717a08b6SSheldon Hearn {
278717a08b6SSheldon Hearn 	enum token nn;
279717a08b6SSheldon Hearn 	int res;
280717a08b6SSheldon Hearn 
281717a08b6SSheldon Hearn 	if (n == EOI)
282717a08b6SSheldon Hearn 		return 0;		/* missing expression */
283717a08b6SSheldon Hearn 	if (n == LPAREN) {
28400e8c94fSJilles Tjoelker 		parenlevel++;
2856c62b047SMaxim Konovalov 		if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
28600e8c94fSJilles Tjoelker 		    RPAREN) {
28700e8c94fSJilles Tjoelker 			parenlevel--;
288717a08b6SSheldon Hearn 			return 0;	/* missing expression */
28900e8c94fSJilles Tjoelker 		}
290717a08b6SSheldon Hearn 		res = oexpr(nn);
2916c62b047SMaxim Konovalov 		if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
292717a08b6SSheldon Hearn 			syntax(NULL, "closing paren expected");
29300e8c94fSJilles Tjoelker 		parenlevel--;
294717a08b6SSheldon Hearn 		return res;
295717a08b6SSheldon Hearn 	}
296577ad9b2SJilles Tjoelker 	if (TOKEN_TYPE(n) == UNOP) {
297717a08b6SSheldon Hearn 		/* unary expression */
2986c62b047SMaxim Konovalov 		if (--nargc == 0)
299577ad9b2SJilles Tjoelker 			syntax(NULL, "argument expected"); /* impossible */
300717a08b6SSheldon Hearn 		switch (n) {
301717a08b6SSheldon Hearn 		case STREZ:
3026c62b047SMaxim Konovalov 			return strlen(*++t_wp) == 0;
303717a08b6SSheldon Hearn 		case STRNZ:
3046c62b047SMaxim Konovalov 			return strlen(*++t_wp) != 0;
305717a08b6SSheldon Hearn 		case FILTT:
3066c62b047SMaxim Konovalov 			return isatty(getn(*++t_wp));
3074b88c807SRodney W. Grimes 		default:
3086c62b047SMaxim Konovalov 			return filstat(*++t_wp, n);
309717a08b6SSheldon Hearn 		}
3104b88c807SRodney W. Grimes 	}
3114b88c807SRodney W. Grimes 
312c80ad859SJilles Tjoelker 	nn = t_lex(nargc > 0 ? t_wp[1] : NULL);
313c80ad859SJilles Tjoelker 	if (TOKEN_TYPE(nn) == BINOP)
314c80ad859SJilles Tjoelker 		return binop(nn);
315717a08b6SSheldon Hearn 
316717a08b6SSheldon Hearn 	return strlen(*t_wp) > 0;
3174b88c807SRodney W. Grimes }
3184b88c807SRodney W. Grimes 
3194b88c807SRodney W. Grimes static int
binop(enum token n)320c80ad859SJilles Tjoelker binop(enum token n)
3214b88c807SRodney W. Grimes {
322577ad9b2SJilles Tjoelker 	const char *opnd1, *op, *opnd2;
3234b88c807SRodney W. Grimes 
324717a08b6SSheldon Hearn 	opnd1 = *t_wp;
325c80ad859SJilles Tjoelker 	op = nargc > 0 ? (--nargc, *++t_wp) : NULL;
3264b88c807SRodney W. Grimes 
3276c62b047SMaxim Konovalov 	if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
328577ad9b2SJilles Tjoelker 		syntax(op, "argument expected");
3294b88c807SRodney W. Grimes 
330577ad9b2SJilles Tjoelker 	switch (n) {
3314b88c807SRodney W. Grimes 	case STREQ:
332717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) == 0;
3334b88c807SRodney W. Grimes 	case STRNE:
334717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) != 0;
335717a08b6SSheldon Hearn 	case STRLT:
336717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) < 0;
337717a08b6SSheldon Hearn 	case STRGT:
338717a08b6SSheldon Hearn 		return strcmp(opnd1, opnd2) > 0;
339717a08b6SSheldon Hearn 	case INTEQ:
340de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) == 0;
341717a08b6SSheldon Hearn 	case INTNE:
342de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) != 0;
343717a08b6SSheldon Hearn 	case INTGE:
344de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) >= 0;
345717a08b6SSheldon Hearn 	case INTGT:
346de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) > 0;
347717a08b6SSheldon Hearn 	case INTLE:
348de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) <= 0;
349717a08b6SSheldon Hearn 	case INTLT:
350de96f240SStefan Eßer 		return intcmp(opnd1, opnd2) < 0;
351f19825afSJilles Tjoelker 	case FILNT:
352f19825afSJilles Tjoelker 		return newerf (opnd1, opnd2);
353f19825afSJilles Tjoelker 	case FILOT:
354f19825afSJilles Tjoelker 		return olderf (opnd1, opnd2);
355717a08b6SSheldon Hearn 	case FILEQ:
356717a08b6SSheldon Hearn 		return equalf (opnd1, opnd2);
357717a08b6SSheldon Hearn 	default:
358717a08b6SSheldon Hearn 		abort();
359717a08b6SSheldon Hearn 		/* NOTREACHED */
360717a08b6SSheldon Hearn 	}
361717a08b6SSheldon Hearn }
362717a08b6SSheldon Hearn 
363717a08b6SSheldon Hearn static int
filstat(char * nm,enum token mode)3645134c3f7SWarner Losh filstat(char *nm, enum token mode)
365717a08b6SSheldon Hearn {
366717a08b6SSheldon Hearn 	struct stat s;
367717a08b6SSheldon Hearn 
368717a08b6SSheldon Hearn 	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
369717a08b6SSheldon Hearn 		return 0;
370717a08b6SSheldon Hearn 
371717a08b6SSheldon Hearn 	switch (mode) {
372717a08b6SSheldon Hearn 	case FILRD:
37389a3a364SMaxim Konovalov 		return (eaccess(nm, R_OK) == 0);
374717a08b6SSheldon Hearn 	case FILWR:
37589a3a364SMaxim Konovalov 		return (eaccess(nm, W_OK) == 0);
376717a08b6SSheldon Hearn 	case FILEX:
37789a3a364SMaxim Konovalov 		/* XXX work around eaccess(2) false positives for superuser */
37889a3a364SMaxim Konovalov 		if (eaccess(nm, X_OK) != 0)
379eb5e5558SBrian Feldman 			return 0;
38089a3a364SMaxim Konovalov 		if (S_ISDIR(s.st_mode) || geteuid() != 0)
381eb5e5558SBrian Feldman 			return 1;
382d2fed466SBrian Feldman 		return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
383717a08b6SSheldon Hearn 	case FILEXIST:
38489a3a364SMaxim Konovalov 		return (eaccess(nm, F_OK) == 0);
385717a08b6SSheldon Hearn 	case FILREG:
386717a08b6SSheldon Hearn 		return S_ISREG(s.st_mode);
387717a08b6SSheldon Hearn 	case FILDIR:
388717a08b6SSheldon Hearn 		return S_ISDIR(s.st_mode);
389717a08b6SSheldon Hearn 	case FILCDEV:
390717a08b6SSheldon Hearn 		return S_ISCHR(s.st_mode);
391717a08b6SSheldon Hearn 	case FILBDEV:
392717a08b6SSheldon Hearn 		return S_ISBLK(s.st_mode);
393717a08b6SSheldon Hearn 	case FILFIFO:
394717a08b6SSheldon Hearn 		return S_ISFIFO(s.st_mode);
395717a08b6SSheldon Hearn 	case FILSOCK:
396717a08b6SSheldon Hearn 		return S_ISSOCK(s.st_mode);
397717a08b6SSheldon Hearn 	case FILSYM:
398717a08b6SSheldon Hearn 		return S_ISLNK(s.st_mode);
399717a08b6SSheldon Hearn 	case FILSUID:
400717a08b6SSheldon Hearn 		return (s.st_mode & S_ISUID) != 0;
401717a08b6SSheldon Hearn 	case FILSGID:
402717a08b6SSheldon Hearn 		return (s.st_mode & S_ISGID) != 0;
403717a08b6SSheldon Hearn 	case FILSTCK:
404717a08b6SSheldon Hearn 		return (s.st_mode & S_ISVTX) != 0;
405717a08b6SSheldon Hearn 	case FILGZ:
406717a08b6SSheldon Hearn 		return s.st_size > (off_t)0;
407717a08b6SSheldon Hearn 	case FILUID:
408717a08b6SSheldon Hearn 		return s.st_uid == geteuid();
409717a08b6SSheldon Hearn 	case FILGID:
410717a08b6SSheldon Hearn 		return s.st_gid == getegid();
411717a08b6SSheldon Hearn 	default:
412717a08b6SSheldon Hearn 		return 1;
413717a08b6SSheldon Hearn 	}
414717a08b6SSheldon Hearn }
415717a08b6SSheldon Hearn 
416f1602fa5SJilles Tjoelker static int
find_op_1char(const struct t_op * op,const struct t_op * end,const char * s)417f1602fa5SJilles Tjoelker find_op_1char(const struct t_op *op, const struct t_op *end, const char *s)
418717a08b6SSheldon Hearn {
419f1602fa5SJilles Tjoelker 	char c;
420717a08b6SSheldon Hearn 
421f1602fa5SJilles Tjoelker 	c = s[0];
422f1602fa5SJilles Tjoelker 	while (op != end) {
423f1602fa5SJilles Tjoelker 		if (c == *op->op_text)
424717a08b6SSheldon Hearn 			return op->op_num;
425717a08b6SSheldon Hearn 		op++;
426717a08b6SSheldon Hearn 	}
427717a08b6SSheldon Hearn 	return OPERAND;
4284b88c807SRodney W. Grimes }
4294b88c807SRodney W. Grimes 
4304b88c807SRodney W. Grimes static int
find_op_2char(const struct t_op * op,const struct t_op * end,const char * s)431f1602fa5SJilles Tjoelker find_op_2char(const struct t_op *op, const struct t_op *end, const char *s)
432f1602fa5SJilles Tjoelker {
433f1602fa5SJilles Tjoelker 	while (op != end) {
434f1602fa5SJilles Tjoelker 		if (s[0] == op->op_text[0] && s[1] == op->op_text[1])
435f1602fa5SJilles Tjoelker 			return op->op_num;
436f1602fa5SJilles Tjoelker 		op++;
437f1602fa5SJilles Tjoelker 	}
438f1602fa5SJilles Tjoelker 	return OPERAND;
439f1602fa5SJilles Tjoelker }
440f1602fa5SJilles Tjoelker 
441f1602fa5SJilles Tjoelker static int
find_op(const char * s)442f1602fa5SJilles Tjoelker find_op(const char *s)
443f1602fa5SJilles Tjoelker {
444f1602fa5SJilles Tjoelker 	if (s[0] == '\0')
445f1602fa5SJilles Tjoelker 		return OPERAND;
446f1602fa5SJilles Tjoelker 	else if (s[1] == '\0')
447f1602fa5SJilles Tjoelker 		return find_op_1char(ops1, (&ops1)[1], s);
448f1602fa5SJilles Tjoelker 	else if (s[2] == '\0')
449f1602fa5SJilles Tjoelker 		return s[0] == '-' ? find_op_1char(opsm1, (&opsm1)[1], s + 1) :
450f1602fa5SJilles Tjoelker 		    find_op_2char(ops2, (&ops2)[1], s);
451f1602fa5SJilles Tjoelker 	else if (s[3] == '\0')
452f1602fa5SJilles Tjoelker 		return s[0] == '-' ? find_op_2char(opsm2, (&opsm2)[1], s + 1) :
453f1602fa5SJilles Tjoelker 		    OPERAND;
454f1602fa5SJilles Tjoelker 	else
455f1602fa5SJilles Tjoelker 		return OPERAND;
456f1602fa5SJilles Tjoelker }
457f1602fa5SJilles Tjoelker 
458f1602fa5SJilles Tjoelker static enum token
t_lex(char * s)459f1602fa5SJilles Tjoelker t_lex(char *s)
460f1602fa5SJilles Tjoelker {
461f1602fa5SJilles Tjoelker 	int num;
462f1602fa5SJilles Tjoelker 
463172c3b0bSMarcelo Araujo 	if (s == NULL) {
464f1602fa5SJilles Tjoelker 		return EOI;
465f1602fa5SJilles Tjoelker 	}
466f1602fa5SJilles Tjoelker 	num = find_op(s);
467f1602fa5SJilles Tjoelker 	if (((TOKEN_TYPE(num) == UNOP || TOKEN_TYPE(num) == BUNOP)
468f1602fa5SJilles Tjoelker 				&& isunopoperand()) ||
469f1602fa5SJilles Tjoelker 	    (num == LPAREN && islparenoperand()) ||
470f1602fa5SJilles Tjoelker 	    (num == RPAREN && isrparenoperand()))
471f1602fa5SJilles Tjoelker 		return OPERAND;
472f1602fa5SJilles Tjoelker 	return num;
473f1602fa5SJilles Tjoelker }
474f1602fa5SJilles Tjoelker 
475f1602fa5SJilles Tjoelker static int
isunopoperand(void)47600e8c94fSJilles Tjoelker isunopoperand(void)
4774b88c807SRodney W. Grimes {
478717a08b6SSheldon Hearn 	char *s;
479717a08b6SSheldon Hearn 	char *t;
480f1602fa5SJilles Tjoelker 	int num;
4814b88c807SRodney W. Grimes 
4826c62b047SMaxim Konovalov 	if (nargc == 1)
483717a08b6SSheldon Hearn 		return 1;
4846c62b047SMaxim Konovalov 	s = *(t_wp + 1);
48500e8c94fSJilles Tjoelker 	if (nargc == 2)
48600e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
4876c62b047SMaxim Konovalov 	t = *(t_wp + 2);
488f1602fa5SJilles Tjoelker 	num = find_op(s);
489f1602fa5SJilles Tjoelker 	return TOKEN_TYPE(num) == BINOP &&
49000e8c94fSJilles Tjoelker 	    (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
4914b88c807SRodney W. Grimes }
4924b88c807SRodney W. Grimes 
49300e8c94fSJilles Tjoelker static int
islparenoperand(void)49400e8c94fSJilles Tjoelker islparenoperand(void)
49500e8c94fSJilles Tjoelker {
49600e8c94fSJilles Tjoelker 	char *s;
497f1602fa5SJilles Tjoelker 	int num;
49800e8c94fSJilles Tjoelker 
49900e8c94fSJilles Tjoelker 	if (nargc == 1)
50000e8c94fSJilles Tjoelker 		return 1;
50100e8c94fSJilles Tjoelker 	s = *(t_wp + 1);
50200e8c94fSJilles Tjoelker 	if (nargc == 2)
50300e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
50400e8c94fSJilles Tjoelker 	if (nargc != 3)
50500e8c94fSJilles Tjoelker 		return 0;
506f1602fa5SJilles Tjoelker 	num = find_op(s);
507f1602fa5SJilles Tjoelker 	return TOKEN_TYPE(num) == BINOP;
50800e8c94fSJilles Tjoelker }
50900e8c94fSJilles Tjoelker 
51000e8c94fSJilles Tjoelker static int
isrparenoperand(void)51100e8c94fSJilles Tjoelker isrparenoperand(void)
51200e8c94fSJilles Tjoelker {
51300e8c94fSJilles Tjoelker 	char *s;
51400e8c94fSJilles Tjoelker 
51500e8c94fSJilles Tjoelker 	if (nargc == 1)
51600e8c94fSJilles Tjoelker 		return 0;
51700e8c94fSJilles Tjoelker 	s = *(t_wp + 1);
51800e8c94fSJilles Tjoelker 	if (nargc == 2)
51900e8c94fSJilles Tjoelker 		return parenlevel == 1 && strcmp(s, ")") == 0;
52000e8c94fSJilles Tjoelker 	return 0;
52100e8c94fSJilles Tjoelker }
52200e8c94fSJilles Tjoelker 
523717a08b6SSheldon Hearn /* atoi with error detection */
5244b88c807SRodney W. Grimes static int
getn(const char * s)5255134c3f7SWarner Losh getn(const char *s)
5264b88c807SRodney W. Grimes {
527717a08b6SSheldon Hearn 	char *p;
528717a08b6SSheldon Hearn 	long r;
5294b88c807SRodney W. Grimes 
5304b88c807SRodney W. Grimes 	errno = 0;
531717a08b6SSheldon Hearn 	r = strtol(s, &p, 10);
532717a08b6SSheldon Hearn 
5333d09cebfSAndrey A. Chernov 	if (s == p)
5343d09cebfSAndrey A. Chernov 		error("%s: bad number", s);
5353d09cebfSAndrey A. Chernov 
536717a08b6SSheldon Hearn 	if (errno != 0)
5379ea42c8eSAndrey A. Chernov 		error((errno == EINVAL) ? "%s: bad number" :
5389ea42c8eSAndrey A. Chernov 					  "%s: out of range", s);
539717a08b6SSheldon Hearn 
540717a08b6SSheldon Hearn 	while (isspace((unsigned char)*p))
541717a08b6SSheldon Hearn 		p++;
542717a08b6SSheldon Hearn 
543717a08b6SSheldon Hearn 	if (*p)
544d919a882SAkinori MUSHA 		error("%s: bad number", s);
545717a08b6SSheldon Hearn 
546717a08b6SSheldon Hearn 	return (int) r;
5474b88c807SRodney W. Grimes }
5484b88c807SRodney W. Grimes 
549de96f240SStefan Eßer /* atoi with error detection and 64 bit range */
55082ea3997SAndrey A. Chernov static intmax_t
getq(const char * s)5515134c3f7SWarner Losh getq(const char *s)
552de96f240SStefan Eßer {
553de96f240SStefan Eßer 	char *p;
55482ea3997SAndrey A. Chernov 	intmax_t r;
555de96f240SStefan Eßer 
556de96f240SStefan Eßer 	errno = 0;
55782ea3997SAndrey A. Chernov 	r = strtoimax(s, &p, 10);
558de96f240SStefan Eßer 
5593d09cebfSAndrey A. Chernov 	if (s == p)
5603d09cebfSAndrey A. Chernov 		error("%s: bad number", s);
5613d09cebfSAndrey A. Chernov 
562de96f240SStefan Eßer 	if (errno != 0)
5639ea42c8eSAndrey A. Chernov 		error((errno == EINVAL) ? "%s: bad number" :
5649ea42c8eSAndrey A. Chernov 					  "%s: out of range", s);
565de96f240SStefan Eßer 
566de96f240SStefan Eßer 	while (isspace((unsigned char)*p))
567de96f240SStefan Eßer 		p++;
568de96f240SStefan Eßer 
569de96f240SStefan Eßer 	if (*p)
570d919a882SAkinori MUSHA 		error("%s: bad number", s);
571de96f240SStefan Eßer 
572de96f240SStefan Eßer 	return r;
573de96f240SStefan Eßer }
574de96f240SStefan Eßer 
575de96f240SStefan Eßer static int
intcmp(const char * s1,const char * s2)5765134c3f7SWarner Losh intcmp (const char *s1, const char *s2)
577de96f240SStefan Eßer {
57882ea3997SAndrey A. Chernov 	intmax_t q1, q2;
579de96f240SStefan Eßer 
580de96f240SStefan Eßer 
581de96f240SStefan Eßer 	q1 = getq(s1);
582de96f240SStefan Eßer 	q2 = getq(s2);
583de96f240SStefan Eßer 
584de96f240SStefan Eßer 	if (q1 > q2)
585de96f240SStefan Eßer 		return 1;
586de96f240SStefan Eßer 
587de96f240SStefan Eßer 	if (q1 < q2)
588de96f240SStefan Eßer 		return -1;
589de96f240SStefan Eßer 
590de96f240SStefan Eßer 	return 0;
591de96f240SStefan Eßer }
592de96f240SStefan Eßer 
593717a08b6SSheldon Hearn static int
newerf(const char * f1,const char * f2)594f19825afSJilles Tjoelker newerf (const char *f1, const char *f2)
5954b88c807SRodney W. Grimes {
596717a08b6SSheldon Hearn 	struct stat b1, b2;
5974b88c807SRodney W. Grimes 
5986576952cSDavid Malone 	if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
5996576952cSDavid Malone 		return 0;
6006576952cSDavid Malone 
601f19825afSJilles Tjoelker 	if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec)
602293beebcSPeter Jeremy 		return 1;
603f19825afSJilles Tjoelker 	if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec)
604293beebcSPeter Jeremy 		return 0;
605293beebcSPeter Jeremy 
606f19825afSJilles Tjoelker        return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec);
607f19825afSJilles Tjoelker }
608f19825afSJilles Tjoelker 
609f19825afSJilles Tjoelker static int
olderf(const char * f1,const char * f2)610f19825afSJilles Tjoelker olderf (const char *f1, const char *f2)
611f19825afSJilles Tjoelker {
612f19825afSJilles Tjoelker 	return (newerf(f2, f1));
613717a08b6SSheldon Hearn }
614717a08b6SSheldon Hearn 
615717a08b6SSheldon Hearn static int
equalf(const char * f1,const char * f2)6165134c3f7SWarner Losh equalf (const char *f1, const char *f2)
617717a08b6SSheldon Hearn {
618717a08b6SSheldon Hearn 	struct stat b1, b2;
619717a08b6SSheldon Hearn 
620717a08b6SSheldon Hearn 	return (stat (f1, &b1) == 0 &&
621717a08b6SSheldon Hearn 		stat (f2, &b2) == 0 &&
622717a08b6SSheldon Hearn 		b1.st_dev == b2.st_dev &&
623717a08b6SSheldon Hearn 		b1.st_ino == b2.st_ino);
6244b88c807SRodney W. Grimes }
625