diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-07 19:56:55 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-07 19:56:55 +0000 |
commit | 02d6f1ad7229b62c9386c7063f6525e744536c66 (patch) | |
tree | a3b0f3a416913121a30eb40d239eae17404c1046 | |
parent | 25af86f73d29ee8b74f8776f4e1defe04cf7dcf1 (diff) | |
download | busybox-w32-02d6f1ad7229b62c9386c7063f6525e744536c66.tar.gz busybox-w32-02d6f1ad7229b62c9386c7063f6525e744536c66.tar.bz2 busybox-w32-02d6f1ad7229b62c9386c7063f6525e744536c66.zip |
hush: fix heredoc expansion of $var and `cmd`
function old new delta
expand_pseudo_dquoted - 104 +104
setup_heredoc 215 275 +60
done_word 669 691 +22
parse_stream 1899 1902 +3
setup_redirects 196 191 -5
free_pipe 189 183 -6
expand_variables 2349 2229 -120
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/3 up/down: 189/-131) Total: 58 bytes
-rw-r--r-- | shell/hush.c | 159 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/heredoc2.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/heredoc2.tests | 1 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/heredoc3.right | 8 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/heredoc3.tests | 11 |
5 files changed, 120 insertions, 60 deletions
diff --git a/shell/hush.c b/shell/hush.c index e636f5829..d5c90a262 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -305,7 +305,9 @@ typedef struct o_string { | |||
305 | * (by prepending \ to *, ?, [, \) */ | 305 | * (by prepending \ to *, ?, [, \) */ |
306 | smallint o_escape; | 306 | smallint o_escape; |
307 | smallint o_glob; | 307 | smallint o_glob; |
308 | smallint nonnull; | 308 | /* At least some part of the string was inside '' or "", |
309 | * possibly empty one: word"", wo''rd etc. */ | ||
310 | smallint o_quoted; | ||
309 | smallint has_empty_slot; | 311 | smallint has_empty_slot; |
310 | smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ | 312 | smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ |
311 | } o_string; | 313 | } o_string; |
@@ -339,12 +341,14 @@ typedef struct in_str { | |||
339 | struct redir_struct { | 341 | struct redir_struct { |
340 | struct redir_struct *next; | 342 | struct redir_struct *next; |
341 | char *rd_filename; /* filename */ | 343 | char *rd_filename; /* filename */ |
342 | int rd_fd; /* file descriptor being redirected */ | 344 | int rd_fd; /* fd to redirect */ |
343 | int rd_dup; /* -1, or file descriptor being duplicated */ | 345 | /* fd to redirect to, or -3 if rd_fd is to be closed (n>&-) */ |
346 | int rd_dup; | ||
344 | smallint rd_type; /* (enum redir_type) */ | 347 | smallint rd_type; /* (enum redir_type) */ |
345 | /* note: for heredocs, rd_filename contains heredoc delimiter, | 348 | /* note: for heredocs, rd_filename contains heredoc delimiter, |
346 | * and subsequently heredoc itself; and rd_dup is | 349 | * and subsequently heredoc itself; and rd_dup is a bitmask: |
347 | * "do we need to trim leading tabs?" bool flag | 350 | * 1: do we need to trim leading tabs? |
351 | * 2: is heredoc quoted (<<'dleim' syntax) ? | ||
348 | */ | 352 | */ |
349 | }; | 353 | }; |
350 | typedef enum redir_type { | 354 | typedef enum redir_type { |
@@ -355,6 +359,9 @@ typedef enum redir_type { | |||
355 | REDIRECT_HEREDOC = 4, | 359 | REDIRECT_HEREDOC = 4, |
356 | REDIRECT_IO = 5, | 360 | REDIRECT_IO = 5, |
357 | REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc is loaded */ | 361 | REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc is loaded */ |
362 | REDIRFD_CLOSE = -3, | ||
363 | HEREDOC_SKIPTABS = 1, | ||
364 | HEREDOC_QUOTED = 2, | ||
358 | } redir_type; | 365 | } redir_type; |
359 | 366 | ||
360 | 367 | ||
@@ -1328,7 +1335,7 @@ static void setup_string_in_str(struct in_str *i, const char *s) | |||
1328 | static void o_reset(o_string *o) | 1335 | static void o_reset(o_string *o) |
1329 | { | 1336 | { |
1330 | o->length = 0; | 1337 | o->length = 0; |
1331 | o->nonnull = 0; | 1338 | o->o_quoted = 0; |
1332 | if (o->data) | 1339 | if (o->data) |
1333 | o->data[0] = '\0'; | 1340 | o->data[0] = '\0'; |
1334 | } | 1341 | } |
@@ -1677,6 +1684,39 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
1677 | return n; | 1684 | return n; |
1678 | } | 1685 | } |
1679 | 1686 | ||
1687 | /* Helper to expand $((...)) and heredoc body. These act as if | ||
1688 | * they are in double quotes, with the exception that they are not :). | ||
1689 | * Just the rules are similar: "expand only $var and `cmd`" | ||
1690 | * | ||
1691 | * Returns malloced string. | ||
1692 | * As an optimization, we return NULL if expansion is not needed. | ||
1693 | */ | ||
1694 | static char *expand_pseudo_dquoted(const char *str) | ||
1695 | { | ||
1696 | char *exp_str; | ||
1697 | struct in_str input; | ||
1698 | o_string dest = NULL_O_STRING; | ||
1699 | |||
1700 | if (strchr(str, '$') == NULL | ||
1701 | #if ENABLE_HUSH_TICK | ||
1702 | && strchr(str, '`') == NULL | ||
1703 | #endif | ||
1704 | ) { | ||
1705 | return NULL; | ||
1706 | } | ||
1707 | |||
1708 | /* We need to expand. Example: | ||
1709 | * echo $(($a + `echo 1`)) $((1 + $((2)) )) | ||
1710 | */ | ||
1711 | setup_string_in_str(&input, str); | ||
1712 | parse_stream_dquoted(NULL, &dest, &input, EOF); | ||
1713 | //bb_error_msg("'%s' -> '%s'", str, dest.data); | ||
1714 | exp_str = expand_string_to_string(dest.data); | ||
1715 | //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); | ||
1716 | o_free_unsafe(&dest); | ||
1717 | return exp_str; | ||
1718 | } | ||
1719 | |||
1680 | /* Expand all variable references in given string, adding words to list[] | 1720 | /* Expand all variable references in given string, adding words to list[] |
1681 | * at n, n+1,... positions. Return updated n (so that list[n] is next one | 1721 | * at n, n+1,... positions. Return updated n (so that list[n] is next one |
1682 | * to be filled). This routine is extremely tricky: has to deal with | 1722 | * to be filled). This routine is extremely tricky: has to deal with |
@@ -1809,26 +1849,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
1809 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ | 1849 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
1810 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); | 1850 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); |
1811 | 1851 | ||
1812 | /* Optional: skip expansion if expr is simple ("a + 3", "i++" etc) */ | 1852 | exp_str = expand_pseudo_dquoted(arg); |
1813 | exp_str = NULL; | ||
1814 | if (strchr(arg, '$') != NULL | ||
1815 | #if ENABLE_HUSH_TICK | ||
1816 | || strchr(arg, '`') != NULL | ||
1817 | #endif | ||
1818 | ) { | ||
1819 | /* We need to expand. Example: | ||
1820 | * echo $(($a + `echo 1`)) $((1 + $((2)) )) | ||
1821 | */ | ||
1822 | struct in_str input; | ||
1823 | o_string dest = NULL_O_STRING; | ||
1824 | |||
1825 | setup_string_in_str(&input, arg); | ||
1826 | parse_stream_dquoted(NULL, &dest, &input, EOF); | ||
1827 | //bb_error_msg("'%s' -> '%s'", arg, dest.data); | ||
1828 | exp_str = expand_string_to_string(dest.data); | ||
1829 | //bb_error_msg("'%s' -> '%s'", dest.data, exp_str); | ||
1830 | o_free_unsafe(&dest); | ||
1831 | } | ||
1832 | hooks.lookupvar = get_local_var_value; | 1853 | hooks.lookupvar = get_local_var_value; |
1833 | hooks.setvar = arith_set_local_var; | 1854 | hooks.setvar = arith_set_local_var; |
1834 | hooks.endofname = endofname; | 1855 | hooks.endofname = endofname; |
@@ -2195,16 +2216,26 @@ static void clean_up_after_re_execute(void) | |||
2195 | #endif /* !BB_MMU */ | 2216 | #endif /* !BB_MMU */ |
2196 | 2217 | ||
2197 | 2218 | ||
2198 | static void setup_heredoc(int fd, const char *heredoc) | 2219 | static void setup_heredoc(struct redir_struct *redir) |
2199 | { | 2220 | { |
2200 | struct fd_pair pair; | 2221 | struct fd_pair pair; |
2201 | pid_t pid; | 2222 | pid_t pid; |
2202 | int len, written; | 2223 | int len, written; |
2224 | /* the _body_ of heredoc (misleading field name) */ | ||
2225 | const char *heredoc = redir->rd_filename; | ||
2226 | char *expanded; | ||
2227 | |||
2228 | expanded = NULL; | ||
2229 | if (!(redir->rd_dup & HEREDOC_QUOTED)) { | ||
2230 | expanded = expand_pseudo_dquoted(heredoc); | ||
2231 | if (expanded) | ||
2232 | heredoc = expanded; | ||
2233 | } | ||
2234 | len = strlen(heredoc); | ||
2203 | 2235 | ||
2204 | xpiped_pair(pair); | 2236 | xpiped_pair(pair); |
2205 | xmove_fd(pair.rd, fd); | 2237 | xmove_fd(pair.rd, redir->rd_fd); |
2206 | 2238 | ||
2207 | len = strlen(heredoc); | ||
2208 | /* Try writing without forking. Newer kernels have | 2239 | /* Try writing without forking. Newer kernels have |
2209 | * dynamically growing pipes. Must use non-blocking write! */ | 2240 | * dynamically growing pipes. Must use non-blocking write! */ |
2210 | ndelay_on(pair.wr); | 2241 | ndelay_on(pair.wr); |
@@ -2237,7 +2268,7 @@ static void setup_heredoc(int fd, const char *heredoc) | |||
2237 | if (pid != 0) | 2268 | if (pid != 0) |
2238 | _exit(0); | 2269 | _exit(0); |
2239 | /* grandchild */ | 2270 | /* grandchild */ |
2240 | close(fd); /* read side of the pipe */ | 2271 | close(redir->rd_fd); /* read side of the pipe */ |
2241 | #if BB_MMU | 2272 | #if BB_MMU |
2242 | full_write(pair.wr, heredoc, len); /* may loop or block */ | 2273 | full_write(pair.wr, heredoc, len); /* may loop or block */ |
2243 | _exit(0); | 2274 | _exit(0); |
@@ -2252,6 +2283,7 @@ static void setup_heredoc(int fd, const char *heredoc) | |||
2252 | enable_restore_tty_pgrp_on_exit(); | 2283 | enable_restore_tty_pgrp_on_exit(); |
2253 | clean_up_after_re_execute(); | 2284 | clean_up_after_re_execute(); |
2254 | close(pair.wr); | 2285 | close(pair.wr); |
2286 | free(expanded); | ||
2255 | wait(NULL); /* wait till child has died */ | 2287 | wait(NULL); /* wait till child has died */ |
2256 | } | 2288 | } |
2257 | 2289 | ||
@@ -2272,7 +2304,7 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
2272 | * of the heredoc */ | 2304 | * of the heredoc */ |
2273 | debug_printf_parse("set heredoc '%s'\n", | 2305 | debug_printf_parse("set heredoc '%s'\n", |
2274 | redir->rd_filename); | 2306 | redir->rd_filename); |
2275 | setup_heredoc(redir->rd_fd, redir->rd_filename); | 2307 | setup_heredoc(redir); |
2276 | continue; | 2308 | continue; |
2277 | } | 2309 | } |
2278 | 2310 | ||
@@ -2302,11 +2334,11 @@ static int setup_redirects(struct command *prog, int squirrel[]) | |||
2302 | if (squirrel && redir->rd_fd < 3) { | 2334 | if (squirrel && redir->rd_fd < 3) { |
2303 | squirrel[redir->rd_fd] = dup(redir->rd_fd); | 2335 | squirrel[redir->rd_fd] = dup(redir->rd_fd); |
2304 | } | 2336 | } |
2305 | if (openfd == -3) { | 2337 | if (openfd == REDIRFD_CLOSE) { |
2306 | /* "-" means "close me" and we use -3 for that */ | 2338 | /* "n>-" means "close me" */ |
2307 | close(redir->rd_fd); | 2339 | close(redir->rd_fd); |
2308 | } else { | 2340 | } else { |
2309 | dup2(openfd, redir->rd_fd); | 2341 | xdup2(openfd, redir->rd_fd); |
2310 | if (redir->rd_dup == -1) | 2342 | if (redir->rd_dup == -1) |
2311 | close(openfd); | 2343 | close(openfd); |
2312 | } | 2344 | } |
@@ -2350,14 +2382,16 @@ static void free_pipe(struct pipe *pi, int indent) | |||
2350 | debug_printf_clean("%s command %d:\n", indenter(indent), i); | 2382 | debug_printf_clean("%s command %d:\n", indenter(indent), i); |
2351 | if (command->argv) { | 2383 | if (command->argv) { |
2352 | for (a = 0, p = command->argv; *p; a++, p++) { | 2384 | for (a = 0, p = command->argv; *p; a++, p++) { |
2353 | debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p); | 2385 | debug_printf_clean("%s argv[%d] = %s\n", |
2386 | indenter(indent), a, *p); | ||
2354 | } | 2387 | } |
2355 | free_strings(command->argv); | 2388 | free_strings(command->argv); |
2356 | command->argv = NULL; | 2389 | command->argv = NULL; |
2357 | } | 2390 | } |
2358 | /* not "else if": on syntax error, we may have both! */ | 2391 | /* not "else if": on syntax error, we may have both! */ |
2359 | if (command->group) { | 2392 | if (command->group) { |
2360 | debug_printf_clean("%s begin group (grp_type:%d)\n", indenter(indent), command->grp_type); | 2393 | debug_printf_clean("%s begin group (grp_type:%d)\n", |
2394 | indenter(indent), command->grp_type); | ||
2361 | free_pipe_list(command->group, indent+3); | 2395 | free_pipe_list(command->group, indent+3); |
2362 | debug_printf_clean("%s end group\n", indenter(indent)); | 2396 | debug_printf_clean("%s end group\n", indenter(indent)); |
2363 | command->group = NULL; | 2397 | command->group = NULL; |
@@ -2367,17 +2401,15 @@ static void free_pipe(struct pipe *pi, int indent) | |||
2367 | command->group_as_string = NULL; | 2401 | command->group_as_string = NULL; |
2368 | #endif | 2402 | #endif |
2369 | for (r = command->redirects; r; r = rnext) { | 2403 | for (r = command->redirects; r; r = rnext) { |
2370 | debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip); | 2404 | debug_printf_clean("%s redirect %d%s", indenter(indent), |
2371 | if (r->rd_dup == -1) { | 2405 | r->fd, redir_table[r->rd_type].descrip); |
2372 | /* guard against the case >$FOO, where foo is unset or blank */ | 2406 | /* guard against the case >$FOO, where foo is unset or blank */ |
2373 | if (r->rd_filename) { | 2407 | if (r->rd_filename) { |
2374 | debug_printf_clean(" %s\n", r->rd_filename); | 2408 | debug_printf_clean(" fname:'%s'\n", r->rd_filename); |
2375 | free(r->rd_filename); | 2409 | free(r->rd_filename); |
2376 | r->rd_filename = NULL; | 2410 | r->rd_filename = NULL; |
2377 | } | ||
2378 | } else { | ||
2379 | debug_printf_clean("&%d\n", r->rd_dup); | ||
2380 | } | 2411 | } |
2412 | debug_printf_clean(" rd_dup:%d\n", r->rd_dup); | ||
2381 | rnext = r->next; | 2413 | rnext = r->next; |
2382 | free(r); | 2414 | free(r); |
2383 | } | 2415 | } |
@@ -3791,7 +3823,7 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3791 | struct command *command = ctx->command; | 3823 | struct command *command = ctx->command; |
3792 | 3824 | ||
3793 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, command); | 3825 | debug_printf_parse("done_word entered: '%s' %p\n", word->data, command); |
3794 | if (word->length == 0 && word->nonnull == 0) { | 3826 | if (word->length == 0 && word->o_quoted == 0) { |
3795 | debug_printf_parse("done_word return 0: true null, ignored\n"); | 3827 | debug_printf_parse("done_word return 0: true null, ignored\n"); |
3796 | return 0; | 3828 | return 0; |
3797 | } | 3829 | } |
@@ -3821,6 +3853,11 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3821 | * the expansion would result in one word." | 3853 | * the expansion would result in one word." |
3822 | */ | 3854 | */ |
3823 | ctx->pending_redirect->rd_filename = xstrdup(word->data); | 3855 | ctx->pending_redirect->rd_filename = xstrdup(word->data); |
3856 | if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC | ||
3857 | && word->o_quoted | ||
3858 | ) { | ||
3859 | ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED; | ||
3860 | } | ||
3824 | word->o_assignment = NOT_ASSIGNMENT; | 3861 | word->o_assignment = NOT_ASSIGNMENT; |
3825 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); | 3862 | debug_printf_parse("word stored in rd_filename: '%s'\n", word->data); |
3826 | } else { | 3863 | } else { |
@@ -3862,7 +3899,7 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3862 | } | 3899 | } |
3863 | } | 3900 | } |
3864 | #endif | 3901 | #endif |
3865 | if (word->nonnull /* word had "xx" or 'xx' at least as part of it. */ | 3902 | if (word->o_quoted /* word had "xx" or 'xx' at least as part of it. */ |
3866 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ | 3903 | /* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ |
3867 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) | 3904 | && (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) |
3868 | /* (otherwise it's known to be not empty and is already safe) */ | 3905 | /* (otherwise it's known to be not empty and is already safe) */ |
@@ -3914,7 +3951,7 @@ static int done_word(o_string *word, struct parse_context *ctx) | |||
3914 | 3951 | ||
3915 | /* Peek ahead in the input to find out if we have a "&n" construct, | 3952 | /* Peek ahead in the input to find out if we have a "&n" construct, |
3916 | * as in "2>&1", that represents duplicating a file descriptor. | 3953 | * as in "2>&1", that represents duplicating a file descriptor. |
3917 | * Return: -3 if >&- "close fd" construct is seen, | 3954 | * Return: REDIRFD_CLOSE (-3) if >&- "close fd" construct is seen, |
3918 | * -2 (syntax error), -1 if no & was seen, or the number found. | 3955 | * -2 (syntax error), -1 if no & was seen, or the number found. |
3919 | */ | 3956 | */ |
3920 | #if BB_MMU | 3957 | #if BB_MMU |
@@ -3935,7 +3972,7 @@ static int redirect_dup_num(o_string *as_string, struct in_str *input) | |||
3935 | if (ch == '-') { | 3972 | if (ch == '-') { |
3936 | ch = i_getch(input); | 3973 | ch = i_getch(input); |
3937 | nommu_addchr(as_string, ch); | 3974 | nommu_addchr(as_string, ch); |
3938 | return -3; /* "-" represents "close me" */ | 3975 | return REDIRFD_CLOSE; |
3939 | } | 3976 | } |
3940 | d = 0; | 3977 | d = 0; |
3941 | ok = 0; | 3978 | ok = 0; |
@@ -3974,7 +4011,7 @@ static int parse_redirect(struct parse_context *ctx, | |||
3974 | return 1; /* syntax error */ | 4011 | return 1; /* syntax error */ |
3975 | } else { | 4012 | } else { |
3976 | int ch = i_peek(input); | 4013 | int ch = i_peek(input); |
3977 | dup_num = (ch == '-'); | 4014 | dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */ |
3978 | if (dup_num) { /* <<-... */ | 4015 | if (dup_num) { /* <<-... */ |
3979 | ch = i_getch(input); | 4016 | ch = i_getch(input); |
3980 | nommu_addchr(&ctx->as_string, ch); | 4017 | nommu_addchr(&ctx->as_string, ch); |
@@ -4011,14 +4048,16 @@ static int parse_redirect(struct parse_context *ctx, | |||
4011 | redir->rd_type = style; | 4048 | redir->rd_type = style; |
4012 | redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; | 4049 | redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; |
4013 | 4050 | ||
4014 | debug_printf_parse("redirect type %d %s\n", redir->rd_fd, redir_table[style].descrip); | 4051 | debug_printf_parse("redirect type %d %s\n", redir->rd_fd, |
4052 | redir_table[style].descrip); | ||
4015 | 4053 | ||
4016 | redir->rd_dup = dup_num; | 4054 | redir->rd_dup = dup_num; |
4017 | if (style != REDIRECT_HEREDOC && dup_num != -1) { | 4055 | if (style != REDIRECT_HEREDOC && dup_num != -1) { |
4018 | /* Erik had a check here that the file descriptor in question | 4056 | /* Erik had a check here that the file descriptor in question |
4019 | * is legit; I postpone that to "run time" | 4057 | * is legit; I postpone that to "run time" |
4020 | * A "-" representation of "close me" shows up as a -3 here */ | 4058 | * A "-" representation of "close me" shows up as a -3 here */ |
4021 | debug_printf_parse("duplicating redirect '%d>&%d'\n", redir->rd_fd, redir->rd_dup); | 4059 | debug_printf_parse("duplicating redirect '%d>&%d'\n", |
4060 | redir->rd_fd, redir->rd_dup); | ||
4022 | } else { | 4061 | } else { |
4023 | /* Set ctx->pending_redirect, so we know what to do at the | 4062 | /* Set ctx->pending_redirect, so we know what to do at the |
4024 | * end of the next parsed word. */ | 4063 | * end of the next parsed word. */ |
@@ -4127,7 +4166,7 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_ | |||
4127 | redir->rd_type = REDIRECT_HEREDOC2; | 4166 | redir->rd_type = REDIRECT_HEREDOC2; |
4128 | /* redir->dup is (ab)used to indicate <<- */ | 4167 | /* redir->dup is (ab)used to indicate <<- */ |
4129 | p = fetch_till_str(&ctx->as_string, input, | 4168 | p = fetch_till_str(&ctx->as_string, input, |
4130 | redir->rd_filename, redir->rd_dup); | 4169 | redir->rd_filename, redir->rd_dup & HEREDOC_SKIPTABS); |
4131 | if (!p) | 4170 | if (!p) |
4132 | return 1; /* unexpected EOF */ | 4171 | return 1; /* unexpected EOF */ |
4133 | free(redir->rd_filename); | 4172 | free(redir->rd_filename); |
@@ -4264,7 +4303,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx, | |||
4264 | #endif | 4303 | #endif |
4265 | if (command->argv /* word [word](... */ | 4304 | if (command->argv /* word [word](... */ |
4266 | || dest->length /* word(... */ | 4305 | || dest->length /* word(... */ |
4267 | || dest->nonnull /* ""(... */ | 4306 | || dest->o_quoted /* ""(... */ |
4268 | ) { | 4307 | ) { |
4269 | syntax(NULL); | 4308 | syntax(NULL); |
4270 | debug_printf_parse("parse_group return 1: " | 4309 | debug_printf_parse("parse_group return 1: " |
@@ -4634,7 +4673,7 @@ static int parse_stream_dquoted(o_string *as_string, | |||
4634 | if (ch != EOF) | 4673 | if (ch != EOF) |
4635 | nommu_addchr(as_string, ch); | 4674 | nommu_addchr(as_string, ch); |
4636 | if (ch == dquote_end) { /* may be only '"' or EOF */ | 4675 | if (ch == dquote_end) { /* may be only '"' or EOF */ |
4637 | dest->nonnull = 1; | 4676 | dest->o_quoted = 1; |
4638 | if (dest->o_assignment == NOT_ASSIGNMENT) | 4677 | if (dest->o_assignment == NOT_ASSIGNMENT) |
4639 | dest->o_escape ^= 1; | 4678 | dest->o_escape ^= 1; |
4640 | debug_printf_parse("parse_stream_dquoted return 0\n"); | 4679 | debug_printf_parse("parse_stream_dquoted return 0\n"); |
@@ -4923,7 +4962,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4923 | } | 4962 | } |
4924 | break; | 4963 | break; |
4925 | case '\'': | 4964 | case '\'': |
4926 | dest.nonnull = 1; | 4965 | dest.o_quoted = 1; |
4927 | while (1) { | 4966 | while (1) { |
4928 | ch = i_getch(input); | 4967 | ch = i_getch(input); |
4929 | if (ch == EOF) { | 4968 | if (ch == EOF) { |
@@ -4940,7 +4979,7 @@ static struct pipe *parse_stream(char **pstring, | |||
4940 | } | 4979 | } |
4941 | break; | 4980 | break; |
4942 | case '"': | 4981 | case '"': |
4943 | dest.nonnull = 1; | 4982 | dest.o_quoted = 1; |
4944 | is_in_dquote ^= 1; /* invert */ | 4983 | is_in_dquote ^= 1; /* invert */ |
4945 | if (dest.o_assignment == NOT_ASSIGNMENT) | 4984 | if (dest.o_assignment == NOT_ASSIGNMENT) |
4946 | dest.o_escape ^= 1; | 4985 | dest.o_escape ^= 1; |
@@ -5068,14 +5107,14 @@ static struct pipe *parse_stream(char **pstring, | |||
5068 | if (ctx.ctx_res_w == RES_MATCH | 5107 | if (ctx.ctx_res_w == RES_MATCH |
5069 | && ctx.command->argv == NULL /* not (word|(... */ | 5108 | && ctx.command->argv == NULL /* not (word|(... */ |
5070 | && dest.length == 0 /* not word(... */ | 5109 | && dest.length == 0 /* not word(... */ |
5071 | && dest.nonnull == 0 /* not ""(... */ | 5110 | && dest.o_quoted == 0 /* not ""(... */ |
5072 | ) { | 5111 | ) { |
5073 | continue; | 5112 | continue; |
5074 | } | 5113 | } |
5075 | #endif | 5114 | #endif |
5076 | #if ENABLE_HUSH_FUNCTIONS | 5115 | #if ENABLE_HUSH_FUNCTIONS |
5077 | if (dest.length != 0 /* not just () but word() */ | 5116 | if (dest.length != 0 /* not just () but word() */ |
5078 | && dest.nonnull == 0 /* not a"b"c() */ | 5117 | && dest.o_quoted == 0 /* not a"b"c() */ |
5079 | && ctx.command->argv == NULL /* it's the first word */ | 5118 | && ctx.command->argv == NULL /* it's the first word */ |
5080 | //TODO: "func ( ) {...}" - note spaces - is valid format too in bash | 5119 | //TODO: "func ( ) {...}" - note spaces - is valid format too in bash |
5081 | && i_peek(input) == ')' | 5120 | && i_peek(input) == ')' |
diff --git a/shell/hush_test/hush-misc/heredoc2.right b/shell/hush_test/hush-misc/heredoc2.right index 72c839c56..66545ae76 100644 --- a/shell/hush_test/hush-misc/heredoc2.right +++ b/shell/hush_test/hush-misc/heredoc2.right | |||
@@ -1,6 +1,7 @@ | |||
1 | exit EOF-f | 1 | exit EOF-f |
2 | " | 2 | " |
3 | echo 1 | 3 | echo 1 |
4 | echo Hello World | ||
4 | moo | 5 | moo |
5 | EOF-f | 6 | EOF-f |
6 | EOF-f f | 7 | EOF-f f |
diff --git a/shell/hush_test/hush-misc/heredoc2.tests b/shell/hush_test/hush-misc/heredoc2.tests index 71ab0e83a..19d9c9681 100755 --- a/shell/hush_test/hush-misc/heredoc2.tests +++ b/shell/hush_test/hush-misc/heredoc2.tests | |||
@@ -3,6 +3,7 @@ f=1 | |||
3 | exit EOF-f | 3 | exit EOF-f |
4 | " | 4 | " |
5 | echo $f | 5 | echo $f |
6 | echo `echo Hello World` | ||
6 | moo | 7 | moo |
7 | EOF-f | 8 | EOF-f |
8 | EOF-f f | 9 | EOF-f f |
diff --git a/shell/hush_test/hush-misc/heredoc3.right b/shell/hush_test/hush-misc/heredoc3.right new file mode 100644 index 000000000..9b114fabf --- /dev/null +++ b/shell/hush_test/hush-misc/heredoc3.right | |||
@@ -0,0 +1,8 @@ | |||
1 | exit EOF-f | ||
2 | " | ||
3 | echo $f | ||
4 | echo `echo Hello World` | ||
5 | moo | ||
6 | EOF-f | ||
7 | EOF-f f | ||
8 | EOF-f | ||
diff --git a/shell/hush_test/hush-misc/heredoc3.tests b/shell/hush_test/hush-misc/heredoc3.tests new file mode 100755 index 000000000..6391e49f9 --- /dev/null +++ b/shell/hush_test/hush-misc/heredoc3.tests | |||
@@ -0,0 +1,11 @@ | |||
1 | f=1 | ||
2 | cat <<- EOF-f"" | ||
3 | exit EOF-f | ||
4 | " | ||
5 | echo $f | ||
6 | echo `echo Hello World` | ||
7 | moo | ||
8 | EOF-f | ||
9 | EOF-f f | ||
10 | EOF-f | ||
11 | EOF-f | ||