aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-07 19:56:55 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-07 19:56:55 +0000
commit02d6f1ad7229b62c9386c7063f6525e744536c66 (patch)
treea3b0f3a416913121a30eb40d239eae17404c1046
parent25af86f73d29ee8b74f8776f4e1defe04cf7dcf1 (diff)
downloadbusybox-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.c159
-rw-r--r--shell/hush_test/hush-misc/heredoc2.right1
-rwxr-xr-xshell/hush_test/hush-misc/heredoc2.tests1
-rw-r--r--shell/hush_test/hush-misc/heredoc3.right8
-rwxr-xr-xshell/hush_test/hush-misc/heredoc3.tests11
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 {
339struct redir_struct { 341struct 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};
350typedef enum redir_type { 354typedef 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)
1328static void o_reset(o_string *o) 1335static 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 */
1694static 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
2198static void setup_heredoc(int fd, const char *heredoc) 2219static 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 @@
1exit EOF-f 1exit EOF-f
2" 2"
3echo 1 3echo 1
4echo Hello World
4moo 5moo
5 EOF-f 6 EOF-f
6EOF-f f 7EOF-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"
5echo $f 5echo $f
6echo `echo Hello World`
6 moo 7 moo
7 EOF-f 8 EOF-f
8EOF-f f 9EOF-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 @@
1exit EOF-f
2"
3echo $f
4echo `echo Hello World`
5moo
6 EOF-f
7EOF-f f
8EOF-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 @@
1f=1
2 cat <<- EOF-f""
3 exit EOF-f
4"
5echo $f
6echo `echo Hello World`
7 moo
8 EOF-f
9EOF-f f
10EOF-f
11EOF-f