1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78#include "libbb.h"
79#include <regex.h>
80#include <fnmatch.h>
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
159enum token {
160 EOI,
161
162 FILRD,
163 FILWR,
164 FILEX,
165
166 FILEXIST,
167
168 FILREG,
169 FILDIR,
170 FILCDEV,
171 FILBDEV,
172 FILFIFO,
173 FILSOCK,
174
175 FILSYM,
176 FILGZ,
177 FILTT,
178
179 FILSUID,
180 FILSGID,
181 FILSTCK,
182
183 FILNT,
184 FILOT,
185 FILEQ,
186
187 FILUID,
188 FILGID,
189
190 STREZ,
191 STRNZ,
192 STREQ,
193 STRNE,
194 STRLT,
195 STRGT,
196
197#if BASH_TEST2
198 REGEX,
199#endif
200
201 INTEQ,
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
223int 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)
245static 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
297enum {
298 UNOP,
299 BINOP,
300 BUNOP,
301 BBINOP,
302 PAREN
303};
304
305struct operator_t {
306 unsigned char op_num, op_type;
307};
308
309static const struct operator_t ops_table[] ALIGN2 = {
310 { FILRD , UNOP },
311 { FILWR , UNOP },
312 { FILEX , UNOP },
313 { FILEXIST, UNOP },
314 { FILREG , UNOP },
315 { FILDIR , UNOP },
316 { FILCDEV , UNOP },
317 { FILBDEV , UNOP },
318 { FILFIFO , UNOP },
319 { FILSUID , UNOP },
320 { FILSGID , UNOP },
321 { FILSTCK , UNOP },
322 { FILGZ , UNOP },
323 { FILTT , UNOP },
324 { STREZ , UNOP },
325 { STRNZ , UNOP },
326 { FILSYM , UNOP },
327
328 { FILUID , UNOP },
329 { FILGID , UNOP },
330 { FILSYM , UNOP },
331 { FILSOCK , UNOP },
332 { STREQ , BINOP },
333
334
335
336 { STREQ , BINOP },
337 { STRNE , BINOP },
338 { STRLT , BINOP },
339 { STRGT , BINOP },
340#if BASH_TEST2
341 { REGEX , BINOP },
342#endif
343 { INTEQ , BINOP },
344 { INTNE , BINOP },
345 { INTGE , BINOP },
346 { INTGT , BINOP },
347 { INTLE , BINOP },
348 { INTLT , BINOP },
349 { FILNT , BINOP },
350 { FILOT , BINOP },
351 { FILEQ , BINOP },
352 { UNOT , BUNOP },
353 { BAND , BBINOP },
354 { BOR , BBINOP },
355#if BASH_TEST2
356 { BAND , BBINOP },
357 { BOR , BBINOP },
358#endif
359 { LPAREN , PAREN },
360 { RPAREN , PAREN },
361};
362
363static 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
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
417typedef int64_t number_t;
418#else
419typedef int number_t;
420#endif
421
422
423
424struct test_statics {
425 char **args;
426
427
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
438extern 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
456static number_t primary(enum token n);
457
458static void syntax(const char *op, const char *msg) NORETURN;
459static 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
470
471static 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
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524static 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
542 return OPERAND;
543 }
544 if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
545
546
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
557static 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
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)) {
601
602 longjmp(leaving, 2);
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
619 return val1 > 0;
620 }
621
622
623
624
625 {
626 struct stat b1, b2;
627
628 if (stat(opnd1, &b1) || stat(opnd2, &b2))
629 return 0;
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
635 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
636 }
637
638}
639
640static void initialize_group_array(void)
641{
642 group_array = bb_getgroups(&ngroups, NULL);
643}
644
645
646
647
648
649static int is_a_group_member(gid_t gid)
650{
651 int i;
652
653
654 if (gid == getgid() || gid == getegid())
655 return 1;
656
657 if (ngroups == 0)
658 initialize_group_array();
659
660
661 for (i = 0; i < ngroups; i++)
662 if (gid == group_array[i])
663 return 1;
664
665 return 0;
666}
667
668
669
670
671
672static int test_eaccess(struct stat *st, int mode)
673{
674 unsigned int euid = geteuid();
675
676 if (euid == 0) {
677
678 if (mode != X_OK)
679 return 0;
680
681
682
683 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
684 return 0;
685 }
686
687 if (st->st_uid == euid)
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
699static int filstat(char *nm, enum token mode)
700{
701 struct stat s;
702 unsigned i = i;
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;
769}
770
771
772static 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
781
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
796static 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
815static 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
834static number_t primary(enum token n)
835{
836#if TEST_DEBUG
837 number_t res = res;
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
856
857 args0_op = last_operator;
858
859 if (check_operator(args[1]) != EOI) {
860 if (args[2]) {
861
862
863
864 if (last_operator->op_type == BINOP)
865 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
866 }
867 }
868
869 if (args0_op->op_type == UNOP) {
870
871 if (args[1] == NULL)
872
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
885 if (last_operator->op_type == BINOP) {
886
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
894int 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 {
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
924
925
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
937
938
939
940
941
942
943
944
945
946 argv++;
947 args = argv;
948
949
950
951
952
953 if (1) {
954 int negate = 0;
955 again:
956 if (!argv[0]) {
957
958 res = 1;
959 goto ret_special;
960 }
961 if (!argv[1]) {
962
963 res = (argv[0][0] == '\0');
964 goto ret_special;
965 }
966 if (argv[2]) {
967 if (!argv[3]) {
968
969
970
971
972
973
974 check_operator(argv[1]);
975 if (last_operator->op_type == BINOP) {
976
977 args = argv;
978 res = (binop() == 0);
979 ret_special:
980
981 res ^= negate;
982 goto ret;
983 }
984
985
986
987 goto check_negate;
988 }
989
990 if (!argv[4]) {
991
992
993
994
995
996
997
998 if (LONE_CHAR(argv[0], '(')
999 && LONE_CHAR(argv[3], ')')
1000 ) {
1001
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
1019
1020
1021
1022 bb_error_msg("%s: unknown operand", *args);
1023 res = 2;
1024 }
1025 ret:
1026 DEINIT_S();
1027 return res;
1028}
1029