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