int fgcmd(int argc, char **argv) { struct job *jp; int i; int status; nextopt(""); jp = getjob(*argptr, 0); if (jp->jobctl == 0) error("job not created under job control"); out1fmt("%s", jp->ps[0].cmd); for (i = 1; i < jp->nprocs; i++) out1fmt(" | %s", jp->ps[i].cmd ); out1c('\n'); flushall(); for (i = 0; i < jp->nprocs; i++) if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1) break; if (i >= jp->nprocs) { error("Cannot set tty process group (%s) at %d", strerror(errno), __LINE__); } restartjob(jp); INTOFF; status = waitforjob(jp); INTON; return status; }
static void evalpipe(union node *n) { struct job *jp; struct nodelist *lp; int pipelen; int prevfd; int pip[2]; TRACE(("evalpipe(%p) called\n", (void *)n)); pipelen = 0; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) pipelen++; INTOFF; jp = makejob(n, pipelen); prevfd = -1; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { prehash(lp->n); pip[1] = -1; if (lp->next) { if (pipe(pip) < 0) { if (prevfd >= 0) close(prevfd); error("Pipe call failed: %s", strerror(errno)); } } if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { INTON; if (prevfd > 0) { dup2(prevfd, 0); close(prevfd); } if (pip[1] >= 0) { if (!(prevfd >= 0 && pip[0] == 0)) close(pip[0]); if (pip[1] != 1) { dup2(pip[1], 1); close(pip[1]); } } evaltree(lp->n, EV_EXIT); } if (prevfd >= 0) close(prevfd); prevfd = pip[0]; if (pip[1] != -1) close(pip[1]); } INTON; if (n->npipe.backgnd == 0) { INTOFF; exitstatus = waitforjob(jp, NULL); TRACE(("evalpipe: job done exit status %d\n", exitstatus)); INTON; } else exitstatus = 0; }
STATIC void evalpipe(union node *n) { struct job *jp; struct nodelist *lp; int pipelen; int prevfd; int pip[2]; TRACE(("evalpipe(0x%lx) called\n", (long)n)); pipelen = 0; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) pipelen++; INTOFF; jp = makejob(n, pipelen); prevfd = -1; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { prehash(lp->n); pip[1] = -1; if (lp->next) { if (sh_pipe(pip) < 0) { if (prevfd >= 0) close(prevfd); error("Pipe call failed"); } } if (forkshell(jp, lp->n, n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) { INTON; if (prevfd > 0) { close(0); copyfd(prevfd, 0, 1, 0); close(prevfd); } if (pip[1] >= 0) { close(pip[0]); if (pip[1] != 1) { close(1); copyfd(pip[1], 1, 1, 0); close(pip[1]); } } evaltree(lp->n, EV_EXIT); } if (prevfd >= 0) close(prevfd); prevfd = pip[0]; close(pip[1]); } if (n->npipe.backgnd == 0) { exitstatus = waitforjob(jp); TRACE(("evalpipe: job done exit status %d\n", exitstatus)); } else exitstatus = 0; INTON; }
STATIC void evalpipe(shinstance *psh, union node *n) { struct job *jp; struct nodelist *lp; int pipelen; int prevfd; int pip[2]; TRACE((psh, "evalpipe(0x%lx) called\n", (long)n)); pipelen = 0; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) pipelen++; INTOFF; jp = makejob(psh, n, pipelen); prevfd = -1; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { prehash(psh, lp->n); pip[1] = -1; if (lp->next) { if (sh_pipe(psh, pip) < 0) { shfile_close(&psh->fdtab, prevfd); error(psh, "Pipe call failed"); } } if (forkshell(psh, jp, lp->n, n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) { INTON; if (prevfd > 0) { movefd(psh, prevfd, 0); } if (pip[1] >= 0) { shfile_close(&psh->fdtab, pip[0]); if (pip[1] != 1) { movefd(psh, pip[1], 1); } } evaltree(psh, lp->n, EV_EXIT); } if (prevfd >= 0) shfile_close(&psh->fdtab, prevfd); prevfd = pip[0]; shfile_close(&psh->fdtab, pip[1]); } if (n->npipe.backgnd == 0) { psh->exitstatus = waitforjob(psh, jp); TRACE((psh, "evalpipe: job done exit status %d\n", psh->exitstatus)); } INTON; }
static void evalsubshell(union node *n, int flags) { struct job *jp; int backgnd = (n->type == NBACKGND); oexitstatus = exitstatus; expredir(n->nredir.redirect); if ((!backgnd && flags & EV_EXIT && !have_traps()) || forkshell(jp = makejob(n, 1), n, backgnd) == 0) { if (backgnd) flags &=~ EV_TESTED; redirect(n->nredir.redirect, 0); evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ } else if (! backgnd) { INTOFF; exitstatus = waitforjob(jp, (int *)NULL); INTON; } }
STATIC void evalsubshell(union node *n, int flags) { struct job *jp; int backgnd = (n->type == NBACKGND); expredir(n->nredir.redirect); jp = makejob(n, 1); if (forkshell(jp, n, backgnd) == 0) { if (backgnd) flags &=~ EV_TESTED; redirect(n->nredir.redirect, 0); evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ } if (! backgnd) { INTOFF; exitstatus = waitforjob(jp, (int *)NULL); INTON; } }
STATIC void evalsubshell(shinstance *psh, union node *n, int flags) { struct job *jp; int backgnd = (n->type == NBACKGND); expredir(psh, n->nredir.redirect); INTOFF; jp = makejob(psh, n, 1); if (forkshell(psh, jp, n, backgnd ? FORK_BG : FORK_FG) == 0) { INTON; if (backgnd) flags &=~ EV_TESTED; redirect(psh, n->nredir.redirect, 0); /* never returns */ evaltree(psh, n->nredir.n, flags | EV_EXIT); } if (! backgnd) psh->exitstatus = waitforjob(psh, jp); INTON; }
int fgcmd(int argc __unused, char **argv) { struct job *jp; pid_t pgrp; int status; jp = getjob(argv[1]); if (jp->jobctl == 0) error("job not created under job control"); printjobcmd(jp); flushout(&output); pgrp = jp->ps[0].pid; tcsetpgrp(ttyfd, pgrp); restartjob(jp); jp->foreground = 1; INTOFF; status = waitforjob(jp, (int *)NULL); INTON; return status; }
/* * Perform command substitution. */ static void expbackq(union node *cmd, int quoted, int flag) { struct backcmd in; int i; char buf[128]; char *p; char *dest = expdest; struct ifsregion saveifs, *savelastp; struct nodelist *saveargbackq; char lastc; int startloc = dest - stackblock(); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int quotes = flag & (EXP_FULL | EXP_CASE); size_t nnl; INTOFF; saveifs = ifsfirst; savelastp = ifslastp; saveargbackq = argbackq; p = grabstackstr(dest); evalbackcmd(cmd, &in); ungrabstackstr(p, dest); ifsfirst = saveifs; ifslastp = savelastp; argbackq = saveargbackq; p = in.buf; lastc = '\0'; nnl = 0; /* Don't copy trailing newlines */ for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; if (lastc != '\0') { if (lastc == '\n') { nnl++; } else { CHECKSTRSPACE(nnl + 2, dest); while (nnl > 0) { nnl--; USTPUTC('\n', dest); } if (quotes && syntax[(int)lastc] == CCTL) USTPUTC(CTLESC, dest); USTPUTC(lastc, dest); } } } if (in.fd >= 0) close(in.fd); if (in.buf) ckfree(in.buf); if (in.jp) exitstatus = waitforjob(in.jp, (int *)NULL); if (quoted == 0) recordregion(startloc, dest - stackblock(), 0); TRACE(("expbackq: size=%td: \"%.*s\"\n", ((dest - stackblock()) - startloc), (int)((dest - stackblock()) - startloc), stackblock() + startloc)); expdest = dest; INTON; }
static void evalcommand(union node *cmd, int flags, struct backcmd *backcmd) { 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 *savehandler; char *savecmdname; struct shparam saveparam; struct localvar *savelocalvars; struct parsefile *savetopfile; volatile int e; char *lastarg; int realstatus; int do_clearcmdentry; const char *path = pathval(); /* First expand the arguments. */ TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); arglist.lastp = &arglist.list; varlist.lastp = &varlist.list; varflag = 1; jp = NULL; do_clearcmdentry = 0; oexitstatus = exitstatus; exitstatus = 0; for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { if (varflag && isassignment(argp->narg.text)) { expandarg(argp, varflag == 1 ? &varlist : &arglist, EXP_VARTILDE); continue; } else if (varflag == 1) varflag = isdeclarationcmd(&argp->narg) ? 2 : 0; expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); } *arglist.lastp = NULL; *varlist.lastp = NULL; expredir(cmd->ncmd.redirect); argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; /* Add one slot at the beginning for tryexec(). */ argv = stalloc(sizeof (char *) * (argc + 2)); argv++; 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; const char *p, *ps4; ps4 = expandstr(ps4val()); out2str(ps4 != NULL ? ps4 : ps4val()); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) out2c(' '); p = strchr(sp->text, '='); if (p != NULL) { p++; outbin(sp->text, p - sp->text, out2); out2qstr(p); } else out2qstr(sp->text); sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) out2c(' '); /* Disambiguate command looking like assignment. */ if (sp == arglist.list && strchr(sp->text, '=') != NULL && strchr(sp->text, '\'') == NULL) { out2c('\''); out2str(sp->text); out2c('\''); } else out2qstr(sp->text); sep = ' '; } out2c('\n'); flushout(&errout); } /* Now locate the command. */ if (argc == 0) { /* Variable assignment(s) without command */ cmdentry.cmdtype = CMDBUILTIN; cmdentry.u.index = BLTINCMD; cmdentry.special = 0; } else { static const char PATH[] = "PATH="; int cmd_flags = 0, bltinonly = 0; /* * 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; /* * On `PATH=... command`, we need to make * sure that the command isn't using the * non-updated hash table of the outer PATH * setting and we need to make sure that * the hash table isn't filled with items * from the temporary setting. * * It would be better to forbit using and * updating the table while this command * runs, by the command finding mechanism * is heavily integrated with hash handling, * so we just delete the hash before and after * the command runs. Partly deleting like * changepatch() does doesn't seem worth the * bookinging effort, since most such runs add * directories in front of the new PATH. */ clearcmdentry(); do_clearcmdentry = 1; } for (;;) { if (bltinonly) { cmdentry.u.index = find_builtin(*argv, &cmdentry.special); if (cmdentry.u.index < 0) { cmdentry.u.index = BLTINCMD; argv--; argc++; break; } } else find_command(argv[0], &cmdentry, cmd_flags, path); /* implement the bltin and command builtins here */ if (cmdentry.cmdtype != CMDBUILTIN) break; if (cmdentry.u.index == BLTINCMD) { if (argc == 1) break; argv++; argc--; bltinonly = 1; } else if (cmdentry.u.index == COMMANDCMD) { if (argc == 1) break; if (!strcmp(argv[1], "-p")) { if (argc == 2) break; if (argv[2][0] == '-') { if (strcmp(argv[2], "--")) break; if (argc == 3) break; argv += 3; argc -= 3; } else { argv += 2; argc -= 2; } path = _PATH_STDPATH; clearcmdentry(); do_clearcmdentry = 1; } else if (!strcmp(argv[1], "--")) { if (argc == 2) break; argv += 2; argc -= 2; } else if (argv[1][0] == '-') break; else { argv++; argc--; } cmd_flags |= DO_NOFUNC; bltinonly = 0; } else break; } /* * Special builtins lose their special properties when * called via 'command'. */ if (cmd_flags & DO_NOFUNC) cmdentry.special = 0; } /* Fork off a child process if necessary. */ if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) && ((flags & EV_EXIT) == 0 || have_traps())) || ((flags & EV_BACKCMD) != 0 && (cmdentry.cmdtype != CMDBUILTIN || !safe_builtin(cmdentry.u.index, argc, argv)))) { jp = makejob(cmd, 1); mode = FORK_FG; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (pipe(pip) < 0) error("Pipe call failed: %s", strerror(errno)); } if (cmdentry.cmdtype == CMDNORMAL && cmd->ncmd.redirect == NULL && varlist.list == NULL && (mode == FORK_FG || mode == FORK_NOJOB) && !disvforkset() && !iflag && !mflag) { vforkexecshell(jp, argv, environment(), path, cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL); goto parent; } if (forkshell(jp, cmd, mode) != 0) goto parent; /* at end of routine */ if (flags & EV_BACKCMD) { FORCEINTON; close(pip[0]); if (pip[1] != 1) { dup2(pip[1], 1); close(pip[1]); } flags &= ~EV_BACKCMD; } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ if (cmdentry.cmdtype == CMDFUNCTION) { #ifdef DEBUG trputs("Shell function: "); trargs(argv); #endif saveparam = shellparam; shellparam.malloc = 0; shellparam.reset = 1; shellparam.nparam = argc - 1; shellparam.p = argv + 1; shellparam.optnext = NULL; INTOFF; savelocalvars = localvars; localvars = NULL; reffunc(cmdentry.u.func); savehandler = handler; if (setjmp(jmploc.loc)) { freeparam(&shellparam); shellparam = saveparam; popredir(); unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; funcnest--; handler = savehandler; longjmp(handler->loc, 1); } handler = &jmploc; funcnest++; redirect(cmd->ncmd.redirect, REDIR_PUSH); INTON; for (sp = varlist.list ; sp ; sp = sp->next) mklocal(sp->text); exitstatus = oexitstatus; evaltree(getfuncnode(cmdentry.u.func), flags & (EV_TESTED | EV_EXIT)); INTOFF; unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; freeparam(&shellparam); shellparam = saveparam; handler = savehandler; funcnest--; popredir(); INTON; if (evalskip == SKIPFUNC) { evalskip = 0; skipcount = 0; } if (jp) exitshell(exitstatus); } else if (cmdentry.cmdtype == CMDBUILTIN) { #ifdef DEBUG trputs("builtin command: "); trargs(argv); #endif mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { memout.nleft = 0; memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; } savecmdname = commandname; savetopfile = getcurrentfile(); cmdenviron = varlist.list; e = -1; savehandler = handler; if (setjmp(jmploc.loc)) { e = exception; if (e == EXINT) exitstatus = SIGINT+128; else if (e != EXEXIT) exitstatus = 2; goto cmddone; } handler = &jmploc; redirect(cmd->ncmd.redirect, mode); outclearerror(out1); /* * If there is no command word, redirection errors should * not be fatal but assignment errors should. */ if (argc == 0) cmdentry.special = 1; listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET); if (argc > 0) bltinsetlocale(); commandname = argv[0]; argptr = argv + 1; nextopt_optptr = NULL; /* initialize nextopt */ builtin_flags = flags; exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); flushall(); if (outiserror(out1)) { warning("write error on stdout"); if (exitstatus == 0 || exitstatus == 1) exitstatus = 2; } cmddone: if (argc > 0) bltinunsetlocale(); cmdenviron = NULL; out1 = &output; out2 = &errout; freestdout(); handler = savehandler; commandname = savecmdname; if (jp) exitshell(exitstatus); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; memout.buf = NULL; } if (cmdentry.u.index != EXECCMD) popredir(); if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.special) exraise(e); popfilesupto(savetopfile); if (flags != EV_BACKCMD) FORCEINTON; } } else { #ifdef DEBUG trputs("normal command: "); trargs(argv); #endif redirect(cmd->ncmd.redirect, 0); for (sp = varlist.list ; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); shellexec(argv, envp, path, cmdentry.u.index); /*NOTREACHED*/ } goto out; parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ INTOFF; exitstatus = waitforjob(jp, &realstatus); INTON; if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { evalskip = SKIPBREAK; skipcount = loopnest; } } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; close(pip[1]); backcmd->jp = jp; } out: if (lastarg) setvar("_", lastarg, 0); if (do_clearcmdentry) clearcmdentry(); }
static void evalcommand(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 *savehandler; char *savecmdname; struct shparam saveparam; struct localvar *savelocalvars; volatile int e; char *lastarg; int realstatus; int do_clearcmdentry; /* First expand the arguments. */ TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); setstackmark(&smark); arglist.lastp = &arglist.list; varlist.lastp = &varlist.list; varflag = 1; do_clearcmdentry = 0; oexitstatus = exitstatus; exitstatus = 0; 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 == '=') { expandarg(argp, &varlist, EXP_VARTILDE); continue; } } expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); varflag = 0; } *arglist.lastp = NULL; *varlist.lastp = NULL; expredir(cmd->ncmd.redirect); 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; const char *p; out2str(ps4val()); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) outc(' ', &errout); p = sp->text; while (*p != '=' && *p != '\0') out2c(*p++); if (*p != '\0') { out2c(*p++); out2qstr(p); } sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) outc(' ', &errout); /* Disambiguate command looking like assignment. */ if (sp == arglist.list && strchr(sp->text, '=') != NULL && strchr(sp->text, '\'') == NULL) { out2c('\''); out2str(sp->text); out2c('\''); } else out2qstr(sp->text); sep = ' '; } outc('\n', &errout); flushout(&errout); } /* Now locate the command. */ if (argc == 0) { /* Variable assignment(s) without command */ cmdentry.cmdtype = CMDBUILTIN; cmdentry.u.index = BLTINCMD; cmdentry.special = 1; } else { static const char PATH[] = "PATH="; char *path = pathval(); /* * 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; /* * On `PATH=... command`, we need to make * sure that the command isn't using the * non-updated hash table of the outer PATH * setting and we need to make sure that * the hash table isn't filled with items * from the temporary setting. * * It would be better to forbit using and * updating the table while this command * runs, by the command finding mechanism * is heavily integrated with hash handling, * so we just delete the hash before and after * the command runs. Partly deleting like * changepatch() does doesn't seem worth the * bookinging effort, since most such runs add * directories in front of the new PATH. */ clearcmdentry(0); do_clearcmdentry = 1; } find_command(argv[0], &cmdentry, 1, path); if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ exitstatus = 127; flushout(&errout); return; } /* implement the bltin builtin here */ if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { for (;;) { argv++; if (--argc == 0) break; if ((cmdentry.u.index = find_builtin(*argv, &cmdentry.special)) < 0) { outfmt(&errout, "%s: not found\n", *argv); exitstatus = 127; flushout(&errout); return; } if (cmdentry.u.index != BLTINCMD) break; } } } /* Fork off a child process if necessary. */ if (cmd->ncmd.backgnd || (cmdentry.cmdtype == CMDNORMAL && ((flags & EV_EXIT) == 0 || have_traps())) || ((flags & EV_BACKCMD) != 0 && (cmdentry.cmdtype != CMDBUILTIN || cmdentry.u.index == CDCMD || cmdentry.u.index == DOTCMD || cmdentry.u.index == EVALCMD)) || (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == COMMANDCMD)) { jp = makejob(cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (pipe(pip) < 0) error("Pipe call failed: %s", strerror(errno)); } if (forkshell(jp, cmd, mode) != 0) goto parent; /* at end of routine */ if (flags & EV_BACKCMD) { FORCEINTON; close(pip[0]); if (pip[1] != 1) { dup2(pip[1], 1); close(pip[1]); } } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ if (cmdentry.cmdtype == 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; reffunc(cmdentry.u.func); INTON; savehandler = handler; if (setjmp(jmploc.loc)) { if (exception == EXSHELLPROC) freeparam(&saveparam); else { freeparam(&shellparam); shellparam = saveparam; } unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; handler = savehandler; longjmp(handler->loc, 1); } handler = &jmploc; for (sp = varlist.list ; sp ; sp = sp->next) mklocal(sp->text); funcnest++; exitstatus = oexitstatus; if (flags & EV_TESTED) evaltree(getfuncnode(cmdentry.u.func), EV_TESTED); else evaltree(getfuncnode(cmdentry.u.func), 0); funcnest--; INTOFF; unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; freeparam(&shellparam); shellparam = saveparam; handler = savehandler; popredir(); INTON; if (evalskip == SKIPFUNC) { evalskip = 0; skipcount = 0; } if (flags & EV_EXIT) exitshell(exitstatus); } else if (cmdentry.cmdtype == CMDBUILTIN) { #ifdef DEBUG trputs("builtin command: "); trargs(argv); #endif mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { memout.nleft = 0; memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; } savecmdname = commandname; cmdenviron = varlist.list; e = -1; savehandler = handler; if (setjmp(jmploc.loc)) { e = exception; exitstatus = (e == EXINT)? SIGINT+128 : 2; goto cmddone; } handler = &jmploc; redirect(cmd->ncmd.redirect, mode); if (cmdentry.special) listsetvar(cmdenviron); commandname = argv[0]; argptr = argv + 1; nextopt_optptr = NULL; /* initialize nextopt */ builtin_flags = flags; exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); flushall(); cmddone: cmdenviron = NULL; out1 = &output; out2 = &errout; freestdout(); if (e != EXSHELLPROC) { commandname = savecmdname; if (flags & EV_EXIT) { exitshell(exitstatus); } } handler = savehandler; if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.special) exraise(e); FORCEINTON; } if (cmdentry.u.index != EXECCMD) popredir(); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; memout.buf = NULL; } } else { #ifdef DEBUG trputs("normal command: "); trargs(argv); #endif clearredir(); redirect(cmd->ncmd.redirect, 0); for (sp = varlist.list ; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); shellexec(argv, envp, pathval(), cmdentry.u.index); /*NOTREACHED*/ } goto out; parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ INTOFF; exitstatus = waitforjob(jp, &realstatus); INTON; if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { evalskip = SKIPBREAK; skipcount = loopnest; } } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; close(pip[1]); backcmd->jp = jp; } out: if (lastarg) setvar("_", lastarg, 0); if (do_clearcmdentry) clearcmdentry(0); popstackmark(&smark); }
STATIC void expbackq(shinstance *psh, union node *cmd, int quoted, int flag) { struct backcmd in; int i; char buf[128]; char *p; char *dest = psh->expdest; struct ifsregion saveifs, *savelastp; struct nodelist *saveargbackq; char lastc; int startloc = (int)(dest - stackblock(psh)); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int saveherefd; int quotes = flag & (EXP_FULL | EXP_CASE); #ifdef SH_DEAL_WITH_CRLF int pending_cr = 0; #endif INTOFF; saveifs = psh->ifsfirst; savelastp = psh->ifslastp; saveargbackq = psh->argbackq; saveherefd = psh->herefd; psh->herefd = -1; p = grabstackstr(psh, dest); evalbackcmd(psh, cmd, &in); ungrabstackstr(psh, p, dest); psh->ifsfirst = saveifs; psh->ifslastp = savelastp; psh->argbackq = saveargbackq; psh->herefd = saveherefd; p = in.buf; lastc = '\0'; for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = shfile_read(&psh->fdtab, in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE((psh, "expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; #ifdef SH_DEAL_WITH_CRLF if (pending_cr) { pending_cr = 0; if (lastc != '\n') { if (quotes && syntax[(int)'\r'] == CCTL) STPUTC(psh, CTLESC, dest); STPUTC(psh, '\r', dest); } } if (lastc == '\r') pending_cr = '\r'; else #endif if (lastc != '\0') { if (quotes && syntax[(int)lastc] == CCTL) STPUTC(psh, CTLESC, dest); STPUTC(psh, lastc, dest); } } #ifdef SH_DEAL_WITH_CRLF if (pending_cr) { if (quotes && syntax[(int)'\r'] == CCTL) STPUTC(psh, CTLESC, dest); STPUTC(psh, '\r', dest); } #endif /* Eat all trailing newlines */ p = stackblock(psh) + startloc; while (dest > p && dest[-1] == '\n') STUNPUTC(psh, dest); if (in.fd >= 0) shfile_close(&psh->fdtab, in.fd); if (in.buf) ckfree(psh, in.buf); if (in.jp) psh->back_exitstatus = waitforjob(psh, in.jp); if (quoted == 0) recordregion(psh, startloc, (int)(dest - stackblock(psh)), 0); TRACE((psh, "evalbackq: size=%d: \"%.*s\"\n", (dest - stackblock(psh)) - startloc, (dest - stackblock(psh)) - startloc, stackblock(psh) + startloc)); psh->expdest = dest; INTON; }
STATIC void evalcommand(shinstance *psh, 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(psh); volatile int temp_path; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &argv; (void) &argc; (void) &lastarg; (void) &flags; #endif psh->vforked = 0; /* First expand the arguments. */ TRACE((psh, "evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(psh, &smark); psh->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(psh, argp, &arglist, EXP_FULL | EXP_TILDE); varflag = 0; } *arglist.lastp = NULL; expredir(psh, 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(psh, argp, &varlist, EXP_VARTILDE); } *varlist.lastp = NULL; argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; argv = stalloc(psh, sizeof (char *) * (argc + 1)); for (sp = arglist.list ; sp ; sp = sp->next) { TRACE((psh, "evalcommand arg: %s\n", sp->text)); *argv++ = sp->text; } *argv = NULL; lastarg = NULL; if (iflag(psh) && psh->funcnest == 0 && argc > 0) lastarg = argv[-1]; argv -= argc; /* Print the command if xflag is set. */ if (xflag(psh)) { char sep = 0; out2str(psh, ps4val(psh)); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) outc(sep, &psh->errout); out2str(psh, sp->text); sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) outc(sep, &psh->errout); out2str(psh, sp->text); sep = ' '; } outc('\n', &psh->errout); flushout(&psh->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(psh, argv[0], &cmdentry, cmd_flags, path); if (cmdentry.cmdtype == CMDUNKNOWN) { psh->exitstatus = 127; flushout(&psh->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(psh, 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(psh) + 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(psh, cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (sh_pipe(psh, pip) < 0) error(psh, "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 = psh->localvars; psh->localvars = NULL; psh->vforked = 1; switch (pid = vfork()) { case -1: TRACE((psh, "Vfork failed, errno=%d\n", errno)); INTON; error(psh, "Cannot vfork"); break; case 0: /* Make sure that exceptions only unwind to * after the vfork(2) */ if (setjmp(jmploc.loc)) { if (psh->exception == EXSHELLPROC) { /* We can't progress with the vfork, * so, set vforked = 2 so the parent * knows, and _exit(); */ psh->vforked = 2; sh__exit(psh, 0); } else { sh__exit(psh, psh->exerrno); } } savehandler = psh->handler; psh->handler = &jmploc; listmklocal(psh, varlist.list, VEXPORT | VNOFUNC); forkchild(psh, jp, cmd, mode, psh->vforked); break; default: psh->handler = savehandler; /* restore from vfork(2) */ poplocalvars(psh); psh->localvars = savelocalvars; if (psh->vforked == 2) { psh->vforked = 0; (void)sh_waitpid(psh, pid, NULL, 0); /* We need to progress in a normal fork fashion */ goto normal_fork; } psh->vforked = 0; forkparent(psh, jp, cmd, mode, pid); goto parent; } } else { normal_fork: #endif if (forkshell(psh, jp, cmd, mode) != 0) goto parent; /* at end of routine */ FORCEINTON; #ifdef DO_SHAREDVFORK } #endif if (flags & EV_BACKCMD) { if (!psh->vforked) { FORCEINTON; } shfile_close(&psh->fdtab, pip[0]); if (pip[1] != 1) { movefd(psh, pip[1], 1); } } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ switch (cmdentry.cmdtype) { case CMDFUNCTION: #ifdef DEBUG trputs(psh, "Shell function: "); trargs(psh, argv); #endif redirect(psh, cmd->ncmd.redirect, REDIR_PUSH); saveparam = psh->shellparam; psh->shellparam.malloc = 0; psh->shellparam.reset = 1; psh->shellparam.nparam = argc - 1; psh->shellparam.p = argv + 1; psh->shellparam.optnext = NULL; INTOFF; savelocalvars = psh->localvars; psh->localvars = NULL; INTON; if (setjmp(jmploc.loc)) { if (psh->exception == EXSHELLPROC) { freeparam(psh, (volatile struct shparam *) &saveparam); } else { freeparam(psh, &psh->shellparam); psh->shellparam = saveparam; } poplocalvars(psh); psh->localvars = savelocalvars; psh->handler = savehandler; longjmp(psh->handler->loc, 1); } savehandler = psh->handler; psh->handler = &jmploc; listmklocal(psh, varlist.list, 0); /* stop shell blowing its stack */ if (++psh->funcnest > 1000) error(psh, "too many nested function calls"); evaltree(psh, cmdentry.u.func, flags & EV_TESTED); psh->funcnest--; INTOFF; poplocalvars(psh); psh->localvars = savelocalvars; freeparam(psh, &psh->shellparam); psh->shellparam = saveparam; psh->handler = savehandler; popredir(psh); INTON; if (psh->evalskip == SKIPFUNC) { psh->evalskip = 0; psh->skipcount = 0; } if (flags & EV_EXIT) exitshell(psh, psh->exitstatus); break; case CMDBUILTIN: case CMDSPLBLTIN: #ifdef DEBUG trputs(psh, "builtin command: "); trargs(psh, argv); #endif mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { psh->memout.nleft = 0; psh->memout.nextc = psh->memout.buf; psh->memout.bufsize = 64; mode |= REDIR_BACKQ; } e = -1; savehandler = psh->handler; savecmdname = psh->commandname; psh->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(psh) && (cmdentry.u.bltin == hashcmd || cmdentry.u.bltin == typecmd)) { savelocalvars = psh->localvars; psh->localvars = 0; mklocal(psh, path - 5 /* PATH= */, 0); temp_path = 1; } else temp_path = 0; redirect(psh, cmd->ncmd.redirect, mode); /* exec is a special builtin, but needs this list... */ psh->cmdenviron = varlist.list; /* we must check 'readonly' flag for all builtins */ listsetvar(psh, varlist.list, cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); psh->commandname = argv[0]; /* initialize nextopt */ psh->argptr = argv + 1; psh->optptr = NULL; /* and getopt */ #if 0 /** @todo fix getop usage! */ #if defined(__FreeBSD__) || defined(__EMX__) || defined(__APPLE__) optreset = 1; optind = 1; #else optind = 0; /* init */ #endif #endif psh->exitstatus = cmdentry.u.bltin(psh, argc, argv); } else { e = psh->exception; psh->exitstatus = e == EXINT ? SIGINT + 128 : e == EXEXEC ? psh->exerrno : 2; } psh->handler = savehandler; output_flushall(psh); psh->out1 = &psh->output; psh->out2 = &psh->errout; freestdout(psh); if (temp_path) { poplocalvars(psh); psh->localvars = savelocalvars; } psh->cmdenviron = NULL; if (e != EXSHELLPROC) { psh->commandname = savecmdname; if (flags & EV_EXIT) exitshell(psh, psh->exitstatus); } if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.cmdtype == CMDSPLBLTIN) exraise(psh, e); FORCEINTON; } if (cmdentry.u.bltin != execcmd) popredir(psh); if (flags == EV_BACKCMD) { backcmd->buf = psh->memout.buf; backcmd->nleft = (int)(psh->memout.nextc - psh->memout.buf); psh->memout.buf = NULL; } break; default: #ifdef DEBUG trputs(psh, "normal command: "); trargs(psh, argv); #endif clearredir(psh, psh->vforked); redirect(psh, cmd->ncmd.redirect, psh->vforked ? REDIR_VFORK : 0); if (!psh->vforked) for (sp = varlist.list ; sp ; sp = sp->next) setvareq(psh, sp->text, VEXPORT|VSTACK); envp = environment(psh); shellexec(psh, argv, envp, path, cmdentry.u.index, psh->vforked); break; } goto out; parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ psh->exitstatus = waitforjob(psh, jp); } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; shfile_close(&psh->fdtab, 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(psh, "_", lastarg, 0); popstackmark(psh, &smark); if (eflag(psh) && psh->exitstatus && !(flags & EV_TESTED)) exitshell(psh, psh->exitstatus); }
STATIC void expbackq(union node *cmd, int quoted, int flag) { struct backcmd in; int i; char buf[128]; char *p; char *dest = expdest; struct ifsregion saveifs, *savelastp; struct nodelist *saveargbackq; char lastc; int startloc = dest - stackblock(); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int saveherefd; int quotes = flag & (EXP_FULL | EXP_CASE); INTOFF; saveifs = ifsfirst; savelastp = ifslastp; saveargbackq = argbackq; saveherefd = herefd; herefd = -1; p = grabstackstr(dest); evalbackcmd(cmd, &in); ungrabstackstr(p, dest); ifsfirst = saveifs; ifslastp = savelastp; argbackq = saveargbackq; herefd = saveherefd; p = in.buf; lastc = '\0'; for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; if (lastc != '\0') { if (quotes && syntax[(int)lastc] == CCTL) STPUTC(CTLESC, dest); STPUTC(lastc, dest); } } /* Eat all trailing newlines */ p = stackblock() + startloc; while (dest > p && dest[-1] == '\n') STUNPUTC(dest); if (in.fd >= 0) close(in.fd); if (in.buf) ckfree(in.buf); if (in.jp) back_exitstatus = waitforjob(in.jp); if (quoted == 0) recordregion(startloc, dest - stackblock(), 0); TRACE(("evalbackq: size=%d: \"%.*s\"\n", (dest - stackblock()) - startloc, (dest - stackblock()) - startloc, stackblock() + startloc)); expdest = dest; INTON; }
/* * Perform command substitution. */ static void expbackq(union node *cmd, int quoted, int flag, struct worddest *dst) { struct backcmd in; int i; char buf[128]; char *p; char *dest = expdest; struct nodelist *saveargbackq; char lastc; char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int quotes = flag & (EXP_GLOB | EXP_CASE); size_t nnl; const char *ifs; INTOFF; saveargbackq = argbackq; p = grabstackstr(dest); evalbackcmd(cmd, &in); ungrabstackstr(p, dest); argbackq = saveargbackq; p = in.buf; lastc = '\0'; nnl = 0; if (!quoted && flag & EXP_SPLIT) ifs = ifsset() ? ifsval() : " \t\n"; else ifs = ""; /* Don't copy trailing newlines */ for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; if (lastc == '\0') continue; if (lastc == '\n') { nnl++; } else { if (nnl > 0) { if (strchr(ifs, '\n') != NULL) { NEXTWORD('\n', flag, dest, dst); nnl = 0; } else { CHECKSTRSPACE(nnl + 2, dest); while (nnl > 0) { nnl--; USTPUTC('\n', dest); } } } if (strchr(ifs, lastc) != NULL) NEXTWORD(lastc, flag, dest, dst); else { CHECKSTRSPACE(2, dest); if (quotes && syntax[(int)lastc] == CCTL) USTPUTC(CTLESC, dest); USTPUTC(lastc, dest); } } } if (in.fd >= 0) close(in.fd); if (in.buf) ckfree(in.buf); if (in.jp) exitstatus = waitforjob(in.jp, (int *)NULL); TRACE(("expbackq: size=%td: \"%.*s\"\n", ((dest - stackblock()) - startloc), (int)((dest - stackblock()) - startloc), stackblock() + startloc)); expdest = dest; INTON; }