Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/bltin/test.c
Parent Directory | Revision Log
Revision 815 -
(show annotations)
(download)
Fri Apr 24 18:32:46 2009 UTC (15 years ago) by niro
File MIME type: text/plain
File size: 9338 byte(s)
Fri Apr 24 18:32:46 2009 UTC (15 years ago) by niro
File MIME type: text/plain
File size: 9338 byte(s)
-updated to klibc-1.5.15
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 | #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 | if ((getgroups(ngroups, group_array)) != ngroups) |
493 | return (0); |
494 | |
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 | } |