1 /* vi: set sw=4 ts=4: */
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
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 source tree.
19 * Original copyright notice states:
20 * "This program is in the Public Domain."
23 //kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
24 //kbuild:lib-$(CONFIG_ASH) += test.o test_ptr_hack.o
25 //kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o
31 //config: test is used to check file types and compare values,
32 //config: returning an appropriate exit code. The bash shell
33 //config: has test built in, ash can build it in optionally.
35 //config:config FEATURE_TEST_64
36 //config: bool "Extend test to 64 bit"
38 //config: depends on TEST || ASH_BUILTIN_TEST || HUSH
40 //config: Enable 64-bit support in test.
42 /* "test --help" does not print help (POSIX compat), only "[ --help" does.
43 * We display "<applet> EXPRESSION ]" here (not "<applet> EXPRESSION")
44 * Unfortunately, it screws up generated BusyBox.html. TODO. */
45 //usage:#define test_trivial_usage
46 //usage: "EXPRESSION ]"
47 //usage:#define test_full_usage "\n\n"
48 //usage: "Check file types, compare values etc. Return a 0/1 exit code\n"
49 //usage: "depending on logical value of EXPRESSION"
51 //usage:#define test_example_usage
52 //usage: "$ test 1 -eq 2\n"
53 //usage: "$ echo $?\n"
55 //usage: "$ test 1 -eq 1\n"
56 //usage: "$ echo $?\n"
58 //usage: "$ [ -d /etc ]\n"
59 //usage: "$ echo $?\n"
61 //usage: "$ [ -d /junk ]\n"
62 //usage: "$ echo $?\n"
68 /* This is a NOFORK applet. Be very careful! */
70 /* test_main() is called from shells, and we need to be extra careful here.
71 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
74 /* test(1) accepts the following grammar:
75 oexpr ::= aexpr | aexpr "-o" oexpr ;
76 aexpr ::= nexpr | nexpr "-a" aexpr ;
77 nexpr ::= primary | "!" primary
78 primary ::= unary-operator operand
79 | operand binary-operator operand
83 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
84 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
86 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
88 operand ::= <any legal UNIX file name>
91 /* TODO: handle [[ expr ]] bashism bash-compatibly.
92 * [[ ]] is meant to be a "better [ ]", with less weird syntax
93 * and without the risk of variables and quoted strings misinterpreted
95 * This will require support from shells - we need to know quote status
96 * of each parameter (see below).
98 * Word splitting and pathname expansion should NOT be performed:
99 * # a="a b"; [[ $a = "a b" ]] && echo YES
101 * # [[ /bin/m* ]] && echo YES
104 * =~ should do regexp match
105 * = and == should do pattern match against right side:
106 * # [[ *a* == bab ]] && echo YES
107 * # [[ bab == *a* ]] && echo YES
109 * != does the negated == (i.e., also with pattern matching).
110 * Pattern matching is quotation-sensitive:
111 * # [[ bab == "b"a* ]] && echo YES
113 * # [[ bab == b"a*" ]] && echo YES
115 * Conditional operators such as -f must be unquoted literals to be recognized:
116 * # [[ -e /bin ]] && echo YES
118 * # [[ '-e' /bin ]] && echo YES
119 * bash: conditional binary operator expected...
120 * # A='-e'; [[ $A /bin ]] && echo YES
121 * bash: conditional binary operator expected...
123 * || and && should work as -o and -a work in [ ]
124 * -a and -o aren't recognized (&& and || are to be used instead)
125 * ( and ) do not need to be quoted unlike in [ ]:
126 * # [[ ( abc ) && '' ]] && echo YES
127 * # [[ ( abc ) || '' ]] && echo YES
129 * # [[ ( abc ) -o '' ]] && echo YES
130 * bash: syntax error in conditional expression...
132 * Apart from the above, [[ expr ]] should work as [ expr ]
140 FILRD
, /* file access */
146 FILREG
, /* file type */
157 FILSUID
, /* file bit */
161 FILNT
, /* file ops */
189 #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
190 #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
191 #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
192 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
193 #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
194 #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
198 #define nest_msg(...) do { \
200 fprintf(stderr, "%*s", depth*2, ""); \
201 fprintf(stderr, __VA_ARGS__); \
203 #define unnest_msg(...) do { \
204 fprintf(stderr, "%*s", depth*2, ""); \
205 fprintf(stderr, __VA_ARGS__); \
208 #define dbg_msg(...) do { \
209 fprintf(stderr, "%*s", depth*2, ""); \
210 fprintf(stderr, __VA_ARGS__); \
212 #define unnest_msg_and_return(expr, ...) do { \
213 number_t __res = (expr); \
214 fprintf(stderr, "%*s", depth*2, ""); \
215 fprintf(stderr, __VA_ARGS__, res); \
219 static const char *const TOKSTR
[] = {
262 #define nest_msg(...) ((void)0)
263 #define unnest_msg(...) ((void)0)
264 #define dbg_msg(...) ((void)0)
265 #define unnest_msg_and_return(expr, ...) return expr
277 unsigned char op_num
, op_type
;
280 static const struct operator_t ops_table
[] = {
281 { /* "-r" */ FILRD
, UNOP
},
282 { /* "-w" */ FILWR
, UNOP
},
283 { /* "-x" */ FILEX
, UNOP
},
284 { /* "-e" */ FILEXIST
, UNOP
},
285 { /* "-f" */ FILREG
, UNOP
},
286 { /* "-d" */ FILDIR
, UNOP
},
287 { /* "-c" */ FILCDEV
, UNOP
},
288 { /* "-b" */ FILBDEV
, UNOP
},
289 { /* "-p" */ FILFIFO
, UNOP
},
290 { /* "-u" */ FILSUID
, UNOP
},
291 { /* "-g" */ FILSGID
, UNOP
},
292 { /* "-k" */ FILSTCK
, UNOP
},
293 { /* "-s" */ FILGZ
, UNOP
},
294 { /* "-t" */ FILTT
, UNOP
},
295 { /* "-z" */ STREZ
, UNOP
},
296 { /* "-n" */ STRNZ
, UNOP
},
297 { /* "-h" */ FILSYM
, UNOP
}, /* for backwards compat */
299 { /* "-O" */ FILUID
, UNOP
},
300 { /* "-G" */ FILGID
, UNOP
},
301 { /* "-L" */ FILSYM
, UNOP
},
302 { /* "-S" */ FILSOCK
, UNOP
},
303 { /* "=" */ STREQ
, BINOP
},
304 { /* "==" */ STREQ
, BINOP
},
305 { /* "!=" */ STRNE
, BINOP
},
306 { /* "<" */ STRLT
, BINOP
},
307 { /* ">" */ STRGT
, BINOP
},
308 { /* "-eq"*/ INTEQ
, BINOP
},
309 { /* "-ne"*/ INTNE
, BINOP
},
310 { /* "-ge"*/ INTGE
, BINOP
},
311 { /* "-gt"*/ INTGT
, BINOP
},
312 { /* "-le"*/ INTLE
, BINOP
},
313 { /* "-lt"*/ INTLT
, BINOP
},
314 { /* "-nt"*/ FILNT
, BINOP
},
315 { /* "-ot"*/ FILOT
, BINOP
},
316 { /* "-ef"*/ FILEQ
, BINOP
},
317 { /* "!" */ UNOT
, BUNOP
},
318 { /* "-a" */ BAND
, BBINOP
},
319 { /* "-o" */ BOR
, BBINOP
},
320 { /* "(" */ LPAREN
, PAREN
},
321 { /* ")" */ RPAREN
, PAREN
},
323 /* Please keep these two tables in sync */
324 static const char ops_texts
[] ALIGN1
=
369 #if ENABLE_FEATURE_TEST_64
370 typedef int64_t number_t
;
372 typedef int number_t
;
376 /* We try to minimize both static and stack usage. */
377 struct test_statics
{
379 /* set only by check_operator(), either to bogus struct
380 * or points to matching operator_t struct. Never NULL. */
381 const struct operator_t
*last_operator
;
387 /* See test_ptr_hack.c */
388 extern struct test_statics
*const test_ptr_to_statics
;
390 #define S (*test_ptr_to_statics)
391 #define args (S.args )
392 #define last_operator (S.last_operator)
393 #define group_array (S.group_array )
394 #define ngroups (S.ngroups )
395 #define leaving (S.leaving )
397 #define INIT_S() do { \
398 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
401 #define DEINIT_S() do { \
402 free(test_ptr_to_statics); \
405 static number_t
primary(enum token n
);
407 static void syntax(const char *op
, const char *msg
) NORETURN
;
408 static void syntax(const char *op
, const char *msg
)
411 bb_error_msg("%s: %s", op
, msg
);
413 bb_error_msg("%s: %s"+4, msg
);
418 /* atoi with error detection */
419 //XXX: FIXME: duplicate of existing libbb function?
420 static number_t
getn(const char *s
)
423 #if ENABLE_FEATURE_TEST_64
430 #if ENABLE_FEATURE_TEST_64
431 r
= strtoll(s
, &p
, 10);
433 r
= strtol(s
, &p
, 10);
437 syntax(s
, "out of range");
439 if (p
== s
|| *(skip_whitespace(p
)) != '\0')
440 syntax(s
, "bad number");
446 static int newerf(const char *f1, const char *f2)
450 return (stat(f1, &b1) == 0 &&
451 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
454 static int olderf(const char *f1, const char *f2)
458 return (stat(f1, &b1) == 0 &&
459 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
462 static int equalf(const char *f1, const char *f2)
466 return (stat(f1, &b1) == 0 &&
467 stat(f2, &b2) == 0 &&
468 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
473 static enum token
check_operator(const char *s
)
475 static const struct operator_t no_op
= {
481 last_operator
= &no_op
;
484 n
= index_in_strings(ops_texts
, s
);
487 last_operator
= &ops_table
[n
];
488 return ops_table
[n
].op_num
;
492 static int binop(void)
494 const char *opnd1
, *opnd2
;
495 const struct operator_t
*op
;
499 check_operator(*++args
);
504 syntax(args
[-1], "argument expected");
506 if (is_int_op(op
->op_num
)) {
509 if (op
->op_num
== INTEQ
)
511 if (op
->op_num
== INTNE
)
513 if (op
->op_num
== INTGE
)
515 if (op
->op_num
== INTGT
)
517 if (op
->op_num
== INTLE
)
519 /*if (op->op_num == INTLT)*/
522 if (is_str_op(op
->op_num
)) {
523 val1
= strcmp(opnd1
, opnd2
);
524 if (op
->op_num
== STREQ
)
526 if (op
->op_num
== STRNE
)
528 if (op
->op_num
== STRLT
)
530 /*if (op->op_num == STRGT)*/
533 /* We are sure that these three are by now the only binops we didn't check
534 * yet, so we do not check if the class is correct:
536 /* if (is_file_op(op->op_num)) */
540 if (stat(opnd1
, &b1
) || stat(opnd2
, &b2
))
541 return 0; /* false, since at least one stat failed */
542 if (op
->op_num
== FILNT
)
543 return b1
.st_mtime
> b2
.st_mtime
;
544 if (op
->op_num
== FILOT
)
545 return b1
.st_mtime
< b2
.st_mtime
;
546 /*if (op->op_num == FILEQ)*/
547 return b1
.st_dev
== b2
.st_dev
&& b1
.st_ino
== b2
.st_ino
;
549 /*return 1; - NOTREACHED */
553 static void initialize_group_array(void)
557 /* getgroups may be expensive, try to use it only once */
560 /* FIXME: ash tries so hard to not die on OOM,
561 * and we spoil it with just one xrealloc here */
562 /* We realloc, because test_main can be entered repeatedly by shell.
563 * Testcase (ash): 'while true; do test -x some_file; done'
564 * and watch top. (some_file must have owner != you) */
566 group_array
= xrealloc(group_array
, n
* sizeof(gid_t
));
567 ngroups
= getgroups(n
, group_array
);
568 } while (ngroups
> n
);
572 /* Return non-zero if GID is one that we have in our groups list. */
573 //XXX: FIXME: duplicate of existing libbb function?
574 // see toplevel TODO file:
575 // possible code duplication ingroup() and is_a_group_member()
576 static int is_a_group_member(gid_t gid
)
580 /* Short-circuit if possible, maybe saving a call to getgroups(). */
581 if (gid
== getgid() || gid
== getegid())
585 initialize_group_array();
587 /* Search through the list looking for GID. */
588 for (i
= 0; i
< ngroups
; i
++)
589 if (gid
== group_array
[i
])
596 /* Do the same thing access(2) does, but use the effective uid and gid,
597 and don't make the mistake of telling root that any file is
599 static int test_eaccess(char *path
, int mode
)
602 unsigned int euid
= geteuid();
604 if (stat(path
, &st
) < 0)
608 /* Root can read or write any file. */
612 /* Root can execute any file that has any one of the execute
614 if (st
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
618 if (st
.st_uid
== euid
) /* owner */
620 else if (is_a_group_member(st
.st_gid
))
623 if (st
.st_mode
& mode
)
630 static int filstat(char *nm
, enum token mode
)
633 unsigned i
= i
; /* gcc 3.x thinks it can be used uninitialized */
635 if (mode
== FILSYM
) {
637 if (lstat(nm
, &s
) == 0) {
645 if (stat(nm
, &s
) != 0)
647 if (mode
== FILEXIST
)
649 if (is_file_access(mode
)) {
656 return test_eaccess(nm
, i
) == 0;
658 if (is_file_type(mode
)) {
667 if (mode
== FILFIFO
) {
674 if (mode
== FILSOCK
) {
682 return ((s
.st_mode
& S_IFMT
) == i
);
684 if (is_file_bit(mode
)) {
691 return ((s
.st_mode
& i
) != 0);
694 return s
.st_size
> 0L;
696 return s
.st_uid
== geteuid();
698 return s
.st_gid
== getegid();
699 return 1; /* NOTREACHED */
703 static number_t
nexpr(enum token n
)
707 nest_msg(">nexpr(%s)\n", TOKSTR
[n
]);
709 n
= check_operator(*++args
);
711 /* special case: [ ! ], [ a -a ! ] are valid */
712 /* IOW, "! ARG" may miss ARG */
714 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args
[0], &args
[0]);
718 unnest_msg("<nexpr:%lld\n", res
);
722 unnest_msg("<nexpr:%lld\n", res
);
727 static number_t
aexpr(enum token n
)
731 nest_msg(">aexpr(%s)\n", TOKSTR
[n
]);
733 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res
, args
[1], &args
[1]);
734 if (check_operator(*++args
) == BAND
) {
735 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args
[1], &args
[1]);
736 res
= aexpr(check_operator(*++args
)) && res
;
737 unnest_msg("<aexpr:%lld\n", res
);
741 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res
, args
[0], &args
[0]);
746 static number_t
oexpr(enum token n
)
750 nest_msg(">oexpr(%s)\n", TOKSTR
[n
]);
752 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res
, args
[1], &args
[1]);
753 if (check_operator(*++args
) == BOR
) {
754 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args
[1], &args
[1]);
755 res
= oexpr(check_operator(*++args
)) || res
;
756 unnest_msg("<oexpr:%lld\n", res
);
760 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res
, args
[0], &args
[0]);
765 static number_t
primary(enum token n
)
768 number_t res
= res
; /* for compiler */
772 const struct operator_t
*args0_op
;
774 nest_msg(">primary(%s)\n", TOKSTR
[n
]);
776 syntax(NULL
, "argument expected");
779 res
= oexpr(check_operator(*++args
));
780 if (check_operator(*++args
) != RPAREN
)
781 syntax(NULL
, "closing paren expected");
782 unnest_msg("<primary:%lld\n", res
);
786 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
788 args0_op
= last_operator
;
789 /* last_operator = operator at args[1] */
790 if (check_operator(args
[1]) != EOI
) { /* if args[1] != NULL */
792 // coreutils also does this:
793 // if (args[3] && args[0]="-l" && args[2] is BINOP)
794 // return binop(1 /* prepended by -l */);
795 if (last_operator
->op_type
== BINOP
)
796 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
799 /* check "is args[0] unop?" second */
800 if (args0_op
->op_type
== UNOP
) {
801 /* unary expression */
803 // syntax(args0_op->op_text, "argument expected");
804 goto check_emptiness
;
807 unnest_msg_and_return(args
[0][0] == '\0', "<primary:%lld\n");
809 unnest_msg_and_return(args
[0][0] != '\0', "<primary:%lld\n");
811 unnest_msg_and_return(isatty(getn(*args
)), "<primary: isatty(%s)%lld\n", *args
);
812 unnest_msg_and_return(filstat(*args
, n
), "<primary: filstat(%s):%lld\n", *args
);
815 /*check_operator(args[1]); - already done */
816 if (last_operator
->op_type
== BINOP
) {
817 /* args[2] is known to be NULL, isn't it bound to fail? */
818 unnest_msg_and_return(binop(), "<primary:%lld\n");
821 unnest_msg_and_return(args
[0][0] != '\0', "<primary:%lld\n");
825 int test_main(int argc
, char **argv
)
831 arg0
= bb_basename(argv
[0]);
832 if (arg0
[0] == '[') {
834 if (!arg0
[1]) { /* "[" ? */
835 if (NOT_LONE_CHAR(argv
[argc
], ']')) {
836 bb_error_msg("missing ]");
839 } else { /* assuming "[[" */
840 if (strcmp(argv
[argc
], "]]") != 0) {
841 bb_error_msg("missing ]]");
848 /* We must do DEINIT_S() prior to returning */
851 res
= setjmp(leaving
);
855 /* resetting ngroups is probably unnecessary. it will
856 * force a new call to getgroups(), which prevents using
857 * group data fetched during a previous call. but the
858 * only way the group data could be stale is if there's
859 * been an intervening call to setgroups(), and this
860 * isn't likely in the case of a shell. paranoia
863 /*ngroups = 0; - done by INIT_S() */
868 /* Implement special cases from POSIX.2, section 4.62.4 */
869 if (!argv
[0]) { /* "test" */
874 // Now it's fixed in the parser and should not be needed
875 if (LONE_CHAR(argv
[0], '!') && argv
[1]) {
880 if (!argv
[1]) { /* "test [!] arg" */
881 res
= (*argv
[0] == '\0');
884 if (argv
[2] && !argv
[3]) {
885 check_operator(argv
[1]);
886 if (last_operator
->op_type
== BINOP
) {
887 /* "test [!] arg1 <binary_op> arg2" */
889 res
= (binop() == 0);
894 /* Some complex expression. Undo '!' removal */
902 res
= !oexpr(check_operator(*args
));
904 if (*args
!= NULL
&& *++args
!= NULL
) {
909 bb_error_msg("%s: unknown operand", *args
);
914 // return negate ? !res : res;