Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/coreutils/test.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 816 - (show annotations) (download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 16709 byte(s)
-updated to busybox-1.13.4
1 /* vi: set sw=4 ts=4: */
2 /*
3 * test implementation for busybox
4 *
5 * Copyright (c) by a whole pile of folks:
6 *
7 * test(1); version 7-like -- author Erik Baalbergen
8 * modified by Eric Gisin to be used as built-in.
9 * modified by Arnold Robbins to add SVR3 compatibility
10 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
11 * modified by J.T. Conklin for NetBSD.
12 * modified by Herbert Xu to be used as built-in in ash.
13 * modified by Erik Andersen <andersen@codepoet.org> to be used
14 * in busybox.
15 * modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty).
16 *
17 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
18 *
19 * Original copyright notice states:
20 * "This program is in the Public Domain."
21 */
22
23 #include "libbb.h"
24 #include <setjmp.h>
25
26 /* This is a NOFORK applet. Be very careful! */
27
28 /* test_main() is called from shells, and we need to be extra careful here.
29 * This is true regardless of PREFER_APPLETS and STANDALONE_SHELL
30 * state. */
31
32
33 /* test(1) accepts the following grammar:
34 oexpr ::= aexpr | aexpr "-o" oexpr ;
35 aexpr ::= nexpr | nexpr "-a" aexpr ;
36 nexpr ::= primary | "!" primary
37 primary ::= unary-operator operand
38 | operand binary-operator operand
39 | operand
40 | "(" oexpr ")"
41 ;
42 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
43 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
44
45 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
46 "-nt"|"-ot"|"-ef";
47 operand ::= <any legal UNIX file name>
48 */
49
50 #define TEST_DEBUG 0
51
52 enum token {
53 EOI,
54 FILRD,
55 FILWR,
56 FILEX,
57 FILEXIST,
58 FILREG,
59 FILDIR,
60 FILCDEV,
61 FILBDEV,
62 FILFIFO,
63 FILSOCK,
64 FILSYM,
65 FILGZ,
66 FILTT,
67 FILSUID,
68 FILSGID,
69 FILSTCK,
70 FILNT,
71 FILOT,
72 FILEQ,
73 FILUID,
74 FILGID,
75 STREZ,
76 STRNZ,
77 STREQ,
78 STRNE,
79 STRLT,
80 STRGT,
81 INTEQ,
82 INTNE,
83 INTGE,
84 INTGT,
85 INTLE,
86 INTLT,
87 UNOT,
88 BAND,
89 BOR,
90 LPAREN,
91 RPAREN,
92 OPERAND
93 };
94 #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
95 #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
96 #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
97 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
98 #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
99 #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
100
101 #if TEST_DEBUG
102 int depth;
103 #define nest_msg(...) do { \
104 depth++; \
105 fprintf(stderr, "%*s", depth*2, ""); \
106 fprintf(stderr, __VA_ARGS__); \
107 } while (0)
108 #define unnest_msg(...) do { \
109 fprintf(stderr, "%*s", depth*2, ""); \
110 fprintf(stderr, __VA_ARGS__); \
111 depth--; \
112 } while (0)
113 #define dbg_msg(...) do { \
114 fprintf(stderr, "%*s", depth*2, ""); \
115 fprintf(stderr, __VA_ARGS__); \
116 } while (0)
117 #define unnest_msg_and_return(expr, ...) do { \
118 number_t __res = (expr); \
119 fprintf(stderr, "%*s", depth*2, ""); \
120 fprintf(stderr, __VA_ARGS__, res); \
121 depth--; \
122 return __res; \
123 } while (0)
124 static const char *const TOKSTR[] = {
125 "EOI",
126 "FILRD",
127 "FILWR",
128 "FILEX",
129 "FILEXIST",
130 "FILREG",
131 "FILDIR",
132 "FILCDEV",
133 "FILBDEV",
134 "FILFIFO",
135 "FILSOCK",
136 "FILSYM",
137 "FILGZ",
138 "FILTT",
139 "FILSUID",
140 "FILSGID",
141 "FILSTCK",
142 "FILNT",
143 "FILOT",
144 "FILEQ",
145 "FILUID",
146 "FILGID",
147 "STREZ",
148 "STRNZ",
149 "STREQ",
150 "STRNE",
151 "STRLT",
152 "STRGT",
153 "INTEQ",
154 "INTNE",
155 "INTGE",
156 "INTGT",
157 "INTLE",
158 "INTLT",
159 "UNOT",
160 "BAND",
161 "BOR",
162 "LPAREN",
163 "RPAREN",
164 "OPERAND"
165 };
166 #else
167 #define nest_msg(...) ((void)0)
168 #define unnest_msg(...) ((void)0)
169 #define dbg_msg(...) ((void)0)
170 #define unnest_msg_and_return(expr, ...) return expr
171 #endif
172
173 enum token_types {
174 UNOP,
175 BINOP,
176 BUNOP,
177 BBINOP,
178 PAREN
179 };
180
181 struct operator_t {
182 char op_text[4];
183 unsigned char op_num, op_type;
184 };
185
186 static const struct operator_t ops[] = {
187 { "-r", FILRD , UNOP },
188 { "-w", FILWR , UNOP },
189 { "-x", FILEX , UNOP },
190 { "-e", FILEXIST, UNOP },
191 { "-f", FILREG , UNOP },
192 { "-d", FILDIR , UNOP },
193 { "-c", FILCDEV , UNOP },
194 { "-b", FILBDEV , UNOP },
195 { "-p", FILFIFO , UNOP },
196 { "-u", FILSUID , UNOP },
197 { "-g", FILSGID , UNOP },
198 { "-k", FILSTCK , UNOP },
199 { "-s", FILGZ , UNOP },
200 { "-t", FILTT , UNOP },
201 { "-z", STREZ , UNOP },
202 { "-n", STRNZ , UNOP },
203 { "-h", FILSYM , UNOP }, /* for backwards compat */
204
205 { "-O" , FILUID , UNOP },
206 { "-G" , FILGID , UNOP },
207 { "-L" , FILSYM , UNOP },
208 { "-S" , FILSOCK, UNOP },
209 { "=" , STREQ , BINOP },
210 { "==" , STREQ , BINOP },
211 { "!=" , STRNE , BINOP },
212 { "<" , STRLT , BINOP },
213 { ">" , STRGT , BINOP },
214 { "-eq", INTEQ , BINOP },
215 { "-ne", INTNE , BINOP },
216 { "-ge", INTGE , BINOP },
217 { "-gt", INTGT , BINOP },
218 { "-le", INTLE , BINOP },
219 { "-lt", INTLT , BINOP },
220 { "-nt", FILNT , BINOP },
221 { "-ot", FILOT , BINOP },
222 { "-ef", FILEQ , BINOP },
223 { "!" , UNOT , BUNOP },
224 { "-a" , BAND , BBINOP },
225 { "-o" , BOR , BBINOP },
226 { "(" , LPAREN , PAREN },
227 { ")" , RPAREN , PAREN },
228 };
229
230
231 #if ENABLE_FEATURE_TEST_64
232 typedef int64_t number_t;
233 #else
234 typedef int number_t;
235 #endif
236
237
238 /* We try to minimize both static and stack usage. */
239 struct test_statics {
240 char **args;
241 /* set only by check_operator(), either to bogus struct
242 * or points to matching operator_t struct. Never NULL. */
243 const struct operator_t *last_operator;
244 gid_t *group_array;
245 int ngroups;
246 jmp_buf leaving;
247 };
248
249 /* See test_ptr_hack.c */
250 extern struct test_statics *const test_ptr_to_statics;
251
252 #define S (*test_ptr_to_statics)
253 #define args (S.args )
254 #define last_operator (S.last_operator)
255 #define group_array (S.group_array )
256 #define ngroups (S.ngroups )
257 #define leaving (S.leaving )
258
259 #define INIT_S() do { \
260 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
261 barrier(); \
262 } while (0)
263 #define DEINIT_S() do { \
264 free(test_ptr_to_statics); \
265 } while (0)
266
267 static number_t primary(enum token n);
268
269 static void syntax(const char *op, const char *msg) NORETURN;
270 static void syntax(const char *op, const char *msg)
271 {
272 if (op && *op) {
273 bb_error_msg("%s: %s", op, msg);
274 } else {
275 bb_error_msg("%s: %s"+4, msg);
276 }
277 longjmp(leaving, 2);
278 }
279
280 /* atoi with error detection */
281 //XXX: FIXME: duplicate of existing libbb function?
282 static number_t getn(const char *s)
283 {
284 char *p;
285 #if ENABLE_FEATURE_TEST_64
286 long long r;
287 #else
288 long r;
289 #endif
290
291 errno = 0;
292 #if ENABLE_FEATURE_TEST_64
293 r = strtoll(s, &p, 10);
294 #else
295 r = strtol(s, &p, 10);
296 #endif
297
298 if (errno != 0)
299 syntax(s, "out of range");
300
301 if (*(skip_whitespace(p)))
302 syntax(s, "bad number");
303
304 return r;
305 }
306
307 /* UNUSED
308 static int newerf(const char *f1, const char *f2)
309 {
310 struct stat b1, b2;
311
312 return (stat(f1, &b1) == 0 &&
313 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
314 }
315
316 static int olderf(const char *f1, const char *f2)
317 {
318 struct stat b1, b2;
319
320 return (stat(f1, &b1) == 0 &&
321 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
322 }
323
324 static int equalf(const char *f1, const char *f2)
325 {
326 struct stat b1, b2;
327
328 return (stat(f1, &b1) == 0 &&
329 stat(f2, &b2) == 0 &&
330 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
331 }
332 */
333
334
335 static enum token check_operator(char *s)
336 {
337 static const struct operator_t no_op = {
338 .op_num = -1,
339 .op_type = -1
340 };
341 const struct operator_t *op;
342
343 last_operator = &no_op;
344 if (s == NULL) {
345 return EOI;
346 }
347
348 op = ops;
349 do {
350 if (strcmp(s, op->op_text) == 0) {
351 last_operator = op;
352 return op->op_num;
353 }
354 op++;
355 } while (op < ops + ARRAY_SIZE(ops));
356
357 return OPERAND;
358 }
359
360
361 static int binop(void)
362 {
363 const char *opnd1, *opnd2;
364 const struct operator_t *op;
365 number_t val1, val2;
366
367 opnd1 = *args;
368 check_operator(*++args);
369 op = last_operator;
370
371 opnd2 = *++args;
372 if (opnd2 == NULL)
373 syntax(op->op_text, "argument expected");
374
375 if (is_int_op(op->op_num)) {
376 val1 = getn(opnd1);
377 val2 = getn(opnd2);
378 if (op->op_num == INTEQ)
379 return val1 == val2;
380 if (op->op_num == INTNE)
381 return val1 != val2;
382 if (op->op_num == INTGE)
383 return val1 >= val2;
384 if (op->op_num == INTGT)
385 return val1 > val2;
386 if (op->op_num == INTLE)
387 return val1 <= val2;
388 if (op->op_num == INTLT)
389 return val1 < val2;
390 }
391 if (is_str_op(op->op_num)) {
392 val1 = strcmp(opnd1, opnd2);
393 if (op->op_num == STREQ)
394 return val1 == 0;
395 if (op->op_num == STRNE)
396 return val1 != 0;
397 if (op->op_num == STRLT)
398 return val1 < 0;
399 if (op->op_num == STRGT)
400 return val1 > 0;
401 }
402 /* We are sure that these three are by now the only binops we didn't check
403 * yet, so we do not check if the class is correct:
404 */
405 /* if (is_file_op(op->op_num)) */
406 {
407 struct stat b1, b2;
408
409 if (stat(opnd1, &b1) || stat(opnd2, &b2))
410 return 0; /* false, since at least one stat failed */
411 if (op->op_num == FILNT)
412 return b1.st_mtime > b2.st_mtime;
413 if (op->op_num == FILOT)
414 return b1.st_mtime < b2.st_mtime;
415 if (op->op_num == FILEQ)
416 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
417 }
418 return 1; /* NOTREACHED */
419 }
420
421
422 static void initialize_group_array(void)
423 {
424 ngroups = getgroups(0, NULL);
425 if (ngroups > 0) {
426 /* FIXME: ash tries so hard to not die on OOM,
427 * and we spoil it with just one xrealloc here */
428 /* We realloc, because test_main can be entered repeatedly by shell.
429 * Testcase (ash): 'while true; do test -x some_file; done'
430 * and watch top. (some_file must have owner != you) */
431 group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
432 getgroups(ngroups, group_array);
433 }
434 }
435
436
437 /* Return non-zero if GID is one that we have in our groups list. */
438 //XXX: FIXME: duplicate of existing libbb function?
439 // see toplevel TODO file:
440 // possible code duplication ingroup() and is_a_group_member()
441 static int is_a_group_member(gid_t gid)
442 {
443 int i;
444
445 /* Short-circuit if possible, maybe saving a call to getgroups(). */
446 if (gid == getgid() || gid == getegid())
447 return 1;
448
449 if (ngroups == 0)
450 initialize_group_array();
451
452 /* Search through the list looking for GID. */
453 for (i = 0; i < ngroups; i++)
454 if (gid == group_array[i])
455 return 1;
456
457 return 0;
458 }
459
460
461 /* Do the same thing access(2) does, but use the effective uid and gid,
462 and don't make the mistake of telling root that any file is
463 executable. */
464 static int test_eaccess(char *path, int mode)
465 {
466 struct stat st;
467 unsigned int euid = geteuid();
468
469 if (stat(path, &st) < 0)
470 return -1;
471
472 if (euid == 0) {
473 /* Root can read or write any file. */
474 if (mode != X_OK)
475 return 0;
476
477 /* Root can execute any file that has any one of the execute
478 bits set. */
479 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
480 return 0;
481 }
482
483 if (st.st_uid == euid) /* owner */
484 mode <<= 6;
485 else if (is_a_group_member(st.st_gid))
486 mode <<= 3;
487
488 if (st.st_mode & mode)
489 return 0;
490
491 return -1;
492 }
493
494
495 static int filstat(char *nm, enum token mode)
496 {
497 struct stat s;
498 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
499
500 if (mode == FILSYM) {
501 #ifdef S_IFLNK
502 if (lstat(nm, &s) == 0) {
503 i = S_IFLNK;
504 goto filetype;
505 }
506 #endif
507 return 0;
508 }
509
510 if (stat(nm, &s) != 0)
511 return 0;
512 if (mode == FILEXIST)
513 return 1;
514 if (is_file_access(mode)) {
515 if (mode == FILRD)
516 i = R_OK;
517 if (mode == FILWR)
518 i = W_OK;
519 if (mode == FILEX)
520 i = X_OK;
521 return test_eaccess(nm, i) == 0;
522 }
523 if (is_file_type(mode)) {
524 if (mode == FILREG)
525 i = S_IFREG;
526 if (mode == FILDIR)
527 i = S_IFDIR;
528 if (mode == FILCDEV)
529 i = S_IFCHR;
530 if (mode == FILBDEV)
531 i = S_IFBLK;
532 if (mode == FILFIFO) {
533 #ifdef S_IFIFO
534 i = S_IFIFO;
535 #else
536 return 0;
537 #endif
538 }
539 if (mode == FILSOCK) {
540 #ifdef S_IFSOCK
541 i = S_IFSOCK;
542 #else
543 return 0;
544 #endif
545 }
546 filetype:
547 return ((s.st_mode & S_IFMT) == i);
548 }
549 if (is_file_bit(mode)) {
550 if (mode == FILSUID)
551 i = S_ISUID;
552 if (mode == FILSGID)
553 i = S_ISGID;
554 if (mode == FILSTCK)
555 i = S_ISVTX;
556 return ((s.st_mode & i) != 0);
557 }
558 if (mode == FILGZ)
559 return s.st_size > 0L;
560 if (mode == FILUID)
561 return s.st_uid == geteuid();
562 if (mode == FILGID)
563 return s.st_gid == getegid();
564 return 1; /* NOTREACHED */
565 }
566
567
568 static number_t nexpr(enum token n)
569 {
570 number_t res;
571
572 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
573 if (n == UNOT) {
574 res = !nexpr(check_operator(*++args));
575 unnest_msg("<nexpr:%lld\n", res);
576 return res;
577 }
578 res = primary(n);
579 unnest_msg("<nexpr:%lld\n", res);
580 return res;
581 }
582
583
584 static number_t aexpr(enum token n)
585 {
586 number_t res;
587
588 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
589 res = nexpr(n);
590 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
591 if (check_operator(*++args) == BAND) {
592 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
593 res = aexpr(check_operator(*++args)) && res;
594 unnest_msg("<aexpr:%lld\n", res);
595 return res;
596 }
597 args--;
598 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
599 return res;
600 }
601
602
603 static number_t oexpr(enum token n)
604 {
605 number_t res;
606
607 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
608 res = aexpr(n);
609 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
610 if (check_operator(*++args) == BOR) {
611 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
612 res = oexpr(check_operator(*++args)) || res;
613 unnest_msg("<oexpr:%lld\n", res);
614 return res;
615 }
616 args--;
617 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
618 return res;
619 }
620
621
622 static number_t primary(enum token n)
623 {
624 #if TEST_DEBUG
625 number_t res = res; /* for compiler */
626 #else
627 number_t res;
628 #endif
629 const struct operator_t *args0_op;
630
631 nest_msg(">primary(%s)\n", TOKSTR[n]);
632 if (n == EOI) {
633 syntax(NULL, "argument expected");
634 }
635 if (n == LPAREN) {
636 res = oexpr(check_operator(*++args));
637 if (check_operator(*++args) != RPAREN)
638 syntax(NULL, "closing paren expected");
639 unnest_msg("<primary:%lld\n", res);
640 return res;
641 }
642
643 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
644 * do the same */
645 args0_op = last_operator;
646 /* last_operator = operator at args[1] */
647 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
648 if (args[2]) {
649 // coreutils also does this:
650 // if (args[3] && args[0]="-l" && args[2] is BINOP)
651 // return binop(1 /* prepended by -l */);
652 if (last_operator->op_type == BINOP)
653 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
654 }
655 }
656 /* check "is args[0] unop?" second */
657 if (args0_op->op_type == UNOP) {
658 /* unary expression */
659 if (args[1] == NULL)
660 // syntax(args0_op->op_text, "argument expected");
661 goto check_emptiness;
662 args++;
663 if (n == STREZ)
664 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
665 if (n == STRNZ)
666 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
667 if (n == FILTT)
668 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
669 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
670 }
671
672 /*check_operator(args[1]); - already done */
673 if (last_operator->op_type == BINOP) {
674 /* args[2] is known to be NULL, isn't it bound to fail? */
675 unnest_msg_and_return(binop(), "<primary:%lld\n");
676 }
677 check_emptiness:
678 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
679 }
680
681
682 int test_main(int argc, char **argv)
683 {
684 int res;
685 const char *arg0;
686 // bool negate = 0;
687
688 arg0 = bb_basename(argv[0]);
689 if (arg0[0] == '[') {
690 --argc;
691 if (!arg0[1]) { /* "[" ? */
692 if (NOT_LONE_CHAR(argv[argc], ']')) {
693 bb_error_msg("missing ]");
694 return 2;
695 }
696 } else { /* assuming "[[" */
697 if (strcmp(argv[argc], "]]") != 0) {
698 bb_error_msg("missing ]]");
699 return 2;
700 }
701 }
702 argv[argc] = NULL;
703 }
704
705 /* We must do DEINIT_S() prior to returning */
706 INIT_S();
707
708 res = setjmp(leaving);
709 if (res)
710 goto ret;
711
712 /* resetting ngroups is probably unnecessary. it will
713 * force a new call to getgroups(), which prevents using
714 * group data fetched during a previous call. but the
715 * only way the group data could be stale is if there's
716 * been an intervening call to setgroups(), and this
717 * isn't likely in the case of a shell. paranoia
718 * prevails...
719 */
720 ngroups = 0;
721
722 //argc--;
723 argv++;
724
725 /* Implement special cases from POSIX.2, section 4.62.4 */
726 if (!argv[0]) { /* "test" */
727 res = 1;
728 goto ret;
729 }
730 #if 0
731 // Now it's fixed in the parser and should not be needed
732 if (LONE_CHAR(argv[0], '!') && argv[1]) {
733 negate = 1;
734 //argc--;
735 argv++;
736 }
737 if (!argv[1]) { /* "test [!] arg" */
738 res = (*argv[0] == '\0');
739 goto ret;
740 }
741 if (argv[2] && !argv[3]) {
742 check_operator(argv[1]);
743 if (last_operator->op_type == BINOP) {
744 /* "test [!] arg1 <binary_op> arg2" */
745 args = &argv[0];
746 res = (binop() == 0);
747 goto ret;
748 }
749 }
750
751 /* Some complex expression. Undo '!' removal */
752 if (negate) {
753 negate = 0;
754 //argc++;
755 argv--;
756 }
757 #endif
758 args = &argv[0];
759 res = !oexpr(check_operator(*args));
760
761 if (*args != NULL && *++args != NULL) {
762 /* TODO: example when this happens? */
763 bb_error_msg("%s: unknown operand", *args);
764 res = 2;
765 }
766 ret:
767 DEINIT_S();
768 // return negate ? !res : res;
769 return res;
770 }