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