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 source tree.
18 *
19 * Original copyright notice states:
20 * "This program is in the Public Domain."
21 */
22 //config:config TEST
23 //config: bool "test (4.1 kb)"
24 //config: default y
25 //config: help
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.
29 //config:
30 //config:config TEST1
31 //config: bool "test as ["
32 //config: default y
33 //config: help
34 //config: Provide test command in the "[ EXPR ]" form
35 //config:
36 //config:config TEST2
37 //config: bool "test as [["
38 //config: default y
39 //config: help
40 //config: Provide test command in the "[[ EXPR ]]" form
41 //config:
42 //config:config FEATURE_TEST_64
43 //config: bool "Extend test to 64 bit"
44 //config: default y
45 //config: depends on TEST || TEST1 || TEST2 || ASH_TEST || HUSH_TEST
46 //config: help
47 //config: Enable 64-bit support in test.
48
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))
52
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
56
57 //kbuild:lib-$(CONFIG_ASH_TEST) += test.o test_ptr_hack.o
58 //kbuild:lib-$(CONFIG_HUSH_TEST) += test.o test_ptr_hack.o
59
60 /* "test --help" is special-cased to ignore --help */
61 //usage:#define test_trivial_usage NOUSAGE_STR
62 //usage:#define test_full_usage ""
63 //usage:
64 //usage:#define test_example_usage
65 //usage: "$ test 1 -eq 2\n"
66 //usage: "$ echo $?\n"
67 //usage: "1\n"
68 //usage: "$ test 1 -eq 1\n"
69 //usage: "$ echo $?\n"
70 //usage: "0\n"
71 //usage: "$ [ -d /etc ]\n"
72 //usage: "$ echo $?\n"
73 //usage: "0\n"
74 //usage: "$ [ -d /junk ]\n"
75 //usage: "$ echo $?\n"
76 //usage: "1\n"
77
78 #include "libbb.h"
79 #include <regex.h>
80 #include <fnmatch.h>
81
82 /* This is a NOFORK applet. Be very careful! */
83
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
86 * state. */
87
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
94 | operand
95 | "(" oexpr ")"
96 ;
97 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
98 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
99
100 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
101 "-nt"|"-ot"|"-ef";
102 operand ::= <any legal UNIX file name>
103 */
104
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
108 * as operators.
109 * This will require support from shells - we need to know quote status
110 * of each parameter (see below).
111 *
112 * Word splitting and pathname expansion should NOT be performed:
113 * # a="a b"; [[ $a = "a b" ]] && echo YES
114 * YES
115 * # [[ /bin/m* ]] && echo YES
116 * YES
117 *
118 * =~ should do regexp match
119 * = and == should do pattern match against right side:
120 * # [[ *a* == bab ]] && echo YES
121 * # [[ bab == *a* ]] && echo YES
122 * YES
123 * != does the negated == (i.e., also with pattern matching).
124 * Pattern matching is quotation-sensitive:
125 * # [[ bab == "b"a* ]] && echo YES
126 * YES
127 * # [[ bab == b"a*" ]] && echo YES
128 *
129 * Conditional operators such as -f must be unquoted literals to be recognized:
130 * # [[ -e /bin ]] && echo YES
131 * 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...
136 *
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
142 * YES
143 * # [[ ( abc ) -o '' ]] && echo YES
144 * bash: syntax error in conditional expression...
145 *
146 * Apart from the above, [[ expr ]] should work as [ expr ]
147 */
148
149 #define TEST_DEBUG 0
150
151 #if ENABLE_TEST2 \
152 || (ENABLE_ASH_BASH_COMPAT && ENABLE_ASH_TEST) \
153 || (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
154 # define BASH_TEST2 1
155 #else
156 # define BASH_TEST2 0
157 #endif
158
159 enum token {
160 EOI,
161
162 FILRD, /* file access */
163 FILWR,
164 FILEX,
165
166 FILEXIST,
167
168 FILREG, /* file type */
169 FILDIR,
170 FILCDEV,
171 FILBDEV,
172 FILFIFO,
173 FILSOCK,
174
175 FILSYM,
176 FILGZ,
177 FILTT,
178
179 FILSUID, /* file bit */
180 FILSGID,
181 FILSTCK,
182
183 FILNT, /* file ops */
184 FILOT,
185 FILEQ,
186
187 FILUID,
188 FILGID,
189
190 STREZ, /* str ops */
191 STRNZ,
192 STREQ,
193 STRNE,
194 STRLT,
195 STRGT,
196
197 #if BASH_TEST2
198 REGEX,
199 #endif
200
201 INTEQ, /* int ops */
202 INTNE,
203 INTGE,
204 INTGT,
205 INTLE,
206 INTLT,
207
208 UNOT,
209 BAND,
210 BOR,
211 LPAREN,
212 RPAREN,
213 OPERAND
214 };
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)
221
222 #if TEST_DEBUG
223 int depth;
224 #define nest_msg(...) do { \
225 depth++; \
226 fprintf(stderr, "%*s", depth*2, ""); \
227 fprintf(stderr, __VA_ARGS__); \
228 } while (0)
229 #define unnest_msg(...) do { \
230 fprintf(stderr, "%*s", depth*2, ""); \
231 fprintf(stderr, __VA_ARGS__); \
232 depth--; \
233 } while (0)
234 #define dbg_msg(...) do { \
235 fprintf(stderr, "%*s", depth*2, ""); \
236 fprintf(stderr, __VA_ARGS__); \
237 } while (0)
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); \
242 depth--; \
243 return __res; \
244 } while (0)
245 static const char *const TOKSTR[] = {
246 "EOI",
247 "FILRD",
248 "FILWR",
249 "FILEX",
250 "FILEXIST",
251 "FILREG",
252 "FILDIR",
253 "FILCDEV",
254 "FILBDEV",
255 "FILFIFO",
256 "FILSOCK",
257 "FILSYM",
258 "FILGZ",
259 "FILTT",
260 "FILSUID",
261 "FILSGID",
262 "FILSTCK",
263 "FILNT",
264 "FILOT",
265 "FILEQ",
266 "FILUID",
267 "FILGID",
268 "STREZ",
269 "STRNZ",
270 "STREQ",
271 "STRNE",
272 "STRLT",
273 "STRGT",
274 #if BASH_TEST2
275 "REGEX",
276 #endif
277 "INTEQ",
278 "INTNE",
279 "INTGE",
280 "INTGT",
281 "INTLE",
282 "INTLT",
283 "UNOT",
284 "BAND",
285 "BOR",
286 "LPAREN",
287 "RPAREN",
288 "OPERAND"
289 };
290 #else
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
295 #endif
296
297 enum {
298 UNOP,
299 BINOP,
300 BUNOP,
301 BBINOP,
302 PAREN
303 };
304
305 struct operator_t {
306 unsigned char op_num, op_type;
307 };
308
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 */
327
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.
335 */
336 { /* "==" */ STREQ , BINOP },
337 { /* "!=" */ STRNE , BINOP },
338 { /* "<" */ STRLT , BINOP },
339 { /* ">" */ STRGT , BINOP },
340 #if BASH_TEST2
341 { /* "=~" */ REGEX , BINOP },
342 #endif
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 },
355 #if BASH_TEST2
356 { /* "&&" */ BAND , BBINOP },
357 { /* "||" */ BOR , BBINOP },
358 #endif
359 { /* "(" */ LPAREN , PAREN },
360 { /* ")" */ RPAREN , PAREN },
361 };
362 /* Please keep these two tables in sync */
363 static const char ops_texts[] ALIGN1 =
364 "-r" "\0"
365 "-w" "\0"
366 "-x" "\0"
367 "-e" "\0"
368 "-f" "\0"
369 "-d" "\0"
370 "-c" "\0"
371 "-b" "\0"
372 "-p" "\0"
373 "-u" "\0"
374 "-g" "\0"
375 "-k" "\0"
376 "-s" "\0"
377 "-t" "\0"
378 "-z" "\0"
379 "-n" "\0"
380 "-h" "\0"
381
382 "-O" "\0"
383 "-G" "\0"
384 "-L" "\0"
385 "-S" "\0"
386 "=" "\0"
387 /* "==" is bashism */
388 "==" "\0"
389 "!=" "\0"
390 "<" "\0"
391 ">" "\0"
392 #if BASH_TEST2
393 "=~" "\0"
394 #endif
395 "-eq" "\0"
396 "-ne" "\0"
397 "-ge" "\0"
398 "-gt" "\0"
399 "-le" "\0"
400 "-lt" "\0"
401 "-nt" "\0"
402 "-ot" "\0"
403 "-ef" "\0"
404 "!" "\0"
405 "-a" "\0"
406 "-o" "\0"
407 #if BASH_TEST2
408 "&&" "\0"
409 "||" "\0"
410 #endif
411 "(" "\0"
412 ")" "\0"
413 ;
414
415
416 #if ENABLE_FEATURE_TEST_64
417 typedef int64_t number_t;
418 #else
419 typedef int number_t;
420 #endif
421
422
423 /* We try to minimize both static and stack usage. */
424 struct test_statics {
425 char **args;
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 gid_t *group_array;
430 int ngroups;
431 #if BASH_TEST2
432 bool bash_test2;
433 #endif
434 jmp_buf leaving;
435 };
436
437 /* See test_ptr_hack.c */
438 extern struct test_statics *BB_GLOBAL_CONST test_ptr_to_statics;
439
440 #define S (*test_ptr_to_statics)
441 #define args (S.args )
442 #define last_operator (S.last_operator)
443 #define group_array (S.group_array )
444 #define ngroups (S.ngroups )
445 #define bash_test2 (S.bash_test2 )
446 #define leaving (S.leaving )
447
448 #define INIT_S() do { \
449 XZALLOC_CONST_PTR(&test_ptr_to_statics, sizeof(S)); \
450 } while (0)
451 #define DEINIT_S() do { \
452 free(group_array); \
453 free(test_ptr_to_statics); \
454 } while (0)
455
456 static number_t primary(enum token n);
457
458 static void syntax(const char *op, const char *msg) NORETURN;
syntax(const char * op,const char * msg)459 static void syntax(const char *op, const char *msg)
460 {
461 if (op && *op) {
462 bb_error_msg("%s: %s", op, msg);
463 } else {
464 bb_error_msg("%s: %s"+4, msg);
465 }
466 longjmp(leaving, 2);
467 }
468
469 /* atoi with error detection */
470 //XXX: FIXME: duplicate of existing libbb function?
getn(const char * s)471 static number_t getn(const char *s)
472 {
473 char *p;
474 #if ENABLE_FEATURE_TEST_64
475 long long r;
476 #else
477 long r;
478 #endif
479
480 errno = 0;
481 #if ENABLE_FEATURE_TEST_64
482 r = strtoll(s, &p, 10);
483 #else
484 r = strtol(s, &p, 10);
485 #endif
486
487 if (errno != 0)
488 syntax(s, "out of range");
489
490 if (p == s || *(skip_whitespace(p)) != '\0')
491 syntax(s, "bad number");
492
493 return r;
494 }
495
496 /* UNUSED
497 static int newerf(const char *f1, const char *f2)
498 {
499 struct stat b1, b2;
500
501 return (stat(f1, &b1) == 0 &&
502 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
503 }
504
505 static int olderf(const char *f1, const char *f2)
506 {
507 struct stat b1, b2;
508
509 return (stat(f1, &b1) == 0 &&
510 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
511 }
512
513 static int equalf(const char *f1, const char *f2)
514 {
515 struct stat b1, b2;
516
517 return (stat(f1, &b1) == 0 &&
518 stat(f2, &b2) == 0 &&
519 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
520 }
521 */
522
523
check_operator(const char * s)524 static enum token check_operator(const char *s)
525 {
526 static const struct operator_t no_op = {
527 .op_num = -1,
528 .op_type = -1
529 };
530 int n;
531
532 last_operator = &no_op;
533 if (s == NULL)
534 return EOI;
535 n = index_in_strings(ops_texts, s);
536 if (n < 0)
537 return OPERAND;
538
539 #if BASH_TEST2
540 if (ops_table[n].op_num == REGEX && !bash_test2) {
541 /* =~ is only for [[ ]] */
542 return OPERAND;
543 }
544 if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
545 /* [ ] accepts -a and -o but not && and || */
546 /* [[ ]] accepts && and || but not -a and -o */
547 if (bash_test2 == (s[0] == '-'))
548 return OPERAND;
549 }
550 #endif
551
552 last_operator = &ops_table[n];
553 return ops_table[n].op_num;
554 }
555
556
binop(void)557 static int binop(void)
558 {
559 const char *opnd1, *opnd2;
560 const struct operator_t *op;
561 number_t val1, val2;
562
563 opnd1 = *args;
564 check_operator(*++args);
565 op = last_operator;
566
567 opnd2 = *++args;
568 if (opnd2 == NULL)
569 syntax(args[-1], "argument expected");
570
571 if (is_int_op(op->op_num)) {
572 val1 = getn(opnd1);
573 val2 = getn(opnd2);
574 if (op->op_num == INTEQ)
575 return val1 == val2;
576 if (op->op_num == INTNE)
577 return val1 != val2;
578 if (op->op_num == INTGE)
579 return val1 >= val2;
580 if (op->op_num == INTGT)
581 return val1 > val2;
582 if (op->op_num == INTLE)
583 return val1 <= val2;
584 /*if (op->op_num == INTLT)*/
585 return val1 < val2;
586 }
587 #if BASH_TEST2
588 if (bash_test2) {
589 if (op->op_num == STREQ) {
590 val1 = fnmatch(opnd2, opnd1, 0);
591 return val1 == 0;
592 }
593 if (op->op_num == STRNE) {
594 val1 = fnmatch(opnd2, opnd1, 0);
595 return val1 != 0;
596 }
597 if (op->op_num == REGEX) {
598 regex_t re_buffer;
599 memset(&re_buffer, 0, sizeof(re_buffer));
600 if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) { // REG_NEWLINE?
601 /* Bad regex */
602 longjmp(leaving, 2); /* [[ a =~ * ]]; echo $? - prints 2 (silently, no error msg) */
603 }
604 val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
605 regfree(&re_buffer);
606 return val1 == 0;
607 }
608 }
609 #endif
610 if (is_str_op(op->op_num)) {
611 val1 = strcmp(opnd1, opnd2);
612 if (op->op_num == STREQ)
613 return val1 == 0;
614 if (op->op_num == STRNE)
615 return val1 != 0;
616 if (op->op_num == STRLT)
617 return val1 < 0;
618 /*if (op->op_num == STRGT)*/
619 return val1 > 0;
620 }
621 /* We are sure that these three are by now the only binops we didn't check
622 * yet, so we do not check if the class is correct:
623 */
624 /* if (is_file_op(op->op_num)) */
625 {
626 struct stat b1, b2;
627
628 if (stat(opnd1, &b1) || stat(opnd2, &b2))
629 return 0; /* false, since at least one stat failed */
630 if (op->op_num == FILNT)
631 return b1.st_mtime > b2.st_mtime;
632 if (op->op_num == FILOT)
633 return b1.st_mtime < b2.st_mtime;
634 /*if (op->op_num == FILEQ)*/
635 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
636 }
637 /*return 1; - NOTREACHED */
638 }
639
initialize_group_array(void)640 static void initialize_group_array(void)
641 {
642 group_array = bb_getgroups(&ngroups, NULL);
643 }
644
645 /* Return non-zero if GID is one that we have in our groups list. */
646 //XXX: FIXME: duplicate of existing libbb function?
647 // see toplevel TODO file:
648 // possible code duplication ingroup() and is_a_group_member()
is_a_group_member(gid_t gid)649 static int is_a_group_member(gid_t gid)
650 {
651 int i;
652
653 /* Short-circuit if possible, maybe saving a call to getgroups(). */
654 if (gid == getgid() || gid == getegid())
655 return 1;
656
657 if (ngroups == 0)
658 initialize_group_array();
659
660 /* Search through the list looking for GID. */
661 for (i = 0; i < ngroups; i++)
662 if (gid == group_array[i])
663 return 1;
664
665 return 0;
666 }
667
668
669 /* Do the same thing access(2) does, but use the effective uid and gid,
670 and don't make the mistake of telling root that any file is
671 executable. */
test_eaccess(struct stat * st,int mode)672 static int test_eaccess(struct stat *st, int mode)
673 {
674 unsigned int euid = geteuid();
675
676 if (euid == 0) {
677 /* Root can read or write any file. */
678 if (mode != X_OK)
679 return 0;
680
681 /* Root can execute any file that has any one of the execute
682 * bits set. */
683 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
684 return 0;
685 }
686
687 if (st->st_uid == euid) /* owner */
688 mode <<= 6;
689 else if (is_a_group_member(st->st_gid))
690 mode <<= 3;
691
692 if (st->st_mode & mode)
693 return 0;
694
695 return -1;
696 }
697
698
filstat(char * nm,enum token mode)699 static int filstat(char *nm, enum token mode)
700 {
701 struct stat s;
702 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
703
704 if (mode == FILSYM) {
705 #ifdef S_IFLNK
706 if (lstat(nm, &s) == 0) {
707 i = S_IFLNK;
708 goto filetype;
709 }
710 #endif
711 return 0;
712 }
713
714 if (stat(nm, &s) != 0)
715 return 0;
716 if (mode == FILEXIST)
717 return 1;
718 if (is_file_access(mode)) {
719 if (mode == FILRD)
720 i = R_OK;
721 if (mode == FILWR)
722 i = W_OK;
723 if (mode == FILEX)
724 i = X_OK;
725 return test_eaccess(&s, i) == 0;
726 }
727 if (is_file_type(mode)) {
728 if (mode == FILREG)
729 i = S_IFREG;
730 if (mode == FILDIR)
731 i = S_IFDIR;
732 if (mode == FILCDEV)
733 i = S_IFCHR;
734 if (mode == FILBDEV)
735 i = S_IFBLK;
736 if (mode == FILFIFO) {
737 #ifdef S_IFIFO
738 i = S_IFIFO;
739 #else
740 return 0;
741 #endif
742 }
743 if (mode == FILSOCK) {
744 #ifdef S_IFSOCK
745 i = S_IFSOCK;
746 #else
747 return 0;
748 #endif
749 }
750 filetype:
751 return ((s.st_mode & S_IFMT) == i);
752 }
753 if (is_file_bit(mode)) {
754 if (mode == FILSUID)
755 i = S_ISUID;
756 if (mode == FILSGID)
757 i = S_ISGID;
758 if (mode == FILSTCK)
759 i = S_ISVTX;
760 return ((s.st_mode & i) != 0);
761 }
762 if (mode == FILGZ)
763 return s.st_size > 0L;
764 if (mode == FILUID)
765 return s.st_uid == geteuid();
766 if (mode == FILGID)
767 return s.st_gid == getegid();
768 return 1; /* NOTREACHED */
769 }
770
771
nexpr(enum token n)772 static number_t nexpr(enum token n)
773 {
774 number_t res;
775
776 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
777 if (n == UNOT) {
778 n = check_operator(*++args);
779 if (n == EOI) {
780 /* special case: [ ! ], [ a -a ! ] are valid */
781 /* IOW, "! ARG" may miss ARG */
782 args--;
783 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
784 return 1;
785 }
786 res = !nexpr(n);
787 unnest_msg("<nexpr:%lld\n", res);
788 return res;
789 }
790 res = primary(n);
791 unnest_msg("<nexpr:%lld\n", res);
792 return res;
793 }
794
795
aexpr(enum token n)796 static number_t aexpr(enum token n)
797 {
798 number_t res;
799
800 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
801 res = nexpr(n);
802 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
803 if (check_operator(*++args) == BAND) {
804 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
805 res = aexpr(check_operator(*++args)) && res;
806 unnest_msg("<aexpr:%lld\n", res);
807 return res;
808 }
809 args--;
810 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
811 return res;
812 }
813
814
oexpr(enum token n)815 static number_t oexpr(enum token n)
816 {
817 number_t res;
818
819 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
820 res = aexpr(n);
821 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
822 if (check_operator(*++args) == BOR) {
823 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
824 res = oexpr(check_operator(*++args)) || res;
825 unnest_msg("<oexpr:%lld\n", res);
826 return res;
827 }
828 args--;
829 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
830 return res;
831 }
832
833
primary(enum token n)834 static number_t primary(enum token n)
835 {
836 #if TEST_DEBUG
837 number_t res = res; /* for compiler */
838 #else
839 number_t res;
840 #endif
841 const struct operator_t *args0_op;
842
843 nest_msg(">primary(%s)\n", TOKSTR[n]);
844 if (n == EOI) {
845 syntax(NULL, "argument expected");
846 }
847 if (n == LPAREN) {
848 res = oexpr(check_operator(*++args));
849 if (check_operator(*++args) != RPAREN)
850 syntax(NULL, "closing paren expected");
851 unnest_msg("<primary:%lld\n", res);
852 return res;
853 }
854
855 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
856 * do the same */
857 args0_op = last_operator;
858 /* last_operator = operator at args[1] */
859 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
860 if (args[2]) {
861 // coreutils also does this:
862 // if (args[3] && args[0]="-l" && args[2] is BINOP)
863 // return binop(1 /* prepended by -l */);
864 if (last_operator->op_type == BINOP)
865 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
866 }
867 }
868 /* check "is args[0] unop?" second */
869 if (args0_op->op_type == UNOP) {
870 /* unary expression */
871 if (args[1] == NULL)
872 // syntax(args0_op->op_text, "argument expected");
873 goto check_emptiness;
874 args++;
875 if (n == STREZ)
876 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
877 if (n == STRNZ)
878 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
879 if (n == FILTT)
880 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
881 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
882 }
883
884 /*check_operator(args[1]); - already done */
885 if (last_operator->op_type == BINOP) {
886 /* args[2] is known to be NULL, isn't it bound to fail? */
887 unnest_msg_and_return(binop(), "<primary:%lld\n");
888 }
889 check_emptiness:
890 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
891 }
892
893
test_main(int argc,char ** argv)894 int test_main(int argc, char **argv)
895 {
896 int res;
897 const char *arg0;
898 #if BASH_TEST2
899 bool bt2 = 0;
900 #endif
901
902 arg0 = bb_basename(argv[0]);
903 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
904 && (arg0[0] == '[')
905 ) {
906 --argc;
907 if (!arg0[1]) { /* "[" ? */
908 if (NOT_LONE_CHAR(argv[argc], ']')) {
909 bb_simple_error_msg("missing ]");
910 return 2;
911 }
912 } else { /* assuming "[[" */
913 if (strcmp(argv[argc], "]]") != 0) {
914 bb_simple_error_msg("missing ]]");
915 return 2;
916 }
917 #if BASH_TEST2
918 bt2 = 1;
919 #endif
920 }
921 argv[argc] = NULL;
922 }
923 /* argc is unused after this point */
924
925 /* We must do DEINIT_S() prior to returning */
926 INIT_S();
927
928 #if BASH_TEST2
929 bash_test2 = bt2;
930 #endif
931
932 res = setjmp(leaving);
933 if (res)
934 goto ret;
935
936 /* resetting ngroups is probably unnecessary. it will
937 * force a new call to getgroups(), which prevents using
938 * group data fetched during a previous call. but the
939 * only way the group data could be stale is if there's
940 * been an intervening call to setgroups(), and this
941 * isn't likely in the case of a shell. paranoia
942 * prevails...
943 */
944 /*ngroups = 0; - done by INIT_S() */
945
946 argv++;
947 args = argv;
948
949 /* Implement special cases from POSIX.2, section 4.62.4.
950 * Testcase: "test '(' = '('"
951 * The general parser would misinterpret '(' as group start.
952 */
953 if (1) {
954 int negate = 0;
955 again:
956 if (!argv[0]) {
957 /* "test" */
958 res = 1;
959 goto ret_special;
960 }
961 if (!argv[1]) {
962 /* "test [!] arg" */
963 res = (argv[0][0] == '\0');
964 goto ret_special;
965 }
966 if (argv[2]) {
967 if (!argv[3]) {
968 /*
969 * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
970 * """ 3 arguments:
971 * If $2 is a binary primary, perform the binary test of $1 and $3.
972 * """
973 */
974 check_operator(argv[1]);
975 if (last_operator->op_type == BINOP) {
976 /* "test [!] arg1 <binary_op> arg2" */
977 args = argv;
978 res = (binop() == 0);
979 ret_special:
980 /* If there was leading "!" op... */
981 res ^= negate;
982 goto ret;
983 }
984 /* """If $1 is '(' and $3 is ')', perform the unary test of $2."""
985 * Looks like this works without additional coding.
986 */
987 goto check_negate;
988 }
989 /* argv[3] exists (at least 4 args), is it exactly 4 args? */
990 if (!argv[4]) {
991 /*
992 * """ 4 arguments:
993 * If $1 is '!', negate the three-argument test of $2, $3, and $4.
994 * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.
995 * """
996 * Example why code below is necessary: test '(' ! -e ')'
997 */
998 if (LONE_CHAR(argv[0], '(')
999 && LONE_CHAR(argv[3], ')')
1000 ) {
1001 /* "test [!] ( x y )" */
1002 argv[3] = NULL;
1003 argv++;
1004 }
1005 }
1006 }
1007 check_negate:
1008 if (LONE_CHAR(argv[0], '!')) {
1009 argv++;
1010 negate ^= 1;
1011 goto again;
1012 }
1013 }
1014
1015 res = !oexpr(check_operator(*args));
1016
1017 if (*args != NULL && *++args != NULL) {
1018 /* Examples:
1019 * test 3 -lt 5 6
1020 * test -t 1 2
1021 */
1022 bb_error_msg("%s: unknown operand", *args);
1023 res = 2;
1024 }
1025 ret:
1026 DEINIT_S();
1027 return res;
1028 }
1029