📄 exec.c
字号:
/* * execute command tree */#include "sh.h"#include "c_test.h"#include <ctype.h>#include "ksh_stat.h"/* Does ps4 get parameter substitutions done? */#ifdef KSH# define PS4_SUBSTITUTE(s) substitute((s), 0)#else# define PS4_SUBSTITUTE(s) (s)#endif /* KSH */static int comexec ARGS((struct op *t, struct tbl *volatile tp, char **ap, int volatile flags));static void scriptexec ARGS((struct op *tp, char **ap));static int call_builtin ARGS((struct tbl *tp, char **wp));static int iosetup ARGS((struct ioword *iop, struct tbl *tp));static int herein ARGS((const char *content, int sub));#ifdef KSHstatic char *do_selectargs ARGS((char **ap, bool_t print_menu));#endif /* KSH */#ifdef KSHstatic int dbteste_isa ARGS((Test_env *te, Test_meta meta));static const char *dbteste_getopnd ARGS((Test_env *te, Test_op op, int do_eval));static int dbteste_eval ARGS((Test_env *te, Test_op op, const char *opnd1, const char *opnd2, int do_eval));static void dbteste_error ARGS((Test_env *te, int offset, const char *msg));#endif /* KSH */#ifdef OS2static int search_access1 ARGS((const char *path, int mode, int *errnop));#endif /* OS2 *//* * handle systems that don't have F_SETFD */#ifndef F_SETFD# ifndef MAXFD# define MAXFD 64# endif/* a bit field would be smaller, but this will work */static char clexec_tab[MAXFD+1];#endif/* * we now use this function always. */intfd_clexec(fd) int fd;{#ifndef F_SETFD if (fd >= 0 && fd < sizeof(clexec_tab)) { clexec_tab[fd] = 1; return 0; } return -1;#else return fcntl(fd, F_SETFD, 1);#endif}/* * execute command tree */intexecute(t, flags) struct op * volatile t; volatile int flags; /* if XEXEC don't fork */{ int i; volatile int rv = 0; int pv[2]; char ** volatile ap; char *s, *cp; struct ioword **iowp; struct tbl *tp = NULL; if (t == NULL) return 0; /* Is this the end of a pipeline? If so, we want to evaluate the * command arguments bool_t eval_done = FALSE; if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) { eval_done = TRUE; tp = eval_execute_args(t, &ap); } */ if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) return exchild(t, flags & ~XTIME, -1); /* run in sub-process */ newenv(E_EXEC); if (trap) runtraps(0); if (t->type == TCOM) { /* Clear subst_exstat before argument expansion. Used by * null commands (see comexec() and c_eval()) and by c_set(). */ subst_exstat = 0; current_lineno = t->lineno; /* for $LINENO */ /* POSIX says expand command words first, then redirections, * and assignments last.. */ ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); if (flags & XTIME) /* Allow option parsing (bizarre, but POSIX) */ timex_hook(t, &ap); if (Flag(FXTRACE) && ap[0]) { shf_fprintf(shl_out, "%s", PS4_SUBSTITUTE(str_val(global("PS4")))); for (i = 0; ap[i]; i++) shf_fprintf(shl_out, "%s%s", ap[i], ap[i + 1] ? space : newline); shf_flush(shl_out); } if (ap[0]) tp = findcom(ap[0], FC_BI|FC_FUNC); } flags &= ~XTIME; if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) { e->savefd = (short *) alloc(sizeofN(short, NUFILE), ATEMP); /* initialize to not redirected */ memset(e->savefd, 0, sizeofN(short, NUFILE)); } /* do redirection, to be restored in quitenv() */ if (t->ioact != NULL) for (iowp = t->ioact; *iowp != NULL; iowp++) { if (iosetup(*iowp, tp) < 0) { exstat = rv = 1; /* Redirection failures for special commands * cause (non-interactive) shell to exit. */ if (tp && tp->type == CSHELL && (tp->flag & SPEC_BI)) errorf(null); /* Deal with FERREXIT, quitenv(), etc. */ goto Break; } } switch(t->type) { case TCOM: rv = comexec(t, tp, ap, flags); break; case TPAREN: rv = execute(t->left, flags|XFORK); break; case TPIPE: flags |= XFORK; flags &= ~XEXEC; e->savefd[0] = savefd(0, 0); (void) ksh_dup2(e->savefd[0], 0, FALSE); /* stdin of first */ e->savefd[1] = savefd(1, 0); while (t->type == TPIPE) { openpipe(pv); (void) ksh_dup2(pv[1], 1, FALSE); /* stdout of curr */ /* Let exchild() close pv[0] in child * (if this isn't done, commands like * (: ; cat /etc/termcap) | sleep 1 * will hang forever). */ exchild(t->left, flags|XPIPEO|XCCLOSE, pv[0]); (void) ksh_dup2(pv[0], 0, FALSE); /* stdin of next */ closepipe(pv); flags |= XPIPEI; t = t->right; } restfd(1, e->savefd[1]); /* stdout of last */ e->savefd[1] = 0; /* no need to re-restore this */ /* Let exchild() close 0 in parent, after fork, before wait */ i = exchild(t, flags|XPCLOSE, 0); if (!(flags&XBGND) && !(flags&XXCOM)) rv = i; break; case TLIST: while (t->type == TLIST) { execute(t->left, flags & XERROK); t = t->right; } rv = execute(t, flags & XERROK); break;#ifdef KSH case TCOPROC: {# ifdef JOB_SIGS sigset_t omask;# endif /* JOB_SIGS */# ifdef JOB_SIGS /* Block sigchild as we are using things changed in the * signal handler */ sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); e->type = E_ERRH; i = ksh_sigsetjmp(e->jbuf, 0); if (i) { sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); quitenv(); unwind(i); /*NOTREACHED*/ }# endif /* JOB_SIGS */ /* Already have a (live) co-process? */ if (coproc.job && coproc.write >= 0) errorf("coprocess already exists"); /* Can we re-use the existing co-process pipe? */ coproc_cleanup(TRUE); /* do this before opening pipes, in case these fail */ e->savefd[0] = savefd(0, 0); e->savefd[1] = savefd(1, 0); openpipe(pv); ksh_dup2(pv[0], 0, FALSE); close(pv[0]); coproc.write = pv[1]; coproc.job = (void *) 0; if (coproc.readw >= 0) ksh_dup2(coproc.readw, 1, FALSE); else { openpipe(pv); coproc.read = pv[0]; ksh_dup2(pv[1], 1, FALSE); coproc.readw = pv[1]; /* closed before first read */ coproc.njobs = 0; /* create new coprocess id */ ++coproc.id; }# ifdef JOB_SIGS sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); e->type = E_EXEC; /* no more need for error handler */# endif /* JOB_SIGS */ /* exchild() closes coproc.* in child after fork, * will also increment coproc.njobs when the * job is actually created. */ flags &= ~XEXEC; exchild(t->left, flags|XBGND|XFORK|XCOPROC|XCCLOSE, coproc.readw); break; }#endif /* KSH */ case TASYNC: /* XXX non-optimal, I think - "(foo &)", forks for (), * forks again for async... parent should optimize * this to "foo &"... */ rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK); break; case TOR: case TAND: rv = execute(t->left, XERROK); if (t->right != NULL && (rv == 0) == (t->type == TAND)) rv = execute(t->right, flags & XERROK); else flags |= XERROK; break; case TBANG: rv = !execute(t->right, XERROK); break;#ifdef KSH case TDBRACKET: { Test_env te; te.flags = TEF_DBRACKET; te.pos.wp = t->args; te.isa = dbteste_isa; te.getopnd = dbteste_getopnd; te.eval = dbteste_eval; te.error = dbteste_error; rv = test_parse(&te); break; }#endif /* KSH */ case TFOR:#ifdef KSH case TSELECT: { volatile bool_t is_first = TRUE;#endif /* KSH */ ap = (t->vars != NULL) ? eval(t->vars, DOBLANK|DOGLOB|DOTILDE) : e->loc->argv + 1; e->type = E_LOOP; while (1) { i = ksh_sigsetjmp(e->jbuf, 0); if (!i) break; if ((e->flags&EF_BRKCONT_PASS) || (i != LBREAK && i != LCONTIN)) { quitenv(); unwind(i); } else if (i == LBREAK) { rv = 0; goto Break; } } rv = 0; /* in case of a continue */ if (t->type == TFOR) { while (*ap != NULL) { setstr(global(t->str), *ap++, KSH_UNWIND_ERROR); rv = execute(t->left, flags & XERROK); } }#ifdef KSH else { /* TSELECT */ for (;;) { if (!(cp = do_selectargs(ap, is_first))) { rv = 1; break; } is_first = FALSE; setstr(global(t->str), cp, KSH_UNWIND_ERROR); rv = execute(t->left, flags & XERROK); } } }#endif /* KSH */ break; case TWHILE: case TUNTIL: e->type = E_LOOP; while (1) { i = ksh_sigsetjmp(e->jbuf, 0); if (!i) break; if ((e->flags&EF_BRKCONT_PASS) || (i != LBREAK && i != LCONTIN)) { quitenv(); unwind(i); } else if (i == LBREAK) { rv = 0; goto Break; } } rv = 0; /* in case of a continue */ while ((execute(t->left, XERROK) == 0) == (t->type == TWHILE)) rv = execute(t->right, flags & XERROK); break; case TIF: case TELIF: if (t->right == NULL) break; /* should be error */ rv = execute(t->left, XERROK) == 0 ? execute(t->right->left, flags & XERROK) : execute(t->right->right, flags & XERROK); break; case TCASE: cp = evalstr(t->str, DOTILDE); for (t = t->left; t != NULL && t->type == TPAT; t = t->right) for (ap = t->vars; *ap; ap++) if ((s = evalstr(*ap, DOTILDE|DOPAT)) && gmatch(cp, s, FALSE)) goto Found; break; Found: rv = execute(t->left, flags & XERROK); break; case TBRACE: rv = execute(t->left, flags & XERROK); break; case TFUNCT: rv = define(t->str, t); break; case TTIME: /* Clear XEXEC so nested execute() call doesn't exit * (allows "ls -l | time grep foo"). */ rv = timex(t, flags & ~XEXEC); break; case TEXEC: /* an eval'd TCOM */ s = t->args[0]; ap = makenv();#ifndef F_SETFD for (i = 0; i < sizeof(clexec_tab); i++) if (clexec_tab[i]) { close(i); clexec_tab[i] = 0; }#endif restoresigs(); cleanup_proc_env(); /* XINTACT bit is for OS2 */ ksh_execve(t->str, t->args, ap, (flags & XINTACT) ? 1 : 0); if (errno == ENOEXEC) scriptexec(t, ap); else errorf("%s: %s", s, strerror(errno)); } Break: exstat = rv; quitenv(); /* restores IO */ if ((flags&XEXEC)) unwind(LEXIT); /* exit child */ if (rv != 0 && !(flags & XERROK)) { if (Flag(FERREXIT)) unwind(LERROR); trapsig(SIGERR_); } return rv;}/* * execute simple command */static intcomexec(t, tp, ap, flags) struct op *t; struct tbl *volatile tp; register char **ap; int volatile flags;{ int i; int rv = 0; register char *cp; register char **lastp; static struct op texec; /* Must be static (XXX but why?) */ int type_flags; int keepasn_ok; int fcflags = FC_BI|FC_FUNC|FC_PATH;#ifdef KSH /* snag the last argument for $_ XXX not the same as at&t ksh, * which only seems to set $_ after a newline (but not in * functions/dot scripts, but in interactive and scipt) - * perhaps save last arg here and set it in shell()?. */ if (Flag(FTALKING) && *(lastp = ap)) { while (*++lastp) ; /* setstr() can't fail here */ setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp, KSH_RETURN_ERROR); }#endif /* KSH */ /* Deal with the shell builtins builtin, exec and command since * they can be followed by other commands. This must be done before * we know if we should create a local block, which must be done * before we can do a path search (in case the assignments change * PATH). * Odd cases: * FOO=bar exec > /dev/null FOO is kept but not exported * FOO=bar exec foobar FOO is exported * FOO=bar command exec > /dev/null FOO is neither kept nor exported * FOO=bar command FOO is neither kept nor exported * PATH=... foobar use new PATH in foobar search */ keepasn_ok = 1; while (tp && tp->type == CSHELL) { fcflags = FC_BI|FC_FUNC|FC_PATH;/* undo effects of command */ if (tp->val.f == c_builtin) { if ((cp = *++ap) == NULL) { tp = NULL; break; } tp = findcom(cp, FC_BI); if (tp == NULL) errorf("builtin: %s: not a builtin", cp); continue; } else if (tp->val.f == c_exec) { if (ap[1] == NULL) break; ap++; flags |= XEXEC; } else if (tp->val.f == c_command) { int optc, saw_p = 0; /* Ugly dealing with options in two places (here and * in c_command(), but such is life) */ ksh_getopt_reset(&builtin_opt, 0); while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p') saw_p = 1; if (optc != EOF) break; /* command -vV or something */ /* don't look for functions */ fcflags = FC_BI|FC_PATH; if (saw_p) { if (Flag(FRESTRICTED)) { warningf(TRUE, "command -p: restricted"); rv = 1; goto Leave; } fcflags |= FC_DEFPATH; } ap += builtin_opt.optind; /* POSIX says special builtins lose their status * if accessed using command. */ keepasn_ok = 0; if (!ap[0]) { /* ensure command with no args exits with 0 */ subst_exstat = 0; break; } } else break; tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); } if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN)))) type_flags = 0; else { /* create new variable/function block */ newblock(); /* ksh functions don't keep assignments, POSIX functions do. */ if (keepasn_ok && tp && tp->type == CFUNC && !(tp->flag & FKSH)) type_flags = 0; else type_flags = LOCAL|LOCAL_COPY|EXPORT; } if (Flag(FEXPORT)) type_flags |= EXPORT; for (i = 0; t->vars[i]; i++) { cp = evalstr(t->vars[i], DOASNTILDE); if (Flag(FXTRACE)) { if (i == 0) shf_fprintf(shl_out, "%s", PS4_SUBSTITUTE(str_val(global("PS4")))); shf_fprintf(shl_out, "%s%s", cp, t->vars[i + 1] ? space : newline); if (!t->vars[i + 1]) shf_flush(shl_out);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -