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