Deleted Added
full compact
test.c (91737) test.c (93345)
1/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */
2
3/*
4 * test(1); version 7-like -- author Erik Baalbergen
5 * modified by Eric Gisin to be used as built-in.
6 * modified by Arnold Robbins to add SVR3 compatibility
7 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8 * modified by J.T. Conklin for NetBSD.
9 *
10 * This program is in the Public Domain.
11 */
12
13#ifndef lint
14static const char rcsid[] =
1/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */
2
3/*
4 * test(1); version 7-like -- author Erik Baalbergen
5 * modified by Eric Gisin to be used as built-in.
6 * modified by Arnold Robbins to add SVR3 compatibility
7 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8 * modified by J.T. Conklin for NetBSD.
9 *
10 * This program is in the Public Domain.
11 */
12
13#ifndef lint
14static const char rcsid[] =
15 "$FreeBSD: head/bin/test/test.c 91737 2002-03-06 11:20:13Z maxim $";
15 "$FreeBSD: head/bin/test/test.c 93345 2002-03-28 16:30:42Z ache $";
16#endif /* not lint */
17
18#include <sys/types.h>
19#include <sys/stat.h>
20
21#include <ctype.h>
22#include <err.h>
23#include <errno.h>
16#endif /* not lint */
17
18#include <sys/types.h>
19#include <sys/stat.h>
20
21#include <ctype.h>
22#include <err.h>
23#include <errno.h>
24#include <inttypes.h>
24#include <limits.h>
25#include <stdarg.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <unistd.h>
30
31#ifdef SHELL
32#define main testcmd
33#include "bltin/bltin.h"
34#else
35#include <locale.h>
36
37static void error(const char *, ...) __attribute__((__noreturn__))
38 __printf0like(1, 2);
39
40static void
41error(const char *msg, ...)
42{
43 va_list ap;
44 va_start(ap, msg);
45 verrx(2, msg, ap);
46 /*NOTREACHED*/
47 va_end(ap);
48}
49#endif
50
51/* test(1) accepts the following grammar:
52 oexpr ::= aexpr | aexpr "-o" oexpr ;
53 aexpr ::= nexpr | nexpr "-a" aexpr ;
54 nexpr ::= primary | "!" primary
55 primary ::= unary-operator operand
56 | operand binary-operator operand
57 | operand
58 | "(" oexpr ")"
59 ;
60 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
61 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
62
63 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
64 "-nt"|"-ot"|"-ef";
65 operand ::= <any legal UNIX file name>
66*/
67
68enum token {
69 EOI,
70 FILRD,
71 FILWR,
72 FILEX,
73 FILEXIST,
74 FILREG,
75 FILDIR,
76 FILCDEV,
77 FILBDEV,
78 FILFIFO,
79 FILSOCK,
80 FILSYM,
81 FILGZ,
82 FILTT,
83 FILSUID,
84 FILSGID,
85 FILSTCK,
86 FILNT,
87 FILOT,
88 FILEQ,
89 FILUID,
90 FILGID,
91 STREZ,
92 STRNZ,
93 STREQ,
94 STRNE,
95 STRLT,
96 STRGT,
97 INTEQ,
98 INTNE,
99 INTGE,
100 INTGT,
101 INTLE,
102 INTLT,
103 UNOT,
104 BAND,
105 BOR,
106 LPAREN,
107 RPAREN,
108 OPERAND
109};
110
111enum token_types {
112 UNOP,
113 BINOP,
114 BUNOP,
115 BBINOP,
116 PAREN
117};
118
119struct t_op {
120 const char *op_text;
121 short op_num, op_type;
122} const ops [] = {
123 {"-r", FILRD, UNOP},
124 {"-w", FILWR, UNOP},
125 {"-x", FILEX, UNOP},
126 {"-e", FILEXIST,UNOP},
127 {"-f", FILREG, UNOP},
128 {"-d", FILDIR, UNOP},
129 {"-c", FILCDEV,UNOP},
130 {"-b", FILBDEV,UNOP},
131 {"-p", FILFIFO,UNOP},
132 {"-u", FILSUID,UNOP},
133 {"-g", FILSGID,UNOP},
134 {"-k", FILSTCK,UNOP},
135 {"-s", FILGZ, UNOP},
136 {"-t", FILTT, UNOP},
137 {"-z", STREZ, UNOP},
138 {"-n", STRNZ, UNOP},
139 {"-h", FILSYM, UNOP}, /* for backwards compat */
140 {"-O", FILUID, UNOP},
141 {"-G", FILGID, UNOP},
142 {"-L", FILSYM, UNOP},
143 {"-S", FILSOCK,UNOP},
144 {"=", STREQ, BINOP},
145 {"!=", STRNE, BINOP},
146 {"<", STRLT, BINOP},
147 {">", STRGT, BINOP},
148 {"-eq", INTEQ, BINOP},
149 {"-ne", INTNE, BINOP},
150 {"-ge", INTGE, BINOP},
151 {"-gt", INTGT, BINOP},
152 {"-le", INTLE, BINOP},
153 {"-lt", INTLT, BINOP},
154 {"-nt", FILNT, BINOP},
155 {"-ot", FILOT, BINOP},
156 {"-ef", FILEQ, BINOP},
157 {"!", UNOT, BUNOP},
158 {"-a", BAND, BBINOP},
159 {"-o", BOR, BBINOP},
160 {"(", LPAREN, PAREN},
161 {")", RPAREN, PAREN},
162 {0, 0, 0}
163};
164
165struct t_op const *t_wp_op;
166char **t_wp;
167
168static int aexpr(enum token);
169static int binop(void);
170static int equalf(const char *, const char *);
171static int filstat(char *, enum token);
172static int getn(const char *);
25#include <limits.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#ifdef SHELL
33#define main testcmd
34#include "bltin/bltin.h"
35#else
36#include <locale.h>
37
38static void error(const char *, ...) __attribute__((__noreturn__))
39 __printf0like(1, 2);
40
41static void
42error(const char *msg, ...)
43{
44 va_list ap;
45 va_start(ap, msg);
46 verrx(2, msg, ap);
47 /*NOTREACHED*/
48 va_end(ap);
49}
50#endif
51
52/* test(1) accepts the following grammar:
53 oexpr ::= aexpr | aexpr "-o" oexpr ;
54 aexpr ::= nexpr | nexpr "-a" aexpr ;
55 nexpr ::= primary | "!" primary
56 primary ::= unary-operator operand
57 | operand binary-operator operand
58 | operand
59 | "(" oexpr ")"
60 ;
61 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
62 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
63
64 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
65 "-nt"|"-ot"|"-ef";
66 operand ::= <any legal UNIX file name>
67*/
68
69enum token {
70 EOI,
71 FILRD,
72 FILWR,
73 FILEX,
74 FILEXIST,
75 FILREG,
76 FILDIR,
77 FILCDEV,
78 FILBDEV,
79 FILFIFO,
80 FILSOCK,
81 FILSYM,
82 FILGZ,
83 FILTT,
84 FILSUID,
85 FILSGID,
86 FILSTCK,
87 FILNT,
88 FILOT,
89 FILEQ,
90 FILUID,
91 FILGID,
92 STREZ,
93 STRNZ,
94 STREQ,
95 STRNE,
96 STRLT,
97 STRGT,
98 INTEQ,
99 INTNE,
100 INTGE,
101 INTGT,
102 INTLE,
103 INTLT,
104 UNOT,
105 BAND,
106 BOR,
107 LPAREN,
108 RPAREN,
109 OPERAND
110};
111
112enum token_types {
113 UNOP,
114 BINOP,
115 BUNOP,
116 BBINOP,
117 PAREN
118};
119
120struct t_op {
121 const char *op_text;
122 short op_num, op_type;
123} const ops [] = {
124 {"-r", FILRD, UNOP},
125 {"-w", FILWR, UNOP},
126 {"-x", FILEX, UNOP},
127 {"-e", FILEXIST,UNOP},
128 {"-f", FILREG, UNOP},
129 {"-d", FILDIR, UNOP},
130 {"-c", FILCDEV,UNOP},
131 {"-b", FILBDEV,UNOP},
132 {"-p", FILFIFO,UNOP},
133 {"-u", FILSUID,UNOP},
134 {"-g", FILSGID,UNOP},
135 {"-k", FILSTCK,UNOP},
136 {"-s", FILGZ, UNOP},
137 {"-t", FILTT, UNOP},
138 {"-z", STREZ, UNOP},
139 {"-n", STRNZ, UNOP},
140 {"-h", FILSYM, UNOP}, /* for backwards compat */
141 {"-O", FILUID, UNOP},
142 {"-G", FILGID, UNOP},
143 {"-L", FILSYM, UNOP},
144 {"-S", FILSOCK,UNOP},
145 {"=", STREQ, BINOP},
146 {"!=", STRNE, BINOP},
147 {"<", STRLT, BINOP},
148 {">", STRGT, BINOP},
149 {"-eq", INTEQ, BINOP},
150 {"-ne", INTNE, BINOP},
151 {"-ge", INTGE, BINOP},
152 {"-gt", INTGT, BINOP},
153 {"-le", INTLE, BINOP},
154 {"-lt", INTLT, BINOP},
155 {"-nt", FILNT, BINOP},
156 {"-ot", FILOT, BINOP},
157 {"-ef", FILEQ, BINOP},
158 {"!", UNOT, BUNOP},
159 {"-a", BAND, BBINOP},
160 {"-o", BOR, BBINOP},
161 {"(", LPAREN, PAREN},
162 {")", RPAREN, PAREN},
163 {0, 0, 0}
164};
165
166struct t_op const *t_wp_op;
167char **t_wp;
168
169static int aexpr(enum token);
170static int binop(void);
171static int equalf(const char *, const char *);
172static int filstat(char *, enum token);
173static int getn(const char *);
173static long long getq(const char *);
174static intmax_t getq(const char *);
174static int intcmp(const char *, const char *);
175static int isoperand(void);
176static int newerf(const char *, const char *);
177static int nexpr(enum token);
178static int oexpr(enum token);
179static int olderf(const char *, const char *);
180static int primary(enum token);
181static void syntax(const char *, const char *);
182static enum token t_lex(char *);
183
184int
185main(int argc, char **argv)
186{
187 int i, res;
188 char *p;
189 char **nargv;
190
191 /*
192 * XXX copy the whole contents of argv to a newly allocated
193 * space with two extra cells filled with NULL's - this source
194 * code totally depends on their presence.
195 */
196 if ((nargv = (char **)malloc((argc + 2) * sizeof(char *))) == NULL)
197 error("Out of space");
198
199 for (i = 0; i < argc; i++)
200 nargv[i] = argv[i];
201
202 nargv[i] = nargv[i + 1] = NULL;
203 argv = nargv;
204
205 if ((p = rindex(argv[0], '/')) == NULL)
206 p = argv[0];
207 else
208 p++;
209 if (strcmp(p, "[") == 0) {
210 if (strcmp(argv[--argc], "]") != 0)
211 error("missing ]");
212 argv[argc] = NULL;
213 }
214
215#ifndef SHELL
216 (void)setlocale(LC_CTYPE, "");
217#endif
218 t_wp = &argv[1];
219 res = !oexpr(t_lex(*t_wp));
220
221 if (*t_wp != NULL && *++t_wp != NULL)
222 syntax(*t_wp, "unexpected operator");
223
224 return res;
225}
226
227static void
228syntax(const char *op, const char *msg)
229{
230
231 if (op && *op)
232 error("%s: %s", op, msg);
233 else
234 error("%s", msg);
235}
236
237static int
238oexpr(enum token n)
239{
240 int res;
241
242 res = aexpr(n);
243 if (t_lex(*++t_wp) == BOR)
244 return oexpr(t_lex(*++t_wp)) || res;
245 t_wp--;
246 return res;
247}
248
249static int
250aexpr(enum token n)
251{
252 int res;
253
254 res = nexpr(n);
255 if (t_lex(*++t_wp) == BAND)
256 return aexpr(t_lex(*++t_wp)) && res;
257 t_wp--;
258 return res;
259}
260
261static int
262nexpr(enum token n)
263{
264 if (n == UNOT)
265 return !nexpr(t_lex(*++t_wp));
266 return primary(n);
267}
268
269static int
270primary(enum token n)
271{
272 enum token nn;
273 int res;
274
275 if (n == EOI)
276 return 0; /* missing expression */
277 if (n == LPAREN) {
278 if ((nn = t_lex(*++t_wp)) == RPAREN)
279 return 0; /* missing expression */
280 res = oexpr(nn);
281 if (t_lex(*++t_wp) != RPAREN)
282 syntax(NULL, "closing paren expected");
283 return res;
284 }
285 if (t_wp_op && t_wp_op->op_type == UNOP) {
286 /* unary expression */
287 if (*++t_wp == NULL)
288 syntax(t_wp_op->op_text, "argument expected");
289 switch (n) {
290 case STREZ:
291 return strlen(*t_wp) == 0;
292 case STRNZ:
293 return strlen(*t_wp) != 0;
294 case FILTT:
295 return isatty(getn(*t_wp));
296 default:
297 return filstat(*t_wp, n);
298 }
299 }
300
301 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
302 return binop();
303 }
304
305 return strlen(*t_wp) > 0;
306}
307
308static int
309binop(void)
310{
311 const char *opnd1, *opnd2;
312 struct t_op const *op;
313
314 opnd1 = *t_wp;
315 (void) t_lex(*++t_wp);
316 op = t_wp_op;
317
318 if ((opnd2 = *++t_wp) == NULL)
319 syntax(op->op_text, "argument expected");
320
321 switch (op->op_num) {
322 case STREQ:
323 return strcmp(opnd1, opnd2) == 0;
324 case STRNE:
325 return strcmp(opnd1, opnd2) != 0;
326 case STRLT:
327 return strcmp(opnd1, opnd2) < 0;
328 case STRGT:
329 return strcmp(opnd1, opnd2) > 0;
330 case INTEQ:
331 return intcmp(opnd1, opnd2) == 0;
332 case INTNE:
333 return intcmp(opnd1, opnd2) != 0;
334 case INTGE:
335 return intcmp(opnd1, opnd2) >= 0;
336 case INTGT:
337 return intcmp(opnd1, opnd2) > 0;
338 case INTLE:
339 return intcmp(opnd1, opnd2) <= 0;
340 case INTLT:
341 return intcmp(opnd1, opnd2) < 0;
342 case FILNT:
343 return newerf (opnd1, opnd2);
344 case FILOT:
345 return olderf (opnd1, opnd2);
346 case FILEQ:
347 return equalf (opnd1, opnd2);
348 default:
349 abort();
350 /* NOTREACHED */
351 }
352}
353
354static int
355filstat(char *nm, enum token mode)
356{
357 struct stat s;
358
359 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
360 return 0;
361
362 switch (mode) {
363 case FILRD:
364 return (eaccess(nm, R_OK) == 0);
365 case FILWR:
366 return (eaccess(nm, W_OK) == 0);
367 case FILEX:
368 /* XXX work around eaccess(2) false positives for superuser */
369 if (eaccess(nm, X_OK) != 0)
370 return 0;
371 if (S_ISDIR(s.st_mode) || geteuid() != 0)
372 return 1;
373 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
374 case FILEXIST:
375 return (eaccess(nm, F_OK) == 0);
376 case FILREG:
377 return S_ISREG(s.st_mode);
378 case FILDIR:
379 return S_ISDIR(s.st_mode);
380 case FILCDEV:
381 return S_ISCHR(s.st_mode);
382 case FILBDEV:
383 return S_ISBLK(s.st_mode);
384 case FILFIFO:
385 return S_ISFIFO(s.st_mode);
386 case FILSOCK:
387 return S_ISSOCK(s.st_mode);
388 case FILSYM:
389 return S_ISLNK(s.st_mode);
390 case FILSUID:
391 return (s.st_mode & S_ISUID) != 0;
392 case FILSGID:
393 return (s.st_mode & S_ISGID) != 0;
394 case FILSTCK:
395 return (s.st_mode & S_ISVTX) != 0;
396 case FILGZ:
397 return s.st_size > (off_t)0;
398 case FILUID:
399 return s.st_uid == geteuid();
400 case FILGID:
401 return s.st_gid == getegid();
402 default:
403 return 1;
404 }
405}
406
407static enum token
408t_lex(char *s)
409{
410 struct t_op const *op = ops;
411
412 if (s == 0) {
413 t_wp_op = NULL;
414 return EOI;
415 }
416 while (op->op_text) {
417 if (strcmp(s, op->op_text) == 0) {
418 if ((op->op_type == UNOP && isoperand()) ||
419 (op->op_num == LPAREN && *(t_wp+1) == 0))
420 break;
421 t_wp_op = op;
422 return op->op_num;
423 }
424 op++;
425 }
426 t_wp_op = NULL;
427 return OPERAND;
428}
429
430static int
431isoperand(void)
432{
433 struct t_op const *op = ops;
434 char *s;
435 char *t;
436
437 if ((s = *(t_wp+1)) == 0)
438 return 1;
439 if ((t = *(t_wp+2)) == 0)
440 return 0;
441 while (op->op_text) {
442 if (strcmp(s, op->op_text) == 0)
443 return op->op_type == BINOP &&
444 (t[0] != ')' || t[1] != '\0');
445 op++;
446 }
447 return 0;
448}
449
450/* atoi with error detection */
451static int
452getn(const char *s)
453{
454 char *p;
455 long r;
456
457 errno = 0;
458 r = strtol(s, &p, 10);
459
460 if (s == p)
461 error("%s: bad number", s);
462
463 if (errno != 0)
464 error((errno == EINVAL) ? "%s: bad number" :
465 "%s: out of range", s);
466
467 while (isspace((unsigned char)*p))
468 p++;
469
470 if (*p)
471 error("%s: bad number", s);
472
473 return (int) r;
474}
475
476/* atoi with error detection and 64 bit range */
175static int intcmp(const char *, const char *);
176static int isoperand(void);
177static int newerf(const char *, const char *);
178static int nexpr(enum token);
179static int oexpr(enum token);
180static int olderf(const char *, const char *);
181static int primary(enum token);
182static void syntax(const char *, const char *);
183static enum token t_lex(char *);
184
185int
186main(int argc, char **argv)
187{
188 int i, res;
189 char *p;
190 char **nargv;
191
192 /*
193 * XXX copy the whole contents of argv to a newly allocated
194 * space with two extra cells filled with NULL's - this source
195 * code totally depends on their presence.
196 */
197 if ((nargv = (char **)malloc((argc + 2) * sizeof(char *))) == NULL)
198 error("Out of space");
199
200 for (i = 0; i < argc; i++)
201 nargv[i] = argv[i];
202
203 nargv[i] = nargv[i + 1] = NULL;
204 argv = nargv;
205
206 if ((p = rindex(argv[0], '/')) == NULL)
207 p = argv[0];
208 else
209 p++;
210 if (strcmp(p, "[") == 0) {
211 if (strcmp(argv[--argc], "]") != 0)
212 error("missing ]");
213 argv[argc] = NULL;
214 }
215
216#ifndef SHELL
217 (void)setlocale(LC_CTYPE, "");
218#endif
219 t_wp = &argv[1];
220 res = !oexpr(t_lex(*t_wp));
221
222 if (*t_wp != NULL && *++t_wp != NULL)
223 syntax(*t_wp, "unexpected operator");
224
225 return res;
226}
227
228static void
229syntax(const char *op, const char *msg)
230{
231
232 if (op && *op)
233 error("%s: %s", op, msg);
234 else
235 error("%s", msg);
236}
237
238static int
239oexpr(enum token n)
240{
241 int res;
242
243 res = aexpr(n);
244 if (t_lex(*++t_wp) == BOR)
245 return oexpr(t_lex(*++t_wp)) || res;
246 t_wp--;
247 return res;
248}
249
250static int
251aexpr(enum token n)
252{
253 int res;
254
255 res = nexpr(n);
256 if (t_lex(*++t_wp) == BAND)
257 return aexpr(t_lex(*++t_wp)) && res;
258 t_wp--;
259 return res;
260}
261
262static int
263nexpr(enum token n)
264{
265 if (n == UNOT)
266 return !nexpr(t_lex(*++t_wp));
267 return primary(n);
268}
269
270static int
271primary(enum token n)
272{
273 enum token nn;
274 int res;
275
276 if (n == EOI)
277 return 0; /* missing expression */
278 if (n == LPAREN) {
279 if ((nn = t_lex(*++t_wp)) == RPAREN)
280 return 0; /* missing expression */
281 res = oexpr(nn);
282 if (t_lex(*++t_wp) != RPAREN)
283 syntax(NULL, "closing paren expected");
284 return res;
285 }
286 if (t_wp_op && t_wp_op->op_type == UNOP) {
287 /* unary expression */
288 if (*++t_wp == NULL)
289 syntax(t_wp_op->op_text, "argument expected");
290 switch (n) {
291 case STREZ:
292 return strlen(*t_wp) == 0;
293 case STRNZ:
294 return strlen(*t_wp) != 0;
295 case FILTT:
296 return isatty(getn(*t_wp));
297 default:
298 return filstat(*t_wp, n);
299 }
300 }
301
302 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
303 return binop();
304 }
305
306 return strlen(*t_wp) > 0;
307}
308
309static int
310binop(void)
311{
312 const char *opnd1, *opnd2;
313 struct t_op const *op;
314
315 opnd1 = *t_wp;
316 (void) t_lex(*++t_wp);
317 op = t_wp_op;
318
319 if ((opnd2 = *++t_wp) == NULL)
320 syntax(op->op_text, "argument expected");
321
322 switch (op->op_num) {
323 case STREQ:
324 return strcmp(opnd1, opnd2) == 0;
325 case STRNE:
326 return strcmp(opnd1, opnd2) != 0;
327 case STRLT:
328 return strcmp(opnd1, opnd2) < 0;
329 case STRGT:
330 return strcmp(opnd1, opnd2) > 0;
331 case INTEQ:
332 return intcmp(opnd1, opnd2) == 0;
333 case INTNE:
334 return intcmp(opnd1, opnd2) != 0;
335 case INTGE:
336 return intcmp(opnd1, opnd2) >= 0;
337 case INTGT:
338 return intcmp(opnd1, opnd2) > 0;
339 case INTLE:
340 return intcmp(opnd1, opnd2) <= 0;
341 case INTLT:
342 return intcmp(opnd1, opnd2) < 0;
343 case FILNT:
344 return newerf (opnd1, opnd2);
345 case FILOT:
346 return olderf (opnd1, opnd2);
347 case FILEQ:
348 return equalf (opnd1, opnd2);
349 default:
350 abort();
351 /* NOTREACHED */
352 }
353}
354
355static int
356filstat(char *nm, enum token mode)
357{
358 struct stat s;
359
360 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
361 return 0;
362
363 switch (mode) {
364 case FILRD:
365 return (eaccess(nm, R_OK) == 0);
366 case FILWR:
367 return (eaccess(nm, W_OK) == 0);
368 case FILEX:
369 /* XXX work around eaccess(2) false positives for superuser */
370 if (eaccess(nm, X_OK) != 0)
371 return 0;
372 if (S_ISDIR(s.st_mode) || geteuid() != 0)
373 return 1;
374 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
375 case FILEXIST:
376 return (eaccess(nm, F_OK) == 0);
377 case FILREG:
378 return S_ISREG(s.st_mode);
379 case FILDIR:
380 return S_ISDIR(s.st_mode);
381 case FILCDEV:
382 return S_ISCHR(s.st_mode);
383 case FILBDEV:
384 return S_ISBLK(s.st_mode);
385 case FILFIFO:
386 return S_ISFIFO(s.st_mode);
387 case FILSOCK:
388 return S_ISSOCK(s.st_mode);
389 case FILSYM:
390 return S_ISLNK(s.st_mode);
391 case FILSUID:
392 return (s.st_mode & S_ISUID) != 0;
393 case FILSGID:
394 return (s.st_mode & S_ISGID) != 0;
395 case FILSTCK:
396 return (s.st_mode & S_ISVTX) != 0;
397 case FILGZ:
398 return s.st_size > (off_t)0;
399 case FILUID:
400 return s.st_uid == geteuid();
401 case FILGID:
402 return s.st_gid == getegid();
403 default:
404 return 1;
405 }
406}
407
408static enum token
409t_lex(char *s)
410{
411 struct t_op const *op = ops;
412
413 if (s == 0) {
414 t_wp_op = NULL;
415 return EOI;
416 }
417 while (op->op_text) {
418 if (strcmp(s, op->op_text) == 0) {
419 if ((op->op_type == UNOP && isoperand()) ||
420 (op->op_num == LPAREN && *(t_wp+1) == 0))
421 break;
422 t_wp_op = op;
423 return op->op_num;
424 }
425 op++;
426 }
427 t_wp_op = NULL;
428 return OPERAND;
429}
430
431static int
432isoperand(void)
433{
434 struct t_op const *op = ops;
435 char *s;
436 char *t;
437
438 if ((s = *(t_wp+1)) == 0)
439 return 1;
440 if ((t = *(t_wp+2)) == 0)
441 return 0;
442 while (op->op_text) {
443 if (strcmp(s, op->op_text) == 0)
444 return op->op_type == BINOP &&
445 (t[0] != ')' || t[1] != '\0');
446 op++;
447 }
448 return 0;
449}
450
451/* atoi with error detection */
452static int
453getn(const char *s)
454{
455 char *p;
456 long r;
457
458 errno = 0;
459 r = strtol(s, &p, 10);
460
461 if (s == p)
462 error("%s: bad number", s);
463
464 if (errno != 0)
465 error((errno == EINVAL) ? "%s: bad number" :
466 "%s: out of range", s);
467
468 while (isspace((unsigned char)*p))
469 p++;
470
471 if (*p)
472 error("%s: bad number", s);
473
474 return (int) r;
475}
476
477/* atoi with error detection and 64 bit range */
477static long long
478static intmax_t
478getq(const char *s)
479{
480 char *p;
479getq(const char *s)
480{
481 char *p;
481 long long r;
482 intmax_t r;
482
483 errno = 0;
483
484 errno = 0;
484 r = strtoll(s, &p, 10);
485 r = strtoimax(s, &p, 10);
485
486 if (s == p)
487 error("%s: bad number", s);
488
489 if (errno != 0)
490 error((errno == EINVAL) ? "%s: bad number" :
491 "%s: out of range", s);
492
493 while (isspace((unsigned char)*p))
494 p++;
495
496 if (*p)
497 error("%s: bad number", s);
498
499 return r;
500}
501
502static int
503intcmp (const char *s1, const char *s2)
504{
486
487 if (s == p)
488 error("%s: bad number", s);
489
490 if (errno != 0)
491 error((errno == EINVAL) ? "%s: bad number" :
492 "%s: out of range", s);
493
494 while (isspace((unsigned char)*p))
495 p++;
496
497 if (*p)
498 error("%s: bad number", s);
499
500 return r;
501}
502
503static int
504intcmp (const char *s1, const char *s2)
505{
505 long long q1, q2;
506 intmax_t q1, q2;
506
507
508 q1 = getq(s1);
509 q2 = getq(s2);
510
511 if (q1 > q2)
512 return 1;
513
514 if (q1 < q2)
515 return -1;
516
517 return 0;
518}
519
520static int
521newerf (const char *f1, const char *f2)
522{
523 struct stat b1, b2;
524
525 return (stat (f1, &b1) == 0 &&
526 stat (f2, &b2) == 0 &&
527 b1.st_mtime > b2.st_mtime);
528}
529
530static int
531olderf (const char *f1, const char *f2)
532{
533 struct stat b1, b2;
534
535 return (stat (f1, &b1) == 0 &&
536 stat (f2, &b2) == 0 &&
537 b1.st_mtime < b2.st_mtime);
538}
539
540static int
541equalf (const char *f1, const char *f2)
542{
543 struct stat b1, b2;
544
545 return (stat (f1, &b1) == 0 &&
546 stat (f2, &b2) == 0 &&
547 b1.st_dev == b2.st_dev &&
548 b1.st_ino == b2.st_ino);
549}
507
508
509 q1 = getq(s1);
510 q2 = getq(s2);
511
512 if (q1 > q2)
513 return 1;
514
515 if (q1 < q2)
516 return -1;
517
518 return 0;
519}
520
521static int
522newerf (const char *f1, const char *f2)
523{
524 struct stat b1, b2;
525
526 return (stat (f1, &b1) == 0 &&
527 stat (f2, &b2) == 0 &&
528 b1.st_mtime > b2.st_mtime);
529}
530
531static int
532olderf (const char *f1, const char *f2)
533{
534 struct stat b1, b2;
535
536 return (stat (f1, &b1) == 0 &&
537 stat (f2, &b2) == 0 &&
538 b1.st_mtime < b2.st_mtime);
539}
540
541static int
542equalf (const char *f1, const char *f2)
543{
544 struct stat b1, b2;
545
546 return (stat (f1, &b1) == 0 &&
547 stat (f2, &b2) == 0 &&
548 b1.st_dev == b2.st_dev &&
549 b1.st_ino == b2.st_ino);
550}