test.c revision 247274
1253883Ssjg/*	$NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $	*/
2236769Sobrien
3236769Sobrien/*-
4236769Sobrien * test(1); version 7-like  --  author Erik Baalbergen
5236769Sobrien * modified by Eric Gisin to be used as built-in.
6236769Sobrien * modified by Arnold Robbins to add SVR3 compatibility
7236769Sobrien * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8236769Sobrien * modified by J.T. Conklin for NetBSD.
9236769Sobrien *
10236769Sobrien * This program is in the Public Domain.
11236769Sobrien */
12236769Sobrien/*
13236769Sobrien * Important: This file is used both as a standalone program /bin/test and
14236769Sobrien * as a builtin for /bin/sh (#define SHELL).
15236769Sobrien */
16236769Sobrien
17236769Sobrien#include <sys/cdefs.h>
18236769Sobrien__FBSDID("$FreeBSD: head/bin/test/test.c 247274 2013-02-25 19:05:40Z peterj $");
19236769Sobrien
20236769Sobrien#include <sys/types.h>
21236769Sobrien#include <sys/stat.h>
22236769Sobrien
23236769Sobrien#include <ctype.h>
24236769Sobrien#include <err.h>
25236769Sobrien#include <errno.h>
26236769Sobrien#include <inttypes.h>
27236769Sobrien#include <limits.h>
28236769Sobrien#include <stdarg.h>
29236769Sobrien#include <stdio.h>
30236769Sobrien#include <stdlib.h>
31236769Sobrien#include <string.h>
32236769Sobrien#include <unistd.h>
33236769Sobrien
34236769Sobrien#ifdef SHELL
35236769Sobrien#define main testcmd
36236769Sobrien#include "bltin/bltin.h"
37236769Sobrien#else
38236769Sobrien#include <locale.h>
39236769Sobrien
40236769Sobrienstatic void error(const char *, ...) __dead2 __printf0like(1, 2);
41236769Sobrien
42236769Sobrienstatic void
43236769Sobrienerror(const char *msg, ...)
44236769Sobrien{
45236769Sobrien	va_list ap;
46236769Sobrien	va_start(ap, msg);
47236769Sobrien	verrx(2, msg, ap);
48236769Sobrien	/*NOTREACHED*/
49236769Sobrien	va_end(ap);
50236769Sobrien}
51236769Sobrien#endif
52236769Sobrien
53236769Sobrien/* test(1) accepts the following grammar:
54236769Sobrien	oexpr	::= aexpr | aexpr "-o" oexpr ;
55236769Sobrien	aexpr	::= nexpr | nexpr "-a" aexpr ;
56236769Sobrien	nexpr	::= primary | "!" primary
57236769Sobrien	primary	::= unary-operator operand
58236769Sobrien		| operand binary-operator operand
59236769Sobrien		| operand
60236769Sobrien		| "(" oexpr ")"
61236769Sobrien		;
62236769Sobrien	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
63236769Sobrien		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
64236769Sobrien
65236769Sobrien	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
66236769Sobrien			"-nt"|"-nt[abcm][abcm]"|"-ot"|"-ot[abcm][abcm])"|"-ef";
67236769Sobrien	operand ::= <any legal UNIX file name>
68236769Sobrien*/
69236769Sobrien
70236769Sobrienenum token {
71236769Sobrien	EOI,
72236769Sobrien	FILRD,
73236769Sobrien	FILWR,
74236769Sobrien	FILEX,
75236769Sobrien	FILEXIST,
76236769Sobrien	FILREG,
77236769Sobrien	FILDIR,
78236769Sobrien	FILCDEV,
79236769Sobrien	FILBDEV,
80236769Sobrien	FILFIFO,
81236769Sobrien	FILSOCK,
82236769Sobrien	FILSYM,
83236769Sobrien	FILGZ,
84236769Sobrien	FILTT,
85236769Sobrien	FILSUID,
86236769Sobrien	FILSGID,
87236769Sobrien	FILSTCK,
88236769Sobrien	FILNTAA,
89236769Sobrien	FILNTAB,
90236769Sobrien	FILNTAC,
91236769Sobrien	FILNTAM,
92236769Sobrien	FILNTBA,
93236769Sobrien	FILNTBB,
94236769Sobrien	FILNTBC,
95236769Sobrien	FILNTBM,
96236769Sobrien	FILNTCA,
97236769Sobrien	FILNTCB,
98236769Sobrien	FILNTCC,
99236769Sobrien	FILNTCM,
100236769Sobrien	FILNTMA,
101236769Sobrien	FILNTMB,
102237578Sobrien	FILNTMC,
103236769Sobrien	FILNTMM,
104236769Sobrien	FILOTAA,
105236769Sobrien	FILOTAB,
106249033Ssjg	FILOTAC,
107236769Sobrien	FILOTAM,
108236769Sobrien	FILOTBA,
109237578Sobrien	FILOTBB,
110237578Sobrien	FILOTBC,
111236769Sobrien	FILOTBM,
112237578Sobrien	FILOTCA,
113236769Sobrien	FILOTCB,
114237578Sobrien	FILOTCC,
115237578Sobrien	FILOTCM,
116237578Sobrien	FILOTMA,
117237578Sobrien	FILOTMB,
118237578Sobrien	FILOTMC,
119237578Sobrien	FILOTMM,
120237578Sobrien	FILEQ,
121236769Sobrien	FILUID,
122236769Sobrien	FILGID,
123237578Sobrien	STREZ,
124237578Sobrien	STRNZ,
125237578Sobrien	STREQ,
126237578Sobrien	STRNE,
127237578Sobrien	STRLT,
128236769Sobrien	STRGT,
129236769Sobrien	INTEQ,
130236769Sobrien	INTNE,
131236769Sobrien	INTGE,
132236769Sobrien	INTGT,
133236769Sobrien	INTLE,
134236769Sobrien	INTLT,
135236769Sobrien	UNOT,
136236769Sobrien	BAND,
137236769Sobrien	BOR,
138236769Sobrien	LPAREN,
139236769Sobrien	RPAREN,
140236769Sobrien	OPERAND
141236769Sobrien};
142236769Sobrien
143236769Sobrienenum token_types {
144236769Sobrien	UNOP,
145236769Sobrien	BINOP,
146236769Sobrien	BUNOP,
147236769Sobrien	BBINOP,
148236769Sobrien	PAREN
149236769Sobrien};
150236769Sobrien
151236769Sobrienenum time_types {
152236769Sobrien	ATIME,
153236769Sobrien	BTIME,
154236769Sobrien	CTIME,
155236769Sobrien	MTIME
156236769Sobrien};
157236769Sobrien
158236769Sobrienstatic struct t_op {
159236769Sobrien	char op_text[6];
160236769Sobrien	char op_num, op_type;
161236769Sobrien} const ops [] = {
162236769Sobrien	{"-r",	FILRD,	UNOP},
163236769Sobrien	{"-w",	FILWR,	UNOP},
164236769Sobrien	{"-x",	FILEX,	UNOP},
165236769Sobrien	{"-e",	FILEXIST,UNOP},
166236769Sobrien	{"-f",	FILREG,	UNOP},
167236769Sobrien	{"-d",	FILDIR,	UNOP},
168236769Sobrien	{"-c",	FILCDEV,UNOP},
169236769Sobrien	{"-b",	FILBDEV,UNOP},
170236769Sobrien	{"-p",	FILFIFO,UNOP},
171236769Sobrien	{"-u",	FILSUID,UNOP},
172236769Sobrien	{"-g",	FILSGID,UNOP},
173236769Sobrien	{"-k",	FILSTCK,UNOP},
174236769Sobrien	{"-s",	FILGZ,	UNOP},
175236769Sobrien	{"-t",	FILTT,	UNOP},
176236769Sobrien	{"-z",	STREZ,	UNOP},
177236769Sobrien	{"-n",	STRNZ,	UNOP},
178236769Sobrien	{"-h",	FILSYM,	UNOP},		/* for backwards compat */
179236769Sobrien	{"-O",	FILUID,	UNOP},
180236769Sobrien	{"-G",	FILGID,	UNOP},
181236769Sobrien	{"-L",	FILSYM,	UNOP},
182236769Sobrien	{"-S",	FILSOCK,UNOP},
183236769Sobrien	{"=",	STREQ,	BINOP},
184236769Sobrien	{"==",	STREQ,	BINOP},
185236769Sobrien	{"!=",	STRNE,	BINOP},
186236769Sobrien	{"<",	STRLT,	BINOP},
187236769Sobrien	{">",	STRGT,	BINOP},
188236769Sobrien	{"-eq",	INTEQ,	BINOP},
189236769Sobrien	{"-ne",	INTNE,	BINOP},
190236769Sobrien	{"-ge",	INTGE,	BINOP},
191236769Sobrien	{"-gt",	INTGT,	BINOP},
192236769Sobrien	{"-le",	INTLE,	BINOP},
193236769Sobrien	{"-lt",	INTLT,	BINOP},
194236769Sobrien	{"-nt",	FILNTMM,	BINOP},
195236769Sobrien	{"-ntaa",	FILNTAA,	BINOP},
196236769Sobrien	{"-ntab",	FILNTAB,	BINOP},
197236769Sobrien	{"-ntac",	FILNTAC,	BINOP},
198236769Sobrien	{"-ntam",	FILNTAM,	BINOP},
199236769Sobrien	{"-ntba",	FILNTBA,	BINOP},
200236769Sobrien	{"-ntbb",	FILNTBB,	BINOP},
201236769Sobrien	{"-ntbc",	FILNTBC,	BINOP},
202236769Sobrien	{"-ntbm",	FILNTBM,	BINOP},
203236769Sobrien	{"-ntca",	FILNTCA,	BINOP},
204236769Sobrien	{"-ntcb",	FILNTCB,	BINOP},
205236769Sobrien	{"-ntcc",	FILNTCC,	BINOP},
206236769Sobrien	{"-ntcm",	FILNTCM,	BINOP},
207236769Sobrien	{"-ntma",	FILNTMA,	BINOP},
208236769Sobrien	{"-ntmb",	FILNTMB,	BINOP},
209236769Sobrien	{"-ntmc",	FILNTMC,	BINOP},
210236769Sobrien	{"-ntmm",	FILNTMM,	BINOP},
211236769Sobrien	{"-ot",	FILOTMM,	BINOP},
212236769Sobrien	{"-otaa",	FILOTAA,	BINOP},
213236769Sobrien	{"-otab",	FILOTBB,	BINOP},
214236769Sobrien	{"-otac",	FILOTAC,	BINOP},
215236769Sobrien	{"-otam",	FILOTAM,	BINOP},
216236769Sobrien	{"-otba",	FILOTBA,	BINOP},
217236769Sobrien	{"-otbb",	FILOTBB,	BINOP},
218236769Sobrien	{"-otbc",	FILOTBC,	BINOP},
219236769Sobrien	{"-otbm",	FILOTBM,	BINOP},
220236769Sobrien	{"-otca",	FILOTCA,	BINOP},
221236769Sobrien	{"-otcb",	FILOTCB,	BINOP},
222236769Sobrien	{"-otcc",	FILOTCC,	BINOP},
223236769Sobrien	{"-otcm",	FILOTCM,	BINOP},
224236769Sobrien	{"-otma",	FILOTMA,	BINOP},
225236769Sobrien	{"-otmb",	FILOTMB,	BINOP},
226236769Sobrien	{"-otmc",	FILOTMC,	BINOP},
227236769Sobrien	{"-otmm",	FILOTMM,	BINOP},
228236769Sobrien	{"-ef",	FILEQ,	BINOP},
229236769Sobrien	{"!",	UNOT,	BUNOP},
230236769Sobrien	{"-a",	BAND,	BBINOP},
231236769Sobrien	{"-o",	BOR,	BBINOP},
232236769Sobrien	{"(",	LPAREN,	PAREN},
233236769Sobrien	{")",	RPAREN,	PAREN},
234236769Sobrien	{"",	0,	0}
235236769Sobrien};
236236769Sobrien
237236769Sobrienstatic struct t_op const *t_wp_op;
238236769Sobrienstatic int nargc;
239236769Sobrienstatic char **t_wp;
240236769Sobrienstatic int parenlevel;
241236769Sobrien
242236769Sobrienstatic int	aexpr(enum token);
243236769Sobrienstatic int	binop(void);
244236769Sobrienstatic int	equalf(const char *, const char *);
245236769Sobrienstatic int	filstat(char *, enum token);
246236769Sobrienstatic int	getn(const char *);
247236769Sobrienstatic intmax_t	getq(const char *);
248236769Sobrienstatic int	intcmp(const char *, const char *);
249236769Sobrienstatic int	isunopoperand(void);
250236769Sobrienstatic int	islparenoperand(void);
251236769Sobrienstatic int	isrparenoperand(void);
252236769Sobrienstatic int	newerf(const char *, const char *, enum time_types,
253236769Sobrien		       enum time_types);
254236769Sobrienstatic int	nexpr(enum token);
255236769Sobrienstatic int	oexpr(enum token);
256236769Sobrienstatic int	primary(enum token);
257236769Sobrienstatic void	syntax(const char *, const char *);
258236769Sobrienstatic enum	token t_lex(char *);
259236769Sobrien
260236769Sobrienint
261236769Sobrienmain(int argc, char **argv)
262236769Sobrien{
263236769Sobrien	int	res;
264236769Sobrien	char	*p;
265236769Sobrien
266236769Sobrien	if ((p = strrchr(argv[0], '/')) == NULL)
267236769Sobrien		p = argv[0];
268236769Sobrien	else
269236769Sobrien		p++;
270236769Sobrien	if (strcmp(p, "[") == 0) {
271236769Sobrien		if (strcmp(argv[--argc], "]") != 0)
272236769Sobrien			error("missing ]");
273236769Sobrien		argv[argc] = NULL;
274236769Sobrien	}
275236769Sobrien
276236769Sobrien	/* no expression => false */
277236769Sobrien	if (--argc <= 0)
278236769Sobrien		return 1;
279236769Sobrien
280236769Sobrien#ifndef SHELL
281236769Sobrien	(void)setlocale(LC_CTYPE, "");
282236769Sobrien#endif
283236769Sobrien	nargc = argc;
284236769Sobrien	t_wp = &argv[1];
285236769Sobrien	parenlevel = 0;
286236769Sobrien	if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
287236769Sobrien		/* Things like ! "" -o x do not fit in the normal grammar. */
288236769Sobrien		--nargc;
289236769Sobrien		++t_wp;
290236769Sobrien		res = oexpr(t_lex(*t_wp));
291236769Sobrien	} else
292236769Sobrien		res = !oexpr(t_lex(*t_wp));
293236769Sobrien
294236769Sobrien	if (--nargc > 0)
295236769Sobrien		syntax(*t_wp, "unexpected operator");
296236769Sobrien
297236769Sobrien	return res;
298236769Sobrien}
299236769Sobrien
300236769Sobrienstatic void
301236769Sobriensyntax(const char *op, const char *msg)
302236769Sobrien{
303236769Sobrien
304236769Sobrien	if (op && *op)
305236769Sobrien		error("%s: %s", op, msg);
306236769Sobrien	else
307236769Sobrien		error("%s", msg);
308236769Sobrien}
309236769Sobrien
310236769Sobrienstatic int
311236769Sobrienoexpr(enum token n)
312236769Sobrien{
313236769Sobrien	int res;
314236769Sobrien
315236769Sobrien	res = aexpr(n);
316236769Sobrien	if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
317236769Sobrien		return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
318236769Sobrien		    res;
319236769Sobrien	t_wp--;
320236769Sobrien	nargc++;
321236769Sobrien	return res;
322236769Sobrien}
323236769Sobrien
324236769Sobrienstatic int
325236769Sobrienaexpr(enum token n)
326236769Sobrien{
327236769Sobrien	int res;
328236769Sobrien
329236769Sobrien	res = nexpr(n);
330236769Sobrien	if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
331236769Sobrien		return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
332236769Sobrien		    res;
333236769Sobrien	t_wp--;
334236769Sobrien	nargc++;
335236769Sobrien	return res;
336236769Sobrien}
337236769Sobrien
338236769Sobrienstatic int
339236769Sobriennexpr(enum token n)
340236769Sobrien{
341236769Sobrien	if (n == UNOT)
342236769Sobrien		return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
343236769Sobrien	return primary(n);
344236769Sobrien}
345236769Sobrien
346236769Sobrienstatic int
347236769Sobrienprimary(enum token n)
348236769Sobrien{
349236769Sobrien	enum token nn;
350236769Sobrien	int res;
351236769Sobrien
352236769Sobrien	if (n == EOI)
353236769Sobrien		return 0;		/* missing expression */
354236769Sobrien	if (n == LPAREN) {
355236769Sobrien		parenlevel++;
356236769Sobrien		if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
357236769Sobrien		    RPAREN) {
358236769Sobrien			parenlevel--;
359236769Sobrien			return 0;	/* missing expression */
360236769Sobrien		}
361236769Sobrien		res = oexpr(nn);
362236769Sobrien		if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
363236769Sobrien			syntax(NULL, "closing paren expected");
364236769Sobrien		parenlevel--;
365236769Sobrien		return res;
366236769Sobrien	}
367236769Sobrien	if (t_wp_op && t_wp_op->op_type == UNOP) {
368236769Sobrien		/* unary expression */
369236769Sobrien		if (--nargc == 0)
370236769Sobrien			syntax(t_wp_op->op_text, "argument expected");
371236769Sobrien		switch (n) {
372236769Sobrien		case STREZ:
373236769Sobrien			return strlen(*++t_wp) == 0;
374236769Sobrien		case STRNZ:
375236769Sobrien			return strlen(*++t_wp) != 0;
376236769Sobrien		case FILTT:
377236769Sobrien			return isatty(getn(*++t_wp));
378236769Sobrien		default:
379236769Sobrien			return filstat(*++t_wp, n);
380236769Sobrien		}
381236769Sobrien	}
382236769Sobrien
383236769Sobrien	if (t_lex(nargc > 0 ? t_wp[1] : NULL), t_wp_op && t_wp_op->op_type ==
384236769Sobrien	    BINOP) {
385236769Sobrien		return binop();
386236769Sobrien	}
387236769Sobrien
388236769Sobrien	return strlen(*t_wp) > 0;
389236769Sobrien}
390236769Sobrien
391236769Sobrienstatic int
392236769Sobrienbinop(void)
393236769Sobrien{
394236769Sobrien	const char *opnd1, *opnd2;
395236769Sobrien	struct t_op const *op;
396236769Sobrien
397236769Sobrien	opnd1 = *t_wp;
398236769Sobrien	(void) t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL);
399236769Sobrien	op = t_wp_op;
400236769Sobrien
401236769Sobrien	if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
402236769Sobrien		syntax(op->op_text, "argument expected");
403236769Sobrien
404236769Sobrien	switch (op->op_num) {
405236769Sobrien	case STREQ:
406236769Sobrien		return strcmp(opnd1, opnd2) == 0;
407236769Sobrien	case STRNE:
408236769Sobrien		return strcmp(opnd1, opnd2) != 0;
409236769Sobrien	case STRLT:
410236769Sobrien		return strcmp(opnd1, opnd2) < 0;
411236769Sobrien	case STRGT:
412236769Sobrien		return strcmp(opnd1, opnd2) > 0;
413236769Sobrien	case INTEQ:
414236769Sobrien		return intcmp(opnd1, opnd2) == 0;
415236769Sobrien	case INTNE:
416236769Sobrien		return intcmp(opnd1, opnd2) != 0;
417236769Sobrien	case INTGE:
418236769Sobrien		return intcmp(opnd1, opnd2) >= 0;
419236769Sobrien	case INTGT:
420236769Sobrien		return intcmp(opnd1, opnd2) > 0;
421236769Sobrien	case INTLE:
422236769Sobrien		return intcmp(opnd1, opnd2) <= 0;
423236769Sobrien	case INTLT:
424236769Sobrien		return intcmp(opnd1, opnd2) < 0;
425236769Sobrien	case FILNTAA:
426236769Sobrien		return newerf(opnd1, opnd2, ATIME, ATIME);
427236769Sobrien	case FILNTAB:
428236769Sobrien		return newerf(opnd1, opnd2, ATIME, BTIME);
429236769Sobrien	case FILNTAC:
430236769Sobrien		return newerf(opnd1, opnd2, ATIME, CTIME);
431236769Sobrien	case FILNTAM:
432236769Sobrien		return newerf(opnd1, opnd2, ATIME, MTIME);
433236769Sobrien	case FILNTBA:
434236769Sobrien		return newerf(opnd1, opnd2, BTIME, ATIME);
435236769Sobrien	case FILNTBB:
436236769Sobrien		return newerf(opnd1, opnd2, BTIME, BTIME);
437236769Sobrien	case FILNTBC:
438236769Sobrien		return newerf(opnd1, opnd2, BTIME, CTIME);
439236769Sobrien	case FILNTBM:
440236769Sobrien		return newerf(opnd1, opnd2, BTIME, MTIME);
441236769Sobrien	case FILNTCA:
442236769Sobrien		return newerf(opnd1, opnd2, CTIME, ATIME);
443236769Sobrien	case FILNTCB:
444236769Sobrien		return newerf(opnd1, opnd2, CTIME, BTIME);
445253883Ssjg	case FILNTCC:
446253883Ssjg		return newerf(opnd1, opnd2, CTIME, CTIME);
447236769Sobrien	case FILNTCM:
448236769Sobrien		return newerf(opnd1, opnd2, CTIME, MTIME);
449236769Sobrien	case FILNTMA:
450236769Sobrien		return newerf(opnd1, opnd2, MTIME, ATIME);
451236769Sobrien	case FILNTMB:
452236769Sobrien		return newerf(opnd1, opnd2, MTIME, BTIME);
453236769Sobrien	case FILNTMC:
454236769Sobrien		return newerf(opnd1, opnd2, MTIME, CTIME);
455236769Sobrien	case FILNTMM:
456236769Sobrien		return newerf(opnd1, opnd2, MTIME, MTIME);
457236769Sobrien	case FILOTAA:
458236769Sobrien		return newerf(opnd2, opnd1, ATIME, ATIME);
459236769Sobrien	case FILOTAB:
460236769Sobrien		return newerf(opnd2, opnd1, BTIME, ATIME);
461236769Sobrien	case FILOTAC:
462236769Sobrien		return newerf(opnd2, opnd1, CTIME, ATIME);
463236769Sobrien	case FILOTAM:
464236769Sobrien		return newerf(opnd2, opnd1, MTIME, ATIME);
465236769Sobrien	case FILOTBA:
466236769Sobrien		return newerf(opnd2, opnd1, ATIME, BTIME);
467236769Sobrien	case FILOTBB:
468236769Sobrien		return newerf(opnd2, opnd1, BTIME, BTIME);
469236769Sobrien	case FILOTBC:
470236769Sobrien		return newerf(opnd2, opnd1, CTIME, BTIME);
471236769Sobrien	case FILOTBM:
472236769Sobrien		return newerf(opnd2, opnd1, MTIME, BTIME);
473236769Sobrien	case FILOTCA:
474236769Sobrien		return newerf(opnd2, opnd1, ATIME, CTIME);
475236769Sobrien	case FILOTCB:
476236769Sobrien		return newerf(opnd2, opnd1, BTIME, CTIME);
477236769Sobrien	case FILOTCC:
478236769Sobrien		return newerf(opnd2, opnd1, CTIME, CTIME);
479236769Sobrien	case FILOTCM:
480236769Sobrien		return newerf(opnd2, opnd1, MTIME, CTIME);
481236769Sobrien	case FILOTMA:
482236769Sobrien		return newerf(opnd2, opnd1, ATIME, MTIME);
483236769Sobrien	case FILOTMB:
484236769Sobrien		return newerf(opnd2, opnd1, BTIME, MTIME);
485236769Sobrien	case FILOTMC:
486236769Sobrien		return newerf(opnd2, opnd1, CTIME, MTIME);
487236769Sobrien	case FILOTMM:
488236769Sobrien		return newerf(opnd2, opnd1, MTIME, MTIME);
489236769Sobrien	case FILEQ:
490236769Sobrien		return equalf (opnd1, opnd2);
491236769Sobrien	default:
492236769Sobrien		abort();
493236769Sobrien		/* NOTREACHED */
494236769Sobrien	}
495236769Sobrien}
496236769Sobrien
497236769Sobrienstatic int
498236769Sobrienfilstat(char *nm, enum token mode)
499236769Sobrien{
500236769Sobrien	struct stat s;
501236769Sobrien
502236769Sobrien	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
503236769Sobrien		return 0;
504236769Sobrien
505236769Sobrien	switch (mode) {
506236769Sobrien	case FILRD:
507236769Sobrien		return (eaccess(nm, R_OK) == 0);
508236769Sobrien	case FILWR:
509236769Sobrien		return (eaccess(nm, W_OK) == 0);
510236769Sobrien	case FILEX:
511236769Sobrien		/* XXX work around eaccess(2) false positives for superuser */
512236769Sobrien		if (eaccess(nm, X_OK) != 0)
513236769Sobrien			return 0;
514236769Sobrien		if (S_ISDIR(s.st_mode) || geteuid() != 0)
515236769Sobrien			return 1;
516236769Sobrien		return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
517236769Sobrien	case FILEXIST:
518		return (eaccess(nm, F_OK) == 0);
519	case FILREG:
520		return S_ISREG(s.st_mode);
521	case FILDIR:
522		return S_ISDIR(s.st_mode);
523	case FILCDEV:
524		return S_ISCHR(s.st_mode);
525	case FILBDEV:
526		return S_ISBLK(s.st_mode);
527	case FILFIFO:
528		return S_ISFIFO(s.st_mode);
529	case FILSOCK:
530		return S_ISSOCK(s.st_mode);
531	case FILSYM:
532		return S_ISLNK(s.st_mode);
533	case FILSUID:
534		return (s.st_mode & S_ISUID) != 0;
535	case FILSGID:
536		return (s.st_mode & S_ISGID) != 0;
537	case FILSTCK:
538		return (s.st_mode & S_ISVTX) != 0;
539	case FILGZ:
540		return s.st_size > (off_t)0;
541	case FILUID:
542		return s.st_uid == geteuid();
543	case FILGID:
544		return s.st_gid == getegid();
545	default:
546		return 1;
547	}
548}
549
550static enum token
551t_lex(char *s)
552{
553	struct t_op const *op = ops;
554
555	if (s == 0) {
556		t_wp_op = NULL;
557		return EOI;
558	}
559	while (*op->op_text) {
560		if (strcmp(s, op->op_text) == 0) {
561			if (((op->op_type == UNOP || op->op_type == BUNOP)
562						&& isunopoperand()) ||
563			    (op->op_num == LPAREN && islparenoperand()) ||
564			    (op->op_num == RPAREN && isrparenoperand()))
565				break;
566			t_wp_op = op;
567			return op->op_num;
568		}
569		op++;
570	}
571	t_wp_op = NULL;
572	return OPERAND;
573}
574
575static int
576isunopoperand(void)
577{
578	struct t_op const *op = ops;
579	char *s;
580	char *t;
581
582	if (nargc == 1)
583		return 1;
584	s = *(t_wp + 1);
585	if (nargc == 2)
586		return parenlevel == 1 && strcmp(s, ")") == 0;
587	t = *(t_wp + 2);
588	while (*op->op_text) {
589		if (strcmp(s, op->op_text) == 0)
590			return op->op_type == BINOP &&
591			    (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
592		op++;
593	}
594	return 0;
595}
596
597static int
598islparenoperand(void)
599{
600	struct t_op const *op = ops;
601	char *s;
602
603	if (nargc == 1)
604		return 1;
605	s = *(t_wp + 1);
606	if (nargc == 2)
607		return parenlevel == 1 && strcmp(s, ")") == 0;
608	if (nargc != 3)
609		return 0;
610	while (*op->op_text) {
611		if (strcmp(s, op->op_text) == 0)
612			return op->op_type == BINOP;
613		op++;
614	}
615	return 0;
616}
617
618static int
619isrparenoperand(void)
620{
621	char *s;
622
623	if (nargc == 1)
624		return 0;
625	s = *(t_wp + 1);
626	if (nargc == 2)
627		return parenlevel == 1 && strcmp(s, ")") == 0;
628	return 0;
629}
630
631/* atoi with error detection */
632static int
633getn(const char *s)
634{
635	char *p;
636	long r;
637
638	errno = 0;
639	r = strtol(s, &p, 10);
640
641	if (s == p)
642		error("%s: bad number", s);
643
644	if (errno != 0)
645		error((errno == EINVAL) ? "%s: bad number" :
646					  "%s: out of range", s);
647
648	while (isspace((unsigned char)*p))
649		p++;
650
651	if (*p)
652		error("%s: bad number", s);
653
654	return (int) r;
655}
656
657/* atoi with error detection and 64 bit range */
658static intmax_t
659getq(const char *s)
660{
661	char *p;
662	intmax_t r;
663
664	errno = 0;
665	r = strtoimax(s, &p, 10);
666
667	if (s == p)
668		error("%s: bad number", s);
669
670	if (errno != 0)
671		error((errno == EINVAL) ? "%s: bad number" :
672					  "%s: out of range", s);
673
674	while (isspace((unsigned char)*p))
675		p++;
676
677	if (*p)
678		error("%s: bad number", s);
679
680	return r;
681}
682
683static int
684intcmp (const char *s1, const char *s2)
685{
686	intmax_t q1, q2;
687
688
689	q1 = getq(s1);
690	q2 = getq(s2);
691
692	if (q1 > q2)
693		return 1;
694
695	if (q1 < q2)
696		return -1;
697
698	return 0;
699}
700
701static int
702newerf (const char *f1, const char *f2, enum time_types t1, enum time_types t2)
703{
704	struct stat b1, b2;
705	struct timespec *ts1, *ts2;
706
707	if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
708		return 0;
709
710	switch (t1) {
711	case ATIME:	ts1 = &b1.st_atim;	break;
712	case BTIME:	ts1 = &b1.st_birthtim;	break;
713	case CTIME:	ts1 = &b1.st_ctim;	break;
714	default:	ts1 = &b1.st_mtim;	break;
715	}
716
717	switch (t2) {
718	case ATIME:	ts2 = &b2.st_atim;	break;
719	case BTIME:	ts2 = &b2.st_birthtim;	break;
720	case CTIME:	ts2 = &b2.st_ctim;	break;
721	default:	ts2 = &b2.st_mtim;	break;
722	}
723
724	if (ts1->tv_sec > ts2->tv_sec)
725		return 1;
726	if (ts1->tv_sec < ts2->tv_sec)
727		return 0;
728
729       return (ts1->tv_nsec > ts2->tv_nsec);
730}
731
732static int
733equalf (const char *f1, const char *f2)
734{
735	struct stat b1, b2;
736
737	return (stat (f1, &b1) == 0 &&
738		stat (f2, &b2) == 0 &&
739		b1.st_dev == b2.st_dev &&
740		b1.st_ino == b2.st_ino);
741}
742