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