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 | |
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>
-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=...") */ |