- case NNOT:
- evaltree(n->nnot.com, EV_TESTED);
- status = !exitstatus;
- goto setstatus;
- case NREDIR:
- expredir(n->nredir.redirect);
- status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
- if (!status) {
- evaltree(n->nredir.n, flags & EV_TESTED);
- status = exitstatus;
- }
- popredir(0);
- goto setstatus;
- case NCMD:
- evalfn = evalcommand;
-checkexit:
- if (eflag && !(flags & EV_TESTED))
- checkexit = ~0;
- goto calleval;
- case NFOR:
- evalfn = evalfor;
- goto calleval;
- case NWHILE:
- case NUNTIL:
- evalfn = evalloop;
- goto calleval;
- case NSUBSHELL:
- case NBACKGND:
- evalfn = evalsubshell;
- goto calleval;
- case NPIPE:
- evalfn = evalpipe;
- goto checkexit;
- case NCASE:
- evalfn = evalcase;
- goto calleval;
- case NAND:
- case NOR:
- case NSEMI:
-#if NAND + 1 != NOR
-#error NAND + 1 != NOR
-#endif
-#if NOR + 1 != NSEMI
-#error NOR + 1 != NSEMI
-#endif
- isor = n->type - NAND;
- evaltree(
- n->nbinary.ch1,
- (flags | ((isor >> 1) - 1)) & EV_TESTED
- );
- if (!exitstatus == isor)
- break;
- if (!evalskip) {
- n = n->nbinary.ch2;
-evaln:
- evalfn = evaltree;
-calleval:
- evalfn(n, flags);
- break;
- }
- break;
- case NIF:
- evaltree(n->nif.test, EV_TESTED);
- if (evalskip)
- break;
- if (exitstatus == 0) {
- n = n->nif.ifpart;
- goto evaln;
- } else if (n->nif.elsepart) {
- n = n->nif.elsepart;
- goto evaln;
- }
- goto success;
- case NDEFUN:
- defun(n->narg.text, n->narg.next);
-success:
- status = 0;
-setstatus:
- exitstatus = status;
- break;
- }
-out:
- if ((checkexit & exitstatus))
- evalskip |= SKIPEVAL;
- else if (pendingsigs && dotrap())
- goto exexit;
-
- if (flags & EV_EXIT) {
-exexit:
- exraise(EXEXIT);
- }
-}
-
-
-#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
-static
-#endif
-void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
-
-
-static void
-evalloop(union node *n, int flags)
-{
- int status;
-
- loopnest++;
- status = 0;
- flags &= EV_TESTED;
- for (;;) {
- int i;
-
- evaltree(n->nbinary.ch1, EV_TESTED);
- if (evalskip) {
-skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
- evalskip = 0;
- continue;
- }
- if (evalskip == SKIPBREAK && --skipcount <= 0)
- evalskip = 0;
- break;
- }
- i = exitstatus;
- if (n->type != NWHILE)
- i = !i;
- if (i != 0)
- break;
- evaltree(n->nbinary.ch2, flags);
- status = exitstatus;
- if (evalskip)
- goto skipping;
- }
- loopnest--;
- exitstatus = status;
-}
-
-
-
-static void
-evalfor(union node *n, int flags)
-{
- struct arglist arglist;
- union node *argp;
- struct strlist *sp;
- struct stackmark smark;
-
- setstackmark(&smark);
- arglist.lastp = &arglist.list;
- for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
- expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
- /* XXX */
- if (evalskip)
- goto out;
- }
- *arglist.lastp = NULL;
-
- exitstatus = 0;
- loopnest++;
- flags &= EV_TESTED;
- for (sp = arglist.list ; sp ; sp = sp->next) {
- setvar(n->nfor.var, sp->text, 0);
- evaltree(n->nfor.body, flags);
- if (evalskip) {
- if (evalskip == SKIPCONT && --skipcount <= 0) {
- evalskip = 0;
- continue;
- }
- if (evalskip == SKIPBREAK && --skipcount <= 0)
- evalskip = 0;
- break;
- }
- }
- loopnest--;
-out:
- popstackmark(&smark);
-}
-
-
-
-static void
-evalcase(union node *n, int flags)
-{
- union node *cp;
- union node *patp;
- struct arglist arglist;
- struct stackmark smark;
-
- setstackmark(&smark);
- arglist.lastp = &arglist.list;
- expandarg(n->ncase.expr, &arglist, EXP_TILDE);
- exitstatus = 0;
- for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
- for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
- if (casematch(patp, arglist.list->text)) {
- if (evalskip == 0) {
- evaltree(cp->nclist.body, flags);
- }
- goto out;
- }
- }
- }
-out:
- popstackmark(&smark);
-}
-
-
-
-/*
- * Kick off a subshell to evaluate a tree.
- */
-
-static void
-evalsubshell(union node *n, int flags)
-{
- struct job *jp;
- int backgnd = (n->type == NBACKGND);
- int status;
-
- expredir(n->nredir.redirect);
- if (!backgnd && flags & EV_EXIT && !trap[0])
- goto nofork;
- INTOFF;
- jp = makejob(n, 1);
- if (forkshell(jp, n, backgnd) == 0) {
- INTON;
- flags |= EV_EXIT;
- if (backgnd)
- flags &=~ EV_TESTED;
-nofork:
- redirect(n->nredir.redirect, 0);
- evaltreenr(n->nredir.n, flags);
- /* never returns */
- }
- status = 0;
- if (! backgnd)
- status = waitforjob(jp);
- exitstatus = status;
- INTON;
-}
-
-
-
-/*
- * Compute the names of the files in a redirection list.
- */
-
-static void
-expredir(union node *n)
-{
- union node *redir;
-
- for (redir = n ; redir ; redir = redir->nfile.next) {
- struct arglist fn;
- memset(&fn, 0, sizeof(struct arglist));
- fn.lastp = &fn.list;
- switch (redir->type) {
- case NFROMTO:
- case NFROM:
- case NTO:
- case NCLOBBER:
- case NAPPEND:
- expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
- redir->nfile.expfname = fn.list->text;
- break;
- case NFROMFD:
- case NTOFD:
- if (redir->ndup.vname) {
- expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
- if (fn.list != NULL)
- fixredir(redir, fn.list->text, 1);
- else
- sh_error("redir error");
- }
- break;
- }
- }
-}
-
-
-
-/*
- * Evaluate a pipeline. All the processes in the pipeline are children
- * of the process creating the pipeline. (This differs from some versions
- * of the shell, which make the last process in a pipeline the parent
- * of all the rest.)
- */
-
-static void
-evalpipe(union node *n, int flags)
-{
- 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++;
- flags |= EV_EXIT;
- 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) {
- close(prevfd);
- sh_error("Pipe call failed");
- }
- }
- if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
- INTON;
- if (pip[1] >= 0) {
- close(pip[0]);
- }
- if (prevfd > 0) {
- dup2(prevfd, 0);
- close(prevfd);
- }
- if (pip[1] > 1) {
- dup2(pip[1], 1);
- close(pip[1]);
- }
- evaltreenr(lp->n, flags);
- /* never returns */
- }
- 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));
- }
- INTON;
-}
-
-
-
-/*
- * Execute a command inside back quotes. If it's a builtin command, we
- * want to save its output in a block obtained from malloc. Otherwise
- * we fork off a subprocess and get the output of the command via a pipe.
- * Should be called with interrupts off.
- */
-
-static void
-evalbackcmd(union node *n, struct backcmd *result)
-{
- int saveherefd;
-
- result->fd = -1;
- result->buf = NULL;
- result->nleft = 0;
- result->jp = NULL;
- if (n == NULL) {
- goto out;
- }
-
- saveherefd = herefd;
- herefd = -1;
-
- {
- int pip[2];
- struct job *jp;
-
- if (pipe(pip) < 0)
- sh_error("Pipe call failed");
- jp = makejob(n, 1);
- if (forkshell(jp, n, FORK_NOJOB) == 0) {
- FORCEINTON;
- close(pip[0]);
- if (pip[1] != 1) {
- close(1);
- copyfd(pip[1], 1);
- close(pip[1]);
- }
- eflag = 0;
- evaltreenr(n, EV_EXIT);
- /* NOTREACHED */
- }
- close(pip[1]);
- result->fd = pip[0];
- result->jp = jp;
- }
- herefd = saveherefd;
-out:
- TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
- result->fd, result->buf, result->nleft, result->jp));
-}
-
-#ifdef CONFIG_ASH_CMDCMD
-static char ** parse_command_args(char **argv, const char **path)
-{
- char *cp, c;
-
- for (;;) {
- cp = *++argv;
- if (!cp)
- return 0;
- if (*cp++ != '-')
- break;
- if (!(c = *cp++))
- break;
- if (c == '-' && !*cp) {
- argv++;
- break;
- }
- do {
- switch (c) {
- case 'p':
- *path = defpath;
- break;
- default:
- /* run 'typecmd' for other options */
- return 0;
- }
- } while ((c = *cp++));
- }
- return argv;
-}
-#endif
-
-static int isassignment(const char *p)
-{
- const char *q = endofname(p);
- if (p == q)
- return 0;
- return *q == '=';
-}
-
-#ifdef CONFIG_ASH_EXPAND_PRMT
-static const char *expandstr(const char *ps);
-#else
-#define expandstr(s) s
-#endif
-
-/*
- * Execute a simple command.
- */
-
-static void
-evalcommand(union node *cmd, int flags)
-{
- struct stackmark smark;
- union node *argp;
- struct arglist arglist;
- struct arglist varlist;
- char **argv;
- int argc;
- const struct strlist *sp;
- struct cmdentry cmdentry;
- struct job *jp;
- char *lastarg;
- const char *path;
- int spclbltin;
- int cmd_is_exec;
- int status;
- char **nargv;
- struct builtincmd *bcmd;
- int pseudovarflag = 0;
-
- /* First expand the arguments. */
- TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
- setstackmark(&smark);
- back_exitstatus = 0;
-
- cmdentry.cmdtype = CMDBUILTIN;
- cmdentry.u.cmd = &bltin;
- varlist.lastp = &varlist.list;
- *varlist.lastp = NULL;
- arglist.lastp = &arglist.list;
- *arglist.lastp = NULL;
-
- argc = 0;
- if (cmd->ncmd.args)
- {
- bcmd = find_builtin(cmd->ncmd.args->narg.text);
- pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
- }
-
- for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
- struct strlist **spp;
-
- spp = arglist.lastp;
- if (pseudovarflag && isassignment(argp->narg.text))
- expandarg(argp, &arglist, EXP_VARTILDE);
- else
- expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
-
- for (sp = *spp; sp; sp = sp->next)
- argc++;
- }
-
- argv = nargv = stalloc(sizeof (char *) * (argc + 1));
- for (sp = arglist.list ; sp ; sp = sp->next) {
- TRACE(("evalcommand arg: %s\n", sp->text));
- *nargv++ = sp->text;
- }
- *nargv = NULL;
-
- lastarg = NULL;
- if (iflag && funcnest == 0 && argc > 0)
- lastarg = nargv[-1];
-
- preverrout_fd = 2;
- expredir(cmd->ncmd.redirect);
- status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH|REDIR_SAVEFD2);
-
- path = vpath.text;
- for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
- struct strlist **spp;
- char *p;
-
- spp = varlist.lastp;
- expandarg(argp, &varlist, EXP_VARTILDE);
-
- /*
- * Modify the command lookup path, if a PATH= assignment
- * is present
- */
- p = (*spp)->text;
- if (varequal(p, path))
- path = p;
- }
-
- /* Print the command if xflag is set. */
- if (xflag) {
- int n;
- const char *p = " %s";
-
- p++;
- dprintf(preverrout_fd, p, expandstr(ps4val()));
-
- sp = varlist.list;
- for(n = 0; n < 2; n++) {
- while (sp) {
- dprintf(preverrout_fd, p, sp->text);
- sp = sp->next;
- if(*p == '%') {
- p--;
- }
- }
- sp = arglist.list;
- }
- full_write(preverrout_fd, "\n", 1);
- }
-
- cmd_is_exec = 0;
- spclbltin = -1;
-
- /* Now locate the command. */
- if (argc) {
- const char *oldpath;
- int cmd_flag = DO_ERR;
-
- path += 5;
- oldpath = path;
- for (;;) {
- find_command(argv[0], &cmdentry, cmd_flag, path);
- if (cmdentry.cmdtype == CMDUNKNOWN) {
- status = 127;
- flusherr();
- goto bail;
- }
-
- /* implement bltin and command here */
- if (cmdentry.cmdtype != CMDBUILTIN)
- break;
- if (spclbltin < 0)
- spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
- if (cmdentry.u.cmd == EXECCMD)
- cmd_is_exec++;
-#ifdef CONFIG_ASH_CMDCMD
- if (cmdentry.u.cmd == COMMANDCMD) {
-
- path = oldpath;
- nargv = parse_command_args(argv, &path);
- if (!nargv)
- break;
- argc -= nargv - argv;
- argv = nargv;
- cmd_flag |= DO_NOFUNC;
- } else
-#endif
- break;
- }
- }
-
- if (status) {
- /* We have a redirection error. */
- if (spclbltin > 0)
- exraise(EXERROR);
-bail:
- exitstatus = status;
- goto out;
- }
-
- /* Execute the command. */
- switch (cmdentry.cmdtype) {
- default:
- /* Fork off a child process if necessary. */
- if (!(flags & EV_EXIT) || trap[0]) {
- INTOFF;
- jp = makejob(cmd, 1);
- if (forkshell(jp, cmd, FORK_FG) != 0) {
- exitstatus = waitforjob(jp);
- INTON;
- break;
- }
- FORCEINTON;
- }
- listsetvar(varlist.list, VEXPORT|VSTACK);
- shellexec(argv, path, cmdentry.u.index);
- /* NOTREACHED */
-
- case CMDBUILTIN:
- cmdenviron = varlist.list;
- if (cmdenviron) {
- struct strlist *list = cmdenviron;
- int i = VNOSET;
- if (spclbltin > 0 || argc == 0) {
- i = 0;
- if (cmd_is_exec && argc > 1)
- i = VEXPORT;
- }
- listsetvar(list, i);
- }
- if (evalbltin(cmdentry.u.cmd, argc, argv)) {
- int exit_status;
- int i, j;
-
- i = exception;
- if (i == EXEXIT)
- goto raise;
-
- exit_status = 2;
- j = 0;
- if (i == EXINT)
- j = SIGINT;
- if (i == EXSIG)
- j = pendingsigs;
- if (j)
- exit_status = j + 128;
- exitstatus = exit_status;
-
- if (i == EXINT || spclbltin > 0) {
-raise:
- longjmp(handler->loc, 1);
- }
- FORCEINTON;
- }
- break;
-
- case CMDFUNCTION:
- listsetvar(varlist.list, 0);
- if (evalfun(cmdentry.u.func, argc, argv, flags))
- goto raise;
- break;
- }
-
-out:
- popredir(cmd_is_exec);
- 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);
-}
-
-static int
-evalbltin(const struct builtincmd *cmd, int argc, char **argv) {
- char *volatile savecmdname;
- struct jmploc *volatile savehandler;
- struct jmploc jmploc;
- int i;
-
- savecmdname = commandname;
- if ((i = setjmp(jmploc.loc)))
- goto cmddone;
- savehandler = handler;
- handler = &jmploc;
- commandname = argv[0];
- argptr = argv + 1;
- optptr = NULL; /* initialize nextopt */
- exitstatus = (*cmd->builtin)(argc, argv);
- flushall();
-cmddone:
- exitstatus |= ferror(stdout);
- clearerr(stdout);
- commandname = savecmdname;
- exsig = 0;
- handler = savehandler;
-
- return i;
-}
-
-static int
-evalfun(struct funcnode *func, int argc, char **argv, int flags)
-{
- volatile struct shparam saveparam;
- struct localvar *volatile savelocalvars;
- struct jmploc *volatile savehandler;
- struct jmploc jmploc;
- int e;
-
- saveparam = shellparam;
- savelocalvars = localvars;
- if ((e = setjmp(jmploc.loc))) {
- goto funcdone;
- }
- INTOFF;
- savehandler = handler;
- handler = &jmploc;
- localvars = NULL;
- shellparam.malloc = 0;
- func->count++;
- funcnest++;
- INTON;
- shellparam.nparam = argc - 1;
- shellparam.p = argv + 1;
-#ifdef CONFIG_ASH_GETOPTS
- shellparam.optind = 1;
- shellparam.optoff = -1;
-#endif
- evaltree(&func->n, flags & EV_TESTED);
-funcdone:
- INTOFF;
- funcnest--;
- freefunc(func);
- poplocalvars();
- localvars = savelocalvars;
- freeparam(&shellparam);
- shellparam = saveparam;
- handler = savehandler;
- INTON;
- evalskip &= ~SKIPFUNC;
- return e;
-}
-
-
-static int goodname(const char *p)
-{
- return !*endofname(p);
-}
-
-/*
- * 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 void
-prehash(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.
- */
-
-static int
-bltincmd(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.
- */
-
-static int
-breakcmd(int argc, char **argv)
-{
- int n = argc > 1 ? number(argv[1]) : 1;
-
- if (n <= 0)
- sh_error(illnum, argv[1]);
- if (n > loopnest)
- n = loopnest;
- if (n > 0) {
- evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
- skipcount = n;
- }
- return 0;
-}
-
-
-/*
- * The return command.
- */
-
-static int
-returncmd(int argc, char **argv)
-{
- /*
- * If called outside a function, do what ksh does;
- * skip the rest of the file.
- */
- evalskip = funcnest ? SKIPFUNC : SKIPFILE;
- return argv[1] ? number(argv[1]) : exitstatus;
-}
-
-
-static int
-falsecmd(int argc, char **argv)
-{
- return 1;
-}
-
-
-static int
-truecmd(int argc, char **argv)
-{
- return 0;
-}
-
-
-static int
-execcmd(int argc, char **argv)
-{
- if (argc > 1) {
- iflag = 0; /* exit on error */
- mflag = 0;
- optschanged();
- shellexec(argv + 1, pathval(), 0);
- }
- return 0;
-}
-
-
-/* exec.c */
-
-/*
- * When commands are first encountered, they are entered in a hash table.
- * This ensures that a full path search will not have to be done for them
- * on each invocation.
- *
- * We should investigate converting to a linear search, even though that
- * would make the command name "hash" a misnomer.
- */
-
-#define CMDTABLESIZE 31 /* should be prime */
-#define ARB 1 /* actual size determined at run time */
-
-
-
-struct tblentry {
- struct tblentry *next; /* next entry in hash chain */
- union param param; /* definition of builtin function */
- short cmdtype; /* index identifying command */
- char rehash; /* if set, cd done since entry created */
- char cmdname[ARB]; /* name of command */
-};
-
-
-static struct tblentry *cmdtable[CMDTABLESIZE];
-static int builtinloc = -1; /* index in path of %builtin, or -1 */
-
-
-static void tryexec(char *, char **, char **);
-static void clearcmdentry(int);
-static struct tblentry *cmdlookup(const char *, int);
-static void delete_cmd_entry(void);
-
-
-/*
- * Exec a program. Never returns. If you change this routine, you may
- * have to change the find_command routine as well.
- */
-
-static void
-shellexec(char **argv, const char *path, int idx)
-{
- char *cmdname;
- int e;
- char **envp;
- int exerrno;
-
- clearredir(1);
- envp = environment();
- if (strchr(argv[0], '/') || is_safe_applet(argv[0])
-#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
- || find_applet_by_name(argv[0])
-#endif
- ) {
- tryexec(argv[0], argv, envp);
- e = errno;
- } else {
- e = ENOENT;
- while ((cmdname = padvance(&path, argv[0])) != NULL) {
- if (--idx < 0 && pathopt == NULL) {
- tryexec(cmdname, argv, envp);
- if (errno != ENOENT && errno != ENOTDIR)
- e = errno;
- }
- stunalloc(cmdname);
- }
- }
-
- /* Map to POSIX errors */
- switch (e) {
- case EACCES:
- exerrno = 126;
- break;
- case ENOENT:
- exerrno = 127;
- break;
- default:
- exerrno = 2;
- break;
- }
- exitstatus = exerrno;
- TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
- argv[0], e, suppressint ));
- exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
- /* NOTREACHED */
-}
-
-
-static void
-tryexec(char *cmd, char **argv, char **envp)
-{
- int repeated = 0;
- struct BB_applet *a;
- int argc = 0;
- char **c;
-
- if (strchr(cmd, '/') == NULL
- && (a = find_applet_by_name(cmd)) != NULL
- && is_safe_applet(cmd)
- ) {
- c = argv;
- while (*c != NULL) {
- c++; argc++;
- }
- applet_name = cmd;
- exit(a->main(argc, argv));
- }
-#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
- if (find_applet_by_name(cmd) != NULL) {
- /* re-exec ourselves with the new arguments */
- execve(CONFIG_BUSYBOX_EXEC_PATH,argv,envp);
- /* If they called chroot or otherwise made the binary no longer
- * executable, fall through */
- }
-#endif
-
-repeat:
-#ifdef SYSV
- do {
- execve(cmd, argv, envp);
- } while (errno == EINTR);
-#else
- execve(cmd, argv, envp);
-#endif
- if (repeated++) {
- ckfree(argv);
- } else if (errno == ENOEXEC) {
- char **ap;
- char **new;
-
- for (ap = argv; *ap; ap++)
- ;
- ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
- ap[1] = cmd;
- *ap = cmd = (char *)DEFAULT_SHELL;
- ap += 2;
- argv++;
- while ((*ap++ = *argv++))
- ;
- argv = new;
- goto repeat;
- }
-}
-
-
-
-/*
- * Do a path search. The variable path (passed by reference) should be
- * set to the start of the path before the first call; padvance will update
- * this value as it proceeds. Successive calls to padvance will return
- * the possible path expansions in sequence. If an option (indicated by
- * a percent sign) appears in the path entry then the global variable
- * pathopt will be set to point to it; otherwise pathopt will be set to
- * NULL.
- */
-
-static char *
-padvance(const char **path, const char *name)
-{
- const char *p;
- char *q;
- const char *start;
- size_t len;
-
- if (*path == NULL)
- return NULL;
- start = *path;
- for (p = start ; *p && *p != ':' && *p != '%' ; p++);
- len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
- while (stackblocksize() < len)
- growstackblock();
- q = stackblock();
- if (p != start) {
- memcpy(q, start, p - start);
- q += p - start;
- *q++ = '/';
- }
- strcpy(q, name);
- pathopt = NULL;
- if (*p == '%') {
- pathopt = ++p;
- while (*p && *p != ':') p++;
- }
- if (*p == ':')
- *path = p + 1;
- else
- *path = NULL;
- return stalloc(len);
-}
-
-
-/*** Command hashing code ***/
-
-static void
-printentry(struct tblentry *cmdp)
-{
- int idx;
- const char *path;
- char *name;
-
- idx = cmdp->param.index;
- path = pathval();
- do {
- name = padvance(&path, cmdp->cmdname);
- stunalloc(name);
- } while (--idx >= 0);
- out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
-}
-
-
-static int
-hashcmd(int argc, char **argv)
-{
- struct tblentry **pp;
- struct tblentry *cmdp;
- int c;
- struct cmdentry entry;
- char *name;
-
- while ((c = nextopt("r")) != '\0') {
- clearcmdentry(0);
- return 0;
- }
- if (*argptr == NULL) {
- for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
- for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
- if (cmdp->cmdtype == CMDNORMAL)
- printentry(cmdp);
- }
- }
- return 0;
- }
- c = 0;
- while ((name = *argptr) != NULL) {
- if ((cmdp = cmdlookup(name, 0)) != NULL
- && (cmdp->cmdtype == CMDNORMAL
- || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
- delete_cmd_entry();
- find_command(name, &entry, DO_ERR, pathval());
- if (entry.cmdtype == CMDUNKNOWN)
- c = 1;
- argptr++;
- }
- return c;
-}
-
-
-/*
- * Resolve a command name. If you change this routine, you may have to
- * change the shellexec routine as well.
- */
-
-static void
-find_command(char *name, struct cmdentry *entry, int act, const char *path)
-{
- struct tblentry *cmdp;
- int idx;
- int prev;
- char *fullname;
- struct stat statb;
- int e;
- int updatetbl;
- struct builtincmd *bcmd;
-
- /* If name contains a slash, don't use PATH or hash table */
- if (strchr(name, '/') != NULL) {
- entry->u.index = -1;
- if (act & DO_ABS) {
- while (stat(name, &statb) < 0) {
-#ifdef SYSV
- if (errno == EINTR)
- continue;
-#endif
- entry->cmdtype = CMDUNKNOWN;
- return;
- }
- }
- entry->cmdtype = CMDNORMAL;
- return;
- }
-
-#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
- if (find_applet_by_name(name)) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = -1;
- return;
- }
-#endif
-
- if (is_safe_applet(name)) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = -1;
- return;
- }
-
- updatetbl = (path == pathval());
- if (!updatetbl) {
- act |= DO_ALTPATH;
- if (strstr(path, "%builtin") != NULL)
- act |= DO_ALTBLTIN;
- }
-
- /* If name is in the table, check answer will be ok */
- if ((cmdp = cmdlookup(name, 0)) != NULL) {
- int bit;
-
- switch (cmdp->cmdtype) {
- default:
-#if DEBUG
- abort();
-#endif
- case CMDNORMAL:
- bit = DO_ALTPATH;
- break;
- case CMDFUNCTION:
- bit = DO_NOFUNC;
- break;
- case CMDBUILTIN:
- bit = DO_ALTBLTIN;
- break;
- }
- if (act & bit) {
- updatetbl = 0;
- cmdp = NULL;
- } else if (cmdp->rehash == 0)
- /* if not invalidated by cd, we're done */
- goto success;
- }
-
- /* If %builtin not in path, check for builtin next */
- bcmd = find_builtin(name);
- if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
- act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
- )))
- goto builtin_success;
-
- /* We have to search path. */
- prev = -1; /* where to start */
- if (cmdp && cmdp->rehash) { /* doing a rehash */
- if (cmdp->cmdtype == CMDBUILTIN)
- prev = builtinloc;
- else
- prev = cmdp->param.index;
- }
-
- e = ENOENT;
- idx = -1;
-loop:
- while ((fullname = padvance(&path, name)) != NULL) {
- stunalloc(fullname);
- idx++;
- if (pathopt) {
- if (prefix(pathopt, "builtin")) {
- if (bcmd)
- goto builtin_success;
- continue;
- } else if (!(act & DO_NOFUNC) &&
- prefix(pathopt, "func")) {
- /* handled below */
- } else {
- /* ignore unimplemented options */
- continue;
- }
- }
- /* if rehash, don't redo absolute path names */
- if (fullname[0] == '/' && idx <= prev) {
- if (idx < prev)
- continue;
- TRACE(("searchexec \"%s\": no change\n", name));
- goto success;
- }
- while (stat(fullname, &statb) < 0) {
-#ifdef SYSV
- if (errno == EINTR)
- continue;
-#endif
- if (errno != ENOENT && errno != ENOTDIR)
- e = errno;
- goto loop;
- }
- e = EACCES; /* if we fail, this will be the error */
- if (!S_ISREG(statb.st_mode))
- continue;
- if (pathopt) { /* this is a %func directory */
- stalloc(strlen(fullname) + 1);
- readcmdfile(fullname);
- if ((cmdp = cmdlookup(name, 0)) == NULL ||
- cmdp->cmdtype != CMDFUNCTION)
- sh_error("%s not defined in %s", name, fullname);
- stunalloc(fullname);
- goto success;
- }
- TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
- if (!updatetbl) {
- entry->cmdtype = CMDNORMAL;
- entry->u.index = idx;
- return;
- }
- INTOFF;
- cmdp = cmdlookup(name, 1);
- cmdp->cmdtype = CMDNORMAL;
- cmdp->param.index = idx;
- INTON;
- goto success;
- }
-
- /* We failed. If there was an entry for this command, delete it */
- if (cmdp && updatetbl)
- delete_cmd_entry();
- if (act & DO_ERR)
- sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
- entry->cmdtype = CMDUNKNOWN;
- return;
-
-builtin_success:
- if (!updatetbl) {
- entry->cmdtype = CMDBUILTIN;
- entry->u.cmd = bcmd;
- return;
- }
- INTOFF;
- cmdp = cmdlookup(name, 1);
- cmdp->cmdtype = CMDBUILTIN;
- cmdp->param.cmd = bcmd;
- INTON;
-success:
- cmdp->rehash = 0;
- entry->cmdtype = cmdp->cmdtype;
- entry->u = cmdp->param;
-}
-
-
-/*
- * Wrapper around strcmp for qsort/bsearch/...
- */
-static int pstrcmp(const void *a, const void *b)
-{
- return strcmp((const char *) a, (*(const char *const *) b) + 1);
-}
-
-/*
- * Search the table of builtin commands.
- */
-
-static struct builtincmd *
-find_builtin(const char *name)
-{
- struct builtincmd *bp;
-
- bp = bsearch(
- name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
- pstrcmp
- );
- return bp;
-}
-
-
-
-/*
- * Called when a cd is done. Marks all commands so the next time they
- * are executed they will be rehashed.
- */
-
-static void
-hashcd(void)
-{
- struct tblentry **pp;
- struct tblentry *cmdp;
-
- for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
- for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
- if (cmdp->cmdtype == CMDNORMAL || (
- cmdp->cmdtype == CMDBUILTIN &&
- !(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
- builtinloc > 0
- ))
- cmdp->rehash = 1;
- }
- }
-}
-
-
-
-/*
- * Fix command hash table when PATH changed.
- * Called before PATH is changed. The argument is the new value of PATH;
- * pathval() still returns the old value at this point.
- * Called with interrupts off.
- */
-
-static void
-changepath(const char *newval)
-{
- const char *old, *new;
- int idx;
- int firstchange;
- int idx_bltin;
-
- old = pathval();
- new = newval;
- firstchange = 9999; /* assume no change */
- idx = 0;
- idx_bltin = -1;
- for (;;) {
- if (*old != *new) {
- firstchange = idx;
- if ((*old == '\0' && *new == ':')
- || (*old == ':' && *new == '\0'))
- firstchange++;
- old = new; /* ignore subsequent differences */
- }
- if (*new == '\0')
- break;
- if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
- idx_bltin = idx;
- if (*new == ':') {
- idx++;
- }
- new++, old++;
- }
- if (builtinloc < 0 && idx_bltin >= 0)
- builtinloc = idx_bltin; /* zap builtins */
- if (builtinloc >= 0 && idx_bltin < 0)
- firstchange = 0;
- clearcmdentry(firstchange);
- builtinloc = idx_bltin;
-}
-
-
-/*
- * Clear out command entries. The argument specifies the first entry in
- * PATH which has changed.
- */
-
-static void
-clearcmdentry(int firstchange)
-{
- struct tblentry **tblp;
- struct tblentry **pp;
- struct tblentry *cmdp;
-
- INTOFF;
- for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
- pp = tblp;
- while ((cmdp = *pp) != NULL) {
- if ((cmdp->cmdtype == CMDNORMAL &&
- cmdp->param.index >= firstchange)
- || (cmdp->cmdtype == CMDBUILTIN &&
- builtinloc >= firstchange)) {
- *pp = cmdp->next;
- ckfree(cmdp);
- } else {
- pp = &cmdp->next;
- }
- }
- }
- INTON;
-}
-
-
-
-/*
- * Locate a command in the command hash table. If "add" is nonzero,
- * add the command to the table if it is not already present. The
- * variable "lastcmdentry" is set to point to the address of the link
- * pointing to the entry, so that delete_cmd_entry can delete the
- * entry.
- *
- * Interrupts must be off if called with add != 0.
- */
-
-static struct tblentry **lastcmdentry;
-
-
-static struct tblentry *
-cmdlookup(const char *name, int add)
-{
- unsigned int hashval;
- const char *p;
- struct tblentry *cmdp;
- struct tblentry **pp;
-
- p = name;
- hashval = (unsigned char)*p << 4;
- while (*p)
- hashval += (unsigned char)*p++;
- hashval &= 0x7FFF;
- pp = &cmdtable[hashval % CMDTABLESIZE];
- for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
- if (equal(cmdp->cmdname, name))
- break;
- pp = &cmdp->next;
- }
- if (add && cmdp == NULL) {
- cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
- + strlen(name) + 1);
- cmdp->next = NULL;
- cmdp->cmdtype = CMDUNKNOWN;
- strcpy(cmdp->cmdname, name);
- }
- lastcmdentry = pp;
- return cmdp;
-}
-
-/*
- * Delete the command entry returned on the last lookup.
- */
-
-static void
-delete_cmd_entry(void)
-{
- struct tblentry *cmdp;
-
- INTOFF;
- cmdp = *lastcmdentry;
- *lastcmdentry = cmdp->next;
- if (cmdp->cmdtype == CMDFUNCTION)
- freefunc(cmdp->param.func);
- ckfree(cmdp);
- INTON;
-}
-
-
-/*
- * Add a new command entry, replacing any existing command entry for
- * the same name - except special builtins.
- */
-
-static void addcmdentry(char *name, struct cmdentry *entry)
-{
- struct tblentry *cmdp;
-
- cmdp = cmdlookup(name, 1);
- if (cmdp->cmdtype == CMDFUNCTION) {
- freefunc(cmdp->param.func);
- }
- cmdp->cmdtype = entry->cmdtype;
- cmdp->param = entry->u;
- cmdp->rehash = 0;
-}
-
-/*
- * Make a copy of a parse tree.
- */
-
-static struct funcnode * copyfunc(union node *n)
-{
- struct funcnode *f;
- size_t blocksize;
-
- funcblocksize = offsetof(struct funcnode, n);
- funcstringsize = 0;
- calcsize(n);
- blocksize = funcblocksize;
- f = ckmalloc(blocksize + funcstringsize);
- funcblock = (char *) f + offsetof(struct funcnode, n);
- funcstring = (char *) f + blocksize;
- copynode(n);
- f->count = 0;
- return f;
-}
-
-/*
- * Define a shell function.
- */
-
-static void
-defun(char *name, union node *func)
-{
- struct cmdentry entry;
-
- INTOFF;
- entry.cmdtype = CMDFUNCTION;
- entry.u.func = copyfunc(func);
- addcmdentry(name, &entry);
- INTON;
-}
-
-
-/*
- * Delete a function if it exists.
- */
-
-static void
-unsetfunc(const char *name)
-{
- struct tblentry *cmdp;
-
- if ((cmdp = cmdlookup(name, 0)) != NULL &&
- cmdp->cmdtype == CMDFUNCTION)
- delete_cmd_entry();
-}
-
-/*
- * Locate and print what a word is...
- */
-
-
-#ifdef CONFIG_ASH_CMDCMD
-static int
-describe_command(char *command, int describe_command_verbose)
-#else
-#define describe_command_verbose 1
-static int
-describe_command(char *command)
-#endif
-{
- struct cmdentry entry;
- struct tblentry *cmdp;
-#ifdef CONFIG_ASH_ALIAS
- const struct alias *ap;
-#endif
- const char *path = pathval();
-
- if (describe_command_verbose) {
- out1str(command);
- }
-
- /* First look at the keywords */
- if (findkwd(command)) {
- out1str(describe_command_verbose ? " is a shell keyword" : command);
- goto out;
- }
-
-#ifdef CONFIG_ASH_ALIAS
- /* Then look at the aliases */
- if ((ap = lookupalias(command, 0)) != NULL) {
- if (describe_command_verbose) {
- out1fmt(" is an alias for %s", ap->val);
- } else {
- out1str("alias ");
- printalias(ap);
- return 0;
- }
- goto out;
- }
-#endif
- /* Then check if it is a tracked alias */
- if ((cmdp = cmdlookup(command, 0)) != NULL) {
- entry.cmdtype = cmdp->cmdtype;
- entry.u = cmdp->param;
- } else {
- /* Finally use brute force */
- find_command(command, &entry, DO_ABS, path);
- }
-
- switch (entry.cmdtype) {
- case CMDNORMAL: {
- int j = entry.u.index;
- char *p;
- if (j == -1) {
- p = command;
- } else {
- do {
- p = padvance(&path, command);
- stunalloc(p);
- } while (--j >= 0);
- }
- if (describe_command_verbose) {
- out1fmt(" is%s %s",
- (cmdp ? " a tracked alias for" : nullstr), p
- );
- } else {
- out1str(p);
- }
- break;
- }
-
- case CMDFUNCTION:
- if (describe_command_verbose) {
- out1str(" is a shell function");
- } else {
- out1str(command);
- }
- break;
-
- case CMDBUILTIN:
- if (describe_command_verbose) {
- out1fmt(" is a %sshell builtin",
- IS_BUILTIN_SPECIAL(entry.u.cmd) ?
- "special " : nullstr
- );
- } else {
- out1str(command);
- }
- break;
-
- default:
- if (describe_command_verbose) {
- out1str(": not found\n");
- }
- return 127;
- }
-
-out:
- outstr("\n", stdout);
- return 0;
-}
-
-static int
-typecmd(int argc, char **argv)
-{
- int i;
- int err = 0;
-
- for (i = 1; i < argc; i++) {
-#ifdef CONFIG_ASH_CMDCMD
- err |= describe_command(argv[i], 1);
-#else
- err |= describe_command(argv[i]);
-#endif
- }
- return err;
-}
-
-#ifdef CONFIG_ASH_CMDCMD
-static int
-commandcmd(int argc, char **argv)
-{
- int c;
- enum {
- VERIFY_BRIEF = 1,
- VERIFY_VERBOSE = 2,
- } verify = 0;
-
- while ((c = nextopt("pvV")) != '\0')
- if (c == 'V')
- verify |= VERIFY_VERBOSE;
- else if (c == 'v')
- verify |= VERIFY_BRIEF;
-#if DEBUG
- else if (c != 'p')
- abort();
-#endif
- if (verify)
- return describe_command(*argptr, verify - VERIFY_BRIEF);
-
- return 0;
-}
-#endif
-
-/* expand.c */
-
-/*
- * Routines to expand arguments to commands. We have to deal with
- * backquotes, shell variables, and file metacharacters.
- */
-
-/*
- * _rmescape() flags
- */
-#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
-#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
-#define RMESCAPE_QUOTED 0x4 /* Remove CTLESC unless in quotes */
-#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
-#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
-
-/*
- * Structure specifying which parts of the string should be searched
- * for IFS characters.
- */
-
-struct ifsregion {
- struct ifsregion *next; /* next region in list */
- int begoff; /* offset of start of region */
- int endoff; /* offset of end of region */
- int nulonly; /* search for nul bytes only */
-};
-
-/* output of current string */
-static char *expdest;
-/* list of back quote expressions */
-static struct nodelist *argbackq;
-/* first struct in list of ifs regions */
-static struct ifsregion ifsfirst;
-/* last struct in list */
-static struct ifsregion *ifslastp;
-/* holds expanded arg list */
-static struct arglist exparg;
-
-static void argstr(char *, int);
-static char *exptilde(char *, char *, int);
-static void expbackq(union node *, int, int);
-static const char *subevalvar(char *, char *, int, int, int, int, int);
-static char *evalvar(char *, int);
-static void strtodest(const char *, int, int);
-static void memtodest(const char *p, size_t len, int syntax, int quotes);
-static ssize_t varvalue(char *, int, int);
-static void recordregion(int, int, int);
-static void removerecordregions(int);
-static void ifsbreakup(char *, struct arglist *);
-static void ifsfree(void);
-static void expandmeta(struct strlist *, int);
-static int patmatch(char *, const char *);
-
-static int cvtnum(arith_t);
-static size_t esclen(const char *, const char *);
-static char *scanleft(char *, char *, char *, char *, int, int);
-static char *scanright(char *, char *, char *, char *, int, int);
-static void varunset(const char *, const char *, const char *, int)
- ATTRIBUTE_NORETURN;
-
-
-#define pmatch(a, b) !fnmatch((a), (b), 0)
-/*
- * Prepare a pattern for a expmeta (internal glob(3)) call.
- *
- * Returns an stalloced string.
- */
-
-static char * preglob(const char *pattern, int quoted, int flag) {
- flag |= RMESCAPE_GLOB;
- if (quoted) {
- flag |= RMESCAPE_QUOTED;
- }
- return _rmescapes((char *)pattern, flag);
-}
-
-
-static size_t
-esclen(const char *start, const char *p) {
- size_t esc = 0;
-
- while (p > start && *--p == CTLESC) {
- esc++;
- }
- return esc;
-}
-
-
-/*
- * Expand shell variables and backquotes inside a here document.
- */
-
-static void expandhere(union node *arg, int fd)
-{
- herefd = fd;
- expandarg(arg, (struct arglist *)NULL, 0);
- full_write(fd, stackblock(), expdest - (char *)stackblock());
-}
-
-
-/*
- * Perform variable substitution and command substitution on an argument,
- * placing the resulting list of arguments in arglist. If EXP_FULL is true,
- * perform splitting and file name expansion. When arglist is NULL, perform
- * here document expansion.
- */
-
-void
-expandarg(union node *arg, struct arglist *arglist, int flag)
-{
- struct strlist *sp;
- char *p;
-
- argbackq = arg->narg.backquote;
- STARTSTACKSTR(expdest);
- ifsfirst.next = NULL;
- ifslastp = NULL;
- argstr(arg->narg.text, flag);
- p = _STPUTC('\0', expdest);
- expdest = p - 1;
- if (arglist == NULL) {
- return; /* here document expanded */
- }
- p = grabstackstr(p);
- exparg.lastp = &exparg.list;
- /*
- * TODO - EXP_REDIR
- */
- if (flag & EXP_FULL) {
- ifsbreakup(p, &exparg);
- *exparg.lastp = NULL;
- exparg.lastp = &exparg.list;
- expandmeta(exparg.list, flag);
- } else {
- if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
- rmescapes(p);
- sp = (struct strlist *)stalloc(sizeof (struct strlist));
- sp->text = p;
- *exparg.lastp = sp;
- exparg.lastp = &sp->next;
- }
- if (ifsfirst.next)
- ifsfree();
- *exparg.lastp = NULL;
- if (exparg.list) {
- *arglist->lastp = exparg.list;
- arglist->lastp = exparg.lastp;
- }
-}
-
-
-/*
- * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
- * characters to allow for further processing. Otherwise treat
- * $@ like $* since no splitting will be performed.
- */
-
-static void
-argstr(char *p, int flag)
-{
- static const char spclchars[] = {
- '=',
- ':',
- CTLQUOTEMARK,
- CTLENDVAR,
- CTLESC,
- CTLVAR,
- CTLBACKQ,
- CTLBACKQ | CTLQUOTE,
-#ifdef CONFIG_ASH_MATH_SUPPORT
- CTLENDARI,
-#endif
- 0
- };
- const char *reject = spclchars;
- int c;
- int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
- int breakall = flag & EXP_WORD;
- int inquotes;
- size_t length;
- int startloc;
-
- if (!(flag & EXP_VARTILDE)) {
- reject += 2;
- } else if (flag & EXP_VARTILDE2) {
- reject++;
- }
- inquotes = 0;
- length = 0;
- if (flag & EXP_TILDE) {
- char *q;
-
- flag &= ~EXP_TILDE;
-tilde:
- q = p;
- if (*q == CTLESC && (flag & EXP_QWORD))
- q++;
- if (*q == '~')
- p = exptilde(p, q, flag);
- }
-start:
- startloc = expdest - (char *)stackblock();
- for (;;) {
- length += strcspn(p + length, reject);
- c = p[length];
- if (c && (!(c & 0x80)
-#ifdef CONFIG_ASH_MATH_SUPPORT
- || c == CTLENDARI
-#endif
- )) {
- /* c == '=' || c == ':' || c == CTLENDARI */
- length++;
- }
- if (length > 0) {
- int newloc;
- expdest = stnputs(p, length, expdest);
- newloc = expdest - (char *)stackblock();
- if (breakall && !inquotes && newloc > startloc) {
- recordregion(startloc, newloc, 0);
- }
- startloc = newloc;
- }
- p += length + 1;
- length = 0;
-
- switch (c) {
- case '\0':
- goto breakloop;
- case '=':
- if (flag & EXP_VARTILDE2) {
- p--;
- continue;
- }
- flag |= EXP_VARTILDE2;
- reject++;
- /* fall through */
- case ':':
- /*
- * sort of a hack - expand tildes in variable
- * assignments (after the first '=' and after ':'s).
- */
- if (*--p == '~') {
- goto tilde;
- }
- continue;
- }
-
- switch (c) {
- case CTLENDVAR: /* ??? */
- goto breakloop;
- case CTLQUOTEMARK:
- /* "$@" syntax adherence hack */
- if (
- !inquotes &&
- !memcmp(p, dolatstr, DOLATSTRLEN) &&
- (p[4] == CTLQUOTEMARK || (
- p[4] == CTLENDVAR &&
- p[5] == CTLQUOTEMARK
- ))
- ) {
- p = evalvar(p + 1, flag) + 1;
- goto start;
- }
- inquotes = !inquotes;
-addquote:
- if (quotes) {
- p--;
- length++;
- startloc++;
- }
- break;
- case CTLESC:
- startloc++;
- length++;
- goto addquote;
- case CTLVAR:
- p = evalvar(p, flag);
- goto start;
- case CTLBACKQ:
- c = 0;
- case CTLBACKQ|CTLQUOTE:
- expbackq(argbackq->n, c, quotes);
- argbackq = argbackq->next;
- goto start;
-#ifdef CONFIG_ASH_MATH_SUPPORT
- case CTLENDARI:
- p--;
- expari(quotes);
- goto start;
-#endif
- }
- }
-breakloop:
- ;
-}
-
-static char *
-exptilde(char *startp, char *p, int flag)
-{
- char c;
- char *name;
- struct passwd *pw;
- const char *home;
- int quotes = flag & (EXP_FULL | EXP_CASE);
- int startloc;
-
- name = p + 1;
-
- while ((c = *++p) != '\0') {
- switch (c) {
- case CTLESC:
- return startp;
- case CTLQUOTEMARK:
- return startp;
- case ':':
- if (flag & EXP_VARTILDE)
- goto done;
- break;
- case '/':
- case CTLENDVAR:
- goto done;
- }
- }
-done:
- *p = '\0';
- if (*name == '\0') {
- home = lookupvar(homestr);
- } else {
- if ((pw = getpwnam(name)) == NULL)
- goto lose;
- home = pw->pw_dir;
- }
- if (!home || !*home)
- goto lose;
- *p = c;
- startloc = expdest - (char *)stackblock();
- strtodest(home, SQSYNTAX, quotes);
- recordregion(startloc, expdest - (char *)stackblock(), 0);
- return p;
-lose:
- *p = c;
- return startp;
-}
+ /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
+ /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL,
+ /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
+ /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
+ /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
+ /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
+ /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL,
+ /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
+ /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
+ /* 11 -119 */ CWORD_CWORD_CWORD_CWORD,
+ /* 12 -118 */ CWORD_CWORD_CWORD_CWORD,
+ /* 13 -117 */ CWORD_CWORD_CWORD_CWORD,
+ /* 14 -116 */ CWORD_CWORD_CWORD_CWORD,
+ /* 15 -115 */ CWORD_CWORD_CWORD_CWORD,
+ /* 16 -114 */ CWORD_CWORD_CWORD_CWORD,
+ /* 17 -113 */ CWORD_CWORD_CWORD_CWORD,
+ /* 18 -112 */ CWORD_CWORD_CWORD_CWORD,
+ /* 19 -111 */ CWORD_CWORD_CWORD_CWORD,
+ /* 20 -110 */ CWORD_CWORD_CWORD_CWORD,
+ /* 21 -109 */ CWORD_CWORD_CWORD_CWORD,
+ /* 22 -108 */ CWORD_CWORD_CWORD_CWORD,
+ /* 23 -107 */ CWORD_CWORD_CWORD_CWORD,
+ /* 24 -106 */ CWORD_CWORD_CWORD_CWORD,
+ /* 25 -105 */ CWORD_CWORD_CWORD_CWORD,
+ /* 26 -104 */ CWORD_CWORD_CWORD_CWORD,
+ /* 27 -103 */ CWORD_CWORD_CWORD_CWORD,
+ /* 28 -102 */ CWORD_CWORD_CWORD_CWORD,
+ /* 29 -101 */ CWORD_CWORD_CWORD_CWORD,
+ /* 30 -100 */ CWORD_CWORD_CWORD_CWORD,
+ /* 31 -99 */ CWORD_CWORD_CWORD_CWORD,
+ /* 32 -98 */ CWORD_CWORD_CWORD_CWORD,
+ /* 33 -97 */ CWORD_CWORD_CWORD_CWORD,
+ /* 34 -96 */ CWORD_CWORD_CWORD_CWORD,
+ /* 35 -95 */ CWORD_CWORD_CWORD_CWORD,
+ /* 36 -94 */ CWORD_CWORD_CWORD_CWORD,
+ /* 37 -93 */ CWORD_CWORD_CWORD_CWORD,
+ /* 38 -92 */ CWORD_CWORD_CWORD_CWORD,
+ /* 39 -91 */ CWORD_CWORD_CWORD_CWORD,
+ /* 40 -90 */ CWORD_CWORD_CWORD_CWORD,
+ /* 41 -89 */ CWORD_CWORD_CWORD_CWORD,
+ /* 42 -88 */ CWORD_CWORD_CWORD_CWORD,
+ /* 43 -87 */ CWORD_CWORD_CWORD_CWORD,
+ /* 44 -86 */ CWORD_CWORD_CWORD_CWORD,
+ /* 45 -85 */ CWORD_CWORD_CWORD_CWORD,
+ /* 46 -84 */ CWORD_CWORD_CWORD_CWORD,
+ /* 47 -83 */ CWORD_CWORD_CWORD_CWORD,
+ /* 48 -82 */ CWORD_CWORD_CWORD_CWORD,
+ /* 49 -81 */ CWORD_CWORD_CWORD_CWORD,
+ /* 50 -80 */ CWORD_CWORD_CWORD_CWORD,
+ /* 51 -79 */ CWORD_CWORD_CWORD_CWORD,
+ /* 52 -78 */ CWORD_CWORD_CWORD_CWORD,
+ /* 53 -77 */ CWORD_CWORD_CWORD_CWORD,
+ /* 54 -76 */ CWORD_CWORD_CWORD_CWORD,
+ /* 55 -75 */ CWORD_CWORD_CWORD_CWORD,
+ /* 56 -74 */ CWORD_CWORD_CWORD_CWORD,
+ /* 57 -73 */ CWORD_CWORD_CWORD_CWORD,
+ /* 58 -72 */ CWORD_CWORD_CWORD_CWORD,
+ /* 59 -71 */ CWORD_CWORD_CWORD_CWORD,
+ /* 60 -70 */ CWORD_CWORD_CWORD_CWORD,
+ /* 61 -69 */ CWORD_CWORD_CWORD_CWORD,
+ /* 62 -68 */ CWORD_CWORD_CWORD_CWORD,
+ /* 63 -67 */ CWORD_CWORD_CWORD_CWORD,
+ /* 64 -66 */ CWORD_CWORD_CWORD_CWORD,
+ /* 65 -65 */ CWORD_CWORD_CWORD_CWORD,
+ /* 66 -64 */ CWORD_CWORD_CWORD_CWORD,
+ /* 67 -63 */ CWORD_CWORD_CWORD_CWORD,
+ /* 68 -62 */ CWORD_CWORD_CWORD_CWORD,
+ /* 69 -61 */ CWORD_CWORD_CWORD_CWORD,
+ /* 70 -60 */ CWORD_CWORD_CWORD_CWORD,
+ /* 71 -59 */ CWORD_CWORD_CWORD_CWORD,
+ /* 72 -58 */ CWORD_CWORD_CWORD_CWORD,
+ /* 73 -57 */ CWORD_CWORD_CWORD_CWORD,
+ /* 74 -56 */ CWORD_CWORD_CWORD_CWORD,
+ /* 75 -55 */ CWORD_CWORD_CWORD_CWORD,
+ /* 76 -54 */ CWORD_CWORD_CWORD_CWORD,
+ /* 77 -53 */ CWORD_CWORD_CWORD_CWORD,
+ /* 78 -52 */ CWORD_CWORD_CWORD_CWORD,
+ /* 79 -51 */ CWORD_CWORD_CWORD_CWORD,
+ /* 80 -50 */ CWORD_CWORD_CWORD_CWORD,
+ /* 81 -49 */ CWORD_CWORD_CWORD_CWORD,
+ /* 82 -48 */ CWORD_CWORD_CWORD_CWORD,
+ /* 83 -47 */ CWORD_CWORD_CWORD_CWORD,
+ /* 84 -46 */ CWORD_CWORD_CWORD_CWORD,
+ /* 85 -45 */ CWORD_CWORD_CWORD_CWORD,
+ /* 86 -44 */ CWORD_CWORD_CWORD_CWORD,
+ /* 87 -43 */ CWORD_CWORD_CWORD_CWORD,
+ /* 88 -42 */ CWORD_CWORD_CWORD_CWORD,
+ /* 89 -41 */ CWORD_CWORD_CWORD_CWORD,
+ /* 90 -40 */ CWORD_CWORD_CWORD_CWORD,
+ /* 91 -39 */ CWORD_CWORD_CWORD_CWORD,
+ /* 92 -38 */ CWORD_CWORD_CWORD_CWORD,
+ /* 93 -37 */ CWORD_CWORD_CWORD_CWORD,
+ /* 94 -36 */ CWORD_CWORD_CWORD_CWORD,
+ /* 95 -35 */ CWORD_CWORD_CWORD_CWORD,
+ /* 96 -34 */ CWORD_CWORD_CWORD_CWORD,
+ /* 97 -33 */ CWORD_CWORD_CWORD_CWORD,
+ /* 98 -32 */ CWORD_CWORD_CWORD_CWORD,
+ /* 99 -31 */ CWORD_CWORD_CWORD_CWORD,
+ /* 100 -30 */ CWORD_CWORD_CWORD_CWORD,
+ /* 101 -29 */ CWORD_CWORD_CWORD_CWORD,
+ /* 102 -28 */ CWORD_CWORD_CWORD_CWORD,
+ /* 103 -27 */ CWORD_CWORD_CWORD_CWORD,
+ /* 104 -26 */ CWORD_CWORD_CWORD_CWORD,
+ /* 105 -25 */ CWORD_CWORD_CWORD_CWORD,
+ /* 106 -24 */ CWORD_CWORD_CWORD_CWORD,
+ /* 107 -23 */ CWORD_CWORD_CWORD_CWORD,
+ /* 108 -22 */ CWORD_CWORD_CWORD_CWORD,
+ /* 109 -21 */ CWORD_CWORD_CWORD_CWORD,
+ /* 110 -20 */ CWORD_CWORD_CWORD_CWORD,
+ /* 111 -19 */ CWORD_CWORD_CWORD_CWORD,
+ /* 112 -18 */ CWORD_CWORD_CWORD_CWORD,
+ /* 113 -17 */ CWORD_CWORD_CWORD_CWORD,
+ /* 114 -16 */ CWORD_CWORD_CWORD_CWORD,
+ /* 115 -15 */ CWORD_CWORD_CWORD_CWORD,
+ /* 116 -14 */ CWORD_CWORD_CWORD_CWORD,
+ /* 117 -13 */ CWORD_CWORD_CWORD_CWORD,
+ /* 118 -12 */ CWORD_CWORD_CWORD_CWORD,
+ /* 119 -11 */ CWORD_CWORD_CWORD_CWORD,
+ /* 120 -10 */ CWORD_CWORD_CWORD_CWORD,
+ /* 121 -9 */ CWORD_CWORD_CWORD_CWORD,
+ /* 122 -8 */ CWORD_CWORD_CWORD_CWORD,
+ /* 123 -7 */ CWORD_CWORD_CWORD_CWORD,
+ /* 124 -6 */ CWORD_CWORD_CWORD_CWORD,
+ /* 125 -5 */ CWORD_CWORD_CWORD_CWORD,
+ /* 126 -4 */ CWORD_CWORD_CWORD_CWORD,
+ /* 127 -3 */ CWORD_CWORD_CWORD_CWORD,
+ /* 128 -2 */ CWORD_CWORD_CWORD_CWORD,
+ /* 129 -1 */ CWORD_CWORD_CWORD_CWORD,
+ /* 130 0 */ CWORD_CWORD_CWORD_CWORD,
+ /* 131 1 */ CWORD_CWORD_CWORD_CWORD,
+ /* 132 2 */ CWORD_CWORD_CWORD_CWORD,
+ /* 133 3 */ CWORD_CWORD_CWORD_CWORD,
+ /* 134 4 */ CWORD_CWORD_CWORD_CWORD,
+ /* 135 5 */ CWORD_CWORD_CWORD_CWORD,
+ /* 136 6 */ CWORD_CWORD_CWORD_CWORD,
+ /* 137 7 */ CWORD_CWORD_CWORD_CWORD,
+ /* 138 8 */ CWORD_CWORD_CWORD_CWORD,
+ /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 140 10 "\n" */ CNL_CNL_CNL_CNL,
+ /* 141 11 */ CWORD_CWORD_CWORD_CWORD,
+ /* 142 12 */ CWORD_CWORD_CWORD_CWORD,
+ /* 143 13 */ CWORD_CWORD_CWORD_CWORD,
+ /* 144 14 */ CWORD_CWORD_CWORD_CWORD,
+ /* 145 15 */ CWORD_CWORD_CWORD_CWORD,
+ /* 146 16 */ CWORD_CWORD_CWORD_CWORD,
+ /* 147 17 */ CWORD_CWORD_CWORD_CWORD,
+ /* 148 18 */ CWORD_CWORD_CWORD_CWORD,
+ /* 149 19 */ CWORD_CWORD_CWORD_CWORD,
+ /* 150 20 */ CWORD_CWORD_CWORD_CWORD,
+ /* 151 21 */ CWORD_CWORD_CWORD_CWORD,
+ /* 152 22 */ CWORD_CWORD_CWORD_CWORD,
+ /* 153 23 */ CWORD_CWORD_CWORD_CWORD,
+ /* 154 24 */ CWORD_CWORD_CWORD_CWORD,
+ /* 155 25 */ CWORD_CWORD_CWORD_CWORD,
+ /* 156 26 */ CWORD_CWORD_CWORD_CWORD,
+ /* 157 27 */ CWORD_CWORD_CWORD_CWORD,
+ /* 158 28 */ CWORD_CWORD_CWORD_CWORD,
+ /* 159 29 */ CWORD_CWORD_CWORD_CWORD,
+ /* 160 30 */ CWORD_CWORD_CWORD_CWORD,
+ /* 161 31 */ CWORD_CWORD_CWORD_CWORD,
+ /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD,
+ /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD,
+ /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
+ /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD,
+ /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR,
+ /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD,
+ /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
+ /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP,
+ /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP,
+ /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD,
+ /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD,
+ /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD,
+ /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD,
+ /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD,
+ /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD,
+ /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD,
+ /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD,
+ /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD,
+ /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD,
+ /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD,
+ /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD,
+ /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD,
+ /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD,
+ /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD,
+ /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD,
+ /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD,
+ /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD,
+ /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD,
+ /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD,
+ /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD,
+ /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD,
+ /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD,
+ /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD,
+ /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD,
+ /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD,
+ /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD,
+ /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD,
+ /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD,
+ /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD,
+ /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD,
+ /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD,
+ /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD,
+ /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD,
+ /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD,
+ /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD,
+ /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
+ /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD,
+ /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD,
+ /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD,
+ /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD,
+ /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD,
+ /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD,
+ /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD,
+ /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
+ /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
+ /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD,
+ /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK,
+ /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD,
+ /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD,
+ /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD,
+ /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
+ /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD,
+ /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD,
+ /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD,
+ /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD,
+ /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD,
+ /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD,
+ /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD,
+ /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD,
+ /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD,
+ /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD,
+ /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD,
+ /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD,
+ /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD,
+ /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD,
+ /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD,
+ /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD,
+ /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD,
+ /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD,
+ /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD,
+ /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD,
+ /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD,
+ /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD,
+ /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD,
+ /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD,
+ /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD,
+ /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD,
+ /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD,
+ /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
+ /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
+ /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD,
+ /* 257 127 */ CWORD_CWORD_CWORD_CWORD,
+};