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