diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-19 08:15:13 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-07-19 08:15:13 +0000 |
commit | bd28f6bf7f53ede8df39112d40cb52f2a3d00177 (patch) | |
tree | 0e2cc7fa0864c8998d4483fc2398a3c3e9791a0f /coreutils | |
parent | 5f116629d80b66bd09d8dc2b849befb1e27cd21a (diff) | |
download | busybox-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.c | 253 |
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 | |||
50 | enum token { | 52 | enum 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 | ||
102 | int 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) | ||
124 | static 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 | |||
98 | enum token_types { | 173 | enum 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 | ||
106 | static const struct t_op { | 181 | struct 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 | |||
186 | static 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 |
155 | typedef int64_t arith_t; | 232 | typedef int64_t number_t; |
156 | #else | 233 | #else |
157 | typedef int arith_t; | 234 | typedef 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. */ |
162 | struct test_statics { | 239 | struct 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 { | |||
171 | extern struct test_statics *const test_ptr_to_statics; | 250 | extern 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 | ||
188 | static arith_t primary(enum token n); | 267 | static number_t primary(enum token n); |
189 | 268 | ||
190 | static void syntax(const char *op, const char *msg) NORETURN; | 269 | static void syntax(const char *op, const char *msg) NORETURN; |
191 | static void syntax(const char *op, const char *msg) | 270 | static 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? |
203 | static arith_t getn(const char *s) | 282 | static 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 | ||
256 | static enum token t_lex(char *s) | 335 | static 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) | |||
278 | static int binop(void) | 361 | static 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 | ||
485 | static arith_t nexpr(enum token n) | 568 | static 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 | ||
493 | static arith_t aexpr(enum token n) | 584 | static 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 | ||
505 | static arith_t oexpr(enum token n) | 603 | static 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 | 622 | static number_t primary(enum token n) | |
519 | static 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 | } |