const char* FAST_FUNC endofname(const char *name) { if (!is_name(*name)) return name; while (*++name) { if (!is_in_name(*name)) break; } return name; }
int goodname(char *name) { char *p; p = name; if (! is_name(*p)) return 0; while (*++p) { if (! is_in_name(*p)) return 0; } return 1; }
void setvar(shinstance *psh, const char *name, const char *val, int flags) { const char *p; const char *q; char *d; size_t len; int namelen; char *nameeq; int isbad; isbad = 0; p = name; if (! is_name(*p)) isbad = 1; p++; for (;;) { if (! is_in_name(*p)) { if (*p == '\0' || *p == '=') break; isbad = 1; } p++; } namelen = (int)(p - name); if (isbad) error(psh, "%.*s: bad variable name", namelen, name); len = namelen + 2; /* 2 is space for '=' and '\0' */ if (val == NULL) { flags |= VUNSET; } else { len += strlen(val); } d = nameeq = ckmalloc(psh, len); q = name; while (--namelen >= 0) *d++ = *q++; *d++ = '='; *d = '\0'; if (val) scopy(val, d); setvareq(psh, nameeq, flags); }
void setvar(char *name, char *val, int flags) { char *p, *q; int len; int namelen; char *nameeq; int isbad; isbad = 0; p = name; if (! is_name(*p)) isbad = 1; p++; for (;;) { if (! is_in_name(*p)) { if (*p == '\0' || *p == '=') break; isbad = 1; } p++; } namelen = p - name; if (isbad) error("%.*s: bad variable name", namelen, name); len = namelen + 2; /* 2 is space for '=' and '\0' */ if (val == NULL) { flags |= VUNSET; } else { len += strlen(val); } p = nameeq = ckmalloc(len); q = name; while (--namelen >= 0) *p++ = *q++; *p++ = '='; *p = '\0'; if (val) scopy(val, p); setvareq(nameeq, flags); }
int yylex(void) { int value; const char *buf = arith_buf; char *end; const char *p; for (;;) { value = *buf; switch (value) { case ' ': case '\t': case '\n': buf++; continue; default: return ARITH_BAD; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': yylval.val = strtoarith_t(buf, &end, 0); arith_buf = end; return ARITH_NUM; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': p = buf; while (buf++, is_in_name(*buf)) ; yylval.name = stalloc(buf - p + 1); memcpy(yylval.name, p, buf - p); yylval.name[buf - p] = '\0'; value = ARITH_VAR; goto out; case '=': value += ARITH_ASS - '='; checkeq: buf++; checkeqcur: if (*buf != '=') goto out; value += 11; break; case '>': switch (*++buf) { case '=': value += ARITH_GE - '>'; break; case '>': value += ARITH_RSHIFT - '>'; goto checkeq; default: value += ARITH_GT - '>'; goto out; } break; case '<': switch (*++buf) { case '=': value += ARITH_LE - '<'; break; case '<': value += ARITH_LSHIFT - '<'; goto checkeq; default: value += ARITH_LT - '<'; goto out; } break; case '|': if (*++buf != '|') { value += ARITH_BOR - '|'; goto checkeqcur; } value += ARITH_OR - '|'; break; case '&': if (*++buf != '&') { value += ARITH_BAND - '&'; goto checkeqcur; } value += ARITH_AND - '&'; break; case '!': if (*++buf != '=') { value += ARITH_NOT - '!'; goto out; } value += ARITH_NE - '!'; break; case 0: goto out; case '(': value += ARITH_LPAREN - '('; break; case ')': value += ARITH_RPAREN - ')'; break; case '*': value += ARITH_MUL - '*'; goto checkeq; case '/': value += ARITH_DIV - '/'; goto checkeq; case '%': value += ARITH_REM - '%'; goto checkeq; case '+': value += ARITH_ADD - '+'; goto checkeq; case '-': value += ARITH_SUB - '-'; goto checkeq; case '~': value += ARITH_BNOT - '~'; break; case '^': value += ARITH_BXOR - '^'; goto checkeq; case '?': value += ARITH_QMARK - '?'; break; case ':': value += ARITH_COLON - ':'; break; } break; } buf++; out: arith_buf = buf; return value; }
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 int readtoken1(int firstc, char const *syntax, char *eofmark, int striptabs) { int c = firstc; char *out; int len; char line[EOFMARKLEN + 1]; struct nodelist *bqlist; int quotef; int dblquote; int varnest; /* levels of variables expansion */ int arinest; /* levels of arithmetic expansion */ int parenlevel; /* levels of parens in arithmetic */ int oldstyle; char const *prevsyntax; /* syntax before arithmetic */ int synentry; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &out; (void) "ef; (void) &dblquote; (void) &varnest; (void) &arinest; (void) &parenlevel; (void) &oldstyle; (void) &prevsyntax; (void) &syntax; (void) &synentry; #endif startlinno = plinno; dblquote = 0; if (syntax == DQSYNTAX) dblquote = 1; quotef = 0; bqlist = NULL; varnest = 0; arinest = 0; parenlevel = 0; STARTSTACKSTR(out); loop: { /* for each line, until end of word */ CHECKEND(); /* set c to PEOF if at end of here document */ for (;;) { /* until end of line or end of word */ CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ synentry = syntax[c]; switch(synentry) { case CNL: /* '\n' */ if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); plinno++; if (doprompt) setprompt(2); else setprompt(0); c = pgetc(); goto loop; /* continue outer loop */ case CWORD: USTPUTC(c, out); break; case CCTL: if (eofmark == NULL || dblquote) USTPUTC(CTLESC, out); USTPUTC(c, out); break; case CBACK: /* backslash */ c = pgetc(); if (c == PEOF) { USTPUTC('\\', out); pungetc(); } else if (c == '\n') { if (doprompt) setprompt(2); else setprompt(0); } else { if (dblquote && c != '\\' && c != '`' && c != '$' && (c != '"' || eofmark != NULL)) USTPUTC('\\', out); if (SQSYNTAX[c] == CCTL) USTPUTC(CTLESC, out); else if (eofmark == NULL) USTPUTC(CTLQUOTEMARK, out); USTPUTC(c, out); quotef++; } break; case CSQUOTE: if (eofmark == NULL) USTPUTC(CTLQUOTEMARK, out); syntax = SQSYNTAX; break; case CDQUOTE: if (eofmark == NULL) USTPUTC(CTLQUOTEMARK, out); syntax = DQSYNTAX; dblquote = 1; break; case CENDQUOTE: if (eofmark != NULL && arinest == 0 && varnest == 0) { USTPUTC(c, out); } else { if (arinest) { syntax = ARISYNTAX; dblquote = 0; } else if (eofmark == NULL) { syntax = BASESYNTAX; dblquote = 0; } quotef++; } break; case CVAR: /* '$' */ PARSESUB(); /* parse substitution */ break; case CENDVAR: /* '}' */ if (varnest > 0) { varnest--; USTPUTC(CTLENDVAR, out); } else { USTPUTC(c, out); } break; case CLP: /* '(' in arithmetic */ parenlevel++; USTPUTC(c, out); break; case CRP: /* ')' in arithmetic */ if (parenlevel > 0) { USTPUTC(c, out); --parenlevel; } else { if (pgetc() == ')') { if (--arinest == 0) { USTPUTC(CTLENDARI, out); syntax = prevsyntax; if (syntax == DQSYNTAX) dblquote = 1; else dblquote = 0; } else USTPUTC(')', out); } else { /* * unbalanced parens * (don't 2nd guess - no error) */ pungetc(); USTPUTC(')', out); } } break; case CBQUOTE: /* '`' */ PARSEBACKQOLD(); break; case CEOF: goto endword; /* exit outer loop */ default: if (varnest == 0) goto endword; /* exit outer loop */ USTPUTC(c, out); } c = pgetc_macro(); } } endword: if (syntax == ARISYNTAX) synerror("Missing '))'"); if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) synerror("Unterminated quoted string"); if (varnest != 0) { startlinno = plinno; synerror("Missing '}'"); } USTPUTC('\0', out); len = out - stackblock(); out = stackblock(); if (eofmark == NULL) { if ((c == '>' || c == '<') && quotef == 0 && len <= 2 && (*out == '\0' || is_digit(*out))) { PARSEREDIR(); return lasttoken = TREDIR; } else { pungetc(); } } quoteflag = quotef; backquotelist = bqlist; grabstackblock(len); wordtext = out; return lasttoken = TWORD; /* end of readtoken routine */ /* * Check to see whether we are at the end of the here document. When this * is called, c is set to the first character of the next input line. If * we are at the end of the here document, this routine sets the c to PEOF. */ checkend: { if (eofmark) { if (striptabs) { while (c == '\t') c = pgetc(); } if (c == *eofmark) { if (pfgets(line, sizeof line) != NULL) { char *p, *q; p = line; for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); if (*p == '\n' && *q == '\0') { c = PEOF; plinno++; needprompt = doprompt; } else { pushstring(line, strlen(line), NULL); } } } } goto checkend_return; } /* * Parse a redirection operator. The variable "out" points to a string * specifying the fd to be redirected. The variable "c" contains the * first character of the redirection operator. */ parseredir: { char fd = *out; union node *np; np = (union node *)stalloc(sizeof (struct nfile)); if (c == '>') { np->nfile.fd = 1; c = pgetc(); if (c == '>') np->type = NAPPEND; else if (c == '&') np->type = NTOFD; else if (c == '|') np->type = NCLOBBER; else { np->type = NTO; pungetc(); } } else { /* c == '<' */ np->nfile.fd = 0; c = pgetc(); if (c == '<') { if (sizeof (struct nfile) != sizeof (struct nhere)) { np = (union node *)stalloc(sizeof (struct nhere)); np->nfile.fd = 0; } np->type = NHERE; heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); heredoc->here = np; if ((c = pgetc()) == '-') { heredoc->striptabs = 1; } else { heredoc->striptabs = 0; pungetc(); } } else if (c == '&') np->type = NFROMFD; else if (c == '>') np->type = NFROMTO; else { np->type = NFROM; pungetc(); } } if (fd != '\0') np->nfile.fd = digit_val(fd); redirnode = np; goto parseredir_return; } /* * Parse a substitution. At this point, we have read the dollar sign * and nothing else. */ parsesub: { int subtype; int typeloc; int flags; char *p; #ifndef GDB_HACK static const char types[] = "}-+?="; #endif int bracketed_name = 0; /* used to handle ${[0-9]*} variables */ c = pgetc(); if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ if (pgetc() == '(') { PARSEARITH(); } else { pungetc(); PARSEBACKQNEW(); } } else { USTPUTC(CTLVAR, out); typeloc = out - stackblock(); USTPUTC(VSNORMAL, out); subtype = VSNORMAL; if (c == '{') { bracketed_name = 1; c = pgetc(); if (c == '#') { if ((c = pgetc()) == '}') c = '#'; else subtype = VSLENGTH; } else subtype = 0; } if (is_name(c)) { do { STPUTC(c, out); c = pgetc(); } while (is_in_name(c)); } else if (is_digit(c)) { if (bracketed_name) { do { STPUTC(c, out); c = pgetc(); } while (is_digit(c)); } else { STPUTC(c, out); c = pgetc(); } } else { if (! is_special(c)) badsub: synerror("Bad substitution"); USTPUTC(c, out); c = pgetc(); } STPUTC('=', out); flags = 0; if (subtype == 0) { switch (c) { case ':': flags = VSNUL; c = pgetc(); /*FALLTHROUGH*/ default: p = strchr(types, c); if (p == NULL) goto badsub; subtype = p - types + VSNORMAL; break; case '%': case '#': { int cc = c; subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT; c = pgetc(); if (c == cc) subtype++; else pungetc(); break; } } } else { pungetc(); } if (subtype != VSLENGTH && (dblquote || arinest)) flags |= VSQUOTE; *(stackblock() + typeloc) = subtype | flags; if (subtype != VSNORMAL) varnest++; } goto parsesub_return; } /* * Called to parse command substitutions. Newstyle is set if the command * is enclosed inside $(...); nlpp is a pointer to the head of the linked * list of commands (passed by reference), and savelen is the number of * characters on the top of the stack which must be preserved. */ parsebackq: { struct nodelist **nlpp; int savepbq; union node *n; char *volatile str; struct jmploc jmploc; struct jmploc *volatile savehandler; int savelen; int saveprompt; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &saveprompt; #endif savepbq = parsebackquote; if (setjmp(jmploc.loc)) { if (str) ckfree(str); parsebackquote = 0; handler = savehandler; longjmp(handler->loc, 1); } INTOFF; str = NULL; savelen = out - stackblock(); if (savelen > 0) { str = ckmalloc(savelen); memcpy(str, stackblock(), savelen); } savehandler = handler; handler = &jmploc; INTON; if (oldstyle) { /* We must read until the closing backquote, giving special treatment to some slashes, and then push the string and reread it as input, interpreting it normally. */ char *out; int c; int savelen; char *str; STARTSTACKSTR(out); for (;;) { if (needprompt) { setprompt(2); needprompt = 0; } switch (c = pgetc()) { case '`': goto done; case '\\': if ((c = pgetc()) == '\n') { plinno++; if (doprompt) setprompt(2); else setprompt(0); /* * If eating a newline, avoid putting * the newline into the new character * stream (via the STPUTC after the * switch). */ continue; } if (c != '\\' && c != '`' && c != '$' && (!dblquote || c != '"')) STPUTC('\\', out); break; case '\n': plinno++; needprompt = doprompt; break; case PEOF: startlinno = plinno; synerror("EOF in backquote substitution"); break; default: break; } STPUTC(c, out); } done: STPUTC('\0', out); savelen = out - stackblock(); if (savelen > 0) { str = ckmalloc(savelen); memcpy(str, stackblock(), savelen); setinputstring(str, 1); } } nlpp = &bqlist; while (*nlpp) nlpp = &(*nlpp)->next; *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); (*nlpp)->next = NULL; parsebackquote = oldstyle; if (oldstyle) { saveprompt = doprompt; doprompt = 0; } n = list(0); if (oldstyle) doprompt = saveprompt; else { if (readtoken() != TRP) synexpect(TRP); } (*nlpp)->n = n; if (oldstyle) { /* * Start reading from old file again, ignoring any pushed back * tokens left from the backquote parsing */ popfile(); tokpushback = 0; } while (stackblocksize() <= savelen) growstackblock(); STARTSTACKSTR(out); if (str) { memcpy(out, str, savelen); STADJUST(savelen, out); INTOFF; ckfree(str); str = NULL; INTON; } parsebackquote = savepbq; handler = savehandler; if (arinest || dblquote) USTPUTC(CTLBACKQ | CTLQUOTE, out); else USTPUTC(CTLBACKQ, out); if (oldstyle) goto parsebackq_oldreturn; else goto parsebackq_newreturn; } /* * Parse an arithmetic expansion (indicate start of one and set state) */ parsearith: { if (++arinest == 1) { prevsyntax = syntax; syntax = ARISYNTAX; USTPUTC(CTLARI, out); if (dblquote) USTPUTC('"',out); else USTPUTC(' ',out); } else { /* * we collapse embedded arithmetic expansion to * parenthesis, which should be equivalent */ USTPUTC('(', out); } goto parsearith_return; } } /* end of readtoken */
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); }