diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2018-02-08 19:19:04 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2018-02-08 19:19:04 +0100 |
| commit | 5807e18f0c4f6fc247103830affcab73ca1ffa37 (patch) | |
| tree | 1f67e7c1ea532803ecbcbd30b6053d3b2fd4222b /shell | |
| parent | 68ae54243cacee6beeb69842c7d562435acd5ad1 (diff) | |
| download | busybox-w32-5807e18f0c4f6fc247103830affcab73ca1ffa37.tar.gz busybox-w32-5807e18f0c4f6fc247103830affcab73ca1ffa37.tar.bz2 busybox-w32-5807e18f0c4f6fc247103830affcab73ca1ffa37.zip | |
hush: LINENO fix
Script triggering the bug:
t=0
echo "at line ${LINENO}"
while [ ${t} -lt 10 ]; do
echo "at line ${LINENO}" # LINENO was 3 instead of 4 here
t=$((t+1))
done
function old new delta
parse_stream 2754 2788 +34
done_word 711 738 +27
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 61/0) Total: 61 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/hush.c | 101 |
1 files changed, 77 insertions, 24 deletions
diff --git a/shell/hush.c b/shell/hush.c index 585c51bd5..f2ffcf54d 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -120,6 +120,11 @@ | |||
| 120 | //config: help | 120 | //config: help |
| 121 | //config: Enable {abc,def} extension. | 121 | //config: Enable {abc,def} extension. |
| 122 | //config: | 122 | //config: |
| 123 | //config:config HUSH_LINENO_VAR | ||
| 124 | //config: bool "$LINENO variable" | ||
| 125 | //config: default y | ||
| 126 | //config: depends on HUSH_BASH_COMPAT | ||
| 127 | //config: | ||
| 123 | //config:config HUSH_BASH_SOURCE_CURDIR | 128 | //config:config HUSH_BASH_SOURCE_CURDIR |
| 124 | //config: bool "'source' and '.' builtins search current directory after $PATH" | 129 | //config: bool "'source' and '.' builtins search current directory after $PATH" |
| 125 | //config: default n # do not encourage non-standard behavior | 130 | //config: default n # do not encourage non-standard behavior |
| @@ -368,7 +373,6 @@ | |||
| 368 | #define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT | 373 | #define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT |
| 369 | #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT | 374 | #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT |
| 370 | #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT | 375 | #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT |
| 371 | #define BASH_LINENO_VAR ENABLE_HUSH_BASH_COMPAT | ||
| 372 | #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) | 376 | #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) |
| 373 | #define BASH_READ_D ENABLE_HUSH_BASH_COMPAT | 377 | #define BASH_READ_D ENABLE_HUSH_BASH_COMPAT |
| 374 | 378 | ||
| @@ -620,7 +624,7 @@ typedef enum redir_type { | |||
| 620 | struct command { | 624 | struct command { |
| 621 | pid_t pid; /* 0 if exited */ | 625 | pid_t pid; /* 0 if exited */ |
| 622 | int assignment_cnt; /* how many argv[i] are assignments? */ | 626 | int assignment_cnt; /* how many argv[i] are assignments? */ |
| 623 | #if BASH_LINENO_VAR | 627 | #if ENABLE_HUSH_LINENO_VAR |
| 624 | unsigned lineno; | 628 | unsigned lineno; |
| 625 | #endif | 629 | #endif |
| 626 | smallint cmd_type; /* CMD_xxx */ | 630 | smallint cmd_type; /* CMD_xxx */ |
| @@ -942,7 +946,7 @@ struct globals { | |||
| 942 | unsigned handled_SIGCHLD; | 946 | unsigned handled_SIGCHLD; |
| 943 | smallint we_have_children; | 947 | smallint we_have_children; |
| 944 | #endif | 948 | #endif |
| 945 | #if BASH_LINENO_VAR | 949 | #if ENABLE_HUSH_LINENO_VAR |
| 946 | unsigned lineno; | 950 | unsigned lineno; |
| 947 | char *lineno_var; | 951 | char *lineno_var; |
| 948 | #endif | 952 | #endif |
| @@ -2152,7 +2156,7 @@ static int set_local_var(char *str, unsigned flags) | |||
| 2152 | } | 2156 | } |
| 2153 | 2157 | ||
| 2154 | name_len = eq_sign - str + 1; /* including '=' */ | 2158 | name_len = eq_sign - str + 1; /* including '=' */ |
| 2155 | #if BASH_LINENO_VAR | 2159 | #if ENABLE_HUSH_LINENO_VAR |
| 2156 | if (G.lineno_var) { | 2160 | if (G.lineno_var) { |
| 2157 | if (name_len == 7 && strncmp("LINENO", str, 6) == 0) | 2161 | if (name_len == 7 && strncmp("LINENO", str, 6) == 0) |
| 2158 | G.lineno_var = NULL; | 2162 | G.lineno_var = NULL; |
| @@ -2285,7 +2289,7 @@ static int unset_local_var_len(const char *name, int name_len) | |||
| 2285 | if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) | 2289 | if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) |
| 2286 | G.getopt_count = 0; | 2290 | G.getopt_count = 0; |
| 2287 | #endif | 2291 | #endif |
| 2288 | #if BASH_LINENO_VAR | 2292 | #if ENABLE_HUSH_LINENO_VAR |
| 2289 | if (name_len == 6 && G.lineno_var && strncmp(name, "LINENO", 6) == 0) | 2293 | if (name_len == 6 && G.lineno_var && strncmp(name, "LINENO", 6) == 0) |
| 2290 | G.lineno_var = NULL; | 2294 | G.lineno_var = NULL; |
| 2291 | #endif | 2295 | #endif |
| @@ -2608,9 +2612,11 @@ static int i_getch(struct in_str *i) | |||
| 2608 | out: | 2612 | out: |
| 2609 | debug_printf("file_get: got '%c' %d\n", ch, ch); | 2613 | debug_printf("file_get: got '%c' %d\n", ch, ch); |
| 2610 | i->last_char = ch; | 2614 | i->last_char = ch; |
| 2611 | #if BASH_LINENO_VAR | 2615 | #if ENABLE_HUSH_LINENO_VAR |
| 2612 | if (ch == '\n') | 2616 | if (ch == '\n') { |
| 2613 | G.lineno++; | 2617 | G.lineno++; |
| 2618 | debug_printf_parse("G.lineno++ = %u\n", G.lineno); | ||
| 2619 | } | ||
| 2614 | #endif | 2620 | #endif |
| 2615 | return ch; | 2621 | return ch; |
| 2616 | } | 2622 | } |
| @@ -3412,8 +3418,13 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
| 3412 | 3418 | ||
| 3413 | pin = 0; | 3419 | pin = 0; |
| 3414 | while (pi) { | 3420 | while (pi) { |
| 3415 | fdprintf(2, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "", | 3421 | fdprintf(2, "%*spipe %d %sres_word=%s followup=%d %s\n", |
| 3416 | pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]); | 3422 | lvl*2, "", |
| 3423 | pin, | ||
| 3424 | (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""), | ||
| 3425 | RES[pi->res_word], | ||
| 3426 | pi->followup, PIPE[pi->followup] | ||
| 3427 | ); | ||
| 3417 | prn = 0; | 3428 | prn = 0; |
| 3418 | while (prn < pi->num_cmds) { | 3429 | while (prn < pi->num_cmds) { |
| 3419 | struct command *command = &pi->cmds[prn]; | 3430 | struct command *command = &pi->cmds[prn]; |
| @@ -3422,6 +3433,9 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
| 3422 | fdprintf(2, "%*s cmd %d assignment_cnt:%d", | 3433 | fdprintf(2, "%*s cmd %d assignment_cnt:%d", |
| 3423 | lvl*2, "", prn, | 3434 | lvl*2, "", prn, |
| 3424 | command->assignment_cnt); | 3435 | command->assignment_cnt); |
| 3436 | #if ENABLE_HUSH_LINENO_VAR | ||
| 3437 | fdprintf(2, " LINENO:%u", command->lineno); | ||
| 3438 | #endif | ||
| 3425 | if (command->group) { | 3439 | if (command->group) { |
| 3426 | fdprintf(2, " group %s: (argv=%p)%s%s\n", | 3440 | fdprintf(2, " group %s: (argv=%p)%s%s\n", |
| 3427 | CMDTYPE[command->cmd_type], | 3441 | CMDTYPE[command->cmd_type], |
| @@ -3494,8 +3508,9 @@ static int done_command(struct parse_context *ctx) | |||
| 3494 | ctx->command = command = &pi->cmds[pi->num_cmds]; | 3508 | ctx->command = command = &pi->cmds[pi->num_cmds]; |
| 3495 | clear_and_ret: | 3509 | clear_and_ret: |
| 3496 | memset(command, 0, sizeof(*command)); | 3510 | memset(command, 0, sizeof(*command)); |
| 3497 | #if BASH_LINENO_VAR | 3511 | #if ENABLE_HUSH_LINENO_VAR |
| 3498 | command->lineno = G.lineno; | 3512 | command->lineno = G.lineno; |
| 3513 | debug_printf_parse("command->lineno = G.lineno (%u)\n", G.lineno); | ||
| 3499 | #endif | 3514 | #endif |
| 3500 | return pi->num_cmds; /* used only for 0/nonzero check */ | 3515 | return pi->num_cmds; /* used only for 0/nonzero check */ |
| 3501 | } | 3516 | } |
| @@ -3684,9 +3699,9 @@ static const struct reserved_combo* match_reserved_word(o_string *word) | |||
| 3684 | } | 3699 | } |
| 3685 | return NULL; | 3700 | return NULL; |
| 3686 | } | 3701 | } |
| 3687 | /* Return 0: not a keyword, 1: keyword | 3702 | /* Return NULL: not a keyword, else: keyword |
| 3688 | */ | 3703 | */ |
| 3689 | static int reserved_word(o_string *word, struct parse_context *ctx) | 3704 | static const struct reserved_combo* reserved_word(o_string *word, struct parse_context *ctx) |
| 3690 | { | 3705 | { |
| 3691 | # if ENABLE_HUSH_CASE | 3706 | # if ENABLE_HUSH_CASE |
| 3692 | static const struct reserved_combo reserved_match = { | 3707 | static const struct reserved_combo reserved_match = { |
| @@ -3699,7 +3714,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
| 3699 | return 0; | 3714 | return 0; |
| 3700 | r = match_reserved_word(word); | 3715 | r = match_reserved_word(word); |
| 3701 | if (!r) | 3716 | if (!r) |
| 3702 | return 0; | 3717 | return r; /* NULL */ |
| 3703 | 3718 | ||
| 3704 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); | 3719 | debug_printf("found reserved word %s, res %d\n", r->literal, r->res); |
| 3705 | # if ENABLE_HUSH_CASE | 3720 | # if ENABLE_HUSH_CASE |
| @@ -3714,7 +3729,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
| 3714 | ctx->ctx_res_w = RES_SNTX; | 3729 | ctx->ctx_res_w = RES_SNTX; |
| 3715 | } | 3730 | } |
| 3716 | ctx->ctx_inverted = 1; | 3731 | ctx->ctx_inverted = 1; |
| 3717 | return 1; | 3732 | return r; |
| 3718 | } | 3733 | } |
| 3719 | if (r->flag & FLAG_START) { | 3734 | if (r->flag & FLAG_START) { |
| 3720 | struct parse_context *old; | 3735 | struct parse_context *old; |
| @@ -3726,7 +3741,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
| 3726 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { | 3741 | } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { |
| 3727 | syntax_error_at(word->data); | 3742 | syntax_error_at(word->data); |
| 3728 | ctx->ctx_res_w = RES_SNTX; | 3743 | ctx->ctx_res_w = RES_SNTX; |
| 3729 | return 1; | 3744 | return r; |
| 3730 | } else { | 3745 | } else { |
| 3731 | /* "{...} fi" is ok. "{...} if" is not | 3746 | /* "{...} fi" is ok. "{...} if" is not |
| 3732 | * Example: | 3747 | * Example: |
| @@ -3776,7 +3791,7 @@ static int reserved_word(o_string *word, struct parse_context *ctx) | |||
| 3776 | *ctx = *old; /* physical copy */ | 3791 | *ctx = *old; /* physical copy */ |
| 3777 | free(old); | 3792 | free(old); |
| 3778 | } | 3793 | } |
| 3779 | return 1; | 3794 | return r; |
| 3780 | } | 3795 | } |
| 3781 | #endif /* HAS_KEYWORDS */ | 3796 | #endif /* HAS_KEYWORDS */ |
| 3782 | 3797 | ||
| @@ -3842,9 +3857,26 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
| 3842 | && ctx->ctx_res_w != RES_CASE | 3857 | && ctx->ctx_res_w != RES_CASE |
| 3843 | # endif | 3858 | # endif |
| 3844 | ) { | 3859 | ) { |
| 3845 | int reserved = reserved_word(word, ctx); | 3860 | const struct reserved_combo *reserved; |
| 3846 | debug_printf_parse("checking for reserved-ness: %d\n", reserved); | 3861 | reserved = reserved_word(word, ctx); |
| 3862 | debug_printf_parse("checking for reserved-ness: %d\n", !!reserved); | ||
| 3847 | if (reserved) { | 3863 | if (reserved) { |
| 3864 | # if ENABLE_HUSH_LINENO_VAR | ||
| 3865 | /* Case: | ||
| 3866 | * "while ...; do | ||
| 3867 | * cmd ..." | ||
| 3868 | * If we don't close the pipe _now_, immediately after "do", lineno logic | ||
| 3869 | * sees "cmd" as starting at "do" - i.e., at the previous line. | ||
| 3870 | */ | ||
| 3871 | if (0 | ||
| 3872 | IF_HUSH_IF(|| reserved->res == RES_THEN) | ||
| 3873 | IF_HUSH_IF(|| reserved->res == RES_ELIF) | ||
| 3874 | IF_HUSH_IF(|| reserved->res == RES_ELSE) | ||
| 3875 | IF_HUSH_LOOPS(|| reserved->res == RES_DO) | ||
| 3876 | ) { | ||
| 3877 | done_pipe(ctx, PIPE_SEQ); | ||
| 3878 | } | ||
| 3879 | # endif | ||
| 3848 | o_reset_to_empty_unquoted(word); | 3880 | o_reset_to_empty_unquoted(word); |
| 3849 | debug_printf_parse("done_word return %d\n", | 3881 | debug_printf_parse("done_word return %d\n", |
| 3850 | (ctx->ctx_res_w == RES_SNTX)); | 3882 | (ctx->ctx_res_w == RES_SNTX)); |
| @@ -4979,6 +5011,27 @@ static struct pipe *parse_stream(char **pstring, | |||
| 4979 | } | 5011 | } |
| 4980 | 5012 | ||
| 4981 | if (is_blank) { | 5013 | if (is_blank) { |
| 5014 | #if ENABLE_HUSH_LINENO_VAR | ||
| 5015 | /* Case: | ||
| 5016 | * "while ...; do<whitespace><newline> | ||
| 5017 | * cmd ..." | ||
| 5018 | * would think that "cmd" starts in <whitespace> - | ||
| 5019 | * i.e., at the previous line. | ||
| 5020 | * We need to skip all whitespace before newlines. | ||
| 5021 | */ | ||
| 5022 | if (ch != '\n') { | ||
| 5023 | /* It was whitespace, but not a newline. | ||
| 5024 | * Eat all whitespace. | ||
| 5025 | */ | ||
| 5026 | for (;;) { | ||
| 5027 | next = i_peek(input); | ||
| 5028 | if (next != ' ' && next != '\t' && next != '\n') | ||
| 5029 | break; /* next char is not ws */ | ||
| 5030 | ch = i_getch(input); | ||
| 5031 | } | ||
| 5032 | /* ch == last eaten whitespace char */ | ||
| 5033 | } | ||
| 5034 | #endif | ||
| 4982 | if (done_word(&dest, &ctx)) { | 5035 | if (done_word(&dest, &ctx)) { |
| 4983 | goto parse_error; | 5036 | goto parse_error; |
| 4984 | } | 5037 | } |
| @@ -6573,7 +6626,7 @@ static void parse_and_run_string(const char *s) | |||
| 6573 | static void parse_and_run_file(FILE *f) | 6626 | static void parse_and_run_file(FILE *f) |
| 6574 | { | 6627 | { |
| 6575 | struct in_str input; | 6628 | struct in_str input; |
| 6576 | #if BASH_LINENO_VAR | 6629 | #if ENABLE_HUSH_LINENO_VAR |
| 6577 | unsigned sv; | 6630 | unsigned sv; |
| 6578 | 6631 | ||
| 6579 | sv = G.lineno; | 6632 | sv = G.lineno; |
| @@ -6581,7 +6634,7 @@ static void parse_and_run_file(FILE *f) | |||
| 6581 | #endif | 6634 | #endif |
| 6582 | setup_file_in_str(&input, f); | 6635 | setup_file_in_str(&input, f); |
| 6583 | parse_and_run_stream(&input, ';'); | 6636 | parse_and_run_stream(&input, ';'); |
| 6584 | #if BASH_LINENO_VAR | 6637 | #if ENABLE_HUSH_LINENO_VAR |
| 6585 | G.lineno = sv; | 6638 | G.lineno = sv; |
| 6586 | #endif | 6639 | #endif |
| 6587 | } | 6640 | } |
| @@ -8130,7 +8183,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
| 8130 | char **new_env = NULL; | 8183 | char **new_env = NULL; |
| 8131 | struct variable *old_vars = NULL; | 8184 | struct variable *old_vars = NULL; |
| 8132 | 8185 | ||
| 8133 | #if BASH_LINENO_VAR | 8186 | #if ENABLE_HUSH_LINENO_VAR |
| 8134 | if (G.lineno_var) | 8187 | if (G.lineno_var) |
| 8135 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); | 8188 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); |
| 8136 | #endif | 8189 | #endif |
| @@ -8339,7 +8392,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
| 8339 | if (cmd_no < pi->num_cmds) | 8392 | if (cmd_no < pi->num_cmds) |
| 8340 | xpiped_pair(pipefds); | 8393 | xpiped_pair(pipefds); |
| 8341 | 8394 | ||
| 8342 | #if BASH_LINENO_VAR | 8395 | #if ENABLE_HUSH_LINENO_VAR |
| 8343 | if (G.lineno_var) | 8396 | if (G.lineno_var) |
| 8344 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); | 8397 | strcpy(G.lineno_var + sizeof("LINENO=")-1, utoa(command->lineno)); |
| 8345 | #endif | 8398 | #endif |
| @@ -9057,8 +9110,8 @@ int hush_main(int argc, char **argv) | |||
| 9057 | */ | 9110 | */ |
| 9058 | #endif | 9111 | #endif |
| 9059 | 9112 | ||
| 9060 | #if BASH_LINENO_VAR | 9113 | #if ENABLE_HUSH_LINENO_VAR |
| 9061 | if (BASH_LINENO_VAR) { | 9114 | if (ENABLE_HUSH_LINENO_VAR) { |
| 9062 | char *p = xasprintf("LINENO=%*s", (int)(sizeof(int)*3), ""); | 9115 | char *p = xasprintf("LINENO=%*s", (int)(sizeof(int)*3), ""); |
| 9063 | set_local_var(p, /*flags*/ 0); | 9116 | set_local_var(p, /*flags*/ 0); |
| 9064 | G.lineno_var = p; /* can't assign before set_local_var("LINENO=...") */ | 9117 | G.lineno_var = p; /* can't assign before set_local_var("LINENO=...") */ |
