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