diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2018-07-24 13:03:03 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2018-07-24 13:03:03 +0200 |
commit | 474cb205554919e4d017b7aeb3722d6a4ffee41d (patch) | |
tree | a6934231956c3f3756b3d78750a83cdddf746b85 | |
parent | 3675c37b9b0bb4ba565b690b95b0b9c7d0ce8123 (diff) | |
download | busybox-w32-474cb205554919e4d017b7aeb3722d6a4ffee41d.tar.gz busybox-w32-474cb205554919e4d017b7aeb3722d6a4ffee41d.tar.bz2 busybox-w32-474cb205554919e4d017b7aeb3722d6a4ffee41d.zip |
hush: fix handling of heredocs not enclosed in groups where they are "declared"
function old new delta
fetch_heredocs - 479 +479
parse_and_run_stream 146 148 +2
parse_stream 2787 2296 -491
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/1 up/down: 481/-491) Total: -10 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
7 files changed, 84 insertions, 45 deletions
diff --git a/shell/ash_test/ash-heredoc/heredoc_after_compound1.right b/shell/ash_test/ash-heredoc/heredoc_after_compound1.right new file mode 100644 index 000000000..9052f7d1f --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_after_compound1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Ok1 | ||
2 | Ok2 | ||
diff --git a/shell/ash_test/ash-heredoc/heredoc_after_compound1.tests b/shell/ash_test/ash-heredoc/heredoc_after_compound1.tests new file mode 100755 index 000000000..e7cfb5be1 --- /dev/null +++ b/shell/ash_test/ash-heredoc/heredoc_after_compound1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | { cat <<EOF; }; echo Ok2 | ||
2 | Ok1 | ||
3 | EOF | ||
diff --git a/shell/hush.c b/shell/hush.c index 130c8e958..75bce337a 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -3534,6 +3534,8 @@ static void debug_print_tree(struct pipe *pi, int lvl) | |||
3534 | fdprintf(2, " '%s'", *argv); | 3534 | fdprintf(2, " '%s'", *argv); |
3535 | argv++; | 3535 | argv++; |
3536 | } | 3536 | } |
3537 | if (command->redirects) | ||
3538 | fdprintf(2, " {redir}"); | ||
3537 | fdprintf(2, "\n"); | 3539 | fdprintf(2, "\n"); |
3538 | prn++; | 3540 | prn++; |
3539 | } | 3541 | } |
@@ -4292,10 +4294,12 @@ static char *fetch_till_str(o_string *as_string, | |||
4292 | /* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs | 4294 | /* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs |
4293 | * and load them all. There should be exactly heredoc_cnt of them. | 4295 | * and load them all. There should be exactly heredoc_cnt of them. |
4294 | */ | 4296 | */ |
4295 | static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input) | 4297 | #if BB_MMU |
4298 | #define fetch_heredocs(as_string, pi, heredoc_cnt, input) \ | ||
4299 | fetch_heredocs(pi, heredoc_cnt, input) | ||
4300 | #endif | ||
4301 | static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt, struct in_str *input) | ||
4296 | { | 4302 | { |
4297 | struct pipe *pi = ctx->list_head; | ||
4298 | |||
4299 | while (pi && heredoc_cnt) { | 4303 | while (pi && heredoc_cnt) { |
4300 | int i; | 4304 | int i; |
4301 | struct command *cmd = pi->cmds; | 4305 | struct command *cmd = pi->cmds; |
@@ -4315,11 +4319,11 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_ | |||
4315 | 4319 | ||
4316 | redir->rd_type = REDIRECT_HEREDOC2; | 4320 | redir->rd_type = REDIRECT_HEREDOC2; |
4317 | /* redir->rd_dup is (ab)used to indicate <<- */ | 4321 | /* redir->rd_dup is (ab)used to indicate <<- */ |
4318 | p = fetch_till_str(&ctx->as_string, input, | 4322 | p = fetch_till_str(as_string, input, |
4319 | redir->rd_filename, redir->rd_dup); | 4323 | redir->rd_filename, redir->rd_dup); |
4320 | if (!p) { | 4324 | if (!p) { |
4321 | syntax_error("unexpected EOF in here document"); | 4325 | syntax_error("unexpected EOF in here document"); |
4322 | return 1; | 4326 | return -1; |
4323 | } | 4327 | } |
4324 | free(redir->rd_filename); | 4328 | free(redir->rd_filename); |
4325 | redir->rd_filename = p; | 4329 | redir->rd_filename = p; |
@@ -4327,29 +4331,36 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_ | |||
4327 | } | 4331 | } |
4328 | redir = redir->next; | 4332 | redir = redir->next; |
4329 | } | 4333 | } |
4334 | if (cmd->group) { | ||
4335 | //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt); | ||
4336 | heredoc_cnt = fetch_heredocs(as_string, cmd->group, heredoc_cnt, input); | ||
4337 | //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt); | ||
4338 | if (heredoc_cnt < 0) | ||
4339 | return heredoc_cnt; /* error */ | ||
4340 | } | ||
4330 | cmd++; | 4341 | cmd++; |
4331 | } | 4342 | } |
4332 | pi = pi->next; | 4343 | pi = pi->next; |
4333 | } | 4344 | } |
4334 | /* Should be 0. If it isn't, it's a parse error */ | 4345 | return heredoc_cnt; |
4335 | if (HUSH_DEBUG && heredoc_cnt) | ||
4336 | bb_error_msg_and_die("heredoc BUG 2"); | ||
4337 | return 0; | ||
4338 | } | 4346 | } |
4339 | 4347 | ||
4340 | 4348 | ||
4341 | static int run_list(struct pipe *pi); | 4349 | static int run_list(struct pipe *pi); |
4342 | #if BB_MMU | 4350 | #if BB_MMU |
4343 | #define parse_stream(pstring, input, end_trigger) \ | 4351 | #define parse_stream(pstring, heredoc_cnt_ptr, input, end_trigger) \ |
4344 | parse_stream(input, end_trigger) | 4352 | parse_stream(heredoc_cnt_ptr, input, end_trigger) |
4345 | #endif | 4353 | #endif |
4346 | static struct pipe *parse_stream(char **pstring, | 4354 | static struct pipe *parse_stream(char **pstring, |
4355 | int *heredoc_cnt_ptr, | ||
4347 | struct in_str *input, | 4356 | struct in_str *input, |
4348 | int end_trigger); | 4357 | int end_trigger); |
4349 | 4358 | ||
4350 | 4359 | /* Returns number of heredocs not yet consumed, | |
4360 | * or -1 on error. | ||
4361 | */ | ||
4351 | static int parse_group(struct parse_context *ctx, | 4362 | static int parse_group(struct parse_context *ctx, |
4352 | struct in_str *input, int ch) | 4363 | struct in_str *input, int ch) |
4353 | { | 4364 | { |
4354 | /* ctx->word contains characters seen prior to ( or {. | 4365 | /* ctx->word contains characters seen prior to ( or {. |
4355 | * Typically it's empty, but for function defs, | 4366 | * Typically it's empty, but for function defs, |
@@ -4360,6 +4371,7 @@ static int parse_group(struct parse_context *ctx, | |||
4360 | char *as_string = NULL; | 4371 | char *as_string = NULL; |
4361 | #endif | 4372 | #endif |
4362 | struct pipe *pipe_list; | 4373 | struct pipe *pipe_list; |
4374 | int heredoc_cnt = 0; | ||
4363 | int endch; | 4375 | int endch; |
4364 | struct command *command = ctx->command; | 4376 | struct command *command = ctx->command; |
4365 | 4377 | ||
@@ -4368,12 +4380,12 @@ static int parse_group(struct parse_context *ctx, | |||
4368 | if (ch == '(' && !ctx->word.has_quoted_part) { | 4380 | if (ch == '(' && !ctx->word.has_quoted_part) { |
4369 | if (ctx->word.length) | 4381 | if (ctx->word.length) |
4370 | if (done_word(ctx)) | 4382 | if (done_word(ctx)) |
4371 | return 1; | 4383 | return -1; |
4372 | if (!command->argv) | 4384 | if (!command->argv) |
4373 | goto skip; /* (... */ | 4385 | goto skip; /* (... */ |
4374 | if (command->argv[1]) { /* word word ... (... */ | 4386 | if (command->argv[1]) { /* word word ... (... */ |
4375 | syntax_error_unexpected_ch('('); | 4387 | syntax_error_unexpected_ch('('); |
4376 | return 1; | 4388 | return -1; |
4377 | } | 4389 | } |
4378 | /* it is "word(..." or "word (..." */ | 4390 | /* it is "word(..." or "word (..." */ |
4379 | do | 4391 | do |
@@ -4381,7 +4393,7 @@ static int parse_group(struct parse_context *ctx, | |||
4381 | while (ch == ' ' || ch == '\t'); | 4393 | while (ch == ' ' || ch == '\t'); |
4382 | if (ch != ')') { | 4394 | if (ch != ')') { |
4383 | syntax_error_unexpected_ch(ch); | 4395 | syntax_error_unexpected_ch(ch); |
4384 | return 1; | 4396 | return -1; |
4385 | } | 4397 | } |
4386 | nommu_addchr(&ctx->as_string, ch); | 4398 | nommu_addchr(&ctx->as_string, ch); |
4387 | do | 4399 | do |
@@ -4389,7 +4401,7 @@ static int parse_group(struct parse_context *ctx, | |||
4389 | while (ch == ' ' || ch == '\t' || ch == '\n'); | 4401 | while (ch == ' ' || ch == '\t' || ch == '\n'); |
4390 | if (ch != '{' && ch != '(') { | 4402 | if (ch != '{' && ch != '(') { |
4391 | syntax_error_unexpected_ch(ch); | 4403 | syntax_error_unexpected_ch(ch); |
4392 | return 1; | 4404 | return -1; |
4393 | } | 4405 | } |
4394 | nommu_addchr(&ctx->as_string, ch); | 4406 | nommu_addchr(&ctx->as_string, ch); |
4395 | command->cmd_type = CMD_FUNCDEF; | 4407 | command->cmd_type = CMD_FUNCDEF; |
@@ -4403,9 +4415,9 @@ static int parse_group(struct parse_context *ctx, | |||
4403 | || ctx->word.has_quoted_part /* ""{... */ | 4415 | || ctx->word.has_quoted_part /* ""{... */ |
4404 | ) { | 4416 | ) { |
4405 | syntax_error(NULL); | 4417 | syntax_error(NULL); |
4406 | debug_printf_parse("parse_group return 1: " | 4418 | debug_printf_parse("parse_group return -1: " |
4407 | "syntax error, groups and arglists don't mix\n"); | 4419 | "syntax error, groups and arglists don't mix\n"); |
4408 | return 1; | 4420 | return -1; |
4409 | } | 4421 | } |
4410 | #endif | 4422 | #endif |
4411 | 4423 | ||
@@ -4423,7 +4435,7 @@ static int parse_group(struct parse_context *ctx, | |||
4423 | && ch != '(' /* but "{(..." is allowed (without whitespace) */ | 4435 | && ch != '(' /* but "{(..." is allowed (without whitespace) */ |
4424 | ) { | 4436 | ) { |
4425 | syntax_error_unexpected_ch(ch); | 4437 | syntax_error_unexpected_ch(ch); |
4426 | return 1; | 4438 | return -1; |
4427 | } | 4439 | } |
4428 | if (ch != '(') { | 4440 | if (ch != '(') { |
4429 | ch = i_getch(input); | 4441 | ch = i_getch(input); |
@@ -4431,7 +4443,9 @@ static int parse_group(struct parse_context *ctx, | |||
4431 | } | 4443 | } |
4432 | } | 4444 | } |
4433 | 4445 | ||
4434 | pipe_list = parse_stream(&as_string, input, endch); | 4446 | debug_printf_heredoc("calling parse_stream, heredoc_cnt:%d\n", heredoc_cnt); |
4447 | pipe_list = parse_stream(&as_string, &heredoc_cnt, input, endch); | ||
4448 | debug_printf_heredoc("parse_stream returned: heredoc_cnt:%d\n", heredoc_cnt); | ||
4435 | #if !BB_MMU | 4449 | #if !BB_MMU |
4436 | if (as_string) | 4450 | if (as_string) |
4437 | o_addstr(&ctx->as_string, as_string); | 4451 | o_addstr(&ctx->as_string, as_string); |
@@ -4442,9 +4456,9 @@ static int parse_group(struct parse_context *ctx, | |||
4442 | /* parse_stream already emitted error msg */ | 4456 | /* parse_stream already emitted error msg */ |
4443 | if (!BB_MMU) | 4457 | if (!BB_MMU) |
4444 | free(as_string); | 4458 | free(as_string); |
4445 | debug_printf_parse("parse_group return 1: " | 4459 | debug_printf_parse("parse_group return -1: " |
4446 | "parse_stream returned %p\n", pipe_list); | 4460 | "parse_stream returned %p\n", pipe_list); |
4447 | return 1; | 4461 | return -1; |
4448 | } | 4462 | } |
4449 | #if !BB_MMU | 4463 | #if !BB_MMU |
4450 | as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ | 4464 | as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */ |
@@ -4475,8 +4489,8 @@ static int parse_group(struct parse_context *ctx, | |||
4475 | 4489 | ||
4476 | command->group = pipe_list; | 4490 | command->group = pipe_list; |
4477 | 4491 | ||
4478 | debug_printf_parse("parse_group return 0\n"); | 4492 | debug_printf_parse("parse_group return %d\n", heredoc_cnt); |
4479 | return 0; | 4493 | return heredoc_cnt; |
4480 | /* command remains "open", available for possible redirects */ | 4494 | /* command remains "open", available for possible redirects */ |
4481 | #undef as_string | 4495 | #undef as_string |
4482 | } | 4496 | } |
@@ -5002,6 +5016,7 @@ static int encode_string(o_string *as_string, | |||
5002 | * or return ERR_PTR. | 5016 | * or return ERR_PTR. |
5003 | */ | 5017 | */ |
5004 | static struct pipe *parse_stream(char **pstring, | 5018 | static struct pipe *parse_stream(char **pstring, |
5019 | int *heredoc_cnt_ptr, | ||
5005 | struct in_str *input, | 5020 | struct in_str *input, |
5006 | int end_trigger) | 5021 | int end_trigger) |
5007 | { | 5022 | { |
@@ -5077,7 +5092,11 @@ static struct pipe *parse_stream(char **pstring, | |||
5077 | else | 5092 | else |
5078 | o_free(&ctx.as_string); | 5093 | o_free(&ctx.as_string); |
5079 | #endif | 5094 | #endif |
5095 | // heredoc_cnt must be 0 here anyway | ||
5096 | //if (heredoc_cnt_ptr) | ||
5097 | // *heredoc_cnt_ptr = heredoc_cnt; | ||
5080 | debug_leave(); | 5098 | debug_leave(); |
5099 | debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); | ||
5081 | debug_printf_parse("parse_stream return %p\n", pi); | 5100 | debug_printf_parse("parse_stream return %p\n", pi); |
5082 | return pi; | 5101 | return pi; |
5083 | } | 5102 | } |
@@ -5236,10 +5255,9 @@ static struct pipe *parse_stream(char **pstring, | |||
5236 | done_pipe(&ctx, PIPE_SEQ); | 5255 | done_pipe(&ctx, PIPE_SEQ); |
5237 | debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt); | 5256 | debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt); |
5238 | if (heredoc_cnt) { | 5257 | if (heredoc_cnt) { |
5239 | if (fetch_heredocs(heredoc_cnt, &ctx, input)) { | 5258 | heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input); |
5259 | if (heredoc_cnt != 0) | ||
5240 | goto parse_error; | 5260 | goto parse_error; |
5241 | } | ||
5242 | heredoc_cnt = 0; | ||
5243 | } | 5261 | } |
5244 | ctx.is_assignment = MAYBE_ASSIGNMENT; | 5262 | ctx.is_assignment = MAYBE_ASSIGNMENT; |
5245 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); | 5263 | debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]); |
@@ -5288,19 +5306,6 @@ static struct pipe *parse_stream(char **pstring, | |||
5288 | ) | 5306 | ) |
5289 | #endif | 5307 | #endif |
5290 | ) { | 5308 | ) { |
5291 | if (heredoc_cnt) { | ||
5292 | /* This is technically valid: | ||
5293 | * { cat <<HERE; }; echo Ok | ||
5294 | * heredoc | ||
5295 | * heredoc | ||
5296 | * HERE | ||
5297 | * but we don't support this. | ||
5298 | * We require heredoc to be in enclosing {}/(), | ||
5299 | * if any. | ||
5300 | */ | ||
5301 | syntax_error_unterm_str("here document"); | ||
5302 | goto parse_error; | ||
5303 | } | ||
5304 | if (done_word(&ctx)) { | 5309 | if (done_word(&ctx)) { |
5305 | goto parse_error; | 5310 | goto parse_error; |
5306 | } | 5311 | } |
@@ -5325,6 +5330,9 @@ static struct pipe *parse_stream(char **pstring, | |||
5325 | syntax_error_unexpected_ch(ch); | 5330 | syntax_error_unexpected_ch(ch); |
5326 | goto parse_error2; | 5331 | goto parse_error2; |
5327 | } | 5332 | } |
5333 | if (heredoc_cnt_ptr) | ||
5334 | *heredoc_cnt_ptr = heredoc_cnt; | ||
5335 | debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt); | ||
5328 | debug_printf_parse("parse_stream return %p: " | 5336 | debug_printf_parse("parse_stream return %p: " |
5329 | "end_trigger char found\n", | 5337 | "end_trigger char found\n", |
5330 | ctx.list_head); | 5338 | ctx.list_head); |
@@ -5546,16 +5554,22 @@ static struct pipe *parse_stream(char **pstring, | |||
5546 | continue; /* get next char */ | 5554 | continue; /* get next char */ |
5547 | } | 5555 | } |
5548 | #endif | 5556 | #endif |
5549 | case '{': | 5557 | /* fall through */ |
5550 | if (parse_group(&ctx, input, ch) != 0) { | 5558 | case '{': { |
5559 | int n = parse_group(&ctx, input, ch); | ||
5560 | if (n < 0) { | ||
5551 | goto parse_error; | 5561 | goto parse_error; |
5552 | } | 5562 | } |
5563 | debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n); | ||
5564 | heredoc_cnt += n; | ||
5553 | goto new_cmd; | 5565 | goto new_cmd; |
5566 | } | ||
5554 | case ')': | 5567 | case ')': |
5555 | #if ENABLE_HUSH_CASE | 5568 | #if ENABLE_HUSH_CASE |
5556 | if (ctx.ctx_res_w == RES_MATCH) | 5569 | if (ctx.ctx_res_w == RES_MATCH) |
5557 | goto case_semi; | 5570 | goto case_semi; |
5558 | #endif | 5571 | #endif |
5572 | |||
5559 | case '}': | 5573 | case '}': |
5560 | /* proper use of this character is caught by end_trigger: | 5574 | /* proper use of this character is caught by end_trigger: |
5561 | * if we see {, we call parse_group(..., end_trigger='}') | 5575 | * if we see {, we call parse_group(..., end_trigger='}') |
@@ -5604,7 +5618,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5604 | IF_HAS_KEYWORDS(pctx = p2;) | 5618 | IF_HAS_KEYWORDS(pctx = p2;) |
5605 | } while (HAS_KEYWORDS && pctx); | 5619 | } while (HAS_KEYWORDS && pctx); |
5606 | 5620 | ||
5607 | o_free_and_set_NULL(&ctx.word); | 5621 | o_free(&ctx.word); |
5608 | #if !BB_MMU | 5622 | #if !BB_MMU |
5609 | if (pstring) | 5623 | if (pstring) |
5610 | *pstring = NULL; | 5624 | *pstring = NULL; |
@@ -7035,7 +7049,7 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger) | |||
7035 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); | 7049 | debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode); |
7036 | } | 7050 | } |
7037 | #endif | 7051 | #endif |
7038 | pipe_list = parse_stream(NULL, inp, end_trigger); | 7052 | pipe_list = parse_stream(NULL, NULL, inp, end_trigger); |
7039 | if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */ | 7053 | if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */ |
7040 | /* If we are in "big" script | 7054 | /* If we are in "big" script |
7041 | * (not in `cmd` or something similar)... | 7055 | * (not in `cmd` or something similar)... |
diff --git a/shell/hush_test/hush-heredoc/heredoc_after_compound1.right b/shell/hush_test/hush-heredoc/heredoc_after_compound1.right new file mode 100644 index 000000000..9052f7d1f --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_after_compound1.right | |||
@@ -0,0 +1,2 @@ | |||
1 | Ok1 | ||
2 | Ok2 | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_after_compound1.tests b/shell/hush_test/hush-heredoc/heredoc_after_compound1.tests new file mode 100755 index 000000000..e7cfb5be1 --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_after_compound1.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | { cat <<EOF; }; echo Ok2 | ||
2 | Ok1 | ||
3 | EOF | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_var_expand1.right b/shell/hush_test/hush-heredoc/heredoc_var_expand1.right new file mode 100644 index 000000000..eb221832d --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_var_expand1.right | |||
@@ -0,0 +1,4 @@ | |||
1 | |||
2 | Ok1:0 | ||
3 | |||
4 | Ok2:0 | ||
diff --git a/shell/hush_test/hush-heredoc/heredoc_var_expand1.tests b/shell/hush_test/hush-heredoc/heredoc_var_expand1.tests new file mode 100755 index 000000000..3b00bab7b --- /dev/null +++ b/shell/hush_test/hush-heredoc/heredoc_var_expand1.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | x='*' | ||
2 | |||
3 | cat <<- EOF | ||
4 | ${x#'*'} | ||
5 | EOF | ||
6 | echo Ok1:$? | ||
7 | |||
8 | cat <<EOF | ||
9 | ${x#'*'} | ||
10 | EOF | ||
11 | echo Ok2:$? | ||