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