Tomato 1.28
[tomato.git] / release / src / router / busybox / coreutils / test.c
blobab7b416848a907cb00d4acf46c329974986d4e61
1 /* vi: set sw=4 ts=4: */
2 /*
3 * test implementation for busybox
5 * Copyright (c) by a whole pile of folks:
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).
17 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
19 * Original copyright notice states:
20 * "This program is in the Public Domain."
23 #include "libbb.h"
24 #include <setjmp.h>
26 /* This is a NOFORK applet. Be very careful! */
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. */
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 ")"
42 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
43 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
45 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
46 "-nt"|"-ot"|"-ef";
47 operand ::= <any legal UNIX file name>
50 #define TEST_DEBUG 0
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
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)
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"
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
173 enum token_types {
174 UNOP,
175 BINOP,
176 BUNOP,
177 BBINOP,
178 PAREN
181 struct operator_t {
182 char op_text[4];
183 unsigned char op_num, op_type;
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 */
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 },
231 #if ENABLE_FEATURE_TEST_64
232 typedef int64_t number_t;
233 #else
234 typedef int number_t;
235 #endif
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;
249 /* See test_ptr_hack.c */
250 extern struct test_statics *const test_ptr_to_statics;
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 )
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)
267 static number_t primary(enum token n);
269 static void syntax(const char *op, const char *msg) NORETURN;
270 static void syntax(const char *op, const char *msg)
272 if (op && *op) {
273 bb_error_msg("%s: %s", op, msg);
274 } else {
275 bb_error_msg("%s: %s"+4, msg);
277 longjmp(leaving, 2);
280 /* atoi with error detection */
281 //XXX: FIXME: duplicate of existing libbb function?
282 static number_t getn(const char *s)
284 char *p;
285 #if ENABLE_FEATURE_TEST_64
286 long long r;
287 #else
288 long r;
289 #endif
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
298 if (errno != 0)
299 syntax(s, "out of range");
301 if (*(skip_whitespace(p)))
302 syntax(s, "bad number");
304 return r;
307 /* UNUSED
308 static int newerf(const char *f1, const char *f2)
310 struct stat b1, b2;
312 return (stat(f1, &b1) == 0 &&
313 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
316 static int olderf(const char *f1, const char *f2)
318 struct stat b1, b2;
320 return (stat(f1, &b1) == 0 &&
321 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
324 static int equalf(const char *f1, const char *f2)
326 struct stat b1, b2;
328 return (stat(f1, &b1) == 0 &&
329 stat(f2, &b2) == 0 &&
330 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
335 static enum token check_operator(char *s)
337 static const struct operator_t no_op = {
338 .op_num = -1,
339 .op_type = -1
341 const struct operator_t *op;
343 last_operator = &no_op;
344 if (s == NULL) {
345 return EOI;
348 op = ops;
349 do {
350 if (strcmp(s, op->op_text) == 0) {
351 last_operator = op;
352 return op->op_num;
354 op++;
355 } while (op < ops + ARRAY_SIZE(ops));
357 return OPERAND;
361 static int binop(void)
363 const char *opnd1, *opnd2;
364 const struct operator_t *op;
365 number_t val1, val2;
367 opnd1 = *args;
368 check_operator(*++args);
369 op = last_operator;
371 opnd2 = *++args;
372 if (opnd2 == NULL)
373 syntax(op->op_text, "argument expected");
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;
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;
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:
405 /* if (is_file_op(op->op_num)) */
407 struct stat b1, b2;
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;
418 return 1; /* NOTREACHED */
422 static void initialize_group_array(void)
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);
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)
443 int i;
445 /* Short-circuit if possible, maybe saving a call to getgroups(). */
446 if (gid == getgid() || gid == getegid())
447 return 1;
449 if (ngroups == 0)
450 initialize_group_array();
452 /* Search through the list looking for GID. */
453 for (i = 0; i < ngroups; i++)
454 if (gid == group_array[i])
455 return 1;
457 return 0;
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)
466 struct stat st;
467 unsigned int euid = geteuid();
469 if (stat(path, &st) < 0)
470 return -1;
472 if (euid == 0) {
473 /* Root can read or write any file. */
474 if (mode != X_OK)
475 return 0;
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;
483 if (st.st_uid == euid) /* owner */
484 mode <<= 6;
485 else if (is_a_group_member(st.st_gid))
486 mode <<= 3;
488 if (st.st_mode & mode)
489 return 0;
491 return -1;
495 static int filstat(char *nm, enum token mode)
497 struct stat s;
498 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
500 if (mode == FILSYM) {
501 #ifdef S_IFLNK
502 if (lstat(nm, &s) == 0) {
503 i = S_IFLNK;
504 goto filetype;
506 #endif
507 return 0;
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;
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
539 if (mode == FILSOCK) {
540 #ifdef S_IFSOCK
541 i = S_IFSOCK;
542 #else
543 return 0;
544 #endif
546 filetype:
547 return ((s.st_mode & S_IFMT) == i);
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);
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 */
568 static number_t nexpr(enum token n)
570 number_t res;
572 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
573 if (n == UNOT) {
574 n = check_operator(*++args);
575 if (n == EOI) {
576 /* special case: [ ! ], [ a -a ! ] are valid */
577 /* IOW, "! ARG" may miss ARG */
578 unnest_msg("<nexpr:1 (!EOI)\n");
579 return 1;
581 res = !nexpr(n);
582 unnest_msg("<nexpr:%lld\n", res);
583 return res;
585 res = primary(n);
586 unnest_msg("<nexpr:%lld\n", res);
587 return res;
591 static number_t aexpr(enum token n)
593 number_t res;
595 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
596 res = nexpr(n);
597 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
598 if (check_operator(*++args) == BAND) {
599 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
600 res = aexpr(check_operator(*++args)) && res;
601 unnest_msg("<aexpr:%lld\n", res);
602 return res;
604 args--;
605 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
606 return res;
610 static number_t oexpr(enum token n)
612 number_t res;
614 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
615 res = aexpr(n);
616 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
617 if (check_operator(*++args) == BOR) {
618 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
619 res = oexpr(check_operator(*++args)) || res;
620 unnest_msg("<oexpr:%lld\n", res);
621 return res;
623 args--;
624 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
625 return res;
629 static number_t primary(enum token n)
631 #if TEST_DEBUG
632 number_t res = res; /* for compiler */
633 #else
634 number_t res;
635 #endif
636 const struct operator_t *args0_op;
638 nest_msg(">primary(%s)\n", TOKSTR[n]);
639 if (n == EOI) {
640 syntax(NULL, "argument expected");
642 if (n == LPAREN) {
643 res = oexpr(check_operator(*++args));
644 if (check_operator(*++args) != RPAREN)
645 syntax(NULL, "closing paren expected");
646 unnest_msg("<primary:%lld\n", res);
647 return res;
650 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
651 * do the same */
652 args0_op = last_operator;
653 /* last_operator = operator at args[1] */
654 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
655 if (args[2]) {
656 // coreutils also does this:
657 // if (args[3] && args[0]="-l" && args[2] is BINOP)
658 // return binop(1 /* prepended by -l */);
659 if (last_operator->op_type == BINOP)
660 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
663 /* check "is args[0] unop?" second */
664 if (args0_op->op_type == UNOP) {
665 /* unary expression */
666 if (args[1] == NULL)
667 // syntax(args0_op->op_text, "argument expected");
668 goto check_emptiness;
669 args++;
670 if (n == STREZ)
671 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
672 if (n == STRNZ)
673 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
674 if (n == FILTT)
675 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
676 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
679 /*check_operator(args[1]); - already done */
680 if (last_operator->op_type == BINOP) {
681 /* args[2] is known to be NULL, isn't it bound to fail? */
682 unnest_msg_and_return(binop(), "<primary:%lld\n");
684 check_emptiness:
685 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
689 int test_main(int argc, char **argv)
691 int res;
692 const char *arg0;
693 // bool negate = 0;
695 arg0 = bb_basename(argv[0]);
696 if (arg0[0] == '[') {
697 --argc;
698 if (!arg0[1]) { /* "[" ? */
699 if (NOT_LONE_CHAR(argv[argc], ']')) {
700 bb_error_msg("missing ]");
701 return 2;
703 } else { /* assuming "[[" */
704 if (strcmp(argv[argc], "]]") != 0) {
705 bb_error_msg("missing ]]");
706 return 2;
709 argv[argc] = NULL;
712 /* We must do DEINIT_S() prior to returning */
713 INIT_S();
715 res = setjmp(leaving);
716 if (res)
717 goto ret;
719 /* resetting ngroups is probably unnecessary. it will
720 * force a new call to getgroups(), which prevents using
721 * group data fetched during a previous call. but the
722 * only way the group data could be stale is if there's
723 * been an intervening call to setgroups(), and this
724 * isn't likely in the case of a shell. paranoia
725 * prevails...
727 ngroups = 0;
729 //argc--;
730 argv++;
732 /* Implement special cases from POSIX.2, section 4.62.4 */
733 if (!argv[0]) { /* "test" */
734 res = 1;
735 goto ret;
737 #if 0
738 // Now it's fixed in the parser and should not be needed
739 if (LONE_CHAR(argv[0], '!') && argv[1]) {
740 negate = 1;
741 //argc--;
742 argv++;
744 if (!argv[1]) { /* "test [!] arg" */
745 res = (*argv[0] == '\0');
746 goto ret;
748 if (argv[2] && !argv[3]) {
749 check_operator(argv[1]);
750 if (last_operator->op_type == BINOP) {
751 /* "test [!] arg1 <binary_op> arg2" */
752 args = argv;
753 res = (binop() == 0);
754 goto ret;
758 /* Some complex expression. Undo '!' removal */
759 if (negate) {
760 negate = 0;
761 //argc++;
762 argv--;
764 #endif
765 args = argv;
766 res = !oexpr(check_operator(*args));
768 if (*args != NULL && *++args != NULL) {
769 /* TODO: example when this happens? */
770 bb_error_msg("%s: unknown operand", *args);
771 res = 2;
773 ret:
774 DEINIT_S();
775 // return negate ? !res : res;
776 return res;