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 | } |
