-
-static void evalloop(union node *, int);
-static void evalfor(union node *, int);
-static void evalcase(union node *, int);
-static void evalsubshell(union node *, int);
-static void expredir(union node *);
-static void evalpipe(union node *, int);
-static void evalcommand(union node *, int);
-static int evalbltin(const struct builtincmd *, int, char **);
-static int evalfun(struct funcnode *, int, char **, int);
-static void prehash(union node *);
-static int bltincmd(int, char **);
-
-
-static const struct builtincmd bltin = {
- "\0\0", bltincmd
-};
-
-
-/*
- * Called to reset things after an exception.
- */
-
-/*
- * The eval command.
- */
-
-static int
-evalcmd(int argc, char **argv)
-{
- char *p;
- char *concat;
- char **ap;
-
- if (argc > 1) {
- p = argv[1];
- if (argc > 2) {
- STARTSTACKSTR(concat);
- ap = argv + 2;
- for (;;) {
- concat = stputs(p, concat);
- if ((p = *ap++) == NULL)
- break;
- STPUTC(' ', concat);
- }
- STPUTC('\0', concat);
- p = grabstackstr(concat);
- }
- evalstring(p, ~SKIPEVAL);
-
- }
- return exitstatus;
-}
-
-
-/*
- * Execute a command or commands contained in a string.
- */
-
-static int
-evalstring(char *s, int mask)
-{
- union node *n;
- struct stackmark smark;
- int skip;
-
- setinputstring(s);
- setstackmark(&smark);
-
- skip = 0;
- while ((n = parsecmd(0)) != NEOF) {
- evaltree(n, 0);
- popstackmark(&smark);
- skip = evalskip;
- if (skip)
- break;
- }
- popfile();
-
- skip &= mask;
- evalskip = skip;
- return skip;
-}
-
-
-/*
- * Evaluate a parse tree. The value is left in the global variable
- * exitstatus.
- */
-
-static void
-evaltree(union node *n, int flags)
-{
- int checkexit = 0;
- void (*evalfn)(union node *, int);
- unsigned isor;
- int status;
- if (n == NULL) {
- TRACE(("evaltree(NULL) called\n"));
- goto out;
- }
- TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
- getpid(), n, n->type, flags));
- switch (n->type) {
- default:
-#if DEBUG
- out1fmt("Node type = %d\n", n->type);
- fflush(stdout);
- break;
-#endif
- 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));
-}
-
-#if ENABLE_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 == '=';
-}
-
-#if ENABLE_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++;
-#if ENABLE_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;
-#if ENABLE_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 && 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])
-#if ENABLE_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));
- }
-#if ENABLE_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;
- }
-
-#if ENABLE_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;
-
- cmdp = cmdlookup(name, 0);
- if (cmdp!= NULL && cmdp->cmdtype == CMDFUNCTION)
- delete_cmd_entry();
-}
-
-/*
- * Locate and print what a word is...
- */
-
-
-#if ENABLE_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;
-#if ENABLE_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;
- }
-
-#if ENABLE_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++) {
-#if ENABLE_ASH_CMDCMD
- err |= describe_command(argv[i], 1);
-#else
- err |= describe_command(argv[i]);
-#endif
- }
- return err;
-}
-
-#if ENABLE_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,
-#if ENABLE_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)
-#if ENABLE_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;
-#if ENABLE_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;
-}
-
-
-static void
-removerecordregions(int endoff)
-{
- if (ifslastp == NULL)
- return;
-
- if (ifsfirst.endoff > endoff) {
- while (ifsfirst.next != NULL) {
- struct ifsregion *ifsp;
- INTOFF;
- ifsp = ifsfirst.next->next;
- ckfree(ifsfirst.next);
- ifsfirst.next = ifsp;
- INTON;
- }
- if (ifsfirst.begoff > endoff)
- ifslastp = NULL;
- else {
- ifslastp = &ifsfirst;
- ifsfirst.endoff = endoff;
- }
- return;
- }
-
- ifslastp = &ifsfirst;
- while (ifslastp->next && ifslastp->next->begoff < endoff)
- ifslastp=ifslastp->next;
- while (ifslastp->next != NULL) {
- struct ifsregion *ifsp;
- INTOFF;
- ifsp = ifslastp->next->next;
- ckfree(ifslastp->next);
- ifslastp->next = ifsp;
- INTON;
- }
- if (ifslastp->endoff > endoff)
- ifslastp->endoff = endoff;
-}
-
-
-#if ENABLE_ASH_MATH_SUPPORT
-/*
- * Expand arithmetic expression. Backup to start of expression,
- * evaluate, place result in (backed up) result, adjust string position.
- */
-void
-expari(int quotes)
-{
- char *p, *start;
- int begoff;
- int flag;
- int len;
-
- /* ifsfree(); */
-
- /*
- * This routine is slightly over-complicated for
- * efficiency. Next we scan backwards looking for the
- * start of arithmetic.
- */
- start = stackblock();
- p = expdest - 1;
- *p = '\0';
- p--;
- do {
- int esc;
-
- while (*p != CTLARI) {
- p--;
-#if DEBUG
- if (p < start) {
- sh_error("missing CTLARI (shouldn't happen)");
- }
-#endif
- }
-
- esc = esclen(start, p);
- if (!(esc % 2)) {
- break;
- }
-
- p -= esc + 1;
- } while (1);
-
- begoff = p - start;
-
- removerecordregions(begoff);
-
- flag = p[1];
-
- expdest = p;
-
- if (quotes)
- rmescapes(p + 2);
-
- len = cvtnum(dash_arith(p + 2));
-
- if (flag != '"')
- recordregion(begoff, begoff + len, 0);
-}
-#endif
-
-/*
- * Expand stuff in backwards quotes.
- */
-
-static void
-expbackq(union node *cmd, int quoted, int quotes)
-{
- struct backcmd in;
- int i;
- char buf[128];
- char *p;
- char *dest;
- int startloc;
- int syntax = quoted? DQSYNTAX : BASESYNTAX;
- struct stackmark smark;
-
- INTOFF;
- setstackmark(&smark);
- dest = expdest;
- startloc = dest - (char *)stackblock();
- grabstackstr(dest);
- evalbackcmd(cmd, (struct backcmd *) &in);
- popstackmark(&smark);
-
- p = in.buf;
- i = in.nleft;
- if (i == 0)
- goto read;
- for (;;) {
- memtodest(p, i, syntax, quotes);
-read:
- if (in.fd < 0)
- break;
- i = safe_read(in.fd, buf, sizeof(buf));
- TRACE(("expbackq: read returns %d\n", i));
- if (i <= 0)
- break;
- p = buf;
- }
-
- if (in.buf)
- ckfree(in.buf);
- if (in.fd >= 0) {
- close(in.fd);
- back_exitstatus = waitforjob(in.jp);
- }
- INTON;
-
- /* Eat all trailing newlines */
- dest = expdest;
- for (; dest > (char *)stackblock() && dest[-1] == '\n';)
- STUNPUTC(dest);
- expdest = dest;
-
- if (quoted == 0)
- recordregion(startloc, dest - (char *)stackblock(), 0);
- TRACE(("evalbackq: size=%d: \"%.*s\"\n",
- (dest - (char *)stackblock()) - startloc,
- (dest - (char *)stackblock()) - startloc,
- stackblock() + startloc));
-}
-
-
-static char *
-scanleft(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
- int zero)
-{
- char *loc;
- char *loc2;
- char c;
-
- loc = startp;
- loc2 = rmesc;
- do {
- int match;
- const char *s = loc2;
- c = *loc2;
- if (zero) {
- *loc2 = '\0';
- s = rmesc;
- }
- match = pmatch(str, s);
- *loc2 = c;
- if (match)
- return loc;
- if (quotes && *loc == CTLESC)
- loc++;
- loc++;
- loc2++;
- } while (c);
- return 0;
-}
-
-
-static char *
-scanright(char *startp, char *rmesc, char *rmescend, char *str, int quotes,
- int zero)
-{
- int esc = 0;
- char *loc;
- char *loc2;
-
- for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
- int match;
- char c = *loc2;
- const char *s = loc2;
- if (zero) {
- *loc2 = '\0';
- s = rmesc;
- }
- match = pmatch(str, s);
- *loc2 = c;
- if (match)
- return loc;
- loc--;
- if (quotes) {
- if (--esc < 0) {
- esc = esclen(startp, loc);
- }
- if (esc % 2) {
- esc--;
- loc--;
- }
- }
- }
- return 0;
-}
-
-static const char *
-subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
-{
- char *startp;
- char *loc;
- int saveherefd = herefd;
- struct nodelist *saveargbackq = argbackq;
- int amount;
- char *rmesc, *rmescend;
- int zero;
- char *(*scan)(char *, char *, char *, char *, int , int);
-
- herefd = -1;
- argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
- STPUTC('\0', expdest);
- herefd = saveherefd;
- argbackq = saveargbackq;
- startp = stackblock() + startloc;
-
- switch (subtype) {
- case VSASSIGN:
- setvar(str, startp, 0);
- amount = startp - expdest;
- STADJUST(amount, expdest);
- return startp;
-
- case VSQUESTION:
- varunset(p, str, startp, varflags);
- /* NOTREACHED */
- }
-
- subtype -= VSTRIMRIGHT;
-#if DEBUG
- if (subtype < 0 || subtype > 3)
- abort();
-#endif
-
- rmesc = startp;
- rmescend = stackblock() + strloc;
- if (quotes) {
- rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
- if (rmesc != startp) {
- rmescend = expdest;
- startp = stackblock() + startloc;
- }
- }
- rmescend--;
- str = stackblock() + strloc;
- preglob(str, varflags & VSQUOTE, 0);
-
- /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
- zero = subtype >> 1;
- /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
- scan = (subtype & 1) ^ zero ? scanleft : scanright;
-
- loc = scan(startp, rmesc, rmescend, str, quotes, zero);
- if (loc) {
- if (zero) {
- memmove(startp, loc, str - loc);
- loc = startp + (str - loc) - 1;
- }
- *loc = '\0';
- amount = loc - expdest;
- STADJUST(amount, expdest);
- }
- return loc;
-}
-
-
-/*
- * Expand a variable, and return a pointer to the next character in the
- * input string.
- */
-static char *
-evalvar(char *p, int flag)
-{
- int subtype;
- int varflags;
- char *var;
- int patloc;
- int c;
- int startloc;
- ssize_t varlen;
- int easy;
- int quotes;
- int quoted;
-
- quotes = flag & (EXP_FULL | EXP_CASE);
- varflags = *p++;
- subtype = varflags & VSTYPE;
- quoted = varflags & VSQUOTE;
- var = p;
- easy = (!quoted || (*var == '@' && shellparam.nparam));
- startloc = expdest - (char *)stackblock();
- p = strchr(p, '=') + 1;
-
-again:
- varlen = varvalue(var, varflags, flag);
- if (varflags & VSNUL)
- varlen--;
-
- if (subtype == VSPLUS) {
- varlen = -1 - varlen;
- goto vsplus;
- }
-
- if (subtype == VSMINUS) {
-vsplus:
- if (varlen < 0) {
- argstr(
- p, flag | EXP_TILDE |
- (quoted ? EXP_QWORD : EXP_WORD)
- );
- goto end;
- }
- if (easy)
- goto record;
- goto end;
- }
-
- if (subtype == VSASSIGN || subtype == VSQUESTION) {
- if (varlen < 0) {
- if (subevalvar(p, var, 0, subtype, startloc, varflags, 0)) {
- varflags &= ~VSNUL;
- /*
- * Remove any recorded regions beyond
- * start of variable
- */
- removerecordregions(startloc);
- goto again;
- }
- goto end;
- }
- if (easy)
- goto record;
- goto end;
- }
-
- if (varlen < 0 && uflag)
- varunset(p, var, 0, 0);
-
- if (subtype == VSLENGTH) {
- cvtnum(varlen > 0 ? varlen : 0);
- goto record;
- }
-
- if (subtype == VSNORMAL) {
- if (!easy)
- goto end;
-record:
- recordregion(startloc, expdest - (char *)stackblock(), quoted);
- goto end;
- }
-
-#if DEBUG
- switch (subtype) {
- case VSTRIMLEFT:
- case VSTRIMLEFTMAX:
- case VSTRIMRIGHT:
- case VSTRIMRIGHTMAX:
- break;
- default:
- abort();
- }
-#endif
-
- if (varlen >= 0) {
- /*
- * Terminate the string and start recording the pattern
- * right after it
- */
- STPUTC('\0', expdest);
- patloc = expdest - (char *)stackblock();
- if (subevalvar(p, NULL, patloc, subtype,
- startloc, varflags, quotes) == 0) {
- int amount = expdest - (
- (char *)stackblock() + patloc - 1
- );
- STADJUST(-amount, expdest);
- }
- /* Remove any recorded regions beyond start of variable */
- removerecordregions(startloc);
- goto record;
- }
-
-end:
- if (subtype != VSNORMAL) { /* skip to end of alternative */
- int nesting = 1;
- for (;;) {
- if ((c = *p++) == CTLESC)
- p++;
- else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
- if (varlen >= 0)
- argbackq = argbackq->next;
- } else if (c == CTLVAR) {
- if ((*p++ & VSTYPE) != VSNORMAL)
- nesting++;
- } else if (c == CTLENDVAR) {
- if (--nesting == 0)
- break;
- }
- }
- }
- return p;
-}
-
-
-/*
- * Put a string on the stack.
- */
-
-static void
-memtodest(const char *p, size_t len, int syntax, int quotes) {
- char *q = expdest;
-
- q = makestrspace(len * 2, q);
-
- while (len--) {
- int c = SC2INT(*p++);
- if (!c)
- continue;
- if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
- USTPUTC(CTLESC, q);
- USTPUTC(c, q);
- }
-
- expdest = q;
-}
-
-
-static void
-strtodest(const char *p, int syntax, int quotes)
-{
- memtodest(p, strlen(p), syntax, quotes);
-}
-
-
-/*
- * Add the value of a specialized variable to the stack string.
- */
-
-static ssize_t
-varvalue(char *name, int varflags, int flags)
-{
- int num;
- char *p;
- int i;
- int sep = 0;
- int sepq = 0;
- ssize_t len = 0;
- char **ap;
- int syntax;
- int quoted = varflags & VSQUOTE;
- int subtype = varflags & VSTYPE;
- int quotes = flags & (EXP_FULL | EXP_CASE);
-
- if (quoted && (flags & EXP_FULL))
- sep = 1 << CHAR_BIT;
-
- syntax = quoted ? DQSYNTAX : BASESYNTAX;
- switch (*name) {
- case '$':
- num = rootpid;
- goto numvar;
- case '?':
- num = exitstatus;
- goto numvar;
- case '#':
- num = shellparam.nparam;
- goto numvar;
- case '!':
- num = backgndpid;
- if (num == 0)
- return -1;
-numvar:
- len = cvtnum(num);
- break;
- case '-':
- p = makestrspace(NOPTS, expdest);
- for (i = NOPTS - 1; i >= 0; i--) {
- if (optlist[i]) {
- USTPUTC(optletters(i), p);
- len++;
- }
- }
- expdest = p;
- break;
- case '@':
- if (sep)
- goto param;
- /* fall through */
- case '*':
- sep = ifsset() ? SC2INT(ifsval()[0]) : ' ';
- if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
- sepq = 1;
-param:
- if (!(ap = shellparam.p))
- return -1;
- while ((p = *ap++)) {
- size_t partlen;
-
- partlen = strlen(p);
- len += partlen;
-
- if (!(subtype == VSPLUS || subtype == VSLENGTH))
- memtodest(p, partlen, syntax, quotes);
-
- if (*ap && sep) {
- char *q;
-
- len++;
- if (subtype == VSPLUS || subtype == VSLENGTH) {
- continue;
- }
- q = expdest;
- if (sepq)
- STPUTC(CTLESC, q);
- STPUTC(sep, q);
- expdest = q;
- }
- }
- return len;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- num = atoi(name);
- if (num < 0 || num > shellparam.nparam)
- return -1;
- p = num ? shellparam.p[num - 1] : arg0;
- goto value;
- default:
- p = lookupvar(name);
-value:
- if (!p)
- return -1;
-
- len = strlen(p);
- if (!(subtype == VSPLUS || subtype == VSLENGTH))
- memtodest(p, len, syntax, quotes);
- return len;
- }
-
- if (subtype == VSPLUS || subtype == VSLENGTH)
- STADJUST(-len, expdest);
- return len;
-}
-
-
-/*
- * Record the fact that we have to scan this region of the
- * string for IFS characters.
- */
-
-static void
-recordregion(int start, int end, int nulonly)
-{
- struct ifsregion *ifsp;
-
- if (ifslastp == NULL) {
- ifsp = &ifsfirst;
- } else {
- INTOFF;
- ifsp = (struct ifsregion *)ckmalloc(sizeof(struct ifsregion));
- ifsp->next = NULL;
- ifslastp->next = ifsp;
- INTON;
- }
- ifslastp = ifsp;
- ifslastp->begoff = start;
- ifslastp->endoff = end;
- ifslastp->nulonly = nulonly;
-}
-
-
-/*
- * Break the argument string into pieces based upon IFS and add the
- * strings to the argument list. The regions of the string to be
- * searched for IFS characters have been stored by recordregion.
- */
-static void
-ifsbreakup(char *string, struct arglist *arglist)
-{
- struct ifsregion *ifsp;
- struct strlist *sp;
- char *start;
- char *p;
- char *q;
- const char *ifs, *realifs;
- int ifsspc;
- int nulonly;
-
- start = string;
- if (ifslastp != NULL) {
- ifsspc = 0;
- nulonly = 0;
- realifs = ifsset() ? ifsval() : defifs;
- ifsp = &ifsfirst;
- do {
- p = string + ifsp->begoff;
- nulonly = ifsp->nulonly;
- ifs = nulonly ? nullstr : realifs;
- ifsspc = 0;
- while (p < string + ifsp->endoff) {
- q = p;
- if (*p == CTLESC)
- p++;
- if (strchr(ifs, *p)) {
- if (!nulonly)
- ifsspc = (strchr(defifs, *p) != NULL);
- /* Ignore IFS whitespace at start */
- if (q == start && ifsspc) {
- p++;
- start = p;
- continue;
- }
- *q = '\0';
- sp = (struct strlist *)stalloc(sizeof(*sp));
- sp->text = start;
- *arglist->lastp = sp;
- arglist->lastp = &sp->next;
- p++;
- if (!nulonly) {
- for (;;) {
- if (p >= string + ifsp->endoff) {
- break;
- }
- q = p;
- if (*p == CTLESC)
- p++;
- if (strchr(ifs, *p) == NULL ) {
- p = q;
- break;
- } else if (strchr(defifs, *p) == NULL) {
- if (ifsspc) {
- p++;
- ifsspc = 0;
- } else {
- p = q;
- break;
- }
- } else
- p++;
- }
- }
- start = p;
- } else
- p++;
- }
- } while ((ifsp = ifsp->next) != NULL);
- if (nulonly)
- goto add;
- }
-
- if (!*start)
- return;
-
-add:
- sp = (struct strlist *)stalloc(sizeof(*sp));
- sp->text = start;
- *arglist->lastp = sp;
- arglist->lastp = &sp->next;
-}
-
-static void
-ifsfree(void)
-{
- struct ifsregion *p;
-
- INTOFF;
- p = ifsfirst.next;
- do {
- struct ifsregion *ifsp;
- ifsp = p->next;
- ckfree(p);
- p = ifsp;
- } while (p);
- ifslastp = NULL;
- ifsfirst.next = NULL;
- INTON;
-}
-
-static void expmeta(char *, char *);
-static struct strlist *expsort(struct strlist *);
-static struct strlist *msort(struct strlist *, int);
-
-static char *expdir;
-
-
-static void
-expandmeta(struct strlist *str, int flag)
-{
- static const char metachars[] = {
- '*', '?', '[', 0
- };
- /* TODO - EXP_REDIR */
-
- while (str) {
- struct strlist **savelastp;
- struct strlist *sp;
- char *p;
-
- if (fflag)
- goto nometa;
- if (!strpbrk(str->text, metachars))
- goto nometa;
- savelastp = exparg.lastp;
-
- INTOFF;
- p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
- {
- int i = strlen(str->text);
- expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
- }
-
- expmeta(expdir, p);
- ckfree(expdir);
- if (p != str->text)
- ckfree(p);
- INTON;
- if (exparg.lastp == savelastp) {
- /*
- * no matches
- */
-nometa:
- *exparg.lastp = str;
- rmescapes(str->text);
- exparg.lastp = &str->next;
- } else {
- *exparg.lastp = NULL;
- *savelastp = sp = expsort(*savelastp);
- while (sp->next != NULL)
- sp = sp->next;
- exparg.lastp = &sp->next;
- }
- str = str->next;
- }
-}
-
-/*
- * Add a file name to the list.
- */
-
-static void
-addfname(const char *name)
-{
- struct strlist *sp;
-
- sp = (struct strlist *)stalloc(sizeof(*sp));
- sp->text = sstrdup(name);
- *exparg.lastp = sp;
- exparg.lastp = &sp->next;
-}
-
-
-/*
- * Do metacharacter (i.e. *, ?, [...]) expansion.
- */
-
-static void
-expmeta(char *enddir, char *name)
-{
- char *p;
- const char *cp;
- char *start;
- char *endname;
- int metaflag;
- struct stat statb;
- DIR *dirp;
- struct dirent *dp;
- int atend;
- int matchdot;
-
- metaflag = 0;
- start = name;
- for (p = name; *p; p++) {
- if (*p == '*' || *p == '?')
- metaflag = 1;
- else if (*p == '[') {
- char *q = p + 1;
- if (*q == '!')
- q++;
- for (;;) {
- if (*q == '\\')
- q++;
- if (*q == '/' || *q == '\0')
- break;
- if (*++q == ']') {
- metaflag = 1;
- break;
- }
- }
- } else if (*p == '\\')
- p++;
- else if (*p == '/') {
- if (metaflag)
- goto out;
- start = p + 1;
- }
- }
-out:
- if (metaflag == 0) { /* we've reached the end of the file name */
- if (enddir != expdir)
- metaflag++;
- p = name;
- do {
- if (*p == '\\')
- p++;
- *enddir++ = *p;
- } while (*p++);
- if (metaflag == 0 || lstat(expdir, &statb) >= 0)
- addfname(expdir);
- return;
- }
- endname = p;
- if (name < start) {
- p = name;
- do {
- if (*p == '\\')
- p++;
- *enddir++ = *p++;
- } while (p < start);
- }
- if (enddir == expdir) {
- cp = ".";
- } else if (enddir == expdir + 1 && *expdir == '/') {
- cp = "/";
- } else {
- cp = expdir;
- enddir[-1] = '\0';
- }
- if ((dirp = opendir(cp)) == NULL)
- return;
- if (enddir != expdir)
- enddir[-1] = '/';
- if (*endname == 0) {
- atend = 1;
- } else {
- atend = 0;
- *endname++ = '\0';
- }
- matchdot = 0;
- p = start;
- if (*p == '\\')
- p++;
- if (*p == '.')
- matchdot++;
- while (! intpending && (dp = readdir(dirp)) != NULL) {
- if (dp->d_name[0] == '.' && ! matchdot)
- continue;
- if (pmatch(start, dp->d_name)) {
- if (atend) {
- scopy(dp->d_name, enddir);
- addfname(expdir);
- } else {
- for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
- continue;
- p[-1] = '/';
- expmeta(p, endname);
- }
- }
- }
- closedir(dirp);
- if (! atend)
- endname[-1] = '/';
-}
-
-/*
- * Sort the results of file name expansion. It calculates the number of
- * strings to sort and then calls msort (short for merge sort) to do the
- * work.
- */
-
-static struct strlist *
-expsort(struct strlist *str)
-{
- int len;
- struct strlist *sp;
-
- len = 0;
- for (sp = str ; sp ; sp = sp->next)
- len++;
- return msort(str, len);
-}
-
-
-static struct strlist *
-msort(struct strlist *list, int len)
-{
- struct strlist *p, *q = NULL;
- struct strlist **lpp;
- int half;
- int n;
-
- if (len <= 1)
- return list;
- half = len >> 1;
- p = list;
- for (n = half ; --n >= 0 ; ) {
- q = p;
- p = p->next;
- }
- q->next = NULL; /* terminate first half of list */
- q = msort(list, half); /* sort first half of list */
- p = msort(p, len - half); /* sort second half */
- lpp = &list;
- for (;;) {
-#if ENABLE_LOCALE_SUPPORT
- if (strcoll(p->text, q->text) < 0)
-#else
- if (strcmp(p->text, q->text) < 0)
-#endif
- {
- *lpp = p;
- lpp = &p->next;
- if ((p = *lpp) == NULL) {
- *lpp = q;
- break;
- }
- } else {
- *lpp = q;
- lpp = &q->next;
- if ((q = *lpp) == NULL) {
- *lpp = p;
- break;
- }
- }
- }
- return list;
-}
-
-
-/*
- * Returns true if the pattern matches the string.
- */
-
-static int patmatch(char *pattern, const char *string)
-{
- return pmatch(preglob(pattern, 0, 0), string);
-}
-
-
-/*
- * Remove any CTLESC characters from a string.
- */
-
-static char *
-_rmescapes(char *str, int flag)
-{
- char *p, *q, *r;
- static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
- unsigned inquotes;
- int notescaped;
- int globbing;
-
- p = strpbrk(str, qchars);
- if (!p) {
- return str;
- }
- q = p;
- r = str;
- if (flag & RMESCAPE_ALLOC) {
- size_t len = p - str;
- size_t fulllen = len + strlen(p) + 1;
-
- if (flag & RMESCAPE_GROW) {
- r = makestrspace(fulllen, expdest);
- } else if (flag & RMESCAPE_HEAP) {
- r = ckmalloc(fulllen);
- } else {
- r = stalloc(fulllen);
- }
- q = r;
- if (len > 0) {
- q = memcpy(q, str, len) + len;
- }
- }
- inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
- globbing = flag & RMESCAPE_GLOB;
- notescaped = globbing;
- while (*p) {
- if (*p == CTLQUOTEMARK) {
- inquotes = ~inquotes;
- p++;
- notescaped = globbing;
- continue;
- }
- if (*p == '\\') {
- /* naked back slash */
- notescaped = 0;
- goto copy;
- }
- if (*p == CTLESC) {
- p++;
- if (notescaped && inquotes && *p != '/') {
- *q++ = '\\';
- }
- }
- notescaped = globbing;
-copy:
- *q++ = *p++;
- }
- *q = '\0';
- if (flag & RMESCAPE_GROW) {
- expdest = r;
- STADJUST(q - r + 1, expdest);
- }
- return r;
-}
-
-
-/*
- * See if a pattern matches in a case statement.
- */
-
-int
-casematch(union node *pattern, char *val)
-{
- struct stackmark smark;
- int result;
-
- setstackmark(&smark);
- argbackq = pattern->narg.backquote;
- STARTSTACKSTR(expdest);
- ifslastp = NULL;
- argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
- STACKSTRNUL(expdest);
- result = patmatch(stackblock(), val);
- popstackmark(&smark);
- return result;
-}
-
-/*
- * Our own itoa().
- */
-
-static int
-cvtnum(arith_t num)
-{
- int len;
-
- expdest = makestrspace(32, expdest);
-#if ENABLE_ASH_MATH_SUPPORT_64
- len = fmtstr(expdest, 32, "%lld", (long long) num);
-#else
- len = fmtstr(expdest, 32, "%ld", num);
-#endif
- STADJUST(len, expdest);
- return len;
-}
-
-static void
-varunset(const char *end, const char *var, const char *umsg, int varflags)
-{
- const char *msg;
- const char *tail;
-
- tail = nullstr;
- msg = "parameter not set";
- if (umsg) {
- if (*end == CTLENDVAR) {
- if (varflags & VSNUL)
- tail = " or null";
- } else
- msg = umsg;
- }
- sh_error("%.*s: %s%s", end - var - 1, var, msg, tail);
-}
-
-
-/* input.c */
-
-/*
- * This implements the input routines used by the parser.
- */
-
-#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
-
-static void pushfile(void);
-
-/*
- * Read a character from the script, returning PEOF on end of file.
- * Nul characters in the input are silently discarded.
- */
-
-
-#define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
-
-#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
-#define pgetc_macro() pgetc()
-static int
-pgetc(void)
-{
- return pgetc_as_macro();
-}
-#else
-#define pgetc_macro() pgetc_as_macro()
-static int
-pgetc(void)
-{
- return pgetc_macro();
-}
-#endif
-
-
-/*
- * Same as pgetc(), but ignores PEOA.
- */