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 /shell | |
| 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>
Diffstat (limited to 'shell')
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:$? | ||
