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 //config: bool "test (4.4 kb)"
26 //config: test is used to check file types and compare values,
27 //config: returning an appropriate exit code. The bash shell
28 //config: has test built in, ash can build it in optionally.
31 //config: bool "test as ["
34 //config: Provide test command in the "[ EXPR ]" form
37 //config: bool "test as [["
40 //config: Provide test command in the "[[ EXPR ]]" form
42 //config:config FEATURE_TEST_64
43 //config: bool "Extend test to 64 bit"
45 //config: depends on TEST || TEST1 || TEST2 || ASH_TEST || HUSH_TEST
47 //config: Enable 64-bit support in test.
49 //applet:IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
50 //applet:IF_TEST1(APPLET_NOFORK([, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
51 //applet:IF_TEST2(APPLET_NOFORK([[, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
53 //kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
54 //kbuild:lib-$(CONFIG_TEST1) += test.o test_ptr_hack.o
55 //kbuild:lib-$(CONFIG_TEST2) += test.o test_ptr_hack.o
57 //kbuild:lib-$(CONFIG_ASH_TEST) += test.o test_ptr_hack.o
58 //kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o
60 /* "test --help" is special-cased to ignore --help */
61 //usage:#define test_trivial_usage NOUSAGE_STR
62 //usage:#define test_full_usage ""
64 //usage:#define test_example_usage
65 //usage: "$ test 1 -eq 2\n"
66 //usage: "$ echo $?\n"
68 //usage: "$ test 1 -eq 1\n"
69 //usage: "$ echo $?\n"
71 //usage: "$ [ -d /etc ]\n"
72 //usage: "$ echo $?\n"
74 //usage: "$ [ -d /junk ]\n"
75 //usage: "$ echo $?\n"
82 /* This is a NOFORK applet. Be very careful! */
84 /* test_main() is called from shells, and we need to be extra careful here.
85 * This is true regardless of PREFER_APPLETS and SH_STANDALONE
88 /* test(1) accepts the following grammar:
89 oexpr ::= aexpr | aexpr "-o" oexpr ;
90 aexpr ::= nexpr | nexpr "-a" aexpr ;
91 nexpr ::= primary | "!" primary
92 primary ::= unary-operator operand
93 | operand binary-operator operand
97 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
98 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
100 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
102 operand ::= <any legal UNIX file name>
105 /* TODO: handle [[ expr ]] bashism bash-compatibly.
106 * [[ ]] is meant to be a "better [ ]", with less weird syntax
107 * and without the risk of variables and quoted strings misinterpreted
109 * This will require support from shells - we need to know quote status
110 * of each parameter (see below).
112 * Word splitting and pathname expansion should NOT be performed:
113 * # a="a b"; [[ $a = "a b" ]] && echo YES
115 * # [[ /bin/m* ]] && echo YES
118 * =~ should do regexp match
119 * = and == should do pattern match against right side:
120 * # [[ *a* == bab ]] && echo YES
121 * # [[ bab == *a* ]] && echo YES
123 * != does the negated == (i.e., also with pattern matching).
124 * Pattern matching is quotation-sensitive:
125 * # [[ bab == "b"a* ]] && echo YES
127 * # [[ bab == b"a*" ]] && echo YES
129 * Conditional operators such as -f must be unquoted literals to be recognized:
130 * # [[ -e /bin ]] && echo YES
132 * # [[ '-e' /bin ]] && echo YES
133 * bash: conditional binary operator expected...
134 * # A='-e'; [[ $A /bin ]] && echo YES
135 * bash: conditional binary operator expected...
137 * || and && should work as -o and -a work in [ ]
138 * -a and -o aren't recognized (&& and || are to be used instead)
139 * ( and ) do not need to be quoted unlike in [ ]:
140 * # [[ ( abc ) && '' ]] && echo YES
141 * # [[ ( abc ) || '' ]] && echo YES
143 * # [[ ( abc ) -o '' ]] && echo YES
144 * bash: syntax error in conditional expression...
146 * Apart from the above, [[ expr ]] should work as [ expr ]
152 || (ENABLE_ASH_BASH_COMPAT && ENABLE_ASH_TEST) \
153 || (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
154 # define BASH_TEST2 1
156 # define BASH_TEST2 0
162 FILRD
, /* file access */
168 FILREG
, /* file type */
179 FILSUID
, /* file bit */
183 FILNT
, /* file ops */
215 #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
216 #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
217 #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
218 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
219 #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
220 #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
224 #define nest_msg(...) do { \
226 fprintf(stderr, "%*s", depth*2, ""); \
227 fprintf(stderr, __VA_ARGS__); \
229 #define unnest_msg(...) do { \
230 fprintf(stderr, "%*s", depth*2, ""); \
231 fprintf(stderr, __VA_ARGS__); \
234 #define dbg_msg(...) do { \
235 fprintf(stderr, "%*s", depth*2, ""); \
236 fprintf(stderr, __VA_ARGS__); \
238 #define unnest_msg_and_return(expr, ...) do { \
239 number_t __res = (expr); \
240 fprintf(stderr, "%*s", depth*2, ""); \
241 fprintf(stderr, __VA_ARGS__, res); \
245 static const char *const TOKSTR
[] ALIGN_PTR
= {
291 #define nest_msg(...) ((void)0)
292 #define unnest_msg(...) ((void)0)
293 #define dbg_msg(...) ((void)0)
294 #define unnest_msg_and_return(expr, ...) return expr
306 unsigned char op_num
, op_type
;
309 static const struct operator_t ops_table
[] ALIGN2
= {
310 { /* "-r" */ FILRD
, UNOP
},
311 { /* "-w" */ FILWR
, UNOP
},
312 { /* "-x" */ FILEX
, UNOP
},
313 { /* "-e" */ FILEXIST
, UNOP
},
314 { /* "-f" */ FILREG
, UNOP
},
315 { /* "-d" */ FILDIR
, UNOP
},
316 { /* "-c" */ FILCDEV
, UNOP
},
317 { /* "-b" */ FILBDEV
, UNOP
},
318 { /* "-p" */ FILFIFO
, UNOP
},
319 { /* "-u" */ FILSUID
, UNOP
},
320 { /* "-g" */ FILSGID
, UNOP
},
321 { /* "-k" */ FILSTCK
, UNOP
},
322 { /* "-s" */ FILGZ
, UNOP
},
323 { /* "-t" */ FILTT
, UNOP
},
324 { /* "-z" */ STREZ
, UNOP
},
325 { /* "-n" */ STRNZ
, UNOP
},
326 { /* "-h" */ FILSYM
, UNOP
}, /* for backwards compat */
328 { /* "-O" */ FILUID
, UNOP
},
329 { /* "-G" */ FILGID
, UNOP
},
330 { /* "-L" */ FILSYM
, UNOP
},
331 { /* "-S" */ FILSOCK
, UNOP
},
332 { /* "=" */ STREQ
, BINOP
},
333 /* "==" is bashism, http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
334 * lists only "=" as comparison operator.
336 { /* "==" */ STREQ
, BINOP
},
337 { /* "!=" */ STRNE
, BINOP
},
338 { /* "<" */ STRLT
, BINOP
},
339 { /* ">" */ STRGT
, BINOP
},
341 { /* "=~" */ REGEX
, BINOP
},
343 { /* "-eq"*/ INTEQ
, BINOP
},
344 { /* "-ne"*/ INTNE
, BINOP
},
345 { /* "-ge"*/ INTGE
, BINOP
},
346 { /* "-gt"*/ INTGT
, BINOP
},
347 { /* "-le"*/ INTLE
, BINOP
},
348 { /* "-lt"*/ INTLT
, BINOP
},
349 { /* "-nt"*/ FILNT
, BINOP
},
350 { /* "-ot"*/ FILOT
, BINOP
},
351 { /* "-ef"*/ FILEQ
, BINOP
},
352 { /* "!" */ UNOT
, BUNOP
},
353 { /* "-a" */ BAND
, BBINOP
},
354 { /* "-o" */ BOR
, BBINOP
},
356 { /* "&&" */ BAND
, BBINOP
},
357 { /* "||" */ BOR
, BBINOP
},
359 { /* "(" */ LPAREN
, PAREN
},
360 { /* ")" */ RPAREN
, PAREN
},
362 /* Please keep these two tables in sync */
363 static const char ops_texts
[] ALIGN1
=
387 /* "==" is bashism */
416 #if ENABLE_FEATURE_TEST_64
417 typedef int64_t number_t
;
419 typedef int number_t
;
423 /* We try to minimize both static and stack usage. */
424 struct test_statics
{
426 /* set only by check_operator(), either to bogus struct
427 * or points to matching operator_t struct. Never NULL. */
428 const struct operator_t
*last_operator
;
429 struct cached_groupinfo
*groupinfo
;
436 /* See test_ptr_hack.c */
437 extern struct test_statics
*BB_GLOBAL_CONST test_ptr_to_statics
;
439 #define S (*test_ptr_to_statics)
440 #define args (S.args )
441 #define last_operator (S.last_operator)
442 #define groupinfo (S.groupinfo )
443 #define bash_test2 (S.bash_test2 )
444 #define leaving (S.leaving )
446 #define INIT_S() do { \
447 XZALLOC_CONST_PTR(&test_ptr_to_statics, sizeof(S)); \
449 #define DEINIT_S() do { \
450 free(test_ptr_to_statics); \
453 static number_t
primary(enum token n
);
455 static void syntax(const char *op
, const char *msg
) NORETURN
;
456 static void syntax(const char *op
, const char *msg
)
459 bb_error_msg("%s: %s", op
, msg
);
461 bb_error_msg("%s: %s"+4, msg
);
466 /* atoi with error detection */
467 //XXX: FIXME: duplicate of existing libbb function?
468 static number_t
getn(const char *s
)
471 #if ENABLE_FEATURE_TEST_64
478 #if ENABLE_FEATURE_TEST_64
479 r
= strtoll(s
, &p
, 10);
481 r
= strtol(s
, &p
, 10);
485 syntax(s
, "out of range");
487 if (p
== s
|| *(skip_whitespace(p
)) != '\0')
488 syntax(s
, "bad number");
494 static int newerf(const char *f1, const char *f2)
498 return (stat(f1, &b1) == 0 &&
499 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
502 static int olderf(const char *f1, const char *f2)
506 return (stat(f1, &b1) == 0 &&
507 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
510 static int equalf(const char *f1, const char *f2)
514 return (stat(f1, &b1) == 0 &&
515 stat(f2, &b2) == 0 &&
516 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
521 static enum token
check_operator(const char *s
)
523 static const struct operator_t no_op
= {
529 last_operator
= &no_op
;
532 n
= index_in_strings(ops_texts
, s
);
537 if (ops_table
[n
].op_num
== REGEX
&& !bash_test2
) {
538 /* =~ is only for [[ ]] */
541 if (ops_table
[n
].op_num
== BAND
|| ops_table
[n
].op_num
== BOR
) {
542 /* [ ] accepts -a and -o but not && and || */
543 /* [[ ]] accepts && and || but not -a and -o */
544 if (bash_test2
== (s
[0] == '-'))
549 last_operator
= &ops_table
[n
];
550 return ops_table
[n
].op_num
;
554 static int binop(void)
556 const char *opnd1
, *opnd2
;
557 const struct operator_t
*op
;
561 check_operator(*++args
);
566 syntax(args
[-1], "argument expected");
568 if (is_int_op(op
->op_num
)) {
571 if (op
->op_num
== INTEQ
)
573 if (op
->op_num
== INTNE
)
575 if (op
->op_num
== INTGE
)
577 if (op
->op_num
== INTGT
)
579 if (op
->op_num
== INTLE
)
581 /*if (op->op_num == INTLT)*/
586 if (op
->op_num
== STREQ
) {
587 val1
= fnmatch(opnd2
, opnd1
, 0);
590 if (op
->op_num
== STRNE
) {
591 val1
= fnmatch(opnd2
, opnd1
, 0);
594 if (op
->op_num
== REGEX
) {
596 memset(&re_buffer
, 0, sizeof(re_buffer
));
597 if (regcomp(&re_buffer
, opnd2
, REG_EXTENDED
)) { // REG_NEWLINE?
599 longjmp(leaving
, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
601 val1
= regexec(&re_buffer
, opnd1
, 0, NULL
, 0);
607 if (is_str_op(op
->op_num
)) {
608 val1
= strcmp(opnd1
, opnd2
);
609 if (op
->op_num
== STREQ
)
611 if (op
->op_num
== STRNE
)
613 if (op
->op_num
== STRLT
)
615 /*if (op->op_num == STRGT)*/
618 /* We are sure that these three are by now the only binops we didn't check
619 * yet, so we do not check if the class is correct:
621 /* if (is_file_op(op->op_num)) */
625 if (stat(opnd1
, &b1
) || stat(opnd2
, &b2
))
626 return 0; /* false, since at least one stat failed */
627 if (op
->op_num
== FILNT
)
628 return b1
.st_mtime
> b2
.st_mtime
;
629 if (op
->op_num
== FILOT
)
630 return b1
.st_mtime
< b2
.st_mtime
;
631 /*if (op->op_num == FILEQ)*/
632 return b1
.st_dev
== b2
.st_dev
&& b1
.st_ino
== b2
.st_ino
;
634 /*return 1; - NOTREACHED */
637 /* Return non-zero if GID is one that we have in our groups list. */
638 static int is_a_group_member(gid_t gid
)
640 /* Short-circuit if possible, maybe saving a call to getgroups(). */
641 if (gid
== get_cached_egid(&groupinfo
->egid
))
644 return is_in_supplementary_groups(groupinfo
, gid
);
648 * Similar to what access(2) does, but uses the effective uid and gid.
649 * Doesn't make the mistake of telling root that any file is executable.
650 * Returns non-zero if the file is accessible.
652 static int test_st_mode(struct stat
*st
, int mode
)
654 enum { ANY_IX
= S_IXUSR
| S_IXGRP
| S_IXOTH
};
658 /* Do we already know with no extra syscalls? */
659 //if (!S_ISREG(st->st_mode))
660 // return 0; /* not a regular file */
661 // ^^^ bash 5.2.15 "test -x" does not check this!
662 if ((st
->st_mode
& ANY_IX
) == 0)
663 return 0; /* no one can execute */
664 if ((st
->st_mode
& ANY_IX
) == ANY_IX
)
665 return 1; /* anyone can execute */
668 euid
= get_cached_euid(&groupinfo
->euid
);
670 /* Root can read or write any file. */
674 /* Root can execute any file that has any one of the execute
676 mode
= S_IXUSR
| S_IXGRP
| S_IXOTH
;
677 } else if (st
->st_uid
== euid
) /* owner */
679 else if (is_a_group_member(st
->st_gid
))
682 return st
->st_mode
& mode
;
686 static int filstat(char *nm
, enum token mode
)
689 unsigned i
= i
; /* gcc 3.x thinks it can be used uninitialized */
691 if (mode
== FILSYM
) {
693 if (lstat(nm
, &s
) == 0) {
701 if (stat(nm
, &s
) != 0)
703 if (mode
== FILEXIST
)
705 if (is_file_access(mode
)) {
712 return test_st_mode(&s
, i
);
714 if (is_file_type(mode
)) {
723 if (mode
== FILFIFO
) {
730 if (mode
== FILSOCK
) {
738 return ((s
.st_mode
& S_IFMT
) == i
);
740 if (is_file_bit(mode
)) {
747 return ((s
.st_mode
& i
) != 0);
750 return s
.st_size
!= 0L; /* shorter than "> 0" test */
752 return s
.st_uid
== geteuid();
754 return s
.st_gid
== getegid();
755 return 1; /* NOTREACHED */
759 static number_t
nexpr(enum token n
)
763 nest_msg(">nexpr(%s)\n", TOKSTR
[n
]);
765 n
= check_operator(*++args
);
767 /* special case: [ ! ], [ a -a ! ] are valid */
768 /* IOW, "! ARG" may miss ARG */
770 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args
[0], &args
[0]);
774 unnest_msg("<nexpr:%lld\n", res
);
778 unnest_msg("<nexpr:%lld\n", res
);
783 static number_t
aexpr(enum token n
)
787 nest_msg(">aexpr(%s)\n", TOKSTR
[n
]);
789 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res
, args
[1], &args
[1]);
790 if (check_operator(*++args
) == BAND
) {
791 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args
[1], &args
[1]);
792 res
= aexpr(check_operator(*++args
)) && res
;
793 unnest_msg("<aexpr:%lld\n", res
);
797 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res
, args
[0], &args
[0]);
802 static number_t
oexpr(enum token n
)
806 nest_msg(">oexpr(%s)\n", TOKSTR
[n
]);
808 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res
, args
[1], &args
[1]);
809 if (check_operator(*++args
) == BOR
) {
810 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args
[1], &args
[1]);
811 res
= oexpr(check_operator(*++args
)) || res
;
812 unnest_msg("<oexpr:%lld\n", res
);
816 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res
, args
[0], &args
[0]);
821 static number_t
primary(enum token n
)
824 number_t res
= res
; /* for compiler */
828 const struct operator_t
*args0_op
;
830 nest_msg(">primary(%s)\n", TOKSTR
[n
]);
832 syntax(NULL
, "argument expected");
835 res
= oexpr(check_operator(*++args
));
836 if (check_operator(*++args
) != RPAREN
)
837 syntax(NULL
, "closing paren expected");
838 unnest_msg("<primary:%lld\n", res
);
842 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
844 args0_op
= last_operator
;
845 /* last_operator = operator at args[1] */
846 if (check_operator(args
[1]) != EOI
) { /* if args[1] != NULL */
848 // coreutils also does this:
849 // if (args[3] && args[0] = "-l" && args[2] is BINOP)
850 // return binop(1 /* prepended by -l */);
851 if (last_operator
->op_type
== BINOP
)
852 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
855 /* check "is args[0] unop?" second */
856 if (args0_op
->op_type
== UNOP
) {
857 /* unary expression */
859 // syntax(args0_op->op_text, "argument expected");
860 goto check_emptiness
;
863 unnest_msg_and_return(args
[0][0] == '\0', "<primary:%lld\n");
865 unnest_msg_and_return(args
[0][0] != '\0', "<primary:%lld\n");
867 unnest_msg_and_return(isatty(getn(*args
)), "<primary: isatty(%s)%lld\n", *args
);
868 unnest_msg_and_return(filstat(*args
, n
), "<primary: filstat(%s):%lld\n", *args
);
871 /*check_operator(args[1]); - already done */
872 if (last_operator
->op_type
== BINOP
) {
873 /* args[2] is known to be NULL, isn't it bound to fail? */
874 unnest_msg_and_return(binop(), "<primary:%lld\n");
877 unnest_msg_and_return(args
[0][0] != '\0', "<primary:%lld\n");
881 int FAST_FUNC
test_main2(struct cached_groupinfo
*pgroupinfo
, int argc
, char **argv
)
889 arg0
= bb_basename(argv
[0]);
890 if ((ENABLE_TEST1
|| ENABLE_TEST2
|| ENABLE_ASH_TEST
|| ENABLE_HUSH_TEST
)
894 if (!arg0
[1]) { /* "[" ? */
895 if (NOT_LONE_CHAR(argv
[argc
], ']')) {
896 bb_simple_error_msg("missing ]");
899 } else { /* assuming "[[" */
900 if (strcmp(argv
[argc
], "]]") != 0) {
901 bb_simple_error_msg("missing ]]");
910 /* argc is unused after this point */
912 /* We must do DEINIT_S() prior to returning */
914 groupinfo
= pgroupinfo
;
920 res
= setjmp(leaving
);
924 /* resetting ngroups is probably unnecessary. it will
925 * force a new call to getgroups(), which prevents using
926 * group data fetched during a previous call. but the
927 * only way the group data could be stale is if there's
928 * been an intervening call to setgroups(), and this
929 * isn't likely in the case of a shell. paranoia
932 /*ngroups = 0; - done by INIT_S() */
937 /* Implement special cases from POSIX.2, section 4.62.4.
938 * Testcase: "test '(' = '('"
939 * The general parser would misinterpret '(' as group start.
951 res
= (argv
[0][0] == '\0');
957 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
959 * If $2 is a binary primary, perform the binary test of $1 and $3.
962 check_operator(argv
[1]);
963 if (last_operator
->op_type
== BINOP
) {
964 /* "test [!] arg1 <binary_op> arg2" */
966 res
= (binop() == 0);
968 /* If there was leading "!" op... */
972 /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
973 * Looks like this works without additional coding.
977 /* argv[3] exists (at least 4 args), is it exactly 4 args? */
981 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
982 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
984 * Example why code below is necessary: test '(' ! -e ')'
986 if (LONE_CHAR(argv
[0], '(')
987 && LONE_CHAR(argv
[3], ')')
989 /* "test [!] ( x y )" */
996 if (LONE_CHAR(argv
[0], '!')) {
1003 res
= !oexpr(check_operator(*args
));
1005 if (*args
!= NULL
&& *++args
!= NULL
) {
1010 bb_error_msg("%s: unknown operand", *args
);
1018 int test_main(int argc
, char **argv
)
1020 struct cached_groupinfo info
;
1026 info
.supplementary_array
= NULL
;
1027 r
= test_main2(&info
, argc
, argv
);
1028 free(info
.supplementary_array
);