+ struct var *vp;
+ struct var *end;
+ struct var **vpp;
+
+ /*
+ * PS1 depends on uid
+ */
+#if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
+ vps1.text = "PS1=\\w \\$ ";
+#else
+ if (!geteuid())
+ vps1.text = "PS1=# ";
+#endif
+ vp = varinit;
+ end = vp + sizeof(varinit) / sizeof(varinit[0]);
+ do {
+ vpp = hashvar(vp->text);
+ vp->next = *vpp;
+ *vpp = vp;
+ } while (++vp < end);
+}
+
+static inline void
+init(void)
+{
+
+ /* from input.c: */
+ {
+ basepf.nextc = basepf.buf = basebuf;
+ }
+
+ /* from trap.c: */
+ {
+ signal(SIGCHLD, SIG_DFL);
+ }
+
+ /* from var.c: */
+ {
+ char **envp;
+ char ppid[32];
+
+ initvar();
+ for (envp = environ ; *envp ; envp++) {
+ if (strchr(*envp, '=')) {
+ setvareq(*envp, VEXPORT|VTEXTFIXED);
+ }
+ }
+
+ snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
+ setvar("PPID", ppid, 0);
+ setpwd(0, 0);
+ }
+}
+
+/* PEOF (the end of file marker) */
+
+/*
+ * The input line number. Input.c just defines this variable, and saves
+ * and restores it when files are pushed and popped. The user of this
+ * package must set its value.
+ */
+
+static int pgetc(void);
+static int pgetc2(void);
+static int preadbuffer(void);
+static void pungetc(void);
+static void pushstring(char *, void *);
+static void popstring(void);
+static void setinputfile(const char *, int);
+static void setinputfd(int, int);
+static void setinputstring(char *);
+static void popfile(void);
+static void popallfiles(void);
+static void closescript(void);
+
+
+/* $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $ */
+
+
+/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
+#define FORK_FG 0
+#define FORK_BG 1
+#define FORK_NOJOB 2
+
+/* mode flags for showjob(s) */
+#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */
+#define SHOW_PID 0x04 /* include process pid */
+#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */
+
+
+/*
+ * A job structure contains information about a job. A job is either a
+ * single process or a set of processes contained in a pipeline. In the
+ * latter case, pidlist will be non-NULL, and will point to a -1 terminated
+ * array of pids.
+ */
+
+struct procstat {
+ pid_t pid; /* process id */
+ int status; /* last process status from wait() */
+ char *cmd; /* text of command being run */
+};
+
+struct job {
+ struct procstat ps0; /* status of process */
+ struct procstat *ps; /* status or processes when more than one */
+#if JOBS
+ int stopstatus; /* status of a stopped job */
+#endif
+ uint32_t
+ nprocs: 16, /* number of processes */
+ state: 8,
+#define JOBRUNNING 0 /* at least one proc running */
+#define JOBSTOPPED 1 /* all procs are stopped */
+#define JOBDONE 2 /* all procs are completed */
+#if JOBS
+ sigint: 1, /* job was killed by SIGINT */
+ jobctl: 1, /* job running under job control */
+#endif
+ waited: 1, /* true if this entry has been waited for */
+ used: 1, /* true if this entry is in used */
+ changed: 1; /* true if status has changed */
+ struct job *prev_job; /* previous job */
+};
+
+static pid_t backgndpid; /* pid of last background process */
+static int job_warning; /* user was warned about stopped jobs */
+#if JOBS
+static int jobctl; /* true if doing job control */
+#endif
+
+static struct job *makejob(union node *, int);
+static int forkshell(struct job *, union node *, int);
+static int waitforjob(struct job *);
+static int stoppedjobs(void);
+
+#if ! JOBS
+#define setjobctl(on) /* do nothing */
+#else
+static void setjobctl(int);
+static void showjobs(FILE *, int);
+#endif
+
+/* $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $ */
+
+
+/* pid of main shell */
+static int rootpid;
+/* true if we aren't a child of the main shell */
+static int rootshell;
+
+static void readcmdfile(char *);
+static void cmdloop(int);
+
+/* $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $ */
+
+
+struct stackmark {
+ struct stack_block *stackp;
+ char *stacknxt;
+ size_t stacknleft;
+ struct stackmark *marknext;
+};
+
+/* minimum size of a block */
+#define MINSIZE SHELL_ALIGN(504)
+
+struct stack_block {
+ struct stack_block *prev;
+ char space[MINSIZE];
+};
+
+static struct stack_block stackbase;
+static struct stack_block *stackp = &stackbase;
+static struct stackmark *markp;
+static char *stacknxt = stackbase.space;
+static size_t stacknleft = MINSIZE;
+static char *sstrend = stackbase.space + MINSIZE;
+static int herefd = -1;
+
+
+static pointer ckmalloc(size_t);
+static pointer ckrealloc(pointer, size_t);
+static char *savestr(const char *);
+static pointer stalloc(size_t);
+static void stunalloc(pointer);
+static void setstackmark(struct stackmark *);
+static void popstackmark(struct stackmark *);
+static void growstackblock(void);
+static void *growstackstr(void);
+static char *makestrspace(size_t, char *);
+static char *stnputs(const char *, size_t, char *);
+static char *stputs(const char *, char *);
+
+
+static inline char *_STPUTC(char c, char *p) {
+ if (p == sstrend)
+ p = growstackstr();
+ *p++ = c;
+ return p;
+}
+
+#define stackblock() ((void *)stacknxt)
+#define stackblocksize() stacknleft
+#define STARTSTACKSTR(p) ((p) = stackblock())
+#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
+#define CHECKSTRSPACE(n, p) \
+ ({ \
+ char *q = (p); \
+ size_t l = (n); \
+ size_t m = sstrend - q; \
+ if (l > m) \
+ (p) = makestrspace(l, q); \
+ 0; \
+ })
+#define USTPUTC(c, p) (*p++ = (c))
+#define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+#define STUNPUTC(p) (--p)
+#define STTOPC(p) p[-1]
+#define STADJUST(amount, p) (p += (amount))
+
+#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
+#define ungrabstackstr(s, p) stunalloc((s))
+#define stackstrend() ((void *)sstrend)
+
+#define ckfree(p) free((pointer)(p))
+
+/* $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $ */
+
+
+#define DOLATSTRLEN 4
+
+static char *prefix(const char *, const char *);
+static int number(const char *);
+static int is_number(const char *);
+static char *single_quote(const char *);
+static char *sstrdup(const char *);
+
+#define equal(s1, s2) (strcmp(s1, s2) == 0)
+#define scopy(s1, s2) ((void)strcpy(s2, s1))
+
+/* $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */
+
+struct shparam {
+ int nparam; /* # of positional parameters (without $0) */
+ unsigned char malloc; /* if parameter list dynamically allocated */
+ char **p; /* parameter list */
+#ifdef CONFIG_ASH_GETOPTS
+ int optind; /* next parameter to be processed by getopts */
+ int optoff; /* used by getopts */
+#endif
+};
+
+
+#define eflag optlist[0]
+#define fflag optlist[1]
+#define Iflag optlist[2]
+#define iflag optlist[3]
+#define mflag optlist[4]
+#define nflag optlist[5]
+#define sflag optlist[6]
+#define xflag optlist[7]
+#define vflag optlist[8]
+#define Cflag optlist[9]
+#define aflag optlist[10]
+#define bflag optlist[11]
+#define uflag optlist[12]
+#define qflag optlist[13]
+
+#ifdef DEBUG
+#define nolog optlist[14]
+#define debug optlist[15]
+#define NOPTS 16
+#else
+#define NOPTS 14
+#endif
+
+/* $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */
+
+
+static const char *const optletters_optnames[NOPTS] = {
+ "e" "errexit",
+ "f" "noglob",
+ "I" "ignoreeof",
+ "i" "interactive",
+ "m" "monitor",
+ "n" "noexec",
+ "s" "stdin",
+ "x" "xtrace",
+ "v" "verbose",
+ "C" "noclobber",
+ "a" "allexport",
+ "b" "notify",
+ "u" "nounset",
+ "q" "quietprofile",
+#ifdef DEBUG
+ "\0" "nolog",
+ "\0" "debug",
+#endif
+};
+
+#define optletters(n) optletters_optnames[(n)][0]
+#define optnames(n) (&optletters_optnames[(n)][1])
+
+
+static char optlist[NOPTS];
+
+
+static char *arg0; /* value of $0 */
+static struct shparam shellparam; /* $@ current positional parameters */
+static char **argptr; /* argument list for builtin commands */
+static char *optionarg; /* set by nextopt (like getopt) */
+static char *optptr; /* used by nextopt */
+
+static char *minusc; /* argument to -c option */
+
+
+static void procargs(int, char **);
+static void optschanged(void);
+static void setparam(char **);
+static void freeparam(volatile struct shparam *);
+static int shiftcmd(int, char **);
+static int setcmd(int, char **);
+static int nextopt(const char *);
+
+/* $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $ */
+
+/* flags passed to redirect */
+#define REDIR_PUSH 01 /* save previous values of file descriptors */
+#define REDIR_SAVEFD2 03 /* set preverrout */
+
+union node;
+static void redirect(union node *, int);
+static void popredir(int);
+static void clearredir(int);
+static int copyfd(int, int);
+static int redirectsafe(union node *, int);
+
+/* $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $ */
+
+
+#ifdef DEBUG
+static void showtree(union node *);
+static void trace(const char *, ...);
+static void tracev(const char *, va_list);
+static void trargs(char **);
+static void trputc(int);
+static void trputs(const char *);
+static void opentrace(void);
+#endif
+
+/* $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $ */
+
+
+/* trap handler commands */
+static char *trap[NSIG];
+/* current value of signal */
+static char sigmode[NSIG - 1];
+/* indicates specified signal received */
+static char gotsig[NSIG - 1];
+
+static void clear_traps(void);
+static void setsignal(int);
+static void ignoresig(int);
+static void onsig(int);
+static void dotrap(void);
+static void setinteractive(int);
+static void exitshell(void) __attribute__((__noreturn__));
+static int decode_signal(const char *, int);
+
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop.
+ */
+
+static void
+reset(void)
+{
+ /* from eval.c: */
+ {
+ evalskip = 0;
+ loopnest = 0;
+ funcnest = 0;
+ }
+
+ /* from input.c: */
+ {
+ parselleft = parsenleft = 0; /* clear input buffer */
+ popallfiles();
+ }
+
+ /* from parser.c: */
+ {
+ tokpushback = 0;
+ checkkwd = 0;
+ }
+
+ /* from redir.c: */
+ {
+ clearredir(0);
+ }
+
+}
+
+#ifdef CONFIG_ASH_ALIAS
+static struct alias *atab[ATABSIZE];
+
+static void setalias(const char *, const char *);
+static struct alias *freealias(struct alias *);
+static struct alias **__lookupalias(const char *);
+
+static void
+setalias(const char *name, const char *val)
+{
+ struct alias *ap, **app;
+
+ app = __lookupalias(name);
+ ap = *app;
+ INTOFF;
+ if (ap) {
+ if (!(ap->flag & ALIASINUSE)) {
+ ckfree(ap->val);
+ }
+ ap->val = savestr(val);
+ ap->flag &= ~ALIASDEAD;
+ } else {
+ /* not found */
+ ap = ckmalloc(sizeof (struct alias));
+ ap->name = savestr(name);
+ ap->val = savestr(val);
+ ap->flag = 0;
+ ap->next = 0;
+ *app = ap;
+ }
+ INTON;
+}
+
+static int
+unalias(const char *name)
+{
+ struct alias **app;
+
+ app = __lookupalias(name);
+
+ if (*app) {
+ INTOFF;
+ *app = freealias(*app);
+ INTON;
+ return (0);
+ }
+
+ return (1);
+}
+
+static void
+rmaliases(void)
+{
+ struct alias *ap, **app;
+ int i;
+
+ INTOFF;
+ for (i = 0; i < ATABSIZE; i++) {
+ app = &atab[i];
+ for (ap = *app; ap; ap = *app) {
+ *app = freealias(*app);
+ if (ap == *app) {
+ app = &ap->next;
+ }
+ }
+ }
+ INTON;
+}
+
+static struct alias *
+lookupalias(const char *name, int check)
+{
+ struct alias *ap = *__lookupalias(name);
+
+ if (check && ap && (ap->flag & ALIASINUSE))
+ return (NULL);
+ return (ap);
+}
+
+/*
+ * TODO - sort output
+ */
+static int
+aliascmd(int argc, char **argv)
+{
+ char *n, *v;
+ int ret = 0;
+ struct alias *ap;
+
+ if (argc == 1) {
+ int i;
+
+ for (i = 0; i < ATABSIZE; i++)
+ for (ap = atab[i]; ap; ap = ap->next) {
+ printalias(ap);
+ }
+ return (0);
+ }
+ while ((n = *++argv) != NULL) {
+ if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
+ if ((ap = *__lookupalias(n)) == NULL) {
+ fprintf(stderr, "%s: %s not found\n", "alias", n);
+ ret = 1;
+ } else
+ printalias(ap);
+ } else {
+ *v++ = '\0';
+ setalias(n, v);
+ }
+ }
+
+ return (ret);
+}
+
+static int
+unaliascmd(int argc, char **argv)
+{
+ int i;
+
+ while ((i = nextopt("a")) != '\0') {
+ if (i == 'a') {
+ rmaliases();
+ return (0);
+ }
+ }
+ for (i = 0; *argptr; argptr++) {
+ if (unalias(*argptr)) {
+ fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
+ i = 1;
+ }
+ }
+
+ return (i);
+}
+
+static struct alias *
+freealias(struct alias *ap) {
+ struct alias *next;
+
+ if (ap->flag & ALIASINUSE) {
+ ap->flag |= ALIASDEAD;
+ return ap;
+ }
+
+ next = ap->next;
+ ckfree(ap->name);
+ ckfree(ap->val);
+ ckfree(ap);
+ return next;
+}
+
+static void
+printalias(const struct alias *ap) {
+ out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
+}
+
+static struct alias **
+__lookupalias(const char *name) {
+ unsigned int hashval;
+ struct alias **app;
+ const char *p;
+ unsigned int ch;
+
+ p = name;
+
+ ch = (unsigned char)*p;
+ hashval = ch << 4;
+ while (ch) {
+ hashval += ch;
+ ch = (unsigned char)*++p;
+ }
+ app = &atab[hashval % ATABSIZE];
+
+ for (; *app; app = &(*app)->next) {
+ if (equal(name, (*app)->name)) {
+ break;
+ }
+ }
+
+ return app;
+}
+#endif /* CONFIG_ASH_ALIAS */
+
+
+/* $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
+
+/*
+ * The cd and pwd commands.
+ */
+
+#define CD_PHYSICAL 1
+#define CD_PRINT 2
+
+static int docd(const char *, int);
+static int cdopt(void);
+
+static char *curdir = nullstr; /* current working directory */
+static char *physdir = nullstr; /* physical working directory */
+
+static int
+cdopt(void)
+{
+ int flags = 0;
+ int i, j;
+
+ j = 'L';
+ while ((i = nextopt("LP"))) {
+ if (i != j) {
+ flags ^= CD_PHYSICAL;
+ j = i;
+ }
+ }
+
+ return flags;
+}
+
+static int
+cdcmd(int argc, char **argv)
+{
+ const char *dest;
+ const char *path;
+ const char *p;
+ char c;
+ struct stat statb;
+ int flags;
+
+ flags = cdopt();
+ dest = *argptr;
+ if (!dest)
+ dest = bltinlookup(homestr);
+ else if (dest[0] == '-' && dest[1] == '\0') {
+ dest = bltinlookup("OLDPWD");
+ flags |= CD_PRINT;
+ goto step7;
+ }
+ if (!dest)
+ dest = nullstr;
+ if (*dest == '/')
+ goto step7;
+ if (*dest == '.') {
+ c = dest[1];
+dotdot:
+ switch (c) {
+ case '\0':
+ case '/':
+ goto step6;
+ case '.':
+ c = dest[2];
+ if (c != '.')
+ goto dotdot;
+ }
+ }
+ if (!*dest)
+ dest = ".";
+ if (!(path = bltinlookup("CDPATH"))) {
+step6:
+step7:
+ p = dest;
+ goto docd;
+ }
+ do {
+ c = *path;
+ p = padvance(&path, dest);
+ if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
+ if (c && c != ':')
+ flags |= CD_PRINT;
+docd:
+ if (!docd(p, flags))
+ goto out;
+ break;
+ }
+ } while (path);
+ error("can't cd to %s", dest);
+ /* NOTREACHED */
+out:
+ if (flags & CD_PRINT)
+ out1fmt(snlfmt, curdir);
+ return 0;
+}
+
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.
+ */
+
+static inline const char *
+updatepwd(const char *dir)
+{
+ char *new;
+ char *p;
+ char *cdcomppath;
+ const char *lim;
+
+ cdcomppath = sstrdup(dir);
+ STARTSTACKSTR(new);
+ if (*dir != '/') {
+ if (curdir == nullstr)
+ return 0;
+ new = stputs(curdir, new);
+ }
+ new = makestrspace(strlen(dir) + 2, new);
+ lim = stackblock() + 1;
+ if (*dir != '/') {
+ if (new[-1] != '/')
+ USTPUTC('/', new);
+ if (new > lim && *lim == '/')
+ lim++;
+ } else {
+ USTPUTC('/', new);
+ cdcomppath++;
+ if (dir[1] == '/' && dir[2] != '/') {
+ USTPUTC('/', new);
+ cdcomppath++;
+ lim++;
+ }
+ }
+ p = strtok(cdcomppath, "/");
+ while (p) {
+ switch(*p) {
+ case '.':
+ if (p[1] == '.' && p[2] == '\0') {
+ while (new > lim) {
+ STUNPUTC(new);
+ if (new[-1] == '/')
+ break;
+ }
+ break;
+ } else if (p[1] == '\0')
+ break;
+ /* fall through */
+ default:
+ new = stputs(p, new);
+ USTPUTC('/', new);
+ }
+ p = strtok(0, "/");
+ }
+ if (new > lim)
+ STUNPUTC(new);
+ *new = 0;
+ return stackblock();
+}
+
+/*
+ * Actually do the chdir. We also call hashcd to let the routines in exec.c
+ * know that the current directory has changed.
+ */
+
+static int
+docd(const char *dest, int flags)
+{
+ const char *dir = 0;
+ int err;
+
+ TRACE(("docd(\"%s\", %d) called\n", dest, flags));
+
+ INTOFF;
+ if (!(flags & CD_PHYSICAL)) {
+ dir = updatepwd(dest);
+ if (dir)
+ dest = dir;
+ }
+ err = chdir(dest);
+ if (err)
+ goto out;
+ setpwd(dir, 1);
+ hashcd();
+out:
+ INTON;
+ return err;
+}
+
+/*
+ * Find out what the current directory is. If we already know the current
+ * directory, this routine returns immediately.
+ */
+static inline char *
+getpwd(void)
+{
+ char *dir = getcwd(0, 0);
+ return dir ? dir : nullstr;
+}
+
+static int
+pwdcmd(int argc, char **argv)
+{
+ int flags;
+ const char *dir = curdir;
+
+ flags = cdopt();
+ if (flags) {
+ if (physdir == nullstr)
+ setpwd(dir, 0);
+ dir = physdir;
+ }
+ out1fmt(snlfmt, dir);
+ return 0;
+}
+
+static void
+setpwd(const char *val, int setold)
+{
+ char *oldcur, *dir;
+
+ oldcur = dir = curdir;
+
+ if (setold) {
+ setvar("OLDPWD", oldcur, VEXPORT);
+ }
+ INTOFF;
+ if (physdir != nullstr) {
+ if (physdir != oldcur)
+ free(physdir);
+ physdir = nullstr;
+ }
+ if (oldcur == val || !val) {
+ char *s = getpwd();
+ physdir = s;
+ if (!val)
+ dir = s;
+ } else
+ dir = savestr(val);
+ if (oldcur != dir && oldcur != nullstr) {
+ free(oldcur);
+ }
+ curdir = dir;
+ INTON;
+ setvar("PWD", dir, VEXPORT);
+}
+
+/* $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $ */
+
+/*
+ * Errors and exceptions.
+ */
+
+/*
+ * Code to handle exceptions in C.
+ */
+
+
+
+static void exverror(int, const char *, va_list)
+ __attribute__((__noreturn__));
+
+/*
+ * Called to raise an exception. Since C doesn't include exceptions, we
+ * just do a longjmp to the exception handler. The type of exception is
+ * stored in the global variable "exception".
+ */
+
+static void
+exraise(int e)
+{
+#ifdef DEBUG
+ if (handler == NULL)
+ abort();
+#endif
+ INTOFF;
+
+ exception = e;
+ longjmp(handler->loc, 1);
+}
+
+
+/*
+ * Called from trap.c when a SIGINT is received. (If the user specifies
+ * that SIGINT is to be trapped or ignored using the trap builtin, then
+ * this routine is not called.) Suppressint is nonzero when interrupts
+ * are held using the INTOFF macro. (The test for iflag is just
+ * defensive programming.)
+ */
+
+static void
+onint(void) {
+ int i;
+
+ intpending = 0;
+ sigsetmask(0);
+ i = EXSIG;
+ if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
+ if (!(rootshell && iflag)) {
+ signal(SIGINT, SIG_DFL);
+ raise(SIGINT);
+ }
+ i = EXINT;
+ }
+ exraise(i);
+ /* NOTREACHED */
+}
+
+static void
+exvwarning(const char *msg, va_list ap)
+{
+ FILE *errs;
+ const char *name;
+ const char *fmt;
+
+ errs = stderr;
+ name = arg0;
+ fmt = "%s: ";
+ if (commandname) {
+ name = commandname;
+ fmt = "%s: %d: ";
+ }
+ fprintf(errs, fmt, name, startlinno);
+ vfprintf(errs, msg, ap);
+ outcslow('\n', errs);
+}
+
+/*
+ * Exverror is called to raise the error exception. If the second argument
+ * is not NULL then error prints an error message using printf style
+ * formatting. It then raises the error exception.
+ */
+static void
+exverror(int cond, const char *msg, va_list ap)
+{
+#ifdef DEBUG
+ if (msg) {
+ TRACE(("exverror(%d, \"", cond));
+ TRACEV((msg, ap));
+ TRACE(("\") pid=%d\n", getpid()));
+ } else
+ TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
+ if (msg)
+#endif
+ exvwarning(msg, ap);
+
+ flushall();
+ exraise(cond);
+ /* NOTREACHED */
+}
+
+
+static void
+error(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ exverror(EXERROR, msg, ap);
+ /* NOTREACHED */
+ va_end(ap);
+}
+
+
+static void
+exerror(int cond, const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ exverror(cond, msg, ap);
+ /* NOTREACHED */
+ va_end(ap);
+}
+
+/*
+ * error/warning routines for external builtins
+ */
+
+static void
+sh_warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ exvwarning(fmt, ap);
+ va_end(ap);
+}
+
+
+/*
+ * Return a string describing an error. The returned string may be a
+ * pointer to a static buffer that will be overwritten on the next call.
+ * Action describes the operation that got the error.
+ */
+
+static const char *
+errmsg(int e, const char *em)
+{
+ if(e == ENOENT || e == ENOTDIR) {
+
+ return em;
+ }
+ return strerror(e);
+}
+
+
+/* $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $ */
+
+/*
+ * Evaluate a command.
+ */
+
+/* flags in argument to evaltree */
+#define EV_EXIT 01 /* exit after evaluating tree */
+#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04 /* command executing within back quotes */
+
+
+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 commmand.
+ */
+
+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, EV_TESTED);
+ }
+ return exitstatus;
+}
+
+
+/*
+ * Execute a command or commands contained in a string.
+ */
+
+static void
+evalstring(char *s, int flag)
+{
+ union node *n;
+ struct stackmark smark;
+
+ setstackmark(&smark);
+ setinputstring(s);
+
+ while ((n = parsecmd(0)) != NEOF) {
+ evaltree(n, flag);
+ popstackmark(&smark);
+ if (evalskip)
+ break;
+ }
+ popfile();
+ popstackmark(&smark);
+}
+
+
+
+/*
+ * 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:
+#ifdef 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 (pendingsigs)
+ dotrap();
+ if (flags & EV_EXIT || checkexit & exitstatus)
+ 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;