📄 hush.c
字号:
} } } for (i = 0; i < pi->num_progs; i++) { child = & (pi->progs[i]); /* pipes are inserted between pairs of commands */ if ((i + 1) < pi->num_progs) { if (pipe(pipefds)<0) perror_msg_and_die("pipe"); nextout = pipefds[1]; } else { nextout=1; pipefds[0] = -1; } /* XXX test for failed fork()? */#if !defined(__UCLIBC__) || defined(__UCLIBC_HAS_MMU__) if (!(child->pid = fork()))#else if (!(child->pid = vfork())) #endif { /* Set the handling for job control signals back to the default. */ signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGTSTP, SIG_DFL); signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); signal(SIGCHLD, SIG_DFL); close_all(); if (nextin != 0) { dup2(nextin, 0); close(nextin); } if (nextout != 1) { dup2(nextout, 1); close(nextout); } if (pipefds[0]!=-1) { close(pipefds[0]); /* opposite end of our output pipe */ } /* Like bash, explicit redirects override pipes, * and the pipe fd is available for dup'ing. */ setup_redirects(child,NULL); if (interactive && pi->followup!=PIPE_BG) { /* If we (the child) win the race, put ourselves in the process * group whose leader is the first process in this pipe. */ if (pi->pgrp < 0) { pi->pgrp = getpid(); } if (setpgid(0, pi->pgrp) == 0) { tcsetpgrp(2, pi->pgrp); } } pseudo_exec(child); } /* put our child in the process group whose leader is the first process in this pipe */ if (pi->pgrp < 0) { pi->pgrp = child->pid; } /* Don't check for errors. The child may be dead already, * in which case setpgid returns error code EACCES. */ setpgid(child->pid, pi->pgrp); if (nextin != 0) close(nextin); if (nextout != 1) close(nextout); /* If there isn't another process, nextin is garbage but it doesn't matter */ nextin = pipefds[0]; } return -1;}static int run_list_real(struct pipe *pi){ int rcode=0; int if_code=0, next_if_code=0; /* need double-buffer to handle elif */ reserved_style rmode, skip_more_in_this_rmode=RES_XXXX; for (;pi;pi=pi->next) { rmode = pi->r_mode; debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode); if (rmode == skip_more_in_this_rmode) continue; skip_more_in_this_rmode = RES_XXXX; if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code; if (rmode == RES_THEN && if_code) continue; if (rmode == RES_ELSE && !if_code) continue; if (rmode == RES_ELIF && !if_code) continue; if (pi->num_progs == 0) continue; rcode = run_pipe_real(pi); debug_printf("run_pipe_real returned %d\n",rcode); if (rcode!=-1) { /* We only ran a builtin: rcode was set by the return value * of run_pipe_real(), and we don't need to wait for anything. */ } else if (pi->followup==PIPE_BG) { /* XXX check bash's behavior with nontrivial pipes */ /* XXX compute jobid */ /* XXX what does bash do with attempts to background builtins? */ insert_bg_job(pi); rcode = EXIT_SUCCESS; } else { if (interactive) { /* move the new process group into the foreground */ if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY) perror_msg("tcsetpgrp-3"); rcode = checkjobs(pi); /* move the shell to the foreground */ if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) perror_msg("tcsetpgrp-4"); } else { rcode = checkjobs(pi); } debug_printf("checkjobs returned %d\n",rcode); } last_return_code=rcode; if ( rmode == RES_IF || rmode == RES_ELIF ) next_if_code=rcode; /* can be overwritten a number of times */ if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) || (rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) skip_more_in_this_rmode=rmode; checkjobs(NULL); } return rcode;}/* broken, of course, but OK for testing */static char *indenter(int i){ static char blanks[]=" "; return &blanks[sizeof(blanks)-i-1];}/* return code is the exit status of the pipe */static int free_pipe(struct pipe *pi, int indent){ char **p; struct child_prog *child; struct redir_struct *r, *rnext; int a, i, ret_code=0; char *ind = indenter(indent); if (pi->stopped_progs > 0) return ret_code; final_printf("%s run pipe: (pid %d)\n",ind,getpid()); for (i=0; i<pi->num_progs; i++) { child = &pi->progs[i]; final_printf("%s command %d:\n",ind,i); if (child->argv) { for (a=0,p=child->argv; *p; a++,p++) { final_printf("%s argv[%d] = %s\n",ind,a,*p); } globfree(&child->glob_result); child->argv=NULL; } else if (child->group) { final_printf("%s begin group (subshell:%d)\n",ind, child->subshell); ret_code = free_pipe_list(child->group,indent+3); final_printf("%s end group\n",ind); } else { final_printf("%s (nil)\n",ind); } for (r=child->redirects; r; r=rnext) { final_printf("%s redirect %d%s", ind, r->fd, redir_table[r->type].descrip); if (r->dup == -1) { /* guard against the case >$FOO, where foo is unset or blank */ if (r->word.gl_pathv) { final_printf(" %s\n", *r->word.gl_pathv); globfree(&r->word); } } else { final_printf("&%d\n", r->dup); } rnext=r->next; free(r); } child->redirects=NULL; } free(pi->progs); /* children are an array, they get freed all at once */ pi->progs=NULL; return ret_code;}static int free_pipe_list(struct pipe *head, int indent){ int rcode=0; /* if list has no members */ struct pipe *pi, *next; char *ind = indenter(indent); for (pi=head; pi; pi=next) { final_printf("%s pipe reserved mode %d\n", ind, pi->r_mode); rcode = free_pipe(pi, indent); final_printf("%s pipe followup code %d\n", ind, pi->followup); next=pi->next; pi->next=NULL; free(pi); } return rcode; }/* Select which version we will use */static int run_list(struct pipe *pi){ int rcode=0; if (fake_mode==0) { rcode = run_list_real(pi); } /* free_pipe_list has the side effect of clearing memory * In the long run that function can be merged with run_list_real, * but doing that now would hobble the debugging effort. */ free_pipe_list(pi,0); return rcode;}/* The API for glob is arguably broken. This routine pushes a non-matching * string into the output structure, removing non-backslashed backslashes. * If someone can prove me wrong, by performing this function within the * original glob(3) api, feel free to rewrite this routine into oblivion. * Return code (0 vs. GLOB_NOSPACE) matches glob(3). * XXX broken if the last character is '\\', check that before calling. */static int globhack(const char *src, int flags, glob_t *pglob){ int cnt=0, pathc; const char *s; char *dest; for (cnt=1, s=src; s && *s; s++) { if (*s == '\\') s++; cnt++; } dest = malloc(cnt); if (!dest) return GLOB_NOSPACE; if (!(flags & GLOB_APPEND)) { pglob->gl_pathv=NULL; pglob->gl_pathc=0; pglob->gl_offs=0; pglob->gl_offs=0; } pathc = ++pglob->gl_pathc; pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv)); if (pglob->gl_pathv == NULL) return GLOB_NOSPACE; pglob->gl_pathv[pathc-1]=dest; pglob->gl_pathv[pathc]=NULL; for (s=src; s && *s; s++, dest++) { if (*s == '\\') s++; *dest = *s; } *dest='\0'; return 0;}/* XXX broken if the last character is '\\', check that before calling */static int glob_needed(const char *s){ for (; *s; s++) { if (*s == '\\') s++; if (strchr("*[?",*s)) return 1; } return 0;}#if 0static void globprint(glob_t *pglob){ int i; debug_printf("glob_t at %p:\n", pglob); debug_printf(" gl_pathc=%d gl_pathv=%p gl_offs=%d gl_flags=%d\n", pglob->gl_pathc, pglob->gl_pathv, pglob->gl_offs, pglob->gl_flags); for (i=0; i<pglob->gl_pathc; i++) debug_printf("pglob->gl_pathv[%d] = %p = %s\n", i, pglob->gl_pathv[i], pglob->gl_pathv[i]);}#endifstatic int xglob(o_string *dest, int flags, glob_t *pglob){ int gr; /* short-circuit for null word */ /* we can code this better when the debug_printf's are gone */ if (dest->length == 0) { if (dest->nonnull) { /* bash man page calls this an "explicit" null */ gr = globhack(dest->data, flags, pglob); debug_printf("globhack returned %d\n",gr); } else { return 0; } } else if (glob_needed(dest->data)) { gr = glob(dest->data, flags, NULL, pglob); debug_printf("glob returned %d\n",gr); if (gr == GLOB_NOMATCH) { /* quote removal, or more accurately, backslash removal */ gr = globhack(dest->data, flags, pglob); debug_printf("globhack returned %d\n",gr); } } else { gr = globhack(dest->data, flags, pglob); debug_printf("globhack returned %d\n",gr); } if (gr == GLOB_NOSPACE) error_msg_and_die("out of memory during glob"); if (gr != 0) { /* GLOB_ABORTED ? */ error_msg("glob(3) error %d",gr); } /* globprint(glob_target); */ return gr;}/* This is used to get/check local shell variables */static char *get_local_var(const char *s){ struct variables *cur; if (!s) return NULL; for (cur = top_vars; cur; cur=cur->next) if(strcmp(cur->name, s)==0) return cur->value; return NULL;}/* This is used to set local shell variables flg_export==0 if only local (not exporting) variable flg_export==1 if "new" exporting environ flg_export>1 if current startup environ (not call putenv()) */static int set_local_var(const char *s, int flg_export){ char *name, *value; int result=0; struct variables *cur; name=strdup(s); /* Assume when we enter this function that we are already in * NAME=VALUE format. So the first order of business is to * split 's' on the '=' into 'name' and 'value' */ value = strchr(name, '='); if (value==0 && ++value==0) { free(name); return -1; } *value++ = 0; for(cur = top_vars; cur; cur = cur->next) { if(strcmp(cur->name, name)==0) break; } if(cur) { if(strcmp(cur->value, value)==0) { if(flg_export>0 && cur->flg_export==0) cur->flg_export=flg_export; else result++; } else { if(cur->flg_read_only) { error_msg("%s: readonly variable", name); result = -1; } else { if(flg_export>0 || cur->flg_export>1) cur->flg_export=1; free(cur->value); cur->value = strdup(value); } } } else { cur = malloc(sizeof(struct variables)); if(!cur) { result = -1; } else { cur->name = strdup(name); if(cur->name == 0) { free(cur); result = -1; } else { struct variables *bottom = top_vars; cur->value = strdup(value); cur->next = 0; cur->flg_export = flg_export; cur->flg_read_only = 0; while(bottom->next) bottom=bottom->next; bottom->next = cur; } } } if(result==0 && cur->flg_export==1) { *(value-1) = '='; result = putenv(name); } else { free(name); if(result>0) /* equivalent to previous set */ result = 0; } return result;}static void unset_local_var(const char *name){ struct variables *cur; if (name) { for (cur = top_vars; cur; cur=cur->next) { if(strcmp(cur->name, name)==0) break; } if(cur!=0) { struct variables *next = top_vars; if(cur->flg_read_only) { error_msg("%s: readonly variable", name); return; } else { if(cur->flg_export) unsetenv(cur->name); free(cur->name); free(cur->value); while (next->next != cur) next = next->next; next->next = cur->next; } free(cur); } }}static int is_assignment(const char *s){ if (s==NULL || !isalpha(*s)) return 0; ++s; while(isalnum(*s) || *s=='_') ++s; return *s=='=';}/* the src parameter allows us to peek forward to a possible &n syntax * for file descriptor duplication, e.g., "2>&1". * Return code is 0 normally, 1 if a syntax error is detected in src. * Resource errors (in xmalloc) cause the process to exit */static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input){ struct child_prog *child=ctx->child; struct redir_struct *redir = child->redirects; struct redir_struct *last_redir=NULL; /* Create a new redir_struct and drop it onto the end of the linked list */ while(redir) { last_redir=redir; redir=redir->next; } redir = xmalloc(sizeof(struct redir_struct)); redir->next=NULL; redir->word.gl_pathv=NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -