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