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 tarball for details.
19 * Original copyright notice states:
20 * "This program is in the Public Domain."
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
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
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"|
47 operand ::= <any legal UNIX file name>
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)
103 #define nest_msg(...) do { \
105 fprintf(stderr, "%*s", depth*2, ""); \
106 fprintf(stderr, __VA_ARGS__); \
108 #define unnest_msg(...) do { \
109 fprintf(stderr, "%*s", depth*2, ""); \
110 fprintf(stderr, __VA_ARGS__); \
113 #define dbg_msg(...) do { \
114 fprintf(stderr, "%*s", depth*2, ""); \
115 fprintf(stderr, __VA_ARGS__); \
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); \
124 static const char *const TOKSTR
[] = {
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
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
;
234 typedef int number_t
;
238 /* We try to minimize both static and stack usage. */
239 struct test_statics
{
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
;
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)); \
263 #define DEINIT_S() do { \
264 free(test_ptr_to_statics); \
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
)
273 bb_error_msg("%s: %s", op
, msg
);
275 bb_error_msg("%s: %s"+4, msg
);
280 /* atoi with error detection */
281 //XXX: FIXME: duplicate of existing libbb function?
282 static number_t
getn(const char *s
)
285 #if ENABLE_FEATURE_TEST_64
292 #if ENABLE_FEATURE_TEST_64
293 r
= strtoll(s
, &p
, 10);
295 r
= strtol(s
, &p
, 10);
299 syntax(s
, "out of range");
301 if (*(skip_whitespace(p
)))
302 syntax(s
, "bad number");
308 static int newerf(const char *f1, const char *f2)
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)
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)
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
= {
341 const struct operator_t
*op
;
343 last_operator
= &no_op
;
350 if (strcmp(s
, op
->op_text
) == 0) {
355 } while (op
< ops
+ ARRAY_SIZE(ops
));
361 static int binop(void)
363 const char *opnd1
, *opnd2
;
364 const struct operator_t
*op
;
368 check_operator(*++args
);
373 syntax(op
->op_text
, "argument expected");
375 if (is_int_op(op
->op_num
)) {
378 if (op
->op_num
== INTEQ
)
380 if (op
->op_num
== INTNE
)
382 if (op
->op_num
== INTGE
)
384 if (op
->op_num
== INTGT
)
386 if (op
->op_num
== INTLE
)
388 if (op
->op_num
== INTLT
)
391 if (is_str_op(op
->op_num
)) {
392 val1
= strcmp(opnd1
, opnd2
);
393 if (op
->op_num
== STREQ
)
395 if (op
->op_num
== STRNE
)
397 if (op
->op_num
== STRLT
)
399 if (op
->op_num
== STRGT
)
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)) */
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
);
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
)
445 /* Short-circuit if possible, maybe saving a call to getgroups(). */
446 if (gid
== getgid() || gid
== getegid())
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
])
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
464 static int test_eaccess(char *path
, int mode
)
467 unsigned int euid
= geteuid();
469 if (stat(path
, &st
) < 0)
473 /* Root can read or write any file. */
477 /* Root can execute any file that has any one of the execute
479 if (st
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
))
483 if (st
.st_uid
== euid
) /* owner */
485 else if (is_a_group_member(st
.st_gid
))
488 if (st
.st_mode
& mode
)
495 static int filstat(char *nm
, enum token mode
)
498 unsigned i
= i
; /* gcc 3.x thinks it can be used uninitialized */
500 if (mode
== FILSYM
) {
502 if (lstat(nm
, &s
) == 0) {
510 if (stat(nm
, &s
) != 0)
512 if (mode
== FILEXIST
)
514 if (is_file_access(mode
)) {
521 return test_eaccess(nm
, i
) == 0;
523 if (is_file_type(mode
)) {
532 if (mode
== FILFIFO
) {
539 if (mode
== FILSOCK
) {
547 return ((s
.st_mode
& S_IFMT
) == i
);
549 if (is_file_bit(mode
)) {
556 return ((s
.st_mode
& i
) != 0);
559 return s
.st_size
> 0L;
561 return s
.st_uid
== geteuid();
563 return s
.st_gid
== getegid();
564 return 1; /* NOTREACHED */
568 static number_t
nexpr(enum token n
)
572 nest_msg(">nexpr(%s)\n", TOKSTR
[n
]);
574 n
= check_operator(*++args
);
576 /* special case: [ ! ], [ a -a ! ] are valid */
577 /* IOW, "! ARG" may miss ARG */
578 unnest_msg("<nexpr:1 (!EOI)\n");
582 unnest_msg("<nexpr:%lld\n", res
);
586 unnest_msg("<nexpr:%lld\n", res
);
591 static number_t
aexpr(enum token n
)
595 nest_msg(">aexpr(%s)\n", TOKSTR
[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
);
605 unnest_msg("<aexpr:%lld, args:%s\n", res
, args
[0]);
610 static number_t
oexpr(enum token n
)
614 nest_msg(">oexpr(%s)\n", TOKSTR
[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
);
624 unnest_msg("<oexpr:%lld, args:%s\n", res
, args
[0]);
629 static number_t
primary(enum token n
)
632 number_t res
= res
; /* for compiler */
636 const struct operator_t
*args0_op
;
638 nest_msg(">primary(%s)\n", TOKSTR
[n
]);
640 syntax(NULL
, "argument expected");
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
);
650 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
652 args0_op
= last_operator
;
653 /* last_operator = operator at args[1] */
654 if (check_operator(args
[1]) != EOI
) { /* if args[1] != NULL */
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 */
667 // syntax(args0_op->op_text, "argument expected");
668 goto check_emptiness
;
671 unnest_msg_and_return(args
[0][0] == '\0', "<primary:%lld\n");
673 unnest_msg_and_return(args
[0][0] != '\0', "<primary:%lld\n");
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");
685 unnest_msg_and_return(args
[0][0] != '\0', "<primary:%lld\n");
689 int test_main(int argc
, char **argv
)
695 arg0
= bb_basename(argv
[0]);
696 if (arg0
[0] == '[') {
698 if (!arg0
[1]) { /* "[" ? */
699 if (NOT_LONE_CHAR(argv
[argc
], ']')) {
700 bb_error_msg("missing ]");
703 } else { /* assuming "[[" */
704 if (strcmp(argv
[argc
], "]]") != 0) {
705 bb_error_msg("missing ]]");
712 /* We must do DEINIT_S() prior to returning */
715 res
= setjmp(leaving
);
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
732 /* Implement special cases from POSIX.2, section 4.62.4 */
733 if (!argv
[0]) { /* "test" */
738 // Now it's fixed in the parser and should not be needed
739 if (LONE_CHAR(argv
[0], '!') && argv
[1]) {
744 if (!argv
[1]) { /* "test [!] arg" */
745 res
= (*argv
[0] == '\0');
748 if (argv
[2] && !argv
[3]) {
749 check_operator(argv
[1]);
750 if (last_operator
->op_type
== BINOP
) {
751 /* "test [!] arg1 <binary_op> arg2" */
753 res
= (binop() == 0);
758 /* Some complex expression. Undo '!' removal */
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
);
775 // return negate ? !res : res;