diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-14 23:49:29 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2025-08-15 00:08:11 +0200 |
| commit | 3e766dce5fa05b6ad95633de00438a04895cb9b4 (patch) | |
| tree | f245f6e6ffb1d3e40966df63015b391b5f447068 | |
| parent | 0d1f6a54f7be96a409a28d12da6ec48caf514e0b (diff) | |
hush: implement <<<here_string syntax
function old new delta
setup_heredoc 299 351 +52
parse_stream 2514 2540 +26
parse_redirect 335 351 +16
redir_table 40 48 +8
static.setup_redirects 394 400 +6
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/0 up/down: 108/0) Total: 108 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | shell/hush.c | 110 | ||||
| -rw-r--r-- | shell/hush_test/hush-heredoc/herestring1.right | 33 | ||||
| -rwxr-xr-x | shell/hush_test/hush-heredoc/herestring1.tests | 35 |
3 files changed, 137 insertions, 41 deletions
diff --git a/shell/hush.c b/shell/hush.c index 9f7d0d751..a8b6f8eac 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -596,17 +596,18 @@ typedef struct in_str { HFILE *file; } in_str; -/* The descrip member of this structure is only used to make +/* The descrip3 member of this structure is only used to make * debugging output pretty */ static const struct { int32_t mode; signed char default_fd; - char descrip[3]; + char descrip3[3]; } redir_table[] ALIGN4 = { { O_RDONLY, 0, "<" }, { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, { O_CREAT|O_RDWR, 1, "<>" }, + { O_RDONLY, 0, "<<<" }, { O_RDONLY, 0, "<<" }, /* Should not be needed. Bogus default_fd helps in debugging */ /* { O_RDONLY, 77, "<<" }, */ @@ -626,12 +627,13 @@ struct redir_struct { */ }; typedef enum redir_type { - REDIRECT_INPUT = 0, - REDIRECT_OVERWRITE = 1, - REDIRECT_APPEND = 2, - REDIRECT_IO = 3, - REDIRECT_HEREDOC = 4, - REDIRECT_HEREDOC2 = 5, /* REDIRECT_HEREDOC after heredoc is loaded */ + REDIRECT_INPUT = 0, + REDIRECT_OVERWRITE = 1, + REDIRECT_APPEND = 2, + REDIRECT_IO = 3, + REDIRECT_HERESTRING = 4, + REDIRECT_HEREDOC = 5, + REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc is loaded */ REDIRFD_CLOSE = -3, REDIRFD_SYNTAX_ERR = -2, @@ -3754,8 +3756,8 @@ static struct pipe *free_pipe(struct pipe *pi) //command->group_as_string = NULL; #endif for (r = command->redirects; r; r = rnext) { - debug_printf_clean(" redirect %d%s", - r->rd_fd, redir_table[r->rd_type].descrip); + debug_printf_clean(" redirect %d%.3s", + r->rd_fd, redir_table[r->rd_type].descrip3); /* guard against the case >$FOO, where foo is unset or blank */ if (r->rd_filename) { debug_printf_clean(" fname:'%s'\n", r->rd_filename); @@ -4514,31 +4516,31 @@ static int parse_redirect(struct parse_context *ctx, int dup_num; dup_num = REDIRFD_TO_FILE; - if (style != REDIRECT_HEREDOC) { + if (style != REDIRECT_HEREDOC && style != REDIRECT_HERESTRING) { /* Check for a '>&1' type redirect */ dup_num = parse_redir_right_fd(&ctx->as_string, input); if (dup_num == REDIRFD_SYNTAX_ERR) return 1; - } else { - int ch = i_peek_and_eat_bkslash_nl(input); - dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ - if (dup_num) { /* <<-... */ - ch = i_getch(input); - nommu_addchr(&ctx->as_string, ch); - ch = i_peek(input); + if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { + int ch = i_peek_and_eat_bkslash_nl(input); + if (ch == '|') { + /* >|FILE redirect ("clobbering" >). + * Since we do not support "set -o noclobber" yet, + * >| and > are the same for now. Just eat |. + */ + ch = i_getch(input); + nommu_addchr(&ctx->as_string, ch); + } } - } - - if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) { + } else if (style == REDIRECT_HEREDOC) { int ch = i_peek_and_eat_bkslash_nl(input); - if (ch == '|') { - /* >|FILE redirect ("clobbering" >). - * Since we do not support "set -o noclobber" yet, - * >| and > are the same for now. Just eat |. - */ + dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ + if (dup_num) { /* "<<-HEREDOC"? */ ch = i_getch(input); nommu_addchr(&ctx->as_string, ch); } + } else { /* REDIRECT_HERESTRING */ + dup_num = 0; /* make sure no bits like HEREDOC_QUOTED are set */ } /* Create a new redir_struct and append it to the linked list */ @@ -4552,11 +4554,14 @@ static int parse_redirect(struct parse_context *ctx, redir->rd_type = style; redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; - debug_printf_parse("redirect type %d %s\n", redir->rd_fd, - redir_table[style].descrip); + debug_printf_parse("redirect type %d %.3s\n", redir->rd_fd, + redir_table[style].descrip3); redir->rd_dup = dup_num; - if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) { + if (style != REDIRECT_HEREDOC + && style != REDIRECT_HERESTRING + && dup_num != REDIRFD_TO_FILE + ) { /* Erik had a check here that the file descriptor in question * is legit; I postpone that to "run time" * A "-" representation of "close me" shows up as a -3 here */ @@ -4565,7 +4570,7 @@ static int parse_redirect(struct parse_context *ctx, } else { #if 0 /* Instead we emit error message at run time */ if (ctx->pending_redirect) { - /* For example, "cmd > <file" */ + /* For example, "CMD > <FILE" */ syntax_error("invalid redirect"); } #endif @@ -4580,10 +4585,10 @@ static int parse_redirect(struct parse_context *ctx, * supposed to tell which file descriptor to redirect. This routine * looks for such preceding numbers. In an ideal world this routine * needs to handle all the following classes of redirects... - * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo - * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo - * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo - * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo + * echo 2>FILE # redirects fd 2 to FILE, nothing passed to echo + * echo 49>FILE # redirects fd 49 to FILE, nothing passed to echo + * echo -2>FILE # redirects fd 1 to FILE, "-2" passed to echo + * echo 49x>FILE # redirects fd 1 to FILE, "49x" passed to echo * * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html * "2.7 Redirection @@ -4620,7 +4625,7 @@ static char *fetch_till_str(o_string *as_string, { o_string heredoc = NULL_O_STRING; unsigned past_EOL; - int prev = 0; /* not \ */ + int prev = 0; /* not '\' */ int ch; /* Starting with "" is necessary for this case: @@ -5913,6 +5918,14 @@ static struct pipe *parse_stream(char **pstring, debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt); ch = i_getch(input); nommu_addchr(&ctx.as_string, ch); + /* Check for here-string (<<<) */ + next = i_peek(input); + if (next == '<') { + redir_style = REDIRECT_HERESTRING; + ch = i_getch(input); + nommu_addchr(&ctx.as_string, ch); + heredoc_cnt--; /* here-strings don't use heredoc lines */ + } } else if (next == '>') { redir_style = REDIRECT_IO; ch = i_getch(input); @@ -6405,7 +6418,7 @@ static char *encode_then_expand_string(const char *str) //TODO: error check (encode_string returns 0 on error)? //bb_error_msg("'%s' -> '%s'", str, dest.data); exp_str = expand_string_to_string(dest.data, - EXP_FLAG_GLOBPROTECT_CHARS, + EXP_FLAG_GLOBPROTECT_CHARS, /* example: `echo '_\t_\\_\"_'` in heredoc */ /*unbackslash:*/ 1 ); //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); @@ -7990,12 +8003,25 @@ static void setup_heredoc(struct redir_struct *redir) #endif expanded = NULL; - if (!(redir->rd_dup & HEREDOC_QUOTED)) { + if (redir->rd_type == REDIRECT_HERESTRING) { + heredoc = expanded = expand_string_to_string(heredoc, + EXP_FLAG_GLOBPROTECT_CHARS, + /* ^^^^why? testcases: + * cat <<<`echo '_\t_\\_\"_'` prints _\t_\_\"_<newline> + * cat <<<"`echo '_\t_\\_\"_'`" prints _\t_\_"_<newline> + */ + /*unbackslash:*/ 1 + /* ^^^^^^^^^^ cat <<<\_ prints _<newline> */ + ); + } else if (!(redir->rd_dup & HEREDOC_QUOTED)) { expanded = encode_then_expand_string(heredoc); if (expanded) heredoc = expanded; } + len = strlen(heredoc); + if (redir->rd_type == REDIRECT_HERESTRING) + expanded[len++] = '\n'; close(redir->rd_fd); /* often saves dup2+close in xmove_fd */ xpiped_pair(pair); @@ -8291,12 +8317,14 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) int newfd; int closed; - if (redir->rd_type == REDIRECT_HEREDOC2) { - /* "rd_fd<<HERE" case */ + if (redir->rd_type == REDIRECT_HEREDOC2 + || redir->rd_type == REDIRECT_HERESTRING + ) { + /* "rd_fd<<HEREDOC_EOF" and "rd_fd<<<WORD" cases */ if (save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp) < 0) return 1; - /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ - * of the heredoc */ + /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ of the heredoc */ + /* for REDIRECT_HERESTRING, rd_filename holds "WORD" */ debug_printf_redir("set heredoc '%s'\n", redir->rd_filename); setup_heredoc(redir); diff --git a/shell/hush_test/hush-heredoc/herestring1.right b/shell/hush_test/hush-heredoc/herestring1.right new file mode 100644 index 000000000..555937daa --- /dev/null +++ b/shell/hush_test/hush-heredoc/herestring1.right @@ -0,0 +1,33 @@ +one +_two +\_three +\_four +\_two +\_three +\\_four +two_newlines + +/bin/c* +star_* +star_* +star_* +star_\* +star_* +star_\* +line1 +line2 + line3 +line1 +line2 + line3 +512 +256 +128 +64 +32 +16 +8 +4 +2 +v-$a-\t-\-\"-\x-`-\--\z-\*-\?- +v-$a-\t-\-"-\x-`-\--\z-\*-\?- diff --git a/shell/hush_test/hush-heredoc/herestring1.tests b/shell/hush_test/hush-heredoc/herestring1.tests new file mode 100755 index 000000000..fb7bd0dda --- /dev/null +++ b/shell/hush_test/hush-heredoc/herestring1.tests @@ -0,0 +1,35 @@ +cat <<<one +cat <<<\_two +cat <<<"\_three" +cat <<<'\_four' +cat <<<\\_two +cat <<<"\\_three" +cat <<<'\\_four' + +cat <<<$'two_newlines\n' + +cat <<</bin/c* + +cat <<<star_* +cat <<<star_\* +cat <<<"star_*" +cat <<<"star_\*" +cat <<<'star_*' +cat <<<'star_\*' + +var=$'line1 +line2 + line3' + +cat <<<$var + +cat <<<"$var" + +i=10 +until test $((--i)) = 0; do + cat <<<$((2**i)) +done + +a=qwerty +cat <<<`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'` +cat <<<"`echo v'-$a-\t-\\-\"-\x-\`-\--\z-\*-\?-'`" |
