aboutsummaryrefslogtreecommitdiff
path: root/coreutils
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-07-19 08:15:13 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-07-19 08:15:13 +0000
commitbd28f6bf7f53ede8df39112d40cb52f2a3d00177 (patch)
tree0e2cc7fa0864c8998d4483fc2398a3c3e9791a0f /coreutils
parent5f116629d80b66bd09d8dc2b849befb1e27cd21a (diff)
downloadbusybox-w32-bd28f6bf7f53ede8df39112d40cb52f2a3d00177.tar.gz
busybox-w32-bd28f6bf7f53ede8df39112d40cb52f2a3d00177.tar.bz2
busybox-w32-bd28f6bf7f53ede8df39112d40cb52f2a3d00177.zip
test: fix parser to prefer binop over unop, as coreutils does.
remove bogus workaround in main(). rename atrocious variables/functions. much expand testsuite. libbb: fix --help to not affect "test --help" function old new delta run_applet_no_and_exit 421 440 +19 nexpr 817 825 +8 static.no_op - 6 +6 test_main 397 257 -140 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 2/1 up/down: 104/-211) Total: -107 bytes
Diffstat (limited to 'coreutils')
-rw-r--r--coreutils/test.c253
1 files changed, 193 insertions, 60 deletions
diff --git a/coreutils/test.c b/coreutils/test.c
index c7c530052..dbfe4af69 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -47,6 +47,8 @@
47 operand ::= <any legal UNIX file name> 47 operand ::= <any legal UNIX file name>
48*/ 48*/
49 49
50#define TEST_DEBUG 0
51
50enum token { 52enum token {
51 EOI, 53 EOI,
52 FILRD, 54 FILRD,
@@ -95,6 +97,79 @@ enum token {
95#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2) 97#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
96#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) 98#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5)
97#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) 99#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2)
100
101#if TEST_DEBUG
102int depth;
103#define nest_msg(...) do { \
104 depth++; \
105 fprintf(stderr, "%*s", depth*2, ""); \
106 fprintf(stderr, __VA_ARGS__); \
107} while (0)
108#define unnest_msg(...) do { \
109 fprintf(stderr, "%*s", depth*2, ""); \
110 fprintf(stderr, __VA_ARGS__); \
111 depth--; \
112} while (0)
113#define dbg_msg(...) do { \
114 fprintf(stderr, "%*s", depth*2, ""); \
115 fprintf(stderr, __VA_ARGS__); \
116} while (0)
117#define unnest_msg_and_return(expr, ...) do { \
118 number_t __res = (expr); \
119 fprintf(stderr, "%*s", depth*2, ""); \
120 fprintf(stderr, __VA_ARGS__, res); \
121 depth--; \
122 return __res; \
123} while (0)
124static const char *const TOKSTR[] = {
125 "EOI",
126 "FILRD",
127 "FILWR",
128 "FILEX",
129 "FILEXIST",
130 "FILREG",
131 "FILDIR",
132 "FILCDEV",
133 "FILBDEV",
134 "FILFIFO",
135 "FILSOCK",
136 "FILSYM",
137 "FILGZ",
138 "FILTT",
139 "FILSUID",
140 "FILSGID",
141 "FILSTCK",
142 "FILNT",
143 "FILOT",
144 "FILEQ",
145 "FILUID",
146 "FILGID",
147 "STREZ",
148 "STRNZ",
149 "STREQ",
150 "STRNE",
151 "STRLT",
152 "STRGT",
153 "INTEQ",
154 "INTNE",
155 "INTGE",
156 "INTGT",
157 "INTLE",
158 "INTLT",
159 "UNOT",
160 "BAND",
161 "BOR",
162 "LPAREN",
163 "RPAREN",
164 "OPERAND"
165};
166#else
167#define nest_msg(...) ((void)0)
168#define unnest_msg(...) ((void)0)
169#define dbg_msg(...) ((void)0)
170#define unnest_msg_and_return(expr, ...) return expr
171#endif
172
98enum token_types { 173enum token_types {
99 UNOP, 174 UNOP,
100 BINOP, 175 BINOP,
@@ -103,10 +178,12 @@ enum token_types {
103 PAREN 178 PAREN
104}; 179};
105 180
106static const struct t_op { 181struct operator_t {
107 char op_text[4]; 182 char op_text[4];
108 unsigned char op_num, op_type; 183 unsigned char op_num, op_type;
109} ops[] = { 184};
185
186static const struct operator_t ops[] = {
110 { "-r", FILRD , UNOP }, 187 { "-r", FILRD , UNOP },
111 { "-w", FILWR , UNOP }, 188 { "-w", FILWR , UNOP },
112 { "-x", FILEX , UNOP }, 189 { "-x", FILEX , UNOP },
@@ -152,16 +229,18 @@ static const struct t_op {
152 229
153 230
154#if ENABLE_FEATURE_TEST_64 231#if ENABLE_FEATURE_TEST_64
155typedef int64_t arith_t; 232typedef int64_t number_t;
156#else 233#else
157typedef int arith_t; 234typedef int number_t;
158#endif 235#endif
159 236
160 237
161/* We try to minimize both static and stack usage. */ 238/* We try to minimize both static and stack usage. */
162struct test_statics { 239struct test_statics {
163 char **t_wp; 240 char **args;
164 const struct t_op *t_wp_op; 241 /* set only by check_operator(), either to bogus struct
242 * or points to matching operator_t struct. Never NULL. */
243 const struct operator_t *last_operator;
165 gid_t *group_array; 244 gid_t *group_array;
166 int ngroups; 245 int ngroups;
167 jmp_buf leaving; 246 jmp_buf leaving;
@@ -171,8 +250,8 @@ struct test_statics {
171extern struct test_statics *const test_ptr_to_statics; 250extern struct test_statics *const test_ptr_to_statics;
172 251
173#define S (*test_ptr_to_statics) 252#define S (*test_ptr_to_statics)
174#define t_wp (S.t_wp ) 253#define args (S.args )
175#define t_wp_op (S.t_wp_op ) 254#define last_operator (S.last_operator)
176#define group_array (S.group_array ) 255#define group_array (S.group_array )
177#define ngroups (S.ngroups ) 256#define ngroups (S.ngroups )
178#define leaving (S.leaving ) 257#define leaving (S.leaving )
@@ -185,7 +264,7 @@ extern struct test_statics *const test_ptr_to_statics;
185 free(test_ptr_to_statics); \ 264 free(test_ptr_to_statics); \
186} while (0) 265} while (0)
187 266
188static arith_t primary(enum token n); 267static number_t primary(enum token n);
189 268
190static void syntax(const char *op, const char *msg) NORETURN; 269static void syntax(const char *op, const char *msg) NORETURN;
191static void syntax(const char *op, const char *msg) 270static void syntax(const char *op, const char *msg)
@@ -200,7 +279,7 @@ static void syntax(const char *op, const char *msg)
200 279
201/* atoi with error detection */ 280/* atoi with error detection */
202//XXX: FIXME: duplicate of existing libbb function? 281//XXX: FIXME: duplicate of existing libbb function?
203static arith_t getn(const char *s) 282static number_t getn(const char *s)
204{ 283{
205 char *p; 284 char *p;
206#if ENABLE_FEATURE_TEST_64 285#if ENABLE_FEATURE_TEST_64
@@ -253,11 +332,15 @@ static int equalf(const char *f1, const char *f2)
253*/ 332*/
254 333
255 334
256static enum token t_lex(char *s) 335static enum token check_operator(char *s)
257{ 336{
258 const struct t_op *op; 337 static const struct operator_t no_op = {
338 .op_num = -1,
339 .op_type = -1
340 };
341 const struct operator_t *op;
259 342
260 t_wp_op = NULL; 343 last_operator = &no_op;
261 if (s == NULL) { 344 if (s == NULL) {
262 return EOI; 345 return EOI;
263 } 346 }
@@ -265,7 +348,7 @@ static enum token t_lex(char *s)
265 op = ops; 348 op = ops;
266 do { 349 do {
267 if (strcmp(s, op->op_text) == 0) { 350 if (strcmp(s, op->op_text) == 0) {
268 t_wp_op = op; 351 last_operator = op;
269 return op->op_num; 352 return op->op_num;
270 } 353 }
271 op++; 354 op++;
@@ -278,14 +361,14 @@ static enum token t_lex(char *s)
278static int binop(void) 361static int binop(void)
279{ 362{
280 const char *opnd1, *opnd2; 363 const char *opnd1, *opnd2;
281 struct t_op const *op; 364 const struct operator_t *op;
282 arith_t val1, val2; 365 number_t val1, val2;
283 366
284 opnd1 = *t_wp; 367 opnd1 = *args;
285 (void) t_lex(*++t_wp); 368 check_operator(*++args);
286 op = t_wp_op; 369 op = last_operator;
287 370
288 opnd2 = *++t_wp; 371 opnd2 = *++args;
289 if (opnd2 == NULL) 372 if (opnd2 == NULL)
290 syntax(op->op_text, "argument expected"); 373 syntax(op->op_text, "argument expected");
291 374
@@ -482,72 +565,117 @@ static int filstat(char *nm, enum token mode)
482} 565}
483 566
484 567
485static arith_t nexpr(enum token n) 568static number_t nexpr(enum token n)
486{ 569{
487 if (n == UNOT) 570 number_t res;
488 return !nexpr(t_lex(*++t_wp)); 571
489 return primary(n); 572 nest_msg(">nexpr(%s)\n", TOKSTR[n]);
573 if (n == UNOT) {
574 res = !nexpr(check_operator(*++args));
575 unnest_msg("<nexpr:%lld\n", res);
576 return res;
577 }
578 res = primary(n);
579 unnest_msg("<nexpr:%lld\n", res);
580 return res;
490} 581}
491 582
492 583
493static arith_t aexpr(enum token n) 584static number_t aexpr(enum token n)
494{ 585{
495 arith_t res; 586 number_t res;
496 587
588 nest_msg(">aexpr(%s)\n", TOKSTR[n]);
497 res = nexpr(n); 589 res = nexpr(n);
498 if (t_lex(*++t_wp) == BAND) 590 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
499 return aexpr(t_lex(*++t_wp)) && res; 591 if (check_operator(*++args) == BAND) {
500 t_wp--; 592 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]);
593 res = aexpr(check_operator(*++args)) && res;
594 unnest_msg("<aexpr:%lld\n", res);
595 return res;
596 }
597 args--;
598 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]);
501 return res; 599 return res;
502} 600}
503 601
504 602
505static arith_t oexpr(enum token n) 603static number_t oexpr(enum token n)
506{ 604{
507 arith_t res; 605 number_t res;
508 606
607 nest_msg(">oexpr(%s)\n", TOKSTR[n]);
509 res = aexpr(n); 608 res = aexpr(n);
510 if (t_lex(*++t_wp) == BOR) { 609 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
511 return oexpr(t_lex(*++t_wp)) || res; 610 if (check_operator(*++args) == BOR) {
611 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]);
612 res = oexpr(check_operator(*++args)) || res;
613 unnest_msg("<oexpr:%lld\n", res);
614 return res;
512 } 615 }
513 t_wp--; 616 args--;
617 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]);
514 return res; 618 return res;
515} 619}
516 620
517 621
518 622static number_t primary(enum token n)
519static arith_t primary(enum token n)
520{ 623{
521 arith_t res; 624#if TEST_DEBUG
625 number_t res = res; /* for compiler */
626#else
627 number_t res;
628#endif
629 const struct operator_t *args0_op;
522 630
631 nest_msg(">primary(%s)\n", TOKSTR[n]);
523 if (n == EOI) { 632 if (n == EOI) {
524 syntax(NULL, "argument expected"); 633 syntax(NULL, "argument expected");
525 } 634 }
526 if (n == LPAREN) { 635 if (n == LPAREN) {
527 res = oexpr(t_lex(*++t_wp)); 636 res = oexpr(check_operator(*++args));
528 if (t_lex(*++t_wp) != RPAREN) 637 if (check_operator(*++args) != RPAREN)
529 syntax(NULL, "closing paren expected"); 638 syntax(NULL, "closing paren expected");
639 unnest_msg("<primary:%lld\n", res);
530 return res; 640 return res;
531 } 641 }
532 if (t_wp_op && t_wp_op->op_type == UNOP) { 642
643 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
644 * do the same */
645 args0_op = last_operator;
646 /* last_operator = operator at args[1] */
647 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
648 if (args[2]) {
649 // coreutils also does this:
650 // if (args[3] && args[0]="-l" && args[2] is BINOP)
651 // return binop(1 /* prepended by -l */);
652 if (last_operator->op_type == BINOP)
653 unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
654 }
655 }
656 /* check "is args[0] unop?" second */
657 if (args0_op->op_type == UNOP) {
533 /* unary expression */ 658 /* unary expression */
534 if (*++t_wp == NULL) 659 if (args[1] == NULL)
535 syntax(t_wp_op->op_text, "argument expected"); 660// syntax(args0_op->op_text, "argument expected");
661 goto check_emptiness;
662 args++;
536 if (n == STREZ) 663 if (n == STREZ)
537 return t_wp[0][0] == '\0'; 664 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
538 if (n == STRNZ) 665 if (n == STRNZ)
539 return t_wp[0][0] != '\0'; 666 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
540 if (n == FILTT) 667 if (n == FILTT)
541 return isatty(getn(*t_wp)); 668 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
542 return filstat(*t_wp, n); 669 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
543 } 670 }
544 671
545 t_lex(t_wp[1]); 672 /*check_operator(args[1]); - already done */
546 if (t_wp_op && t_wp_op->op_type == BINOP) { 673 if (last_operator->op_type == BINOP) {
547 return binop(); 674 /* args[2] is known to be NULL, isn't it bound to fail? */
675 unnest_msg_and_return(binop(), "<primary:%lld\n");
548 } 676 }
549 677 check_emptiness:
550 return t_wp[0][0] != '\0'; 678 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
551} 679}
552 680
553 681
@@ -555,7 +683,7 @@ int test_main(int argc, char **argv)
555{ 683{
556 int res; 684 int res;
557 const char *arg0; 685 const char *arg0;
558 bool negate = 0; 686// bool negate = 0;
559 687
560 arg0 = bb_basename(argv[0]); 688 arg0 = bb_basename(argv[0]);
561 if (arg0[0] == '[') { 689 if (arg0[0] == '[') {
@@ -599,6 +727,8 @@ int test_main(int argc, char **argv)
599 res = 1; 727 res = 1;
600 goto ret; 728 goto ret;
601 } 729 }
730#if 0
731// Now it's fixed in the parser and should not be needed
602 if (LONE_CHAR(argv[0], '!') && argv[1]) { 732 if (LONE_CHAR(argv[0], '!') && argv[1]) {
603 negate = 1; 733 negate = 1;
604 //argc--; 734 //argc--;
@@ -609,10 +739,10 @@ int test_main(int argc, char **argv)
609 goto ret; 739 goto ret;
610 } 740 }
611 if (argv[2] && !argv[3]) { 741 if (argv[2] && !argv[3]) {
612 t_lex(argv[1]); 742 check_operator(argv[1]);
613 if (t_wp_op && t_wp_op->op_type == BINOP) { 743 if (last_operator->op_type == BINOP) {
614 /* "test [!] arg1 <binary_op> arg2" */ 744 /* "test [!] arg1 <binary_op> arg2" */
615 t_wp = &argv[0]; 745 args = &argv[0];
616 res = (binop() == 0); 746 res = (binop() == 0);
617 goto ret; 747 goto ret;
618 } 748 }
@@ -624,14 +754,17 @@ int test_main(int argc, char **argv)
624 //argc++; 754 //argc++;
625 argv--; 755 argv--;
626 } 756 }
627 t_wp = &argv[0]; 757#endif
628 res = !oexpr(t_lex(*t_wp)); 758 args = &argv[0];
759 res = !oexpr(check_operator(*args));
629 760
630 if (*t_wp != NULL && *++t_wp != NULL) { 761 if (*args != NULL && *++args != NULL) {
631 bb_error_msg("%s: unknown operand", *t_wp); 762 /* TODO: example when this happens? */
763 bb_error_msg("%s: unknown operand", *args);
632 res = 2; 764 res = 2;
633 } 765 }
634 ret: 766 ret:
635 DEINIT_S(); 767 DEINIT_S();
636 return negate ? !res : res; 768// return negate ? !res : res;
769 return res;
637} 770}