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 *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 (*(struct test_statics**)not_const_pp(&test_ptr_to_statics)) = xzalloc(sizeof(S)); \
450 barrier(); \
451} while (0)
452#define DEINIT_S() do { \
453 free(group_array); \
454 free(test_ptr_to_statics); \
455} while (0)
456
457static number_t primary(enum token n);
458
459static void syntax(const char *op, const char *msg) NORETURN;
460static void syntax(const char *op, const char *msg)
461{
462 if (op && *op) {
463 bb_error_msg("%s: %s", op, msg);
464 } else {
465 bb_error_msg("%s: %s"+4, msg);
466 }
467 longjmp(leaving, 2);
468}
469
470
471
472static number_t getn(const char *s)
473{
474 char *p;
475#if ENABLE_FEATURE_TEST_64
476 long long r;
477#else
478 long r;
479#endif
480
481 errno = 0;
482#if ENABLE_FEATURE_TEST_64
483 r = strtoll(s, &p, 10);
484#else
485 r = strtol(s, &p, 10);
486#endif
487
488 if (errno != 0)
489 syntax(s, "out of range");
490
491 if (p == s || *(skip_whitespace(p)) != '\0')
492 syntax(s, "bad number");
493
494 return r;
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
524
525static enum token check_operator(const char *s)
526{
527 static const struct operator_t no_op = {
528 .op_num = -1,
529 .op_type = -1
530 };
531 int n;
532
533 last_operator = &no_op;
534 if (s == NULL)
535 return EOI;
536 n = index_in_strings(ops_texts, s);
537 if (n < 0)
538 return OPERAND;
539
540#if BASH_TEST2
541 if (ops_table[n].op_num == REGEX && !bash_test2) {
542
543 return OPERAND;
544 }
545 if (ops_table[n].op_num == BAND || ops_table[n].op_num == BOR) {
546
547
548 if (bash_test2 == (s[0] == '-'))
549 return OPERAND;
550 }
551#endif
552
553 last_operator = &ops_table[n];
554 return ops_table[n].op_num;
555}
556
557
558static int binop(void)
559{
560 const char *opnd1, *opnd2;
561 const struct operator_t *op;
562 number_t val1, val2;
563
564 opnd1 = *args;
565 check_operator(*++args);
566 op = last_operator;
567
568 opnd2 = *++args;
569 if (opnd2 == NULL)
570 syntax(args[-1], "argument expected");
571
572 if (is_int_op(op->op_num)) {
573 val1 = getn(opnd1);
574 val2 = getn(opnd2);
575 if (op->op_num == INTEQ)
576 return val1 == val2;
577 if (op->op_num == INTNE)
578 return val1 != val2;
579 if (op->op_num == INTGE)
580 return val1 >= val2;
581 if (op->op_num == INTGT)
582 return val1 > val2;
583 if (op->op_num == INTLE)
584 return val1 <= val2;
585
586 return val1 < val2;
587 }
588#if BASH_TEST2
589 if (bash_test2) {
590 if (op->op_num == STREQ) {
591 val1 = fnmatch(opnd2, opnd1, 0);
592 return val1 == 0;
593 }
594 if (op->op_num == STRNE) {
595 val1 = fnmatch(opnd2, opnd1, 0);
596 return val1 != 0;
597 }
598 if (op->op_num == REGEX) {
599 regex_t re_buffer;
600 memset(&re_buffer, 0, sizeof(re_buffer));
601 if (regcomp(&re_buffer, opnd2, REG_EXTENDED)) {
602
603 longjmp(leaving, 2);
604 }
605 val1 = regexec(&re_buffer, opnd1, 0, NULL, 0);
606 regfree(&re_buffer);
607 return val1 == 0;
608 }
609 }
610#endif
611 if (is_str_op(op->op_num)) {
612 val1 = strcmp(opnd1, opnd2);
613 if (op->op_num == STREQ)
614 return val1 == 0;
615 if (op->op_num == STRNE)
616 return val1 != 0;
617 if (op->op_num == STRLT)
618 return val1 < 0;
619
620 return val1 > 0;
621 }
622
623
624
625
626 {
627 struct stat b1, b2;
628
629 if (stat(opnd1, &b1) || stat(opnd2, &b2))
630 return 0;
631 if (op->op_num == FILNT)
632 return b1.st_mtime > b2.st_mtime;
633 if (op->op_num == FILOT)
634 return b1.st_mtime < b2.st_mtime;
635
636 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
637 }
638
639}
640
641static void initialize_group_array(void)
642{
643 group_array = bb_getgroups(&ngroups, NULL);
644}
645
646
647
648
649
650static int is_a_group_member(gid_t gid)
651{
652 int i;
653
654
655 if (gid == getgid() || gid == getegid())
656 return 1;
657
658 if (ngroups == 0)
659 initialize_group_array();
660
661
662 for (i = 0; i < ngroups; i++)
663 if (gid == group_array[i])
664 return 1;
665
666 return 0;
667}
668
669
670
671
672
673static int test_eaccess(struct stat *st, int mode)
674{
675 unsigned int euid = geteuid();
676
677 if (euid == 0) {
678
679 if (mode != X_OK)
680 return 0;
681
682
683
684 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
685 return 0;
686 }
687
688 if (st->st_uid == euid)
689 mode <<= 6;
690 else if (is_a_group_member(st->st_gid))
691 mode <<= 3;
692
693 if (st->st_mode & mode)
694 return 0;
695
696 return -1;
697}
698
699
700static int filstat(char *nm, enum token mode)
701{
702 struct stat s;
703 unsigned i = i;
704
705 if (mode == FILSYM) {
706#ifdef S_IFLNK
707 if (lstat(nm, &s) == 0) {
708 i = S_IFLNK;
709 goto filetype;
710 }
711#endif
712 return 0;
713 }
714
715 if (stat(nm, &s) != 0)
716 return 0;
717 if (mode == FILEXIST)
718 return 1;
719 if (is_file_access(mode)) {
720 if (mode == FILRD)
721 i = R_OK;
722 if (mode == FILWR)
723 i = W_OK;
724 if (mode == FILEX)
725 i = X_OK;
726 return test_eaccess(&s, i) == 0;
727 }
728 if (is_file_type(mode)) {
729 if (mode == FILREG)
730 i = S_IFREG;
731 if (mode == FILDIR)
732 i = S_IFDIR;
733 if (mode == FILCDEV)
734 i = S_IFCHR;
735 if (mode == FILBDEV)
736 i = S_IFBLK;
737 if (mode == FILFIFO) {
738#ifdef S_IFIFO
739 i = S_IFIFO;
740#else
741 return 0;
742#endif
743 }
744 if (mode == FILSOCK) {
745#ifdef S_IFSOCK
746 i = S_IFSOCK;
747#else
748 return 0;
749#endif
750 }
751 filetype:
752 return ((s.st_mode & S_IFMT) == i);
753 }
754 if (is_file_bit(mode)) {
755 if (mode == FILSUID)
756 i = S_ISUID;
757 if (mode == FILSGID)
758 i = S_ISGID;
759 if (mode == FILSTCK)
760 i = S_ISVTX;
761 return ((s.st_mode & i) != 0);
762 }
763 if (mode == FILGZ)
764 return s.st_size > 0L;
765 if (mode == FILUID)
766 return s.st_uid == geteuid();
767 if (mode == FILGID)
768 return s.st_gid == getegid();
769 return 1;
770}
771
772
773static number_t nexpr(enum token n)
774{
775 number_t res;
776
777 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
778 if (n == UNOT) {
779 n = check_operator(*++args);
780 if (n == EOI) {
781
782
783 args--;
784 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
785 return 1;
786 }
787 res = !nexpr(n);
788 unnest_msg("<nexpr:%lld\n", res);
789 return res;
790 }
791 res = primary(n);
792 unnest_msg("<nexpr:%lld\n", res);
793 return res;
794}
795
796
797static number_t aexpr(enum token n)
798{
799 number_t res;
800
801 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
802 res = nexpr(n);
803 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
804 if (check_operator(*++args) == BAND) {
805 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
806 res = aexpr(check_operator(*++args)) && res;
807 unnest_msg("<aexpr:%lld\n", res);
808 return res;
809 }
810 args--;
811 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
812 return res;
813}
814
815
816static number_t oexpr(enum token n)
817{
818 number_t res;
819
820 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
821 res = aexpr(n);
822 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
823 if (check_operator(*++args) == BOR) {
824 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
825 res = oexpr(check_operator(*++args)) || res;
826 unnest_msg("<oexpr:%lld\n", res);
827 return res;
828 }
829 args--;
830 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
831 return res;
832}
833
834
835static number_t primary(enum token n)
836{
837#if TEST_DEBUG
838 number_t res = res;
839#else
840 number_t res;
841#endif
842 const struct operator_t *args0_op;
843
844 nest_msg(">primary(%s)\n", TOKSTR[n]);
845 if (n == EOI) {
846 syntax(NULL, "argument expected");
847 }
848 if (n == LPAREN) {
849 res = oexpr(check_operator(*++args));
850 if (check_operator(*++args) != RPAREN)
851 syntax(NULL, "closing paren expected");
852 unnest_msg("<primary:%lld\n", res);
853 return res;
854 }
855
856
857
858 args0_op = last_operator;
859
860 if (check_operator(args[1]) != EOI) {
861 if (args[2]) {
862
863
864
865 if (last_operator->op_type == BINOP)
866 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
867 }
868 }
869
870 if (args0_op->op_type == UNOP) {
871
872 if (args[1] == NULL)
873
874 goto check_emptiness;
875 args++;
876 if (n == STREZ)
877 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
878 if (n == STRNZ)
879 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
880 if (n == FILTT)
881 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
882 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
883 }
884
885
886 if (last_operator->op_type == BINOP) {
887
888 unnest_msg_and_return(binop(), "<primary:%lld\n");
889 }
890 check_emptiness:
891 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
892}
893
894
895int test_main(int argc, char **argv)
896{
897 int res;
898 const char *arg0;
899#if BASH_TEST2
900 bool bt2 = 0;
901#endif
902
903 arg0 = bb_basename(argv[0]);
904 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
905 && (arg0[0] == '[')
906 ) {
907 --argc;
908 if (!arg0[1]) {
909 if (NOT_LONE_CHAR(argv[argc], ']')) {
910 bb_simple_error_msg("missing ]");
911 return 2;
912 }
913 } else {
914 if (strcmp(argv[argc], "]]") != 0) {
915 bb_simple_error_msg("missing ]]");
916 return 2;
917 }
918#if BASH_TEST2
919 bt2 = 1;
920#endif
921 }
922 argv[argc] = NULL;
923 }
924
925
926
927 INIT_S();
928
929#if BASH_TEST2
930 bash_test2 = bt2;
931#endif
932
933 res = setjmp(leaving);
934 if (res)
935 goto ret;
936
937
938
939
940
941
942
943
944
945
946
947 argv++;
948 args = argv;
949
950
951
952
953
954 if (1) {
955 int negate = 0;
956 again:
957 if (!argv[0]) {
958
959 res = 1;
960 goto ret_special;
961 }
962 if (!argv[1]) {
963
964 res = (argv[0][0] == '\0');
965 goto ret_special;
966 }
967 if (argv[2]) {
968 if (!argv[3]) {
969
970
971
972
973
974
975 check_operator(argv[1]);
976 if (last_operator->op_type == BINOP) {
977
978 args = argv;
979 res = (binop() == 0);
980 ret_special:
981
982 res ^= negate;
983 goto ret;
984 }
985
986
987
988 goto check_negate;
989 }
990
991 if (!argv[4]) {
992
993
994
995
996
997
998
999 if (LONE_CHAR(argv[0], '(')
1000 && LONE_CHAR(argv[3], ')')
1001 ) {
1002
1003 argv[3] = NULL;
1004 argv++;
1005 }
1006 }
1007 }
1008 check_negate:
1009 if (LONE_CHAR(argv[0], '!')) {
1010 argv++;
1011 negate ^= 1;
1012 goto again;
1013 }
1014 }
1015
1016 res = !oexpr(check_operator(*args));
1017
1018 if (*args != NULL && *++args != NULL) {
1019
1020
1021
1022
1023 bb_error_msg("%s: unknown operand", *args);
1024 res = 2;
1025 }
1026 ret:
1027 DEINIT_S();
1028 return res;
1029}
1030