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
80
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#define TEST_DEBUG 0
148
149enum token {
150 EOI,
151
152 FILRD,
153 FILWR,
154 FILEX,
155
156 FILEXIST,
157
158 FILREG,
159 FILDIR,
160 FILCDEV,
161 FILBDEV,
162 FILFIFO,
163 FILSOCK,
164
165 FILSYM,
166 FILGZ,
167 FILTT,
168
169 FILSUID,
170 FILSGID,
171 FILSTCK,
172
173 FILNT,
174 FILOT,
175 FILEQ,
176
177 FILUID,
178 FILGID,
179
180 STREZ,
181 STRNZ,
182 STREQ,
183 STRNE,
184 STRLT,
185 STRGT,
186
187 INTEQ,
188 INTNE,
189 INTGE,
190 INTGT,
191 INTLE,
192 INTLT,
193
194 UNOT,
195 BAND,
196 BOR,
197 LPAREN,
198 RPAREN,
199 OPERAND
200};
201#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)
202#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)
203#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)
204#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
205#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
206#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
207
208#if TEST_DEBUG
209int depth;
210#define nest_msg(...) do { \
211 depth++; \
212 fprintf(stderr, "%*s", depth*2, ""); \
213 fprintf(stderr, __VA_ARGS__); \
214} while (0)
215#define unnest_msg(...) do { \
216 fprintf(stderr, "%*s", depth*2, ""); \
217 fprintf(stderr, __VA_ARGS__); \
218 depth--; \
219} while (0)
220#define dbg_msg(...) do { \
221 fprintf(stderr, "%*s", depth*2, ""); \
222 fprintf(stderr, __VA_ARGS__); \
223} while (0)
224#define unnest_msg_and_return(expr, ...) do { \
225 number_t __res = (expr); \
226 fprintf(stderr, "%*s", depth*2, ""); \
227 fprintf(stderr, __VA_ARGS__, res); \
228 depth--; \
229 return __res; \
230} while (0)
231static const char *const TOKSTR[] = {
232 "EOI",
233 "FILRD",
234 "FILWR",
235 "FILEX",
236 "FILEXIST",
237 "FILREG",
238 "FILDIR",
239 "FILCDEV",
240 "FILBDEV",
241 "FILFIFO",
242 "FILSOCK",
243 "FILSYM",
244 "FILGZ",
245 "FILTT",
246 "FILSUID",
247 "FILSGID",
248 "FILSTCK",
249 "FILNT",
250 "FILOT",
251 "FILEQ",
252 "FILUID",
253 "FILGID",
254 "STREZ",
255 "STRNZ",
256 "STREQ",
257 "STRNE",
258 "STRLT",
259 "STRGT",
260 "INTEQ",
261 "INTNE",
262 "INTGE",
263 "INTGT",
264 "INTLE",
265 "INTLT",
266 "UNOT",
267 "BAND",
268 "BOR",
269 "LPAREN",
270 "RPAREN",
271 "OPERAND"
272};
273#else
274#define nest_msg(...) ((void)0)
275#define unnest_msg(...) ((void)0)
276#define dbg_msg(...) ((void)0)
277#define unnest_msg_and_return(expr, ...) return expr
278#endif
279
280enum {
281 UNOP,
282 BINOP,
283 BUNOP,
284 BBINOP,
285 PAREN
286};
287
288struct operator_t {
289 unsigned char op_num, op_type;
290};
291
292static const struct operator_t ops_table[] = {
293 { FILRD , UNOP },
294 { FILWR , UNOP },
295 { FILEX , UNOP },
296 { FILEXIST, UNOP },
297 { FILREG , UNOP },
298 { FILDIR , UNOP },
299 { FILCDEV , UNOP },
300 { FILBDEV , UNOP },
301 { FILFIFO , UNOP },
302 { FILSUID , UNOP },
303 { FILSGID , UNOP },
304 { FILSTCK , UNOP },
305 { FILGZ , UNOP },
306 { FILTT , UNOP },
307 { STREZ , UNOP },
308 { STRNZ , UNOP },
309 { FILSYM , UNOP },
310
311 { FILUID , UNOP },
312 { FILGID , UNOP },
313 { FILSYM , UNOP },
314 { FILSOCK , UNOP },
315 { STREQ , BINOP },
316
317
318
319 { STREQ , BINOP },
320 { STRNE , BINOP },
321 { STRLT , BINOP },
322 { STRGT , BINOP },
323 { INTEQ , BINOP },
324 { INTNE , BINOP },
325 { INTGE , BINOP },
326 { INTGT , BINOP },
327 { INTLE , BINOP },
328 { INTLT , BINOP },
329 { FILNT , BINOP },
330 { FILOT , BINOP },
331 { FILEQ , BINOP },
332 { UNOT , BUNOP },
333 { BAND , BBINOP },
334 { BOR , BBINOP },
335 { LPAREN , PAREN },
336 { RPAREN , PAREN },
337};
338
339static const char ops_texts[] ALIGN1 =
340 "-r" "\0"
341 "-w" "\0"
342 "-x" "\0"
343 "-e" "\0"
344 "-f" "\0"
345 "-d" "\0"
346 "-c" "\0"
347 "-b" "\0"
348 "-p" "\0"
349 "-u" "\0"
350 "-g" "\0"
351 "-k" "\0"
352 "-s" "\0"
353 "-t" "\0"
354 "-z" "\0"
355 "-n" "\0"
356 "-h" "\0"
357
358 "-O" "\0"
359 "-G" "\0"
360 "-L" "\0"
361 "-S" "\0"
362 "=" "\0"
363
364 "==" "\0"
365 "!=" "\0"
366 "<" "\0"
367 ">" "\0"
368 "-eq" "\0"
369 "-ne" "\0"
370 "-ge" "\0"
371 "-gt" "\0"
372 "-le" "\0"
373 "-lt" "\0"
374 "-nt" "\0"
375 "-ot" "\0"
376 "-ef" "\0"
377 "!" "\0"
378 "-a" "\0"
379 "-o" "\0"
380 "(" "\0"
381 ")" "\0"
382;
383
384
385#if ENABLE_FEATURE_TEST_64
386typedef int64_t number_t;
387#else
388typedef int number_t;
389#endif
390
391
392
393struct test_statics {
394 char **args;
395
396
397 const struct operator_t *last_operator;
398 gid_t *group_array;
399 int ngroups;
400 jmp_buf leaving;
401};
402
403
404extern struct test_statics *const test_ptr_to_statics;
405
406#define S (*test_ptr_to_statics)
407#define args (S.args )
408#define last_operator (S.last_operator)
409#define group_array (S.group_array )
410#define ngroups (S.ngroups )
411#define leaving (S.leaving )
412
413#define INIT_S() do { \
414 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
415 barrier(); \
416} while (0)
417#define DEINIT_S() do { \
418 free(group_array); \
419 free(test_ptr_to_statics); \
420} while (0)
421
422static number_t primary(enum token n);
423
424static void syntax(const char *op, const char *msg) NORETURN;
425static void syntax(const char *op, const char *msg)
426{
427 if (op && *op) {
428 bb_error_msg("%s: %s", op, msg);
429 } else {
430 bb_error_msg("%s: %s"+4, msg);
431 }
432 longjmp(leaving, 2);
433}
434
435
436
437static number_t getn(const char *s)
438{
439 char *p;
440#if ENABLE_FEATURE_TEST_64
441 long long r;
442#else
443 long r;
444#endif
445
446 errno = 0;
447#if ENABLE_FEATURE_TEST_64
448 r = strtoll(s, &p, 10);
449#else
450 r = strtol(s, &p, 10);
451#endif
452
453 if (errno != 0)
454 syntax(s, "out of range");
455
456 if (p == s || *(skip_whitespace(p)) != '\0')
457 syntax(s, "bad number");
458
459 return r;
460}
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490static enum token check_operator(const char *s)
491{
492 static const struct operator_t no_op = {
493 .op_num = -1,
494 .op_type = -1
495 };
496 int n;
497
498 last_operator = &no_op;
499 if (s == NULL)
500 return EOI;
501 n = index_in_strings(ops_texts, s);
502 if (n < 0)
503 return OPERAND;
504 last_operator = &ops_table[n];
505 return ops_table[n].op_num;
506}
507
508
509static int binop(void)
510{
511 const char *opnd1, *opnd2;
512 const struct operator_t *op;
513 number_t val1, val2;
514
515 opnd1 = *args;
516 check_operator(*++args);
517 op = last_operator;
518
519 opnd2 = *++args;
520 if (opnd2 == NULL)
521 syntax(args[-1], "argument expected");
522
523 if (is_int_op(op->op_num)) {
524 val1 = getn(opnd1);
525 val2 = getn(opnd2);
526 if (op->op_num == INTEQ)
527 return val1 == val2;
528 if (op->op_num == INTNE)
529 return val1 != val2;
530 if (op->op_num == INTGE)
531 return val1 >= val2;
532 if (op->op_num == INTGT)
533 return val1 > val2;
534 if (op->op_num == INTLE)
535 return val1 <= val2;
536
537 return val1 < val2;
538 }
539 if (is_str_op(op->op_num)) {
540 val1 = strcmp(opnd1, opnd2);
541 if (op->op_num == STREQ)
542 return val1 == 0;
543 if (op->op_num == STRNE)
544 return val1 != 0;
545 if (op->op_num == STRLT)
546 return val1 < 0;
547
548 return val1 > 0;
549 }
550
551
552
553
554 {
555 struct stat b1, b2;
556
557 if (stat(opnd1, &b1) || stat(opnd2, &b2))
558 return 0;
559 if (op->op_num == FILNT)
560 return b1.st_mtime > b2.st_mtime;
561 if (op->op_num == FILOT)
562 return b1.st_mtime < b2.st_mtime;
563
564 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
565 }
566
567}
568
569static void initialize_group_array(void)
570{
571 group_array = bb_getgroups(&ngroups, NULL);
572}
573
574
575
576
577
578static int is_a_group_member(gid_t gid)
579{
580 int i;
581
582
583 if (gid == getgid() || gid == getegid())
584 return 1;
585
586 if (ngroups == 0)
587 initialize_group_array();
588
589
590 for (i = 0; i < ngroups; i++)
591 if (gid == group_array[i])
592 return 1;
593
594 return 0;
595}
596
597
598
599
600
601static int test_eaccess(struct stat *st, int mode)
602{
603 unsigned int euid = geteuid();
604
605 if (euid == 0) {
606
607 if (mode != X_OK)
608 return 0;
609
610
611
612 if (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
613 return 0;
614 }
615
616 if (st->st_uid == euid)
617 mode <<= 6;
618 else if (is_a_group_member(st->st_gid))
619 mode <<= 3;
620
621 if (st->st_mode & mode)
622 return 0;
623
624 return -1;
625}
626
627
628static int filstat(char *nm, enum token mode)
629{
630 struct stat s;
631 unsigned i = i;
632
633 if (mode == FILSYM) {
634#ifdef S_IFLNK
635 if (lstat(nm, &s) == 0) {
636 i = S_IFLNK;
637 goto filetype;
638 }
639#endif
640 return 0;
641 }
642
643 if (stat(nm, &s) != 0)
644 return 0;
645 if (mode == FILEXIST)
646 return 1;
647 if (is_file_access(mode)) {
648 if (mode == FILRD)
649 i = R_OK;
650 if (mode == FILWR)
651 i = W_OK;
652 if (mode == FILEX)
653 i = X_OK;
654 return test_eaccess(&s, i) == 0;
655 }
656 if (is_file_type(mode)) {
657 if (mode == FILREG)
658 i = S_IFREG;
659 if (mode == FILDIR)
660 i = S_IFDIR;
661 if (mode == FILCDEV)
662 i = S_IFCHR;
663 if (mode == FILBDEV)
664 i = S_IFBLK;
665 if (mode == FILFIFO) {
666#ifdef S_IFIFO
667 i = S_IFIFO;
668#else
669 return 0;
670#endif
671 }
672 if (mode == FILSOCK) {
673#ifdef S_IFSOCK
674 i = S_IFSOCK;
675#else
676 return 0;
677#endif
678 }
679 filetype:
680 return ((s.st_mode & S_IFMT) == i);
681 }
682 if (is_file_bit(mode)) {
683 if (mode == FILSUID)
684 i = S_ISUID;
685 if (mode == FILSGID)
686 i = S_ISGID;
687 if (mode == FILSTCK)
688 i = S_ISVTX;
689 return ((s.st_mode & i) != 0);
690 }
691 if (mode == FILGZ)
692 return s.st_size > 0L;
693 if (mode == FILUID)
694 return s.st_uid == geteuid();
695 if (mode == FILGID)
696 return s.st_gid == getegid();
697 return 1;
698}
699
700
701static number_t nexpr(enum token n)
702{
703 number_t res;
704
705 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
706 if (n == UNOT) {
707 n = check_operator(*++args);
708 if (n == EOI) {
709
710
711 args--;
712 unnest_msg("<nexpr:1 (!EOI), args:%s(%p)\n", args[0], &args[0]);
713 return 1;
714 }
715 res = !nexpr(n);
716 unnest_msg("<nexpr:%lld\n", res);
717 return res;
718 }
719 res = primary(n);
720 unnest_msg("<nexpr:%lld\n", res);
721 return res;
722}
723
724
725static number_t aexpr(enum token n)
726{
727 number_t res;
728
729 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
730 res = nexpr(n);
731 dbg_msg("aexpr: nexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
732 if (check_operator(*++args) == BAND) {
733 dbg_msg("aexpr: arg is AND, next args:%s(%p)\n", args[1], &args[1]);
734 res = aexpr(check_operator(*++args)) && res;
735 unnest_msg("<aexpr:%lld\n", res);
736 return res;
737 }
738 args--;
739 unnest_msg("<aexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
740 return res;
741}
742
743
744static number_t oexpr(enum token n)
745{
746 number_t res;
747
748 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
749 res = aexpr(n);
750 dbg_msg("oexpr: aexpr:%lld, next args:%s(%p)\n", res, args[1], &args[1]);
751 if (check_operator(*++args) == BOR) {
752 dbg_msg("oexpr: next arg is OR, next args:%s(%p)\n", args[1], &args[1]);
753 res = oexpr(check_operator(*++args)) || res;
754 unnest_msg("<oexpr:%lld\n", res);
755 return res;
756 }
757 args--;
758 unnest_msg("<oexpr:%lld, args:%s(%p)\n", res, args[0], &args[0]);
759 return res;
760}
761
762
763static number_t primary(enum token n)
764{
765#if TEST_DEBUG
766 number_t res = res;
767#else
768 number_t res;
769#endif
770 const struct operator_t *args0_op;
771
772 nest_msg(">primary(%s)\n", TOKSTR[n]);
773 if (n == EOI) {
774 syntax(NULL, "argument expected");
775 }
776 if (n == LPAREN) {
777 res = oexpr(check_operator(*++args));
778 if (check_operator(*++args) != RPAREN)
779 syntax(NULL, "closing paren expected");
780 unnest_msg("<primary:%lld\n", res);
781 return res;
782 }
783
784
785
786 args0_op = last_operator;
787
788 if (check_operator(args[1]) != EOI) {
789 if (args[2]) {
790
791
792
793 if (last_operator->op_type == BINOP)
794 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
795 }
796 }
797
798 if (args0_op->op_type == UNOP) {
799
800 if (args[1] == NULL)
801
802 goto check_emptiness;
803 args++;
804 if (n == STREZ)
805 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
806 if (n == STRNZ)
807 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
808 if (n == FILTT)
809 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
810 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
811 }
812
813
814 if (last_operator->op_type == BINOP) {
815
816 unnest_msg_and_return(binop(), "<primary:%lld\n");
817 }
818 check_emptiness:
819 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
820}
821
822
823int test_main(int argc, char **argv)
824{
825 int res;
826 const char *arg0;
827
828 arg0 = bb_basename(argv[0]);
829 if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_TEST || ENABLE_HUSH_TEST)
830 && (arg0[0] == '[')
831 ) {
832 --argc;
833 if (!arg0[1]) {
834 if (NOT_LONE_CHAR(argv[argc], ']')) {
835 bb_error_msg("missing ]");
836 return 2;
837 }
838 } else {
839 if (strcmp(argv[argc], "]]") != 0) {
840 bb_error_msg("missing ]]");
841 return 2;
842 }
843 }
844 argv[argc] = NULL;
845 }
846
847
848
849 INIT_S();
850
851 res = setjmp(leaving);
852 if (res)
853 goto ret;
854
855
856
857
858
859
860
861
862
863
864
865 argv++;
866 args = argv;
867
868
869
870
871
872 if (1) {
873 int negate = 0;
874 again:
875 if (!argv[0]) {
876
877 res = 1;
878 goto ret_special;
879 }
880 if (!argv[1]) {
881
882 res = (argv[0][0] == '\0');
883 goto ret_special;
884 }
885 if (argv[2]) {
886 if (!argv[3]) {
887
888
889
890
891
892
893 check_operator(argv[1]);
894 if (last_operator->op_type == BINOP) {
895
896 args = argv;
897 res = (binop() == 0);
898 ret_special:
899
900 res ^= negate;
901 goto ret;
902 }
903
904
905
906 goto check_negate;
907 }
908
909 if (!argv[4]) {
910
911
912
913
914
915
916
917 if (LONE_CHAR(argv[0], '(')
918 && LONE_CHAR(argv[3], ')')
919 ) {
920
921 argv[3] = NULL;
922 argv++;
923 }
924 }
925 }
926 check_negate:
927 if (LONE_CHAR(argv[0], '!')) {
928 argv++;
929 negate ^= 1;
930 goto again;
931 }
932 }
933
934 res = !oexpr(check_operator(*args));
935
936 if (*args != NULL && *++args != NULL) {
937
938
939
940
941 bb_error_msg("%s: unknown operand", *args);
942 res = 2;
943 }
944 ret:
945 DEINIT_S();
946 return res;
947}
948