From af07b7c5b6717a4609ea9c77ab6ca9008456a8b0 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Tue, 7 Apr 2009 13:26:18 +0000 Subject: hush: fix heredoc <<- handling. hush-misc/heredoc2.tests testcase needs only one fix now - $var and `cmd` expancsion in heredocs function old new delta parse_redirect 265 306 +41 parse_stream 1866 1899 +33 --- shell/hush.c | 272 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 135 insertions(+), 137 deletions(-) (limited to 'shell/hush.c') diff --git a/shell/hush.c b/shell/hush.c index f7e5fbc5b..d82be3d70 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -250,16 +250,6 @@ static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER; #define SPECIAL_VAR_SYMBOL 3 -typedef enum redir_type { - REDIRECT_INVALID = 0, - REDIRECT_INPUT = 1, - REDIRECT_OVERWRITE = 2, - REDIRECT_APPEND = 3, - REDIRECT_HEREDOC = 4, - REDIRECT_IO = 5, - REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc load */ -} redir_type; - /* The descrip member of this structure is only used to make * debugging output pretty */ static const struct { @@ -349,10 +339,24 @@ typedef struct in_str { struct redir_struct { struct redir_struct *next; char *rd_filename; /* filename */ - int fd; /* file descriptor being redirected */ - int dup; /* -1, or file descriptor being duplicated */ - smallint /*enum redir_type*/ rd_type; + int rd_fd; /* file descriptor being redirected */ + int rd_dup; /* -1, or file descriptor being duplicated */ + smallint rd_type; /* (enum redir_type) */ + /* note: for heredocs, rd_filename contains heredoc delimiter, + * and subsequently heredoc itself; and rd_dup is + * "do we need to trim leading tabs?" bool flag + */ }; +typedef enum redir_type { + REDIRECT_INVALID = 0, + REDIRECT_INPUT = 1, + REDIRECT_OVERWRITE = 2, + REDIRECT_APPEND = 3, + REDIRECT_HEREDOC = 4, + REDIRECT_IO = 5, + REDIRECT_HEREDOC2 = 6, /* REDIRECT_HEREDOC after heredoc is loaded */ +} redir_type; + struct command { pid_t pid; /* 0 if exited */ @@ -910,6 +914,11 @@ static int check_and_run_traps(int sig) } #if ENABLE_HUSH_JOB +/* After [v]fork, in child: do not restore tty pgrp on xfunc death */ +#define disable_restore_tty_pgrp_on_exit() (die_sleep = 0) +/* After [v]fork, in parent: do not restore tty pgrp on xfunc death */ +#define enable_restore_tty_pgrp_on_exit() (die_sleep = -1) + /* Restores tty foreground process group, and exits. * May be called as signal handler for fatal signal * (will faithfully resend signal to itself, producing correct exit state) @@ -932,6 +941,11 @@ static void sigexit(int sig) kill_myself_with_sig(sig); /* does not return */ } +#else + +#define disable_restore_tty_pgrp_on_exit() ((void)0) +#define enable_restore_tty_pgrp_on_exit() ((void)0) + #endif /* Restores tty foreground process group, and exits. */ @@ -1359,6 +1373,13 @@ static void o_addstr(o_string *o, const char *str) { o_addblock(o, str, strlen(str)); } +static void nommu_addchr(o_string *o, int ch) +{ + if (o) + o_addchr(o, ch); +} +#else +#define nommu_addchr(o, str) ((void)0) #endif static void o_addstr_with_NUL(o_string *o, const char *str) @@ -2201,8 +2222,8 @@ static void setup_heredoc(int fd, const char *heredoc) /* Okay, pipe buffer was not big enough */ /* Note: we must not create a stray child (bastard? :) - * for the unsuspecting parent process. We create a grandchild - * and exit before we exec the process which consumes heredoc + * for the unsuspecting parent process. Child creates a grandchild + * and exits before parent execs the process which consumes heredoc * (that exec happens after we return from this function) */ pid = vfork(); if (pid < 0) @@ -2217,24 +2238,20 @@ static void setup_heredoc(int fd, const char *heredoc) /* grandchild */ close(fd); /* read side of the pipe */ #if BB_MMU - full_write(pair.wr, heredoc, len); + full_write(pair.wr, heredoc, len); /* may loop or block */ _exit(0); #else /* Delegate blocking writes to another process */ -# if ENABLE_HUSH_JOB - die_sleep = 0; /* do not restore tty pgrp on xfunc death */ -# endif + disable_restore_tty_pgrp_on_exit(); xmove_fd(pair.wr, STDOUT_FILENO); re_execute_shell(heredoc, 1); #endif } /* parent */ -#if ENABLE_HUSH_JOB - die_sleep = -1; /* restore tty pgrp on xfunc death */ -#endif + enable_restore_tty_pgrp_on_exit(); clean_up_after_re_execute(); close(pair.wr); - wait(NULL); /* wiat till child has died */ + wait(NULL); /* wait till child has died */ } /* squirrel != NULL means we squirrel away copies of stdin, stdout, @@ -2247,18 +2264,18 @@ static int setup_redirects(struct command *prog, int squirrel[]) for (redir = prog->redirects; redir; redir = redir->next) { if (redir->rd_type == REDIRECT_HEREDOC2) { - if (squirrel && redir->fd < 3) { - squirrel[redir->fd] = dup(redir->fd); + if (squirrel && redir->rd_fd < 3) { + squirrel[redir->rd_fd] = dup(redir->rd_fd); } /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ * of the heredoc */ debug_printf_parse("set heredoc '%s'\n", redir->rd_filename); - setup_heredoc(redir->fd, redir->rd_filename); + setup_heredoc(redir->rd_fd, redir->rd_filename); continue; } - if (redir->dup == -1) { + if (redir->rd_dup == -1) { char *p; if (redir->rd_filename == NULL) { /* Something went wrong in the parse. @@ -2277,19 +2294,19 @@ static int setup_redirects(struct command *prog, int squirrel[]) return 1; } } else { - openfd = redir->dup; + openfd = redir->rd_dup; } - if (openfd != redir->fd) { - if (squirrel && redir->fd < 3) { - squirrel[redir->fd] = dup(redir->fd); + if (openfd != redir->rd_fd) { + if (squirrel && redir->rd_fd < 3) { + squirrel[redir->rd_fd] = dup(redir->rd_fd); } if (openfd == -3) { /* "-" means "close me" and we use -3 for that */ - close(redir->fd); + close(redir->rd_fd); } else { - dup2(openfd, redir->fd); - if (redir->dup == -1) + dup2(openfd, redir->rd_fd); + if (redir->rd_dup == -1) close(openfd); } } @@ -2350,7 +2367,7 @@ static void free_pipe(struct pipe *pi, int indent) #endif for (r = command->redirects; r; r = rnext) { debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip); - if (r->dup == -1) { + if (r->rd_dup == -1) { /* guard against the case >$FOO, where foo is unset or blank */ if (r->rd_filename) { debug_printf_clean(" %s\n", r->rd_filename); @@ -2358,7 +2375,7 @@ static void free_pipe(struct pipe *pi, int indent) r->rd_filename = NULL; } } else { - debug_printf_clean("&%d\n", r->dup); + debug_printf_clean("&%d\n", r->rd_dup); } rnext = r->next; free(r); @@ -2968,7 +2985,7 @@ static int run_pipe(struct pipe *pi) command->pid = BB_MMU ? fork() : vfork(); if (!command->pid) { /* child */ #if ENABLE_HUSH_JOB - die_sleep = 0; /* do not restore tty pgrp on xfunc death */ + disable_restore_tty_pgrp_on_exit(); /* Every child adds itself to new process group * with pgid == pid_of_first_child_in_pipe */ @@ -3003,9 +3020,7 @@ static int run_pipe(struct pipe *pi) } /* parent or error */ -#if ENABLE_HUSH_JOB - die_sleep = -1; /* restore tty pgrp on xfunc death */ -#endif + enable_restore_tty_pgrp_on_exit(); #if !BB_MMU /* Clean up after vforked child */ clean_up_after_re_execute(); @@ -3914,15 +3929,11 @@ static int redirect_dup_num(o_string *as_string, struct in_str *input) return -1; ch = i_getch(input); /* get the & */ -#if !BB_MMU - o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); ch = i_peek(input); if (ch == '-') { ch = i_getch(input); -#if !BB_MMU - o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); return -3; /* "-" represents "close me" */ } d = 0; @@ -3931,9 +3942,7 @@ static int redirect_dup_num(o_string *as_string, struct in_str *input) d = d*10 + (ch-'0'); ok = 1; ch = i_getch(input); -#if !BB_MMU - o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); ch = i_peek(input); } if (ok) return d; @@ -3962,8 +3971,21 @@ static int parse_redirect(struct parse_context *ctx, dup_num = redirect_dup_num(&ctx->as_string, input); if (dup_num == -2) return 1; /* syntax error */ + } else { + int ch = i_peek(input); + dup_num = (ch == '-'); + if (dup_num) { /* <<-... */ + ch = i_getch(input); + nommu_addchr(&ctx->as_string, ch); + ch = i_peek(input); + } + /* <<[-] word is the same as <<[-]word */ + while (ch == ' ' || ch == '\t') { + ch = i_getch(input); + nommu_addchr(&ctx->as_string, ch); + ch = i_peek(input); + } } -//TODO: else { check for <<-word } if (style == REDIRECT_OVERWRITE && dup_num == -1) { int ch = i_peek(input); @@ -3973,9 +3995,7 @@ static int parse_redirect(struct parse_context *ctx, * >| and > are the same for now. Just eat |. */ ch = i_getch(input); -#if !BB_MMU - o_addchr(&ctx->as_string, ch); -#endif + nommu_addchr(&ctx->as_string, ch); } } @@ -3988,16 +4008,16 @@ static int parse_redirect(struct parse_context *ctx, /* redir->next = NULL; */ /* redir->rd_filename = NULL; */ redir->rd_type = style; - redir->fd = (fd == -1) ? redir_table[style].default_fd : fd; + redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd; - debug_printf_parse("redirect type %d %s\n", redir->fd, redir_table[style].descrip); + debug_printf_parse("redirect type %d %s\n", redir->rd_fd, redir_table[style].descrip); - redir->dup = dup_num; - if (dup_num != -1) { + redir->rd_dup = dup_num; + if (style != REDIRECT_HEREDOC && dup_num != -1) { /* 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 */ - debug_printf_parse("duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); + debug_printf_parse("duplicating redirect '%d>&%d'\n", redir->rd_fd, redir->rd_dup); } else { /* Set ctx->pending_redirect, so we know what to do at the * end of the next parsed word. */ @@ -4039,28 +4059,45 @@ static int redirect_opt_num(o_string *o) return num; } -//TODO: add NOMMU as_string fill -static char *fetch_till_str(struct in_str *input, const char *word) +#if BB_MMU +#define fetch_till_str(as_string, input, word, skip_tabs) \ + fetch_till_str(input, word, skip_tabs) +#endif +static char *fetch_till_str(o_string *as_string, + struct in_str *input, + const char *word, + int skip_tabs) { o_string heredoc = NULL_O_STRING; int past_EOL = 0; int ch; + goto jump_in; while (1) { ch = i_getch(input); - if (ch == EOF) { - o_free_unsafe(&heredoc); - return NULL; - } + nommu_addchr(as_string, ch); if (ch == '\n') { if (strcmp(heredoc.data + past_EOL, word) == 0) { heredoc.data[past_EOL] = '\0'; debug_printf_parse("parsed heredoc '%s'\n", heredoc.data); return heredoc.data; } - past_EOL = heredoc.length + 1; + do { + o_addchr(&heredoc, ch); + past_EOL = heredoc.length; + jump_in: + do { + ch = i_getch(input); + nommu_addchr(as_string, ch); + } while (skip_tabs && ch == '\t'); + } while (ch == '\n'); + } + if (ch == EOF) { + o_free_unsafe(&heredoc); + return NULL; } o_addchr(&heredoc, ch); + nommu_addchr(as_string, ch); } } @@ -4076,25 +4113,27 @@ static int fetch_heredocs(int heredoc_cnt, struct parse_context *ctx, struct in_ pi->num_cmds, cmd->argv ? cmd->argv[0] : "NONE"); for (i = 0; i < pi->num_cmds; i++) { - struct redir_struct *redirect = cmd->redirects; + struct redir_struct *redir = cmd->redirects; debug_printf_parse("fetch_heredocs: %d cmd argv0:'%s'\n", i, cmd->argv ? cmd->argv[0] : "NONE"); - while (redirect) { - if (redirect->rd_type == REDIRECT_HEREDOC) { + while (redir) { + if (redir->rd_type == REDIRECT_HEREDOC) { char *p; if (heredoc_cnt <= 0) return 1; /* error */ - redirect->rd_type = REDIRECT_HEREDOC2; - p = fetch_till_str(input, redirect->rd_filename); + redir->rd_type = REDIRECT_HEREDOC2; + /* redir->dup is (ab)used to indicate <<- */ + p = fetch_till_str(&ctx->as_string, input, + redir->rd_filename, redir->rd_dup); if (!p) return 1; /* unexpected EOF */ - free(redirect->rd_filename); - redirect->rd_filename = p; + free(redir->rd_filename); + redir->rd_filename = p; heredoc_cnt--; } - redirect = redirect->next; + redir = redir->next; } cmd++; } @@ -4126,9 +4165,7 @@ static FILE *generate_stream_from_string(const char *s) bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); if (pid == 0) { /* child */ -#if ENABLE_HUSH_JOB - die_sleep = 0; /* do not restore tty pgrp on xfunc death */ -#endif + disable_restore_tty_pgrp_on_exit(); /* Process substitution is not considered to be usual * 'command execution'. * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. @@ -4157,9 +4194,7 @@ static FILE *generate_stream_from_string(const char *s) } /* parent */ -#if ENABLE_HUSH_JOB - die_sleep = -1; /* restore tty pgrp on xfunc death */ -#endif + enable_restore_tty_pgrp_on_exit(); clean_up_after_re_execute(); close(channel[1]); pf = fdopen(channel[0], "r"); @@ -4409,9 +4444,7 @@ static int handle_dollar(o_string *as_string, debug_printf_parse("handle_dollar entered: ch='%c'\n", ch); if (isalpha(ch)) { ch = i_getch(input); -#if !BB_MMU - if (as_string) o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); make_var: o_addchr(dest, SPECIAL_VAR_SYMBOL); while (1) { @@ -4422,17 +4455,13 @@ static int handle_dollar(o_string *as_string, if (!isalnum(ch) && ch != '_') break; ch = i_getch(input); -#if !BB_MMU - if (as_string) o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); } o_addchr(dest, SPECIAL_VAR_SYMBOL); } else if (isdigit(ch)) { make_one_char_var: ch = i_getch(input); -#if !BB_MMU - if (as_string) o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); o_addchr(dest, SPECIAL_VAR_SYMBOL); debug_printf_parse(": '%c'\n", ch); o_addchr(dest, ch | quote_mask); @@ -4450,18 +4479,14 @@ static int handle_dollar(o_string *as_string, o_addchr(dest, SPECIAL_VAR_SYMBOL); ch = i_getch(input); -#if !BB_MMU - if (as_string) o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); /* XXX maybe someone will try to escape the '}' */ expansion = 0; first_char = true; all_digits = false; while (1) { ch = i_getch(input); -#if !BB_MMU - if (as_string) o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); if (ch == '}') break; @@ -4530,15 +4555,11 @@ static int handle_dollar(o_string *as_string, int pos; # endif ch = i_getch(input); -# if !BB_MMU - if (as_string) o_addchr(as_string, ch); -# endif + nommu_addchr(as_string, ch); # if ENABLE_SH_MATH_SUPPORT if (i_peek(input) == '(') { ch = i_getch(input); -# if !BB_MMU - if (as_string) o_addchr(as_string, ch); -# endif + nommu_addchr(as_string, ch); o_addchr(dest, SPECIAL_VAR_SYMBOL); o_addchr(dest, /*quote_mask |*/ '+'); # if !BB_MMU @@ -4578,9 +4599,7 @@ static int handle_dollar(o_string *as_string, #endif case '_': ch = i_getch(input); -#if !BB_MMU - if (as_string) o_addchr(as_string, ch); -#endif + nommu_addchr(as_string, ch); ch = i_peek(input); if (isalnum(ch)) { /* it's $_name or $_123 */ ch = '_'; @@ -4611,9 +4630,8 @@ static int parse_stream_dquoted(o_string *as_string, again: ch = i_getch(input); -#if !BB_MMU - if (as_string && ch != EOF) o_addchr(as_string, ch); -#endif + if (ch != EOF) + nommu_addchr(as_string, ch); if (ch == dquote_end) { /* may be only '"' or EOF */ dest->nonnull = 1; if (dest->o_assignment == NOT_ASSIGNMENT) @@ -4769,9 +4787,7 @@ static struct pipe *parse_stream(char **pstring, #endif return pi; } -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); is_ifs = strchr(G.ifs, ch); is_special = strchr("<>;&|(){}#'" /* special outside of "str" */ "\\$\"" USE_HUSH_TICK("`") /* always special */ @@ -4882,10 +4898,8 @@ static struct pipe *parse_stream(char **pstring, i_getch(input); /* note: we do not add it to &ctx.as_string */ } -#if !BB_MMU //TODO: go back one char? - o_addchr(&ctx.as_string, '\n'); -#endif + nommu_addchr(&ctx.as_string, '\n'); } else { o_addQchr(&dest, ch); } @@ -4898,9 +4912,7 @@ static struct pipe *parse_stream(char **pstring, o_addchr(&dest, '\\'); ch = i_getch(input); o_addchr(&dest, ch); -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); break; case '$': if (handle_dollar(&ctx.as_string, &dest, input) != 0) { @@ -4917,9 +4929,7 @@ static struct pipe *parse_stream(char **pstring, syntax("unterminated '"); goto parse_error; } -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); if (ch == '\'') break; if (dest.o_assignment == NOT_ASSIGNMENT) @@ -4954,9 +4964,7 @@ static struct pipe *parse_stream(char **pstring, if (next == '>') { redir_style = REDIRECT_APPEND; ch = i_getch(input); -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); } #if 0 else if (next == '(') { @@ -4978,15 +4986,11 @@ static struct pipe *parse_stream(char **pstring, heredoc_cnt++; debug_printf_parse("++heredoc_cnt=%d\n", heredoc_cnt); ch = i_getch(input); -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); } else if (next == '>') { redir_style = REDIRECT_IO; ch = i_getch(input); -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); } #if 0 else if (next == '(') { @@ -5013,9 +5017,7 @@ static struct pipe *parse_stream(char **pstring, if (ch != ';') break; ch = i_getch(input); -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); if (ctx.ctx_res_w == RES_CASEI) { ctx.ctx_dsemicolon = 1; ctx.ctx_res_w = RES_MATCH; @@ -5034,9 +5036,7 @@ static struct pipe *parse_stream(char **pstring, } if (next == '&') { ch = i_getch(input); -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); done_pipe(&ctx, PIPE_AND); } else { done_pipe(&ctx, PIPE_BG); @@ -5052,9 +5052,7 @@ static struct pipe *parse_stream(char **pstring, #endif if (next == '|') { /* || */ ch = i_getch(input); -#if !BB_MMU - o_addchr(&ctx.as_string, ch); -#endif + nommu_addchr(&ctx.as_string, ch); done_pipe(&ctx, PIPE_OR); } else { /* we could pick up a file descriptor choice here @@ -5537,7 +5535,7 @@ int hush_main(int argc, char **argv) tcsetpgrp(G_interactive_fd, getpid()); /* -1 is special - makes xfuncs longjmp, not exit * (we reset die_sleep = 0 whereever we [v]fork) */ - die_sleep = -1; + enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ if (setjmp(die_jmp)) { /* xfunc has failed! die die die */ hush_exit(xfunc_error_retval); -- cgit v1.2.3