diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-07 02:29:51 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-07 02:29:51 +0000 |
commit | 6c9be7f4518bf5594f5b9aaf981ed5dcc4a6939c (patch) | |
tree | 67f6d56a1d7c7fe251bb2dd41cd5b0c0118a0a10 | |
parent | ab2b06434240bb11f5bef7201f9d01027df79896 (diff) | |
download | busybox-w32-6c9be7f4518bf5594f5b9aaf981ed5dcc4a6939c.tar.gz busybox-w32-6c9be7f4518bf5594f5b9aaf981ed5dcc4a6939c.tar.bz2 busybox-w32-6c9be7f4518bf5594f5b9aaf981ed5dcc4a6939c.zip |
hush: heredoc support, based on patch by Mike Frysinger (vapier AT gentoo.org)
some TODOs are to be attacked later
function old new delta
parse_stream 1461 1866 +405
parse_redirect - 265 +265
setup_heredoc - 116 +116
setup_redirects 151 196 +45
builtin_exit 49 47 -2
redirect_opt_num 61 55 -6
setup_redirect 212 - -212
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 2/2 up/down: 831/-220) Total: 611 bytes
-rw-r--r-- | shell/hush.c | 398 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/exec.tests | 22 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/heredoc1.right | 5 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/heredoc1.tests | 9 |
4 files changed, 319 insertions, 115 deletions
diff --git a/shell/hush.c b/shell/hush.c index 14ca52ec0..792886944 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -21,8 +21,8 @@ | |||
21 | * rewrites. | 21 | * rewrites. |
22 | * | 22 | * |
23 | * Other credits: | 23 | * Other credits: |
24 | * o_addchr() derived from similar w_addchar function in glibc-2.2. | 24 | * o_addchr derived from similar w_addchar function in glibc-2.2. |
25 | * setup_redirect(), redirect_opt_num(), and big chunks of main() | 25 | * setup_redirect, redirect_opt_num, and big chunks of main |
26 | * and many builtins derived from contributions by Erik Andersen. | 26 | * and many builtins derived from contributions by Erik Andersen. |
27 | * Miscellaneous bugfixes from Matt Kraai. | 27 | * Miscellaneous bugfixes from Matt Kraai. |
28 | * | 28 | * |
@@ -249,11 +249,13 @@ static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER; | |||
249 | #define SPECIAL_VAR_SYMBOL 3 | 249 | #define SPECIAL_VAR_SYMBOL 3 |
250 | 250 | ||
251 | typedef enum redir_type { | 251 | typedef enum redir_type { |
252 | REDIRECT_INVALID = 0, | ||
252 | REDIRECT_INPUT = 1, | 253 | REDIRECT_INPUT = 1, |
253 | REDIRECT_OVERWRITE = 2, | 254 | REDIRECT_OVERWRITE = 2, |
254 | REDIRECT_APPEND = 3, | 255 | REDIRECT_APPEND = 3, |
255 | REDIRECT_HEREIS = 4, | 256 | REDIRECT_HEREDOC = 4, |
256 | REDIRECT_IO = 5 | 257 | REDIRECT_IO = 5, |
258 | REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc load */ | ||
257 | } redir_type; | 259 | } redir_type; |
258 | 260 | ||
259 | /* The descrip member of this structure is only used to make | 261 | /* The descrip member of this structure is only used to make |
@@ -263,12 +265,14 @@ static const struct { | |||
263 | signed char default_fd; | 265 | signed char default_fd; |
264 | char descrip[3]; | 266 | char descrip[3]; |
265 | } redir_table[] = { | 267 | } redir_table[] = { |
266 | { 0, 0, "()" }, | 268 | { 0, 0, "??" }, |
267 | { O_RDONLY, 0, "<" }, | 269 | { O_RDONLY, 0, "<" }, |
268 | { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, | 270 | { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, |
269 | { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, | 271 | { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" }, |
270 | { O_RDONLY, -1, "<<" }, | 272 | { O_RDONLY, 0, "<<" }, |
271 | { O_RDWR, 1, "<>" } | 273 | { O_CREAT|O_RDWR, 1, "<>" }, |
274 | /* Should not be needed. Bogus default_fd helps in debugging */ | ||
275 | /* { O_RDONLY, 77, "<<" }, */ | ||
272 | }; | 276 | }; |
273 | 277 | ||
274 | typedef enum reserved_style { | 278 | typedef enum reserved_style { |
@@ -2030,20 +2034,56 @@ static char **expand_assignments(char **argv, int count) | |||
2030 | } | 2034 | } |
2031 | 2035 | ||
2032 | 2036 | ||
2037 | //TODO: fix big string case! | ||
2038 | static void setup_heredoc(int fd, const char *heredoc) | ||
2039 | { | ||
2040 | struct fd_pair pair; | ||
2041 | pid_t pid; | ||
2042 | |||
2043 | xpiped_pair(pair); | ||
2044 | pid = vfork(); | ||
2045 | if (pid < 0) | ||
2046 | bb_perror_msg_and_die("vfork"); | ||
2047 | if (pid == 0) { /* child */ | ||
2048 | die_sleep = 0; | ||
2049 | close(pair.rd); | ||
2050 | xwrite_str(pair.wr, heredoc); | ||
2051 | _exit(0); | ||
2052 | } | ||
2053 | /* parent */ | ||
2054 | die_sleep = -1; | ||
2055 | close(pair.wr); | ||
2056 | xmove_fd(pair.rd, fd); | ||
2057 | } | ||
2058 | |||
2033 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, | 2059 | /* squirrel != NULL means we squirrel away copies of stdin, stdout, |
2034 | * and stderr if they are redirected. */ | 2060 | * and stderr if they are redirected. */ |
2035 | static int setup_redirects(struct command *prog, int squirrel[]) | 2061 | static int setup_redirects(struct command *prog, int squirrel[]) |
2036 | { | 2062 | { |
2063 | //TODO: no callers ever check return value - ?! | ||
2037 | int openfd, mode; | 2064 | int openfd, mode; |
2038 | struct redir_struct *redir; | 2065 | struct redir_struct *redir; |
2039 | 2066 | ||
2040 | for (redir = prog->redirects; redir; redir = redir->next) { | 2067 | for (redir = prog->redirects; redir; redir = redir->next) { |
2041 | if (redir->dup == -1 && redir->rd_filename == NULL) { | 2068 | if (redir->rd_type == REDIRECT_HEREDOC2) { |
2042 | /* something went wrong in the parse. Pretend it didn't happen */ | 2069 | if (squirrel && redir->fd < 3) { |
2070 | squirrel[redir->fd] = dup(redir->fd); | ||
2071 | } | ||
2072 | /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ | ||
2073 | * of the heredoc */ | ||
2074 | debug_printf_parse("set heredoc '%s'\n", | ||
2075 | redir->rd_filename); | ||
2076 | setup_heredoc(redir->fd, redir->rd_filename); | ||
2043 | continue; | 2077 | continue; |
2044 | } | 2078 | } |
2079 | |||
2045 | if (redir->dup == -1) { | 2080 | if (redir->dup == -1) { |
2046 | char *p; | 2081 | char *p; |
2082 | if (redir->rd_filename == NULL) { | ||
2083 | /* Something went wrong in the parse. | ||
2084 | * Pretend it didn't happen */ | ||
2085 | continue; | ||
2086 | } | ||
2047 | mode = redir_table[redir->rd_type].mode; | 2087 | mode = redir_table[redir->rd_type].mode; |
2048 | //TODO: check redir for names like '\\' | 2088 | //TODO: check redir for names like '\\' |
2049 | p = expand_string_to_string(redir->rd_filename); | 2089 | p = expand_string_to_string(redir->rd_filename); |
@@ -2051,7 +2091,8 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
2051 | free(p); | 2091 | free(p); |
2052 | if (openfd < 0) { | 2092 | if (openfd < 0) { |
2053 | /* this could get lost if stderr has been redirected, but | 2093 | /* this could get lost if stderr has been redirected, but |
2054 | bash and ash both lose it as well (though zsh doesn't!) */ | 2094 | * bash and ash both lose it as well (though zsh doesn't!) */ |
2095 | //what the above comment tries to say? | ||
2055 | return 1; | 2096 | return 1; |
2056 | } | 2097 | } |
2057 | } else { | 2098 | } else { |
@@ -3407,83 +3448,6 @@ static int run_and_free_list(struct pipe *pi) | |||
3407 | } | 3448 | } |
3408 | 3449 | ||
3409 | 3450 | ||
3410 | /* Peek ahead in the in_str to find out if we have a "&n" construct, | ||
3411 | * as in "2>&1", that represents duplicating a file descriptor. | ||
3412 | * Return either -2 (syntax error), -1 (no &), or the number found. | ||
3413 | */ | ||
3414 | static int redirect_dup_num(struct in_str *input) | ||
3415 | { | ||
3416 | int ch, d = 0, ok = 0; | ||
3417 | ch = i_peek(input); | ||
3418 | if (ch != '&') return -1; | ||
3419 | |||
3420 | i_getch(input); /* get the & */ | ||
3421 | ch = i_peek(input); | ||
3422 | if (ch == '-') { | ||
3423 | i_getch(input); | ||
3424 | return -3; /* "-" represents "close me" */ | ||
3425 | } | ||
3426 | while (isdigit(ch)) { | ||
3427 | d = d*10 + (ch-'0'); | ||
3428 | ok = 1; | ||
3429 | i_getch(input); | ||
3430 | ch = i_peek(input); | ||
3431 | } | ||
3432 | if (ok) return d; | ||
3433 | |||
3434 | bb_error_msg("ambiguous redirect"); | ||
3435 | return -2; | ||
3436 | } | ||
3437 | |||
3438 | /* The src parameter allows us to peek forward to a possible &n syntax | ||
3439 | * for file descriptor duplication, e.g., "2>&1". | ||
3440 | * Return code is 0 normally, 1 if a syntax error is detected in src. | ||
3441 | * Resource errors (in xmalloc) cause the process to exit */ | ||
3442 | static int setup_redirect(struct parse_context *ctx, | ||
3443 | int fd, | ||
3444 | redir_type style, | ||
3445 | struct in_str *input) | ||
3446 | { | ||
3447 | struct command *command = ctx->command; | ||
3448 | struct redir_struct *redir; | ||
3449 | struct redir_struct **redirp; | ||
3450 | int dup_num; | ||
3451 | |||
3452 | /* Check for a '2>&1' type redirect */ | ||
3453 | dup_num = redirect_dup_num(input); | ||
3454 | if (dup_num == -2) | ||
3455 | return 1; /* syntax error */ | ||
3456 | |||
3457 | /* Create a new redir_struct and drop it onto the end of the linked list */ | ||
3458 | redirp = &command->redirects; | ||
3459 | while ((redir = *redirp) != NULL) { | ||
3460 | redirp = &(redir->next); | ||
3461 | } | ||
3462 | *redirp = redir = xzalloc(sizeof(*redir)); | ||
3463 | /* redir->next = NULL; */ | ||
3464 | /* redir->rd_filename = NULL; */ | ||
3465 | redir->rd_type = style; | ||
3466 | redir->fd = (fd == -1) ? redir_table[style].default_fd : fd; | ||
3467 | |||
3468 | debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); | ||
3469 | |||
3470 | redir->dup = dup_num; | ||
3471 | if (dup_num != -1) { | ||
3472 | /* Erik had a check here that the file descriptor in question | ||
3473 | * is legit; I postpone that to "run time" | ||
3474 | * A "-" representation of "close me" shows up as a -3 here */ | ||
3475 | debug_printf("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); | ||
3476 | } else { | ||
3477 | /* We do _not_ try to open the file that src points to, | ||
3478 | * since we need to return and let src be expanded first. | ||
3479 | * Set ctx->pending_redirect, so we know what to do at the | ||
3480 | * end of the next parsed word. */ | ||
3481 | ctx->pending_redirect = redir; | ||
3482 | } | ||
3483 | return 0; | ||
3484 | } | ||
3485 | |||
3486 | |||
3487 | static struct pipe *new_pipe(void) | 3451 | static struct pipe *new_pipe(void) |
3488 | { | 3452 | { |
3489 | struct pipe *pi; | 3453 | struct pipe *pi; |
@@ -3602,7 +3566,6 @@ static void initialize_context(struct parse_context *ctx) | |||
3602 | done_command(ctx); | 3566 | done_command(ctx); |
3603 | } | 3567 | } |
3604 | 3568 | ||
3605 | |||
3606 | /* If a reserved word is found and processed, parse context is modified | 3569 | /* If a reserved word is found and processed, parse context is modified |
3607 | * and 1 is returned. | 3570 | * and 1 is returned. |
3608 | */ | 3571 | */ |
@@ -3766,14 +3729,26 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3766 | if (ctx->pending_redirect) { | 3729 | if (ctx->pending_redirect) { |
3767 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here | 3730 | /* We do not glob in e.g. >*.tmp case. bash seems to glob here |
3768 | * only if run as "bash", not "sh" */ | 3731 | * only if run as "bash", not "sh" */ |
3732 | /* http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html | ||
3733 | * "2.7 Redirection | ||
3734 | * ...the word that follows the redirection operator | ||
3735 | * shall be subjected to tilde expansion, parameter expansion, | ||
3736 | * command substitution, arithmetic expansion, and quote | ||
3737 | * removal. Pathname expansion shall not be performed | ||
3738 | * on the word by a non-interactive shell; an interactive | ||
3739 | * shell may perform it, but shall do so only when | ||
3740 | * the expansion would result in one word." | ||
3741 | */ | ||
3769 | ctx->pending_redirect->rd_filename = xstrdup(word->data); | 3742 | ctx->pending_redirect->rd_filename = xstrdup(word->data); |
3770 | word->o_assignment = NOT_ASSIGNMENT; | 3743 | word->o_assignment = NOT_ASSIGNMENT; |
3771 | debug_printf("word stored in rd_filename: '%s'\n", word->data); | 3744 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); |
3772 | } else { | 3745 | } else { |
3773 | /* "{ echo foo; } echo bar" - bad */ | 3746 | /* "{ echo foo; } echo bar" - bad */ |
3774 | /* NB: bash allows e.g.: | 3747 | /* NB: bash allows e.g.: |
3775 | * if true; then { echo foo; } fi | 3748 | * if true; then { echo foo; } fi |
3776 | * while if false; then false; fi do break; done | 3749 | * while if false; then false; fi do break; done |
3750 | * and disallows: | ||
3751 | * while if false; then false; fi; do; break; done | ||
3777 | * TODO? */ | 3752 | * TODO? */ |
3778 | if (command->group) { | 3753 | if (command->group) { |
3779 | syntax(word->data); | 3754 | syntax(word->data); |
@@ -3855,6 +3830,117 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3855 | return 0; | 3830 | return 0; |
3856 | } | 3831 | } |
3857 | 3832 | ||
3833 | |||
3834 | /* Peek ahead in the input to find out if we have a "&n" construct, | ||
3835 | * as in "2>&1", that represents duplicating a file descriptor. | ||
3836 | * Return: -3 if >&- "close fd" construct is seen, | ||
3837 | * -2 (syntax error), -1 if no & was seen, or the number found. | ||
3838 | */ | ||
3839 | #if BB_MMU | ||
3840 | #define redirect_dup_num(as_string, input) \ | ||
3841 | redirect_dup_num(input) | ||
3842 | #endif | ||
3843 | static int redirect_dup_num(o_string *as_string, struct in_str *input) | ||
3844 | { | ||
3845 | int ch, d, ok; | ||
3846 | |||
3847 | ch = i_peek(input); | ||
3848 | if (ch != '&') | ||
3849 | return -1; | ||
3850 | |||
3851 | ch = i_getch(input); /* get the & */ | ||
3852 | #if !BB_MMU | ||
3853 | o_addchr(as_string, ch); | ||
3854 | #endif | ||
3855 | ch = i_peek(input); | ||
3856 | if (ch == '-') { | ||
3857 | ch = i_getch(input); | ||
3858 | #if !BB_MMU | ||
3859 | o_addchr(as_string, ch); | ||
3860 | #endif | ||
3861 | return -3; /* "-" represents "close me" */ | ||
3862 | } | ||
3863 | d = 0; | ||
3864 | ok = 0; | ||
3865 | while (ch != EOF && isdigit(ch)) { | ||
3866 | d = d*10 + (ch-'0'); | ||
3867 | ok = 1; | ||
3868 | ch = i_getch(input); | ||
3869 | #if !BB_MMU | ||
3870 | o_addchr(as_string, ch); | ||
3871 | #endif | ||
3872 | ch = i_peek(input); | ||
3873 | } | ||
3874 | if (ok) return d; | ||
3875 | |||
3876 | //TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2) | ||
3877 | |||
3878 | bb_error_msg("ambiguous redirect"); | ||
3879 | return -2; | ||
3880 | } | ||
3881 | |||
3882 | /* Return code is 0 normally, 1 if a syntax error is detected | ||
3883 | */ | ||
3884 | static int parse_redirect(struct parse_context *ctx, | ||
3885 | int fd, | ||
3886 | redir_type style, | ||
3887 | struct in_str *input) | ||
3888 | { | ||
3889 | struct command *command = ctx->command; | ||
3890 | struct redir_struct *redir; | ||
3891 | struct redir_struct **redirp; | ||
3892 | int dup_num; | ||
3893 | |||
3894 | dup_num = -1; | ||
3895 | if (style != REDIRECT_HEREDOC) { | ||
3896 | /* Check for a '2>&1' type redirect */ | ||
3897 | dup_num = redirect_dup_num(&ctx->as_string, input); | ||
3898 | if (dup_num == -2) | ||
3899 | return 1; /* syntax error */ | ||
3900 | } | ||
3901 | //TODO: else { check for <<-word } | ||
3902 | |||
3903 | if (style == REDIRECT_OVERWRITE && dup_num == -1) { | ||
3904 | int ch = i_peek(input); | ||
3905 | if (ch == '|') { | ||
3906 | /* >|FILE redirect ("clobbering" >). | ||
3907 | * Since we do not support "set -o noclobber" yet, | ||
3908 | * >| and > are the same for now. Just eat |. | ||
3909 | */ | ||
3910 | ch = i_getch(input); | ||
3911 | #if !BB_MMU | ||
3912 | o_addchr(&ctx->as_string, ch); | ||
3913 | #endif | ||
3914 | } | ||
3915 | } | ||
3916 | |||
3917 | /* Create a new redir_struct and append it to the linked list */ | ||
3918 | redirp = &command->redirects; | ||
3919 | while ((redir = *redirp) != NULL) { | ||
3920 | redirp = &(redir->next); | ||
3921 | } | ||
3922 | *redirp = redir = xzalloc(sizeof(*redir)); | ||
3923 | /* redir->next = NULL; */ | ||
3924 | /* redir->rd_filename = NULL; */ | ||
3925 | redir->rd_type = style; | ||
3926 | redir->fd = (fd == -1) ? redir_table[style].default_fd : fd; | ||
3927 | |||
3928 | debug_printf_parse("redirect type %d %s\n", redir->fd, redir_table[style].descrip); | ||
3929 | |||
3930 | redir->dup = dup_num; | ||
3931 | if (dup_num != -1) { | ||
3932 | /* Erik had a check here that the file descriptor in question | ||
3933 | * is legit; I postpone that to "run time" | ||
3934 | * A "-" representation of "close me" shows up as a -3 here */ | ||
3935 | debug_printf_parse("duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); | ||
3936 | } else { | ||
3937 | /* Set ctx->pending_redirect, so we know what to do at the | ||
3938 | * end of the next parsed word. */ | ||
3939 | ctx->pending_redirect = redir; | ||
3940 | } | ||
3941 | return 0; | ||
3942 | } | ||
3943 | |||
3858 | /* If a redirect is immediately preceded by a number, that number is | 3944 | /* If a redirect is immediately preceded by a number, that number is |
3859 | * supposed to tell which file descriptor to redirect. This routine | 3945 | * supposed to tell which file descriptor to redirect. This routine |
3860 | * looks for such preceding numbers. In an ideal world this routine | 3946 | * looks for such preceding numbers. In an ideal world this routine |
@@ -3863,25 +3949,97 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3863 | * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo | 3949 | * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo |
3864 | * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo | 3950 | * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo |
3865 | * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo | 3951 | * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo |
3866 | * A -1 output from this program means no valid number was found, so the | 3952 | * |
3867 | * caller should use the appropriate default for this redirection. | 3953 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html |
3954 | * "2.7 Redirection | ||
3955 | * ... If n is quoted, the number shall not be recognized as part of | ||
3956 | * the redirection expression. For example: | ||
3957 | * echo \2>a | ||
3958 | * writes the character 2 into file a" | ||
3959 | * I am not sure we do it right (and not sure we care) | ||
3960 | * | ||
3961 | * A -1 return means no valid number was found, | ||
3962 | * the caller should use the appropriate default for this redirection. | ||
3868 | */ | 3963 | */ |
3869 | static int redirect_opt_num(o_string *o) | 3964 | static int redirect_opt_num(o_string *o) |
3870 | { | 3965 | { |
3871 | int num; | 3966 | int num; |
3872 | 3967 | ||
3873 | if (o->length == 0) | 3968 | if (o->data == NULL) |
3969 | return -1; | ||
3970 | num = bb_strtou(o->data, NULL, 10); | ||
3971 | if (errno || num < 0) | ||
3874 | return -1; | 3972 | return -1; |
3875 | for (num = 0; num < o->length; num++) { | ||
3876 | if (!isdigit(o->data[num])) { | ||
3877 | return -1; | ||
3878 | } | ||
3879 | } | ||
3880 | num = atoi(o->data); | ||
3881 | o_reset(o); | 3973 | o_reset(o); |
3882 | return num; | 3974 | return num; |
3883 | } | 3975 | } |
3884 | 3976 | ||
3977 | //TODO: add NOMMU as_string fill | ||
3978 | static char *fetch_till_str(struct in_str *input, const char *word) | ||
3979 | { | ||
3980 | o_string heredoc = NULL_O_STRING; | ||
3981 | int past_EOL = 0; | ||
3982 | int ch; | ||
3983 | |||
3984 | while (1) { | ||
3985 | ch = i_getch(input); | ||
3986 | if (ch == EOF) { | ||
3987 | o_free_unsafe(&heredoc); | ||
3988 | return NULL; | ||
3989 | } | ||
3990 | if (ch == '\n') { | ||
3991 | if (strcmp(heredoc.data + past_EOL, word) == 0) { | ||
3992 | heredoc.data[past_EOL] = '\0'; | ||
3993 | debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); | ||
3994 | return heredoc.data; | ||
3995 | } | ||
3996 | past_EOL = heredoc.length + 1; | ||
3997 | } | ||
3998 | o_addchr(&heredoc, ch); | ||
3999 | } | ||
4000 | } | ||
4001 | |||
4002 | static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_str *input) | ||
4003 | { | ||
4004 | struct pipe *pi = ctx->list_head; | ||
4005 | |||
4006 | while (pi) { | ||
4007 | int i; | ||
4008 | struct command *cmd = pi->cmds; | ||
4009 | |||
4010 | debug_printf_parse("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n", | ||
4011 | pi->num_cmds, | ||
4012 | cmd->argv ? cmd->argv[0] : "NONE"); | ||
4013 | for (i = 0; i < pi->num_cmds; i++) { | ||
4014 | struct redir_struct *redirect = cmd->redirects; | ||
4015 | |||
4016 | debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n", | ||
4017 | i, cmd->argv ? cmd->argv[0] : "NONE"); | ||
4018 | while (redirect) { | ||
4019 | if (redirect->rd_type == REDIRECT_HEREDOC) { | ||
4020 | char *p; | ||
4021 | |||
4022 | if (heredoc_cnt <= 0) | ||
4023 | return 1; /* error */ | ||
4024 | redirect->rd_type = REDIRECT_HEREDOC2; | ||
4025 | p = fetch_till_str(input, redirect->rd_filename); | ||
4026 | if (!p) | ||
4027 | return 1; /* unexpected EOF */ | ||
4028 | free(redirect->rd_filename); | ||
4029 | redirect->rd_filename = p; | ||
4030 | heredoc_cnt--; | ||
4031 | } | ||
4032 | redirect = redirect->next; | ||
4033 | } | ||
4034 | cmd++; | ||
4035 | } | ||
4036 | pi = pi->next; | ||
4037 | } | ||
4038 | /* Should be 0. If it isn't, it's a parse error */ | ||
4039 | return heredoc_cnt; | ||
4040 | } | ||
4041 | |||
4042 | |||
3885 | #if BB_MMU | 4043 | #if BB_MMU |
3886 | #define parse_stream(pstring, input, end_trigger) \ | 4044 | #define parse_stream(pstring, input, end_trigger) \ |
3887 | parse_stream(input, end_trigger) | 4045 | parse_stream(input, end_trigger) |
@@ -4391,8 +4549,7 @@ static int parse_stream_dquoted(o_string *as_string, | |||
4391 | again: | 4549 | again: |
4392 | ch = i_getch(input); | 4550 | ch = i_getch(input); |
4393 | #if !BB_MMU | 4551 | #if !BB_MMU |
4394 | if (as_string && ch != EOF) | 4552 | if (as_string && ch != EOF) o_addchr(as_string, ch); |
4395 | o_addchr(as_string, ch); | ||
4396 | #endif | 4553 | #endif |
4397 | if (ch == dquote_end) { /* may be only '"' or EOF */ | 4554 | if (ch == dquote_end) { /* may be only '"' or EOF */ |
4398 | dest->nonnull = 1; | 4555 | dest->nonnull = 1; |
@@ -4479,6 +4636,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4479 | struct parse_context ctx; | 4636 | struct parse_context ctx; |
4480 | o_string dest = NULL_O_STRING; | 4637 | o_string dest = NULL_O_STRING; |
4481 | int is_in_dquote; | 4638 | int is_in_dquote; |
4639 | int heredoc_cnt; | ||
4482 | 4640 | ||
4483 | /* Double-quote state is handled in the state variable is_in_dquote. | 4641 | /* Double-quote state is handled in the state variable is_in_dquote. |
4484 | * A single-quote triggers a bypass of the main loop until its mate is | 4642 | * A single-quote triggers a bypass of the main loop until its mate is |
@@ -4498,6 +4656,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4498 | /* dest.o_assignment = MAYBE_ASSIGNMENT; - already is */ | 4656 | /* dest.o_assignment = MAYBE_ASSIGNMENT; - already is */ |
4499 | initialize_context(&ctx); | 4657 | initialize_context(&ctx); |
4500 | is_in_dquote = 0; | 4658 | is_in_dquote = 0; |
4659 | heredoc_cnt = 0; | ||
4501 | while (1) { | 4660 | while (1) { |
4502 | const char *is_ifs; | 4661 | const char *is_ifs; |
4503 | const char *is_special; | 4662 | const char *is_special; |
@@ -4518,6 +4677,11 @@ static struct pipe *parse_stream(char **pstring, | |||
4518 | ch, ch, dest.o_escape); | 4677 | ch, ch, dest.o_escape); |
4519 | if (ch == EOF) { | 4678 | if (ch == EOF) { |
4520 | struct pipe *pi; | 4679 | struct pipe *pi; |
4680 | |||
4681 | if (heredoc_cnt) { | ||
4682 | syntax("unterminated here document"); | ||
4683 | goto parse_error; | ||
4684 | } | ||
4521 | if (done_word(&dest, &ctx)) { | 4685 | if (done_word(&dest, &ctx)) { |
4522 | goto parse_error; | 4686 | goto parse_error; |
4523 | } | 4687 | } |
@@ -4578,14 +4742,36 @@ static struct pipe *parse_stream(char **pstring, | |||
4578 | #endif | 4742 | #endif |
4579 | /* Treat newline as a command separator. */ | 4743 | /* Treat newline as a command separator. */ |
4580 | done_pipe(&ctx, PIPE_SEQ); | 4744 | done_pipe(&ctx, PIPE_SEQ); |
4745 | debug_printf_parse("heredoc_cnt:%d\n", heredoc_cnt); | ||
4746 | if (heredoc_cnt) { | ||
4747 | if (fetch_heredocs(heredoc_cnt, &ctx, input)) | ||
4748 | goto parse_error; | ||
4749 | heredoc_cnt = 0; | ||
4750 | } | ||
4581 | dest.o_assignment = MAYBE_ASSIGNMENT; | 4751 | dest.o_assignment = MAYBE_ASSIGNMENT; |
4582 | ch = ';'; | 4752 | ch = ';'; |
4583 | /* note: if (is_ifs) continue; | 4753 | /* note: if (is_ifs) continue; |
4584 | * will still trigger for us */ | 4754 | * will still trigger for us */ |
4585 | } | 4755 | } |
4586 | } | 4756 | } |
4587 | if (end_trigger && end_trigger == ch) { | 4757 | if (end_trigger && end_trigger == ch |
4758 | && (heredoc_cnt == 0 || end_trigger != ';') | ||
4759 | ) { | ||
4588 | //TODO: disallow "{ cmd }" without semicolon | 4760 | //TODO: disallow "{ cmd }" without semicolon |
4761 | if (heredoc_cnt) { | ||
4762 | /* This is technically valid: | ||
4763 | * { cat <<HERE; }; echo Ok | ||
4764 | * heredoc | ||
4765 | * heredoc | ||
4766 | * heredoc | ||
4767 | * HERE | ||
4768 | * but we don't support this. | ||
4769 | * We require heredoc to be in enclosing {}/(), | ||
4770 | * if any. | ||
4771 | */ | ||
4772 | syntax("unterminated here document"); | ||
4773 | goto parse_error; | ||
4774 | } | ||
4589 | if (done_word(&dest, &ctx)) { | 4775 | if (done_word(&dest, &ctx)) { |
4590 | goto parse_error; | 4776 | goto parse_error; |
4591 | } | 4777 | } |
@@ -4715,7 +4901,8 @@ static struct pipe *parse_stream(char **pstring, | |||
4715 | goto parse_error; | 4901 | goto parse_error; |
4716 | } | 4902 | } |
4717 | #endif | 4903 | #endif |
4718 | setup_redirect(&ctx, redir_fd, redir_style, input); | 4904 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) |
4905 | goto parse_error; | ||
4719 | break; | 4906 | break; |
4720 | case '<': | 4907 | case '<': |
4721 | redir_fd = redirect_opt_num(&dest); | 4908 | redir_fd = redirect_opt_num(&dest); |
@@ -4724,7 +4911,9 @@ static struct pipe *parse_stream(char **pstring, | |||
4724 | } | 4911 | } |
4725 | redir_style = REDIRECT_INPUT; | 4912 | redir_style = REDIRECT_INPUT; |
4726 | if (next == '<') { | 4913 | if (next == '<') { |
4727 | redir_style = REDIRECT_HEREIS; | 4914 | redir_style = REDIRECT_HEREDOC; |
4915 | heredoc_cnt++; | ||
4916 | debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt); | ||
4728 | ch = i_getch(input); | 4917 | ch = i_getch(input); |
4729 | #if !BB_MMU | 4918 | #if !BB_MMU |
4730 | o_addchr(&ctx.as_string, ch); | 4919 | o_addchr(&ctx.as_string, ch); |
@@ -4742,7 +4931,8 @@ static struct pipe *parse_stream(char **pstring, | |||
4742 | goto parse_error; | 4931 | goto parse_error; |
4743 | } | 4932 | } |
4744 | #endif | 4933 | #endif |
4745 | setup_redirect(&ctx, redir_fd, redir_style, input); | 4934 | if (parse_redirect(&ctx, redir_fd, redir_style, input)) |
4935 | goto parse_error; | ||
4746 | break; | 4936 | break; |
4747 | case ';': | 4937 | case ';': |
4748 | #if ENABLE_HUSH_CASE | 4938 | #if ENABLE_HUSH_CASE |
diff --git a/shell/hush_test/hush-misc/exec.tests b/shell/hush_test/hush-misc/exec.tests index 01a7f591c..6de50fa7b 100755 --- a/shell/hush_test/hush-misc/exec.tests +++ b/shell/hush_test/hush-misc/exec.tests | |||
@@ -1,30 +1,30 @@ | |||
1 | # make sure we have a way of checking these things | 1 | # make sure we have a way of checking these things |
2 | [ ! -e /dev/fd ] && exit 1 | 2 | cd /proc/$$/fd || cd /dev/fd || exit 1 |
3 | 3 | ||
4 | [ -e /dev/fd/44 ] && exit 1 | 4 | [ -e 44 ] && exit 1 |
5 | exec 44>/dev/null | 5 | exec 44>/dev/null |
6 | [ -e /dev/fd/44 ] || exit 1 | 6 | [ -e 44 ] || exit 1 |
7 | echo pass fd out open | 7 | echo pass fd out open |
8 | 8 | ||
9 | [ -e /dev/fd/55 ] && exit 1 | 9 | [ -e 55 ] && exit 1 |
10 | exec 55>&44 | 10 | exec 55>&44 |
11 | [ -e /dev/fd/55 ] || exit 1 | 11 | [ -e 55 ] || exit 1 |
12 | echo pass fd out dup | 12 | echo pass fd out dup |
13 | 13 | ||
14 | exec 44>&- | 14 | exec 44>&- |
15 | [ -e /dev/fd/44 ] && exit 1 | 15 | [ -e 44 ] && exit 1 |
16 | echo pass fd out close | 16 | echo pass fd out close |
17 | 17 | ||
18 | [ -e /dev/fd/66 ] && exit 1 | 18 | [ -e 66 ] && exit 1 |
19 | exec 66</dev/null | 19 | exec 66</dev/null |
20 | [ -e /dev/fd/66 ] || exit 1 | 20 | [ -e 66 ] || exit 1 |
21 | echo pass fd in open | 21 | echo pass fd in open |
22 | 22 | ||
23 | [ -e /dev/fd/77 ] && exit 1 | 23 | [ -e 77 ] && exit 1 |
24 | exec 77<&66 | 24 | exec 77<&66 |
25 | [ -e /dev/fd/77 ] || exit 1 | 25 | [ -e 77 ] || exit 1 |
26 | echo pass fd in dup | 26 | echo pass fd in dup |
27 | 27 | ||
28 | exec 66<&- | 28 | exec 66<&- |
29 | [ -e /dev/fd/66 ] && exit 1 | 29 | [ -e 66 ] && exit 1 |
30 | echo pass fd in close | 30 | echo pass fd in close |
diff --git a/shell/hush_test/hush-misc/heredoc1.right b/shell/hush_test/hush-misc/heredoc1.right new file mode 100644 index 000000000..7fc68f3e1 --- /dev/null +++ b/shell/hush_test/hush-misc/heredoc1.right | |||
@@ -0,0 +1,5 @@ | |||
1 | qwe | ||
2 | asd | ||
3 | 123 | ||
4 | 456 | ||
5 | Ok | ||
diff --git a/shell/hush_test/hush-misc/heredoc1.tests b/shell/hush_test/hush-misc/heredoc1.tests new file mode 100755 index 000000000..2eeb4726b --- /dev/null +++ b/shell/hush_test/hush-misc/heredoc1.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | cat <<000; cat <<www; cat <<eee | ||
2 | 000 | ||
3 | qwe | ||
4 | asd | ||
5 | www | ||
6 | 123 | ||
7 | 456 | ||
8 | eee | ||
9 | echo Ok | ||