📄 eval.c
字号:
return sys_path;}static intparse_command_args(int argc, char **argv, int *use_syspath){ int sv_argc = argc; char *cp, c; *use_syspath = 0; for (;;) { argv++; if (--argc == 0) break; cp = *argv; if (*cp++ != '-') break; if (*cp == '-' && cp[1] == 0) { argv++; argc--; break; } while ((c = *cp++)) { switch (c) { case 'p': *use_syspath = 1; break; default: /* run 'typecmd' for other options */ return 0; } } } return sv_argc - argc;}int vforked = 0;/* * Execute a simple command. */STATIC voidevalcommand(union node *cmd, int flags, struct backcmd *backcmd){ struct stackmark smark; union node *argp; struct arglist arglist; struct arglist varlist; char **argv; int argc; char **envp; int varflag; struct strlist *sp; int mode; int pip[2]; struct cmdentry cmdentry; struct job *jp; struct jmploc jmploc; struct jmploc *volatile savehandler; char *volatile savecmdname; volatile struct shparam saveparam; struct localvar *volatile savelocalvars; volatile int e; char *lastarg; const char *path = pathval(); volatile int temp_path;#if __GNUC__ /* Avoid longjmp clobbering */ (void) &argv; (void) &argc; (void) &lastarg; (void) &flags;#endif vforked = 0; /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(&smark); back_exitstatus = 0; arglist.lastp = &arglist.list; varflag = 1; /* Expand arguments, ignoring the initial 'name=value' ones */ for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { char *p = argp->narg.text; if (varflag && is_name(*p)) { do { p++; } while (is_in_name(*p)); if (*p == '=') continue; } expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); varflag = 0; } *arglist.lastp = NULL; expredir(cmd->ncmd.redirect); /* Now do the initial 'name=value' ones we skipped above */ varlist.lastp = &varlist.list; for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { char *p = argp->narg.text; if (!is_name(*p)) break; do p++; while (is_in_name(*p)); if (*p != '=') break; expandarg(argp, &varlist, EXP_VARTILDE); } *varlist.lastp = NULL; argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; argv = stalloc(sizeof (char *) * (argc + 1)); for (sp = arglist.list ; sp ; sp = sp->next) { TRACE(("evalcommand arg: %s\n", sp->text)); *argv++ = sp->text; } *argv = NULL; lastarg = NULL; if (iflag && funcnest == 0 && argc > 0) lastarg = argv[-1]; argv -= argc; /* Print the command if xflag is set. */ if (xflag) { char sep = 0; out2str(ps4val()); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) outc(sep, &errout); out2str(sp->text); sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) outc(sep, &errout); out2str(sp->text); sep = ' '; } outc('\n', &errout); flushout(&errout); } /* Now locate the command. */ if (argc == 0) { cmdentry.cmdtype = CMDSPLBLTIN; cmdentry.u.bltin = bltincmd; } else { static const char PATH[] = "PATH="; int cmd_flags = DO_ERR; /* * Modify the command lookup path, if a PATH= assignment * is present */ for (sp = varlist.list; sp; sp = sp->next) if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) path = sp->text + sizeof(PATH) - 1; do { int argsused, use_syspath; find_command(argv[0], &cmdentry, cmd_flags, path); if (cmdentry.cmdtype == CMDUNKNOWN) { exitstatus = 127; flushout(&errout); goto out; } /* implement the 'command' builtin here */ if (cmdentry.cmdtype != CMDBUILTIN || cmdentry.u.bltin != bltincmd) break; cmd_flags |= DO_NOFUNC; argsused = parse_command_args(argc, argv, &use_syspath); if (argsused == 0) { /* use 'type' builting to display info */ cmdentry.u.bltin = typecmd; break; } argc -= argsused; argv += argsused; if (use_syspath) path = syspath() + 5; } while (argc != 0); if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) /* posix mandates that 'command <splbltin>' act as if <splbltin> was a normal builtin */ cmdentry.cmdtype = CMDBUILTIN; } /* Fork off a child process if necessary. */ if (cmd->ncmd.backgnd || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) || ((flags & EV_BACKCMD) != 0 && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) || cmdentry.u.bltin == dotcmd || cmdentry.u.bltin == evalcmd))) { INTOFF; jp = makejob(cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (sh_pipe(pip) < 0) error("Pipe call failed"); }#ifdef DO_SHAREDVFORK /* It is essential that if DO_SHAREDVFORK is defined that the * child's address space is actually shared with the parent as * we rely on this. */ if (cmdentry.cmdtype == CMDNORMAL) { pid_t pid; savelocalvars = localvars; localvars = NULL; vforked = 1; switch (pid = vfork()) { case -1: TRACE(("Vfork failed, errno=%d\n", errno)); INTON; error("Cannot vfork"); break; case 0: /* Make sure that exceptions only unwind to * after the vfork(2) */ if (setjmp(jmploc.loc)) { if (exception == EXSHELLPROC) { /* We can't progress with the vfork, * so, set vforked = 2 so the parent * knows, and _exit(); */ vforked = 2; _exit(0); } else { _exit(exerrno); } } savehandler = handler; handler = &jmploc; listmklocal(varlist.list, VEXPORT | VNOFUNC); forkchild(jp, cmd, mode, vforked); break; default: handler = savehandler; /* restore from vfork(2) */ poplocalvars(); localvars = savelocalvars; if (vforked == 2) { vforked = 0; (void)waitpid(pid, NULL, 0); /* We need to progress in a normal fork fashion */ goto normal_fork; } vforked = 0; forkparent(jp, cmd, mode, pid); goto parent; } } else {normal_fork:#endif if (forkshell(jp, cmd, mode) != 0) goto parent; /* at end of routine */ FORCEINTON;#ifdef DO_SHAREDVFORK }#endif if (flags & EV_BACKCMD) { if (!vforked) { FORCEINTON; } close(pip[0]); if (pip[1] != 1) { close(1); copyfd(pip[1], 1); close(pip[1]); } } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ switch (cmdentry.cmdtype) { case CMDFUNCTION:#ifdef DEBUG trputs("Shell function: "); trargs(argv);#endif redirect(cmd->ncmd.redirect, REDIR_PUSH); saveparam = shellparam; shellparam.malloc = 0; shellparam.reset = 1; shellparam.nparam = argc - 1; shellparam.p = argv + 1; shellparam.optnext = NULL; INTOFF; savelocalvars = localvars; localvars = NULL; INTON; if (setjmp(jmploc.loc)) { if (exception == EXSHELLPROC) { freeparam((volatile struct shparam *) &saveparam); } else { freeparam(&shellparam); shellparam = saveparam; } poplocalvars(); localvars = savelocalvars; handler = savehandler; longjmp(handler->loc, 1); } savehandler = handler; handler = &jmploc; listmklocal(varlist.list, 0); /* stop shell blowing its stack */ if (++funcnest > 1000) error("too many nested function calls"); evaltree(cmdentry.u.func, flags & EV_TESTED); funcnest--; INTOFF; poplocalvars(); localvars = savelocalvars; freeparam(&shellparam); shellparam = saveparam; handler = savehandler; popredir(); INTON; if (evalskip == SKIPFUNC) { evalskip = 0; skipcount = 0; } if (flags & EV_EXIT) exitshell(exitstatus); break; case CMDBUILTIN: case CMDSPLBLTIN:#ifdef DEBUG trputs("builtin command: "); trargs(argv);#endif mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { memout.nleft = 0; memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; } e = -1; savehandler = handler; savecmdname = commandname; handler = &jmploc; if (!setjmp(jmploc.loc)) { /* We need to ensure the command hash table isn't * corruped by temporary PATH assignments. * However we must ensure the 'local' command works! */ if (path != pathval() && (cmdentry.u.bltin == hashcmd || cmdentry.u.bltin == typecmd)) { savelocalvars = localvars; localvars = 0; mklocal(path - 5 /* PATH= */, 0); temp_path = 1; } else temp_path = 0; redirect(cmd->ncmd.redirect, mode); /* exec is a special builtin, but needs this list... */ cmdenviron = varlist.list; /* we must check 'readonly' flag for all builtins */ listsetvar(varlist.list, cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); commandname = argv[0]; /* initialize nextopt */ argptr = argv + 1; optptr = NULL; /* and getopt */#ifndef __linux__ optreset = 1;#endif optind = 1; exitstatus = cmdentry.u.bltin(argc, argv); } else { e = exception; exitstatus = e == EXINT ? SIGINT + 128 : e == EXEXEC ? exerrno : 2; } handler = savehandler; flushall(); out1 = &output; out2 = &errout; freestdout(); if (temp_path) { poplocalvars(); localvars = savelocalvars; } cmdenviron = NULL; if (e != EXSHELLPROC) { commandname = savecmdname; if (flags & EV_EXIT) exitshell(exitstatus); } if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.cmdtype == CMDSPLBLTIN) exraise(e); FORCEINTON; } if (cmdentry.u.bltin != execcmd) popredir(); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; memout.buf = NULL; } break; default:#ifdef DEBUG trputs("normal command: "); trargs(argv);#endif clearredir(vforked); redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0); if (!vforked) for (sp = varlist.list ; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); shellexec(argv, envp, path, cmdentry.u.index, vforked); break; } goto out;parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ exitstatus = waitforjob(jp); } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; close(pip[1]); backcmd->jp = jp; } FORCEINTON;out: if (lastarg) /* dsl: I think this is intended to be used to support * '_' in 'vi' command mode during line editing... * However I implemented that within libedit itself. */ setvar("_", lastarg, 0); popstackmark(&smark); if (eflag && exitstatus && !(flags & EV_TESTED)) exitshell(exitstatus);}/* * Search for a command. This is called before we fork so that the * location of the command will be available in the parent as well as * the child. The check for "goodname" is an overly conservative * check that the name will not be subject to expansion. */STATIC voidprehash(union node *n){ struct cmdentry entry; if (n->type == NCMD && n->ncmd.args) if (goodname(n->ncmd.args->narg.text)) find_command(n->ncmd.args->narg.text, &entry, 0, pathval());}/* * Builtin commands. Builtin commands whose functions are closely * tied to evaluation are implemented here. *//* * No command given. */intbltincmd(int argc, char **argv){ /* * Preserve exitstatus of a previous possible redirection * as POSIX mandates */ return back_exitstatus;}/* * Handle break and continue commands. Break, continue, and return are * all handled by setting the evalskip flag. The evaluation routines * above all check this flag, and if it is set they start skipping * commands rather than executing them. The variable skipcount is * the number of loops to break/continue, or the number of function * levels to return. (The latter is always 1.) It should probably * be an error to break out of more loops than exist, but it isn't * in the standard shell so we don't make it one here. */intbreakcmd(int argc, char **argv){ int n = argc > 1 ? number(argv[1]) : 1; if (n > loopnest) n = loopnest; if (n > 0) { evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; skipcount = n; } return 0;}/* * The return command. */intreturncmd(int argc, char **argv){ int ret = argc > 1 ? number(argv[1]) : exitstatus; if (funcnest) { evalskip = SKIPFUNC; skipcount = 1; return ret; } else { /* Do what ksh does; skip the rest of the file */ evalskip = SKIPFILE; skipcount = 1; return ret; }}intfalsecmd(int argc, char **argv){ return 1;}inttruecmd(int argc, char **argv){ return 0;}intexeccmd(int argc, char **argv){ if (argc > 1) { struct strlist *sp; iflag = 0; /* exit on error */ mflag = 0; optschanged(); for (sp = cmdenviron; sp; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); shellexec(argv + 1, environment(), pathval(), 0, 0); } return 0;}static intconv_time(clock_t ticks, char *seconds, size_t l){ static clock_t tpm = 0; clock_t mins; int i; mins = ticks / tpm; snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); if (seconds[0] == '6' && seconds[1] == '0') { /* 59.99995 got rounded up... */ mins++; strlcpy(seconds, "0.0", l); return mins; } /* suppress trailing zeros */ i = strlen(seconds) - 1; for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) seconds[i] = 0; return mins;}inttimescmd(int argc, char **argv){ struct tms tms; int u, s, cu, cs; char us[8], ss[8], cus[8], css[8]; nextopt(""); times(&tms); u = conv_time(tms.tms_utime, us, sizeof(us)); s = conv_time(tms.tms_stime, ss, sizeof(ss)); cu = conv_time(tms.tms_cutime, cus, sizeof(cus)); cs = conv_time(tms.tms_cstime, css, sizeof(css)); outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", u, us, s, ss, cu, cus, cs, css); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -