📄 hush.c
字号:
if (last_redir) { last_redir->next=redir; } else { child->redirects=redir; } redir->type=style; redir->fd= (fd==-1) ? redir_table[style].default_fd : fd ; debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); /* Check for a '2>&1' type redirect */ redir->dup = redirect_dup_num(input); if (redir->dup == -2) return 1; /* syntax error */ if (redir->dup != -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("Duplicating redirect '%d>&%d'\n", redir->fd, redir->dup); } else { /* We do _not_ try to open the file that src points to, * since we need to return and let src be expanded first. * Set ctx->pending_redirect, so we know what to do at the * end of the next parsed word. */ ctx->pending_redirect = redir; } return 0;}struct pipe *new_pipe(void) { struct pipe *pi; pi = xmalloc(sizeof(struct pipe)); pi->num_progs = 0; pi->progs = NULL; pi->next = NULL; pi->followup = 0; /* invalid */ return pi;}static void initialize_context(struct p_context *ctx){ ctx->pipe=NULL; ctx->pending_redirect=NULL; ctx->child=NULL; ctx->list_head=new_pipe(); ctx->pipe=ctx->list_head; ctx->w=RES_NONE; ctx->stack=NULL; done_command(ctx); /* creates the memory for working child */}/* normal return is 0 * if a reserved word is found, and processed, return 1 * should handle if, then, elif, else, fi, for, while, until, do, done. * case, function, and select are obnoxious, save those for later. */int reserved_word(o_string *dest, struct p_context *ctx){ struct reserved_combo { char *literal; int code; long flag; }; /* Mostly a list of accepted follow-up reserved words. * FLAG_END means we are done with the sequence, and are ready * to turn the compound list into a command. * FLAG_START means the word must start a new compound list. */ static struct reserved_combo reserved_list[] = { { "if", RES_IF, FLAG_THEN | FLAG_START }, { "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, { "elif", RES_ELIF, FLAG_THEN }, { "else", RES_ELSE, FLAG_FI }, { "fi", RES_FI, FLAG_END }, { "for", RES_FOR, FLAG_DO | FLAG_START }, { "while", RES_WHILE, FLAG_DO | FLAG_START }, { "until", RES_UNTIL, FLAG_DO | FLAG_START }, { "do", RES_DO, FLAG_DONE }, { "done", RES_DONE, FLAG_END } }; struct reserved_combo *r; for (r=reserved_list;#define NRES sizeof(reserved_list)/sizeof(struct reserved_combo) r<reserved_list+NRES; r++) { if (strcmp(dest->data, r->literal) == 0) { debug_printf("found reserved word %s, code %d\n",r->literal,r->code); if (r->flag & FLAG_START) { struct p_context *new = xmalloc(sizeof(struct p_context)); debug_printf("push stack\n"); *new = *ctx; /* physical copy */ initialize_context(ctx); ctx->stack=new; } else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<<r->code))) { syntax(); ctx->w = RES_SNTX; b_reset (dest); return 1; } ctx->w=r->code; ctx->old_flag = r->flag; if (ctx->old_flag & FLAG_END) { struct p_context *old; debug_printf("pop stack\n"); old = ctx->stack; old->child->group = ctx->list_head; old->child->subshell = 0; *ctx = *old; /* physical copy */ free(old); } b_reset (dest); return 1; } } return 0;}/* normal return is 0. * Syntax or xglob errors return 1. */static int done_word(o_string *dest, struct p_context *ctx){ struct child_prog *child=ctx->child; glob_t *glob_target; int gr, flags = 0; debug_printf("done_word: %s %p\n", dest->data, child); if (dest->length == 0 && !dest->nonnull) { debug_printf(" true null, ignored\n"); return 0; } if (ctx->pending_redirect) { glob_target = &ctx->pending_redirect->word; } else { if (child->group) { syntax(); return 1; /* syntax error, groups and arglists don't mix */ } if (!child->argv) { debug_printf("checking %s for reserved-ness\n",dest->data); if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX; } glob_target = &child->glob_result; if (child->argv) flags |= GLOB_APPEND; } gr = xglob(dest, flags, glob_target); if (gr != 0) return 1; b_reset(dest); if (ctx->pending_redirect) { ctx->pending_redirect=NULL; if (glob_target->gl_pathc != 1) { error_msg("ambiguous redirect"); return 1; } } else { child->argv = glob_target->gl_pathv; } return 0;}/* The only possible error here is out of memory, in which case * xmalloc exits. */static int done_command(struct p_context *ctx){ /* The child is really already in the pipe structure, so * advance the pipe counter and make a new, null child. * Only real trickiness here is that the uncommitted * child structure, to which ctx->child points, is not * counted in pi->num_progs. */ struct pipe *pi=ctx->pipe; struct child_prog *prog=ctx->child; if (prog && prog->group == NULL && prog->argv == NULL && prog->redirects == NULL) { debug_printf("done_command: skipping null command\n"); return 0; } else if (prog) { pi->num_progs++; debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs); } else { debug_printf("done_command: initializing\n"); } pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1)); prog = pi->progs + pi->num_progs; prog->redirects = NULL; prog->argv = NULL; prog->is_stopped = 0; prog->group = NULL; prog->glob_result.gl_pathv = NULL; prog->family = pi; ctx->child=prog; /* but ctx->pipe and ctx->list_head remain unchanged */ return 0;}static int done_pipe(struct p_context *ctx, pipe_style type){ struct pipe *new_p; done_command(ctx); /* implicit closure of previous command */ debug_printf("done_pipe, type %d\n", type); ctx->pipe->followup = type; ctx->pipe->r_mode = ctx->w; new_p=new_pipe(); ctx->pipe->next = new_p; ctx->pipe = new_p; ctx->child = NULL; done_command(ctx); /* set up new pipe to accept commands */ return 0;}/* peek ahead in the in_str to find out if we have a "&n" construct, * as in "2>&1", that represents duplicating a file descriptor. * returns either -2 (syntax error), -1 (no &), or the number found. */static int redirect_dup_num(struct in_str *input){ int ch, d=0, ok=0; ch = b_peek(input); if (ch != '&') return -1; b_getch(input); /* get the & */ ch=b_peek(input); if (ch == '-') { b_getch(input); return -3; /* "-" represents "close me" */ } while (isdigit(ch)) { d = d*10+(ch-'0'); ok=1; b_getch(input); ch = b_peek(input); } if (ok) return d; error_msg("ambiguous redirect"); return -2;}/* If a redirect is immediately preceded by a number, that number is * supposed to tell which file descriptor to redirect. This routine * looks for such preceding numbers. In an ideal world this routine * needs to handle all the following classes of redirects... * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo * A -1 output from this program means no valid number was found, so the * caller should use the appropriate default for this redirection. */static int redirect_opt_num(o_string *o){ int num; if (o->length==0) return -1; for(num=0; num<o->length; num++) { if (!isdigit(*(o->data+num))) { return -1; } } /* reuse num (and save an int) */ num=atoi(o->data); b_reset(o); return num;}FILE *generate_stream_from_list(struct pipe *head){ FILE *pf;#if 1 int pid, channel[2]; if (pipe(channel)<0) perror_msg_and_die("pipe");#if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__) pid=fork();#else pid=vfork();#endif if (pid<0) { perror_msg_and_die("fork"); } else if (pid==0) { close(channel[0]); if (channel[1] != 1) { dup2(channel[1],1); close(channel[1]); }#if 0#define SURROGATE "surrogate response" write(1,SURROGATE,sizeof(SURROGATE)); _exit(run_list(head));#else _exit(run_list_real(head)); /* leaks memory */#endif } debug_printf("forked child %d\n",pid); close(channel[1]); pf = fdopen(channel[0],"r"); debug_printf("pipe on FILE *%p\n",pf);#else free_pipe_list(head,0); pf=popen("echo surrogate response","r"); debug_printf("started fake pipe on FILE *%p\n",pf);#endif return pf;}/* this version hacked for testing purposes *//* return code is exit status of the process that is run. */static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end){ int retcode; o_string result=NULL_O_STRING; struct p_context inner; FILE *p; struct in_str pipe_str; initialize_context(&inner); /* recursion to generate command */ retcode = parse_stream(&result, &inner, input, subst_end); if (retcode != 0) return retcode; /* syntax error or EOF */ done_word(&result, &inner); done_pipe(&inner, PIPE_SEQ); b_free(&result); p=generate_stream_from_list(inner.list_head); if (p==NULL) return 1; mark_open(fileno(p)); setup_file_in_str(&pipe_str, p); /* now send results of command back into original context */ retcode = parse_stream(dest, ctx, &pipe_str, '\0'); /* XXX In case of a syntax error, should we try to kill the child? * That would be tough to do right, so just read until EOF. */ if (retcode == 1) { while (b_getch(&pipe_str)!=EOF) { /* discard */ }; } debug_printf("done reading from pipe, pclose()ing\n"); /* This is the step that wait()s for the child. Should be pretty * safe, since we just read an EOF from its stdout. We could try * to better, by using wait(), and keeping track of background jobs * at the same time. That would be a lot of work, and contrary * to the KISS philosophy of this program. */ mark_closed(fileno(p)); retcode=pclose(p); free_pipe_list(inner.list_head,0); debug_printf("pclosed, retcode=%d\n",retcode); /* XXX this process fails to trim a single trailing newline */ return retcode;}static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch){ int rcode, endch=0; struct p_context sub; struct child_prog *child = ctx->child; if (child->argv) { syntax(); return 1; /* syntax error, groups and arglists don't mix */ } initialize_context(&sub); switch(ch) { case '(': endch=')'; child->subshell=1; break; case '{': endch='}'; break; default: syntax(); /* really logic error */ } rcode=parse_stream(dest,&sub,input,endch); done_word(dest,&sub); /* finish off the final word in the subcontext */ done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */ child->group = sub.list_head; return rcode; /* child remains "open", available for possible redirects */}/* basically useful version until someone wants to get fancier, * see the bash man page under "Parameter Expansion" */static void lookup_param(o_string *dest, struct p_context *ctx, o_string *src){ const char *p=NULL; if (src->data) { p = getenv(src->data); if (!p) p = get_local_var(src->data); } if (p) parse_string(dest, ctx, p); /* recursion */ b_free(src);}/* return code: 0 for OK, 1 for syntax error */static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input){ int i, advance=0; o_string alt=NULL_O_STRING; char sep[]=" "; int ch = input->peek(input); /* first character after the $ */ debug_printf("handle_dollar: ch=%c\n",ch); if (isalpha(ch)) { while(ch=b_peek(input),isalnum(ch) || ch=='_') { b_getch(input); b_addchr(&alt,ch); } lookup_param(dest, ctx, &alt); } else if (isdigit(ch)) { i = ch-'0'; /* XXX is $0 special? */ if (i<global_argc) { parse_string(dest, ctx, global_argv[i]); /* recursion */ } advance = 1; } else switch (ch) { case '$': b_adduint(dest,getpid()); advance = 1; break; case '!': if (last_bg_pid > 0) b_adduint(dest, last_bg_pid); advance = 1; break; case '?': b_adduint(dest,last_return_code); advance = 1; break; case '#': b_adduint(dest,global_argc ? global_argc-1 : 0); advance = 1; break; case '{': b_getch(input); /* XXX maybe someone will try to escape the '}' */ while(ch=b_getch(input),ch!=EOF && ch!='}') { b_addchr(&alt,ch); } if (ch != '}') { syntax(); return 1; } lookup_param(dest, ctx, &alt); break; case '(': b_getch(input); process_command_subs(dest, ctx, input, ')'); break; case '*': sep[0]=ifs[0]; for (i=1; i<global_argc; i++) { parse_string(dest, ctx, global_argv[i]); if (i+1 < global_argc) parse_string(dest, ctx, sep); } break; case '@': case '-': case '_': /* still unhandled, but should be eventually */ error_msg("unhandled syntax: $%c",ch); return 1; break; default: b_addqchr(dest,'$',dest->quote); } /* Eat the character if the flag was set. If the compiler * is smart en
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -