sync with dash_0.5.3-1
author"Vladimir N. Oleynik" <dzo@simtreas.ru>
Sun, 15 Jan 2006 14:21:01 +0000 (14:21 -0000)
committer"Vladimir N. Oleynik" <dzo@simtreas.ru>
Sun, 15 Jan 2006 14:21:01 +0000 (14:21 -0000)
shell/ash.c

index 4fc3e0e151f90ad18cd70a6b5995a8e3e8e1eb20..0697a4785d30a098a2f3a8a48f7aea533493b1f3 100644 (file)
@@ -36,7 +36,7 @@
  * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
  * dynamic variables.
  *
- * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2004 to be
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
  * used in busybox and size optimizations,
  * rewrote arith (see notes to this), added locale support,
  * rewrote dynamic variables.
@@ -66,7 +66,6 @@
 #endif
 
 #include <sys/types.h>
-#include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/param.h>
 #include <sys/resource.h>
@@ -91,7 +90,6 @@
 #include <setjmp.h>
 #include <signal.h>
 #include <stdint.h>
-#include <sysexits.h>
 #include <time.h>
 #include <fnmatch.h>
 
@@ -130,7 +128,7 @@ static int *dash_errno;
 
 
 #ifdef CONFIG_ASH_ALIAS
-/*      $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $       */
+/*      alias.h       */
 
 #define ALIASINUSE      1
 #define ALIASDEAD       2
@@ -150,12 +148,12 @@ static int unalias(const char *);
 static void printalias(const struct alias *);
 #endif
 
-/*      $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $  */
+/*      cd.h  */
 
 
 static void    setpwd(const char *, int);
 
-/*      $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $      */
+/*      error.h      */
 
 
 /*
@@ -189,8 +187,6 @@ static int exception;
 static volatile int suppressint;
 static volatile sig_atomic_t intpending;
 
-static int exerrno;            /* Last exec error, error for EXEXEC */
-
 /* exceptions */
 #define EXINT 0         /* SIGINT received */
 #define EXERROR 1       /* a generic error */
@@ -240,7 +236,7 @@ static volatile sig_atomic_t pendingsigs;
 static void exraise(int) __attribute__((__noreturn__));
 static void onint(void) __attribute__((__noreturn__));
 
-static void error(const char *, ...) __attribute__((__noreturn__));
+static void sh_error(const char *, ...) __attribute__((__noreturn__));
 static void exerror(int, const char *, ...) __attribute__((__noreturn__));
 
 static void sh_warnx(const char *, ...);
@@ -276,7 +272,7 @@ static void forceinton(void)
        })
 #endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */
 
-/*      $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $     */
+/*      expand.h     */
 
 struct strlist {
        struct strlist *next;
@@ -313,7 +309,7 @@ static int casematch(union node *, char *);
 static void expari(int);
 #endif
 
-/*      $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $       */
+/*      eval.h       */
 
 static char *commandname;              /* currently executing command */
 static struct strlist *cmdenviron;     /* environment for builtin command */
@@ -492,7 +488,7 @@ struct funcnode {
 
 
 static void freefunc(struct funcnode *);
-/*      $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $     */
+/*      parser.h     */
 
 /* control characters in argument strings */
 #define CTL_FIRST '\201'        /* first 'special' character */
@@ -591,7 +587,7 @@ static void fixredir(union node *, const char *, int);
 static const char *const *findkwd(const char *);
 static char *endofname(const char *);
 
-/*      $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $   */
+/*      shell.h   */
 
 typedef void *pointer;
 
@@ -693,7 +689,7 @@ static const char *tokname(int tok)
        return buf;
 }
 
-/*      $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $    */
+/*      machdep.h    */
 
 /*
  * Most machines require the value returned from malloc to be aligned
@@ -1160,7 +1156,7 @@ static const char syntax_index_table[258] = {
 
 #endif                                                  /* USE_SIT_FUNCTION */
 
-/*      $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $      */
+/*      alias.c      */
 
 
 #define ATABSIZE 39
@@ -1207,29 +1203,27 @@ static struct nodelist *copynodelist(struct nodelist *);
 static char *nodesavestr(char *);
 
 
-
-static void evalstring(char *);
+static int evalstring(char *, int mask);
 union node;     /* BLETCH for ansi C */
 static void evaltree(union node *, int);
 static void evalbackcmd(union node *, struct backcmd *);
 
-/* in_function returns nonzero if we are currently evaluating a function */
-#define in_function()   funcnest
 static int evalskip;                   /* set if we are skipping commands */
 static int skipcount;           /* number of levels to skip */
 static int funcnest;                   /* depth of function calls */
 
 /* reasons for skipping commands (see comment on breakcmd routine) */
-#define SKIPBREAK       1
-#define SKIPCONT        2
-#define SKIPFUNC        3
-#define SKIPFILE        4
+#define SKIPBREAK      (1 << 0)
+#define SKIPCONT       (1 << 1)
+#define SKIPFUNC       (1 << 2)
+#define SKIPFILE       (1 << 3)
+#define SKIPEVAL       (1 << 4)
 
 /*
  * This file was generated by the mkbuiltins program.
  */
 
-#ifdef JOBS
+#if JOBS
 static int bgcmd(int, char **);
 #endif
 static int breakcmd(int, char **);
@@ -1246,7 +1240,7 @@ static int execcmd(int, char **);
 static int exitcmd(int, char **);
 static int exportcmd(int, char **);
 static int falsecmd(int, char **);
-#ifdef JOBS
+#if JOBS
 static int fgcmd(int, char **);
 #endif
 #ifdef CONFIG_ASH_GETOPTS
@@ -1256,7 +1250,7 @@ static int hashcmd(int, char **);
 #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
 static int helpcmd(int argc, char **argv);
 #endif
-#ifdef JOBS
+#if JOBS
 static int jobscmd(int, char **);
 #endif
 #ifdef CONFIG_ASH_MATH_SUPPORT
@@ -1276,18 +1270,18 @@ static int umaskcmd(int, char **);
 static int unsetcmd(int, char **);
 static int waitcmd(int, char **);
 static int ulimitcmd(int, char **);
-#ifdef JOBS
+#if JOBS
 static int killcmd(int, char **);
 #endif
 
-/*      $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $        */
+/*      mail.h        */
 
 #ifdef CONFIG_ASH_MAIL
 static void chkmail(void);
 static void changemail(const char *);
 #endif
 
-/*      $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $    */
+/*      exec.h    */
 
 /* values of cmdtype */
 #define CMDUNKNOWN      -1      /* no entry in table for command */
@@ -1327,7 +1321,7 @@ static const struct builtincmd builtincmd[] = {
 #ifdef CONFIG_ASH_ALIAS
        { BUILTIN_REG_ASSG      "alias", aliascmd },
 #endif
-#ifdef JOBS
+#if JOBS
        { BUILTIN_REGULAR       "bg", bgcmd },
 #endif
        { BUILTIN_SPEC_REG      "break", breakcmd },
@@ -1345,7 +1339,7 @@ static const struct builtincmd builtincmd[] = {
        { BUILTIN_SPEC_REG      "exit", exitcmd },
        { BUILTIN_SPEC_REG_ASSG "export", exportcmd },
        { BUILTIN_REGULAR       "false", falsecmd },
-#ifdef JOBS
+#if JOBS
        { BUILTIN_REGULAR       "fg", fgcmd },
 #endif
 #ifdef CONFIG_ASH_GETOPTS
@@ -1355,7 +1349,7 @@ static const struct builtincmd builtincmd[] = {
 #ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
        { BUILTIN_NOSPEC        "help", helpcmd },
 #endif
-#ifdef JOBS
+#if JOBS
        { BUILTIN_REGULAR       "jobs", jobscmd },
        { BUILTIN_REGULAR       "kill", killcmd },
 #endif
@@ -1434,11 +1428,11 @@ static void change_random(const char *);
 # endif
 #endif
 
-/*      $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $        */
+/*      init.h        */
 
 static void reset(void);
 
-/*      $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $     */
+/*      var.h     */
 
 /*
  * Shell variables.
@@ -1613,7 +1607,7 @@ static int nullredirs;
 
 extern char **environ;
 
-/*      $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $     */
+/*      output.h     */
 
 
 static void outstr(const char *, FILE *);
@@ -1714,6 +1708,11 @@ init(void)
 
 /* PEOF (the end of file marker) */
 
+enum {
+       INPUT_PUSH_FILE = 1,
+       INPUT_NOFILE_OK = 2,
+};
+
 /*
  * 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
@@ -1726,7 +1725,6 @@ 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);
@@ -1734,7 +1732,7 @@ static void popallfiles(void);
 static void closescript(void);
 
 
-/*      $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $    */
+/*      jobs.h    */
 
 
 /* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
@@ -1801,18 +1799,19 @@ static void setjobctl(int);
 static void showjobs(FILE *, int);
 #endif
 
-/*      $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $        */
+/*      main.h        */
 
 
 /* pid of main shell */
 static int rootpid;
-/* true if we aren't a child of the main shell */
-static int rootshell;
+/* shell level: 0 for the main shell, 1 for its children, and so on */
+static int shlvl;
+#define rootshell (!shlvl)
 
 static void readcmdfile(char *);
-static void cmdloop(int);
+static int cmdloop(int);
 
-/*      $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $        */
+/*      memalloc.h        */
 
 
 struct stackmark {
@@ -1853,7 +1852,7 @@ static char *stnputs(const char *, size_t, char *);
 static char *stputs(const char *, char *);
 
 
-static inline char *_STPUTC(char c, char *p) {
+static inline char *_STPUTC(int c, char *p) {
        if (p == sstrend)
                p = growstackstr();
        *p++ = c;
@@ -1885,7 +1884,7 @@ static inline char *_STPUTC(char c, char *p) {
 
 #define ckfree(p)       free((pointer)(p))
 
-/*      $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $   */
+/*      mystring.h   */
 
 
 #define DOLATSTRLEN 4
@@ -1899,7 +1898,7 @@ 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 $ */
+/*      options.h */
 
 struct shparam {
        int nparam;             /* # of positional parameters (without $0) */
@@ -1925,19 +1924,18 @@ struct shparam {
 #define aflag optlist[10]
 #define bflag optlist[11]
 #define uflag optlist[12]
-#define qflag optlist[13]
-#define viflag optlist[14]
+#define viflag optlist[13]
 
 #ifdef DEBUG
-#define nolog optlist[15]
-#define debug optlist[16]
+#define nolog optlist[14]
+#define debug optlist[15]
 #endif
 
 #ifndef CONFIG_FEATURE_COMMAND_EDITING_VI
 #define setvimode(on) viflag = 0   /* forcibly keep the option off */
 #endif
 
-/*      $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */
+/*      options.c */
 
 
 static const char *const optletters_optnames[] = {
@@ -1954,7 +1952,6 @@ static const char *const optletters_optnames[] = {
        "a"   "allexport",
        "b"   "notify",
        "u"   "nounset",
-       "q"   "quietprofile",
        "\0"  "vi",
 #ifdef DEBUG
        "\0"  "nolog",
@@ -1987,7 +1984,7 @@ 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 $      */
+/*      redir.h      */
 
 /* flags passed to redirect */
 #define REDIR_PUSH 01           /* save previous values of file descriptors */
@@ -2000,7 +1997,7 @@ 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 $     */
+/*      show.h     */
 
 
 #ifdef DEBUG
@@ -2013,7 +2010,7 @@ 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.h       */
 
 
 /* trap handler commands */
@@ -2027,7 +2024,7 @@ static void clear_traps(void);
 static void setsignal(int);
 static void ignoresig(int);
 static void onsig(int);
-static void dotrap(void);
+static int dotrap(void);
 static void setinteractive(int);
 static void exitshell(void) __attribute__((__noreturn__));
 static int decode_signal(const char *, int);
@@ -2044,7 +2041,6 @@ reset(void)
       {
              evalskip = 0;
              loopnest = 0;
-             funcnest = 0;
       }
 
       /* from input.c: */
@@ -2250,7 +2246,7 @@ __lookupalias(const char *name) {
 #endif /* CONFIG_ASH_ALIAS */
 
 
-/*      $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $      */
+/*      cd.c      */
 
 /*
  * The cd and pwd commands.
@@ -2337,7 +2333,7 @@ docd:
                        break;
                }
        } while (path);
-       error("can't cd to %s", dest);
+       sh_error("can't cd to %s", dest);
        /* NOTREACHED */
 out:
        if (flags & CD_PRINT)
@@ -2495,7 +2491,7 @@ setpwd(const char *val, int setold)
        setvar("PWD", dir, VEXPORT);
 }
 
-/*      $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $   */
+/*      error.c   */
 
 /*
  * Errors and exceptions.
@@ -2602,7 +2598,7 @@ exverror(int cond, const char *msg, va_list ap)
 
 
 static void
-error(const char *msg, ...)
+sh_error(const char *msg, ...)
 {
        va_list ap;
 
@@ -2656,7 +2652,7 @@ errmsg(int e, const char *em)
 }
 
 
-/*      $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $  */
+/*      eval.c  */
 
 /*
  * Evaluate a command.
@@ -2715,7 +2711,8 @@ evalcmd(int argc, char **argv)
                        STPUTC('\0', concat);
                        p = grabstackstr(concat);
                }
-               evalstring(p);
+               evalstring(p, ~SKIPEVAL);
+
        }
        return exitstatus;
 }
@@ -2725,23 +2722,29 @@ evalcmd(int argc, char **argv)
  * Execute a command or commands contained in a string.
  */
 
-static void
-evalstring(char *s)
+static int
+evalstring(char *s, int mask)
 {
        union node *n;
        struct stackmark smark;
+       int skip;
 
-       setstackmark(&smark);
        setinputstring(s);
+       setstackmark(&smark);
 
+       skip = 0;
        while ((n = parsecmd(0)) != NEOF) {
                evaltree(n, 0);
                popstackmark(&smark);
-               if (evalskip)
+               skip = evalskip;
+               if (skip)
                        break;
        }
        popfile();
-       popstackmark(&smark);
+
+       skip &= mask;
+       evalskip = skip;
+       return skip;
 }
 
 
@@ -2853,10 +2856,15 @@ setstatus:
                break;
        }
 out:
-       if (pendingsigs)
-               dotrap();
-       if (flags & EV_EXIT || checkexit & exitstatus)
+       if ((checkexit & exitstatus))
+               evalskip |= SKIPEVAL;
+       else if (pendingsigs && dotrap())
+               goto exexit;
+
+       if (flags & EV_EXIT) {
+exexit:
                exraise(EXEXIT);
+       }
 }
 
 
@@ -3071,7 +3079,7 @@ evalpipe(union node *n, int flags)
                if (lp->next) {
                        if (pipe(pip) < 0) {
                                close(prevfd);
-                               error("Pipe call failed");
+                               sh_error("Pipe call failed");
                        }
                }
                if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
@@ -3132,7 +3140,7 @@ evalbackcmd(union node *n, struct backcmd *result)
                struct job *jp;
 
                if (pipe(pip) < 0)
-                       error("Pipe call failed");
+                       sh_error("Pipe call failed");
                jp = makejob(n, 1);
                if (forkshell(jp, n, FORK_NOJOB) == 0) {
                        FORCEINTON;
@@ -3485,6 +3493,7 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
        localvars = NULL;
        shellparam.malloc = 0;
        func->count++;
+       funcnest++;
        INTON;
        shellparam.nparam = argc - 1;
        shellparam.p = argv + 1;
@@ -3492,11 +3501,10 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
        shellparam.optind = 1;
        shellparam.optoff = -1;
 #endif
-       funcnest++;
        evaltree(&func->n, flags & EV_TESTED);
-       funcnest--;
 funcdone:
        INTOFF;
+       funcnest--;
        freefunc(func);
        poplocalvars();
        localvars = savelocalvars;
@@ -3504,10 +3512,7 @@ funcdone:
        shellparam = saveparam;
        handler = savehandler;
        INTON;
-       if (evalskip == SKIPFUNC) {
-               evalskip = 0;
-               skipcount = 0;
-       }
+       evalskip &= ~SKIPFUNC;
        return e;
 }
 
@@ -3575,7 +3580,7 @@ breakcmd(int argc, char **argv)
        int n = argc > 1 ? number(argv[1]) : 1;
 
        if (n <= 0)
-               error(illnum, argv[1]);
+               sh_error(illnum, argv[1]);
        if (n > loopnest)
                n = loopnest;
        if (n > 0) {
@@ -3593,19 +3598,12 @@ breakcmd(int argc, char **argv)
 static int
 returncmd(int argc, char **argv)
 {
-       int ret = argc > 1 ? number(argv[1]) : exitstatus;
-
-       if (funcnest) {
-               evalskip = SKIPFUNC;
-               skipcount = 1;
-               return ret;
-       }
-       else {
-               /* Do what ksh does; skip the rest of the file */
-               evalskip = SKIPFILE;
-               skipcount = 1;
-               return ret;
-       }
+       /*
+        * 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;
 }
 
 
@@ -3636,7 +3634,7 @@ execcmd(int argc, char **argv)
 }
 
 
-/*      $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $    */
+/*      exec.c    */
 
 /*
  * When commands are first encountered, they are entered in a hash table.
@@ -3682,6 +3680,7 @@ shellexec(char **argv, const char *path, int idx)
        char *cmdname;
        int e;
        char **envp;
+       int exerrno;
 
        clearredir(1);
        envp = environment();
@@ -3716,6 +3715,7 @@ shellexec(char **argv, const char *path, int idx)
                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));
@@ -4004,7 +4004,7 @@ loop:
                        readcmdfile(fullname);
                        if ((cmdp = cmdlookup(name, 0)) == NULL ||
                            cmdp->cmdtype != CMDFUNCTION)
-                               error("%s not defined in %s", name, fullname);
+                               sh_error("%s not defined in %s", name, fullname);
                        stunalloc(fullname);
                        goto success;
                }
@@ -4438,46 +4438,28 @@ static int
 commandcmd(int argc, char **argv)
 {
        int c;
-       int default_path = 0;
-       int verify_only = 0;
-       int verbose_verify_only = 0;
+       enum {
+               VERIFY_BRIEF = 1,
+               VERIFY_VERBOSE = 2,
+       } verify = 0;
 
        while ((c = nextopt("pvV")) != '\0')
-               switch (c) {
-               default:
+               if (c == 'V')
+                       verify |= VERIFY_VERBOSE;
+               else if (c == 'v')
+                       verify |= VERIFY_BRIEF;
 #ifdef DEBUG
-                       fprintf(stderr,
-"command: nextopt returned character code 0%o\n", c);
-                       return EX_SOFTWARE;
+               else if (c != 'p')
+                       abort();
 #endif
-               case 'p':
-                       default_path = 1;
-                       break;
-               case 'v':
-                       verify_only = 1;
-                       break;
-               case 'V':
-                       verbose_verify_only = 1;
-                       break;
-               }
-
-       if (default_path + verify_only + verbose_verify_only > 1 ||
-           !*argptr) {
-                       fprintf(stderr,
-                               "command [-p] command [arg ...]\n"
-                               "command {-v|-V} command\n");
-                       return EX_USAGE;
-       }
-
-       if (verify_only || verbose_verify_only) {
-               return describe_command(*argptr, verbose_verify_only);
-       }
+       if (verify)
+               return describe_command(*argptr, verify - VERIFY_BRIEF);
 
        return 0;
 }
 #endif
 
-/*      $NetBSD: expand.c,v 1.56 2002/11/24 22:35:39 christos Exp $     */
+/*      expand.c     */
 
 /*
  * Routines to expand arguments to commands.  We have to deal with
@@ -4806,14 +4788,13 @@ exptilde(char *startp, char *p, int flag)
 done:
        *p = '\0';
        if (*name == '\0') {
-               if ((home = lookupvar(homestr)) == NULL)
-                       goto lose;
+               home = lookupvar(homestr);
        } else {
                if ((pw = getpwnam(name)) == NULL)
                        goto lose;
                home = pw->pw_dir;
        }
-       if (*home == '\0')
+       if (!home || !*home)
                goto lose;
        *p = c;
        startloc = expdest - (char *)stackblock();
@@ -4897,7 +4878,7 @@ expari(int quotes)
                        p--;
 #ifdef DEBUG
                        if (p < start) {
-                               error("missing CTLARI (shouldn't happen)");
+                               sh_error("missing CTLARI (shouldn't happen)");
                        }
 #endif
                }
@@ -5275,7 +5256,7 @@ memtodest(const char *p, size_t len, int syntax, int quotes) {
        q = makestrspace(len * 2, q);
 
        while (len--) {
-               int c = *p++;
+               int c = (unsigned char)*p++;
                if (!c)
                        continue;
                if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
@@ -5932,11 +5913,11 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
                } else
                        msg = umsg;
        }
-       error("%.*s: %s%s", end - var - 1, var, msg, tail);
+       sh_error("%.*s: %s%s", end - var - 1, var, msg, tail);
 }
 
 
-/*      $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $      */
+/*      input.c      */
 
 /*
  * This implements the input routines used by the parser.
@@ -6103,7 +6084,7 @@ retry:
 int
 preadbuffer(void)
 {
-       char *p, *q;
+       char *q;
        int more;
        char savec;
 
@@ -6122,39 +6103,42 @@ preadbuffer(void)
                return PEOF;
        flushall();
 
+       more = parselleft;
+       if (more <= 0) {
 again:
-       if (parselleft <= 0) {
-               if ((parselleft = preadfd()) <= 0) {
+               if ((more = preadfd()) <= 0) {
                        parselleft = parsenleft = EOF_NLEFT;
                        return PEOF;
                }
        }
 
-       q = p = parsenextc;
+       q = parsenextc;
 
        /* delete nul characters */
-       for (more = 1; more;) {
-               switch (*p) {
-               case '\0':
-                       p++;    /* Skip nul */
-                       goto check;
+       for (;;) {
+               int c;
 
-               case '\n':
-                       parsenleft = q - parsenextc;
-                       more = 0; /* Stop processing here */
-                       break;
+               more--;
+               c = *q;
 
+               if (!c)
+                       memmove(q, q + 1, more);
+               else {
+                       q++;
+                       if (c == '\n') {
+                               parsenleft = q - parsenextc - 1;
+                               break;
+                       }
                }
 
-               *q++ = *p++;
-check:
-               if (--parselleft <= 0 && more) {
+               if (more <= 0) {
                        parsenleft = q - parsenextc - 1;
                        if (parsenleft < 0)
                                goto again;
-                       more = 0;
+                       break;
                }
        }
+       parselleft = more;
 
        savec = *q;
        *q = '\0';
@@ -6247,24 +6231,29 @@ popstring(void)
  * old input onto the stack first.
  */
 
-void
-setinputfile(const char *fname, int push)
+static int
+setinputfile(const char *fname, int flags)
 {
        int fd;
        int fd2;
 
        INTOFF;
-       if ((fd = open(fname, O_RDONLY)) < 0)
-               error("Can't open %s", fname);
+       if ((fd = open(fname, O_RDONLY)) < 0) {
+               if (flags & INPUT_NOFILE_OK)
+                       goto out;
+               sh_error("Can't open %s", fname);
+       }
        if (fd < 10) {
                fd2 = copyfd(fd, 10);
                close(fd);
                if (fd2 < 0)
-                       error("Out of file descriptors");
+                       sh_error("Out of file descriptors");
                fd = fd2;
        }
-       setinputfd(fd, push);
+       setinputfd(fd, flags & INPUT_PUSH_FILE);
+out:
        INTON;
+       return fd;
 }
 
 
@@ -6378,7 +6367,7 @@ closescript(void)
        }
 }
 
-/*      $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $    */
+/*      jobs.c    */
 
 /* mode flags for set_curjob */
 #define CUR_DELETE 2
@@ -6453,14 +6442,14 @@ set_curjob(struct job *jp, unsigned mode)
                   put after all stopped jobs. */
                do {
                        jp1 = *jpp;
-#ifdef JOBS
+#if JOBS
                        if (!jp1 || jp1->state != JOBSTOPPED)
 #endif
                                break;
                        jpp = &jp1->prev_job;
                } while (1);
                /* FALLTHROUGH */
-#ifdef JOBS
+#if JOBS
        case CUR_STOPPED:
 #endif
                /* newly stopped job - becomes curjob */
@@ -6549,7 +6538,7 @@ killcmd(int argc, char **argv)
 
        if (argc <= 1) {
 usage:
-               error(
+               sh_error(
 "Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
 "kill -l [exitstatus]"
                );
@@ -6572,7 +6561,7 @@ usage:
                                case 's':
                                        signo = decode_signal(optionarg, 1);
                                        if (signo < 0) {
-                                               error(
+                                               sh_error(
                                                        "invalid signal number or name: %s",
                                                        optionarg
                                                );
@@ -6606,7 +6595,7 @@ usage:
                if (name)
                        out1fmt(snlfmt, name);
                else
-                       error("invalid signal number or exit status: %s", *argptr);
+                       sh_error("invalid signal number or exit status: %s", *argptr);
                return 0;
        }
 
@@ -6637,7 +6626,7 @@ jobno(const struct job *jp)
 }
 #endif
 
-#ifdef JOBS
+#if JOBS
 static int
 fgcmd(int argc, char **argv)
 {
@@ -7020,7 +7009,7 @@ gotit:
 #endif
        return jp;
 err:
-       error(err_msg, name);
+       sh_error(err_msg, name);
 }
 
 
@@ -7132,18 +7121,18 @@ growjobtab(void)
 static inline void
 forkchild(struct job *jp, union node *n, int mode)
 {
-       int wasroot;
+       int oldlvl;
 
        TRACE(("Child shell %d\n", getpid()));
-       wasroot = rootshell;
-       rootshell = 0;
+       oldlvl = shlvl;
+       shlvl++;
 
        closescript();
        clear_traps();
 #if JOBS
        /* do job control only in root shell */
        jobctl = 0;
-       if (mode != FORK_NOJOB && jp->jobctl && wasroot) {
+       if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
                pid_t pgrp;
 
                if (jp->nprocs == 0)
@@ -7164,10 +7153,10 @@ forkchild(struct job *jp, union node *n, int mode)
                if (jp->nprocs == 0) {
                        close(0);
                        if (open(_PATH_DEVNULL, O_RDONLY) != 0)
-                               error("Can't open %s", _PATH_DEVNULL);
+                               sh_error("Can't open %s", _PATH_DEVNULL);
                }
        }
-       if (wasroot && iflag) {
+       if (!oldlvl && iflag) {
                setsignal(SIGINT);
                setsignal(SIGQUIT);
                setsignal(SIGTERM);
@@ -7225,7 +7214,7 @@ forkshell(struct job *jp, union node *n, int mode)
                TRACE(("Fork failed, errno=%d", errno));
                if (jp)
                        freejob(jp);
-               error("Cannot fork");
+               sh_error("Cannot fork");
        }
        if (pid == 0)
                forkchild(jp, n, mode);
@@ -7365,7 +7354,7 @@ dowait(int block, struct job *job)
                        }
                        if (sp->status == -1)
                                state = JOBRUNNING;
-#ifdef JOBS
+#if JOBS
                        if (state == JOBRUNNING)
                                continue;
                        if (WIFSTOPPED(sp->status)) {
@@ -7377,7 +7366,7 @@ dowait(int block, struct job *job)
                if (thisjob)
                        goto gotjob;
        }
-#ifdef JOBS
+#if JOBS
        if (!WIFSTOPPED(status))
 #endif
 
@@ -7391,7 +7380,7 @@ gotjob:
                if (thisjob->state != state) {
                        TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
                        thisjob->state = state;
-#ifdef JOBS
+#if JOBS
                        if (state == JOBSTOPPED) {
                                set_curjob(thisjob, CUR_STOPPED);
                        }
@@ -7739,7 +7728,7 @@ static void
 xtcsetpgrp(int fd, pid_t pgrp)
 {
        if (tcsetpgrp(fd, pgrp))
-               error("Cannot set tty process group (%m)");
+               sh_error("Cannot set tty process group (%m)");
 }
 #endif /* JOBS */
 
@@ -7771,7 +7760,7 @@ getstatus(struct job *job) {
 }
 
 #ifdef CONFIG_ASH_MAIL
-/*      $NetBSD: mail.c,v 1.15 2002/11/24 22:35:40 christos Exp $       */
+/*      mail.c       */
 
 /*
  * Routines to check for mail.  (Perhaps make part of main.c?)
@@ -7842,7 +7831,7 @@ changemail(const char *val)
 
 #endif /* CONFIG_ASH_MAIL */
 
-/*      $NetBSD: main.c,v 1.46 2002/12/11 19:12:18 christos Exp $       */
+/*      main.c       */
 
 
 #if PROFILE
@@ -7879,28 +7868,16 @@ ash_main(int argc, char **argv)
 #endif
        state = 0;
        if (setjmp(jmploc.loc)) {
-               int status;
                int e;
+               int s;
 
                reset();
 
                e = exception;
-               switch (exception) {
-               case EXEXEC:
-                       status = exerrno;
-                       break;
-
-               case EXERROR:
-                       status = 2;
-                       break;
-
-               default:
-                       status = exitstatus;
-                       break;
-               }
-               exitstatus = status;
-
-               if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
+               if (e == EXERROR)
+                       exitstatus = 2;
+               s = state;
+               if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
                        exitshell();
 
                if (e == EXINT) {
@@ -7908,11 +7885,11 @@ ash_main(int argc, char **argv)
                }
                popstackmark(&smark);
                FORCEINTON;                             /* enable interrupts */
-               if (state == 1)
+               if (s == 1)
                        goto state1;
-               else if (state == 2)
+               else if (s == 2)
                        goto state2;
-               else if (state == 3)
+               else if (s == 3)
                        goto state3;
                else
                        goto state4;
@@ -7927,7 +7904,6 @@ ash_main(int argc, char **argv)
 #ifdef CONFIG_ASH_RANDOM_SUPPORT
        rseed = rootpid + ((time_t)time((time_t *)0));
 #endif
-       rootshell = 1;
        init();
        setstackmark(&smark);
        procargs(argc, argv);
@@ -7969,7 +7945,7 @@ state2:
 state3:
        state = 4;
        if (minusc)
-               evalstring(minusc);
+               evalstring(minusc, 0);
 
        if (sflag || minusc == NULL) {
 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
@@ -8002,7 +7978,7 @@ state4: /* XXX ??? - why isn't this before the "if" statement */
  * loop; it turns on prompting if the shell is interactive.
  */
 
-static void
+static int
 cmdloop(int top)
 {
        union node *n;
@@ -8012,9 +7988,9 @@ cmdloop(int top)
 
        TRACE(("cmdloop(%d) called\n", top));
        for (;;) {
+               int skip;
+
                setstackmark(&smark);
-               if (pendingsigs)
-                       dotrap();
 #if JOBS
                if (jobctl)
                        showjobs(stderr, SHOW_CHANGED);
@@ -8037,17 +8013,21 @@ cmdloop(int top)
                                out2str("\nUse \"exit\" to leave shell.\n");
                        }
                        numeof++;
-               } else if (n != NULL && nflag == 0) {
+               } else if (nflag == 0) {
                        job_warning = (job_warning == 2) ? 1 : 0;
                        numeof = 0;
                        evaltree(n, 0);
                }
                popstackmark(&smark);
-               if (evalskip) {
+               skip = evalskip;
+
+               if (skip) {
                        evalskip = 0;
-                       break;
+                       return skip & SKIPEVAL;
                }
        }
+
+       return 0;
 }
 
 
@@ -8058,31 +8038,16 @@ cmdloop(int top)
 static void
 read_profile(const char *name)
 {
-       int fd;
-       int xflag_set = 0;
-       int vflag_set = 0;
+       int skip;
 
-       INTOFF;
-       if ((fd = open(name, O_RDONLY)) >= 0)
-               setinputfd(fd, 1);
-       INTON;
-       if (fd < 0)
+       if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
                return;
-       /* -q turns off -x and -v just when executing init files */
-       if (qflag)  {
-           if (xflag)
-                   xflag = 0, xflag_set = 1;
-           if (vflag)
-                   vflag = 0, vflag_set = 1;
-       }
-       cmdloop(0);
-       if (qflag)  {
-           if (xflag_set)
-                   xflag = 1;
-           if (vflag_set)
-                   vflag = 1;
-       }
+
+       skip = cmdloop(0);
        popfile();
+
+       if (skip)
+               exitshell();
 }
 
 
@@ -8093,14 +8058,7 @@ read_profile(const char *name)
 static void
 readcmdfile(char *name)
 {
-       int fd;
-
-       INTOFF;
-       if ((fd = open(name, O_RDONLY)) >= 0)
-               setinputfd(fd, 1);
-       else
-               error("Can't open %s", name);
-       INTON;
+       setinputfile(name, INPUT_PUSH_FILE);
        cmdloop(0);
        popfile();
 }
@@ -8134,7 +8092,7 @@ find_dot_file(char *name)
        }
 
        /* not found in the PATH */
-       error(not_found_msg, name);
+       sh_error(not_found_msg, name);
        /* NOTREACHED */
 }
 
@@ -8142,17 +8100,14 @@ static int dotcmd(int argc, char **argv)
 {
        struct strlist *sp;
        volatile struct shparam saveparam;
-
-       exitstatus = 0;
+       int status = 0;
 
        for (sp = cmdenviron; sp; sp = sp->next)
                setvareq(bb_xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
 
        if (argc >= 2) {        /* That's what SVR2 does */
                char *fullname;
-               struct stackmark smark;
 
-               setstackmark(&smark);
                fullname = find_dot_file(argv[1]);
 
                if (argc > 2) {
@@ -8162,7 +8117,7 @@ static int dotcmd(int argc, char **argv)
                        shellparam.p = argv + 2;
                };
 
-               setinputfile(fullname, 1);
+               setinputfile(fullname, INPUT_PUSH_FILE);
                commandname = fullname;
                cmdloop(0);
                popfile();
@@ -8171,10 +8126,9 @@ static int dotcmd(int argc, char **argv)
                        freeparam(&shellparam);
                        shellparam = saveparam;
                };
-
-               popstackmark(&smark);
+               status = exitstatus;
        }
-       return exitstatus;
+       return status;
 }
 
 
@@ -8196,7 +8150,7 @@ echocmd(int argc, char **argv)
        return bb_echo(argc, argv);
 }
 #endif
-/*      $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $        */
+/*      memalloc.c        */
 
 /*
  * Same for malloc, realloc, but returns an error when out of space.
@@ -8207,7 +8161,7 @@ ckrealloc(pointer p, size_t nbytes)
 {
        p = realloc(p, nbytes);
        if (p == NULL)
-               error(bb_msg_memory_exhausted);
+               sh_error(bb_msg_memory_exhausted);
        return p;
 }
 
@@ -8226,7 +8180,7 @@ savestr(const char *s)
 {
        char *p = strdup(s);
        if (!p)
-               error(bb_msg_memory_exhausted);
+               sh_error(bb_msg_memory_exhausted);
        return p;
 }
 
@@ -8258,7 +8212,7 @@ stalloc(size_t nbytes)
                        blocksize = MINSIZE;
                len = sizeof(struct stack_block) - MINSIZE + blocksize;
                if (len < blocksize)
-                       error(bb_msg_memory_exhausted);
+                       sh_error(bb_msg_memory_exhausted);
                INTOFF;
                sp = ckmalloc(len);
                sp->prev = stackp;
@@ -8336,7 +8290,7 @@ growstackblock(void)
 
        newlen = stacknleft * 2;
        if (newlen < stacknleft)
-               error(bb_msg_memory_exhausted);
+               sh_error(bb_msg_memory_exhausted);
        if (newlen < 128)
                newlen += 128;
 
@@ -8456,7 +8410,7 @@ stputs(const char *s, char *p)
        return stnputs(s, strlen(s), p);
 }
 
-/*      $NetBSD: mystring.c,v 1.15 2002/11/24 22:35:42 christos Exp $   */
+/*      mystring.c   */
 
 /*
  * String functions.
@@ -8490,7 +8444,7 @@ number(const char *s)
 {
 
        if (! is_number(s))
-               error(illnum, s);
+               sh_error(illnum, s);
        return atoi(s);
 }
 
@@ -8815,7 +8769,7 @@ procargs(int argc, char **argv)
        xminusc = minusc;
        if (*xargv == NULL) {
                if (xminusc)
-                       error("-c requires an argument");
+                       sh_error("-c requires an argument");
                sflag = 1;
        }
        if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
@@ -8881,7 +8835,7 @@ minus_o(char *name, int val)
                                optlist[i] = val;
                                return;
                        }
-               error("Illegal option -o %s", name);
+               sh_error("Illegal option -o %s", name);
        }
 }
 
@@ -8949,7 +8903,7 @@ setoption(int flag, int val)
                        optlist[i] = val;
                        return;
                }
-       error("Illegal option -%c", flag);
+       sh_error("Illegal option -%c", flag);
        /* NOTREACHED */
 }
 
@@ -9015,7 +8969,7 @@ shiftcmd(int argc, char **argv)
        if (argc > 1)
                n = number(argv[1]);
        if (n > shellparam.nparam)
-               error("can't shift that many");
+               sh_error("can't shift that many");
        INTOFF;
        shellparam.nparam -= n;
        for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
@@ -9203,7 +9157,7 @@ getoptscmd(int argc, char **argv)
        char **optbase;
 
        if (argc < 3)
-               error("Usage: getopts optstring var [arg]");
+               sh_error("Usage: getopts optstring var [arg]");
        else if (argc == 3) {
                optbase = shellparam.p;
                if (shellparam.optind > shellparam.nparam + 1) {
@@ -9253,13 +9207,13 @@ nextopt(const char *optstring)
        c = *p++;
        for (q = optstring ; *q != c ; ) {
                if (*q == '\0')
-                       error("Illegal option -%c", c);
+                       sh_error("Illegal option -%c", c);
                if (*++q == ':')
                        q++;
        }
        if (*++q == ':') {
                if (*p == '\0' && (p = *argptr++) == NULL)
-                       error("No arg for -%c option", c);
+                       sh_error("No arg for -%c option", c);
                optionarg = p;
                p = NULL;
        }
@@ -9268,7 +9222,7 @@ nextopt(const char *optstring)
 }
 
 
-/*      $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $     */
+/*      output.c     */
 
 void
 outstr(const char *p, FILE *file)
@@ -9336,7 +9290,7 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...)
 
 
 
-/*      $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $     */
+/*      parser.c     */
 
 
 /*
@@ -10855,7 +10809,7 @@ static void synexpect(int token)
 static void
 synerror(const char *msg)
 {
-       error("Syntax error: %s", msg);
+       sh_error("Syntax error: %s", msg);
        /* NOTREACHED */
 }
 
@@ -10923,7 +10877,7 @@ static const char *const *findkwd(const char *s)
                                   sizeof(const char *), pstrcmp);
 }
 
-/*      $NetBSD: redir.c,v 1.27 2002/11/24 22:35:42 christos Exp $      */
+/*      redir.c      */
 
 /*
  * Code for dealing with input/output redirection.
@@ -11007,7 +10961,7 @@ openhere(union node *redir)
        size_t len = 0;
 
        if (pipe(pip) < 0)
-               error("Pipe call failed");
+               sh_error("Pipe call failed");
        if (redir->type == NHERE) {
                len = strlen(redir->nhere.doc->narg.text);
                if (len <= PIPESIZE) {
@@ -11088,9 +11042,9 @@ openredirect(union node *redir)
 
        return f;
 ecreate:
-       error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+       sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
 eopen:
-       error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+       sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
 }
 
 static inline void
@@ -11164,7 +11118,7 @@ redirect(union node *redir, int flags)
                                if (i != EBADF) {
                                        close(newfd);
                                        errno = i;
-                                       error("%d: %m", fd);
+                                       sh_error("%d: %m", fd);
                                        /* NOTREACHED */
                                }
                        } else {
@@ -11247,7 +11201,7 @@ copyfd(int from, int to)
                if (errno == EMFILE)
                        return EMPTY;
                else
-                       error("%d: %m", from);
+                       sh_error("%d: %m", from);
        }
        return newfd;
 }
@@ -11273,7 +11227,7 @@ redirectsafe(union node *redir, int flags)
        return err;
 }
 
-/*      $NetBSD: show.c,v 1.24 2003/01/22 20:36:04 dsl Exp $    */
+/*      show.c    */
 
 #ifdef DEBUG
 static void shtree(union node *, int, char *, FILE*);
@@ -11620,7 +11574,7 @@ opentrace(void)
 #endif /* DEBUG */
 
 
-/*      $NetBSD: trap.c,v 1.28 2002/11/24 22:35:43 christos Exp $       */
+/*      trap.c       */
 
 /*
  * Sigmode records the current value of the signal handlers for the various
@@ -11669,7 +11623,7 @@ trapcmd(int argc, char **argv)
                action = *ap++;
        while (*ap) {
                if ((signo = decode_signal(*ap, 0)) < 0)
-                       error("%s: bad trap", *ap);
+                       sh_error("%s: bad trap", *ap);
                INTOFF;
                if (action) {
                        if (action[0] == '-' && action[1] == '\0')
@@ -11834,23 +11788,34 @@ onsig(int signo)
  * handlers while we are executing a trap handler.
  */
 
-void
+int
 dotrap(void)
 {
        char *p;
        char *q;
+       int i;
        int savestatus;
+       int skip = 0;
 
        savestatus = exitstatus;
-       q = gotsig;
-       while (pendingsigs = 0, xbarrier(), (p = memchr(q, 1, NSIG - 1))) {
-               *p = 0;
-               p = trap[p - q + 1];
+       pendingsigs = 0;
+       xbarrier();
+
+       for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
+               if (!*q)
+                       continue;
+               *q = 0;
+
+               p = trap[i + 1];
                if (!p)
                        continue;
-               evalstring(p);
+               skip = evalstring(p, SKIPEVAL);
                exitstatus = savestatus;
+               if (skip)
+                       break;
        }
+
+       return skip;
 }
 
 
@@ -11931,17 +11896,18 @@ exitshell(void)
        struct jmploc loc;
        char *p;
        int status;
-       int jmp;
 
-       jmp = setjmp(loc.loc);
        status = exitstatus;
        TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
-       if (jmp)
+       if (setjmp(loc.loc)) {
+               if (exception == EXEXIT)
+                       _exit(exitstatus);
                goto out;
+       }
        handler = &loc;
-       if ((p = trap[0]) != NULL && *p != '\0') {
+       if ((p = trap[0])) {
                trap[0] = NULL;
-               evalstring(p);
+               evalstring(p, 0);
        }
        flushall();
        setjobctl(0);
@@ -11966,7 +11932,7 @@ static int decode_signal(const char *string, int minsig)
        return name ? signo : -1;
 }
 
-/*      $NetBSD: var.c,v 1.32 2003/01/22 20:36:04 dsl Exp $     */
+/*      var.c     */
 
 static struct var *vartab[VTABSIZE];
 
@@ -12022,7 +11988,7 @@ setvar(const char *name, const char *val, int flags)
        p = strchrnul(q, '=');
        namelen = p - name;
        if (!namelen || p != q)
-               error("%.*s: bad variable name", namelen, name);
+               sh_error("%.*s: bad variable name", namelen, name);
        vallen = 0;
        if (val == NULL) {
                flags |= VUNSET;
@@ -12031,9 +11997,8 @@ setvar(const char *name, const char *val, int flags)
        }
        INTOFF;
        p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
-       *p++ = '\0';
        if (val) {
-               p[-1] = '=';
+               *p++ = '=';
                p = mempcpy(p, val, vallen);
        }
        *p = '\0';
@@ -12065,7 +12030,7 @@ setvareq(char *s, int flags)
                        if (flags & VNOSAVE)
                                free(s);
                        n = vp->text;
-                       error("%.*s: is read only", strchrnul(n, '=') - n, n);
+                       sh_error("%.*s: is read only", strchrnul(n, '=') - n, n);
                }
 
                if (flags & VNOSET)
@@ -12495,7 +12460,7 @@ findvar(struct var **vpp, const char *name)
        }
        return vpp;
 }
-/*      $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $      */
+/*      setmode.c      */
 
 #include <sys/times.h>
 
@@ -12540,11 +12505,11 @@ dash_arith(const char *s)
        result = arith(s, &errcode);
        if (errcode < 0) {
                if (errcode == -3)
-                       error("exponent less than 0");
+                       sh_error("exponent less than 0");
                else if (errcode == -2)
-                       error("divide by zero");
+                       sh_error("divide by zero");
                else if (errcode == -5)
-                       error("expression recursion loop detected");
+                       sh_error("expression recursion loop detected");
                else
                        synerror(s);
        }
@@ -12569,7 +12534,7 @@ letcmd(int argc, char **argv)
 
        ap = argv + 1;
        if(!*ap)
-               error("expression expected");
+               sh_error("expression expected");
        for (ap = argv + 1; *ap; ap++) {
                i = dash_arith(*ap);
        }
@@ -12578,7 +12543,7 @@ letcmd(int argc, char **argv)
 }
 #endif /* CONFIG_ASH_MATH_SUPPORT */
 
-/*      $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $  */
+/*      miscbltin.c  */
 
 /*
  * Miscellaneous builtins.
@@ -12646,7 +12611,7 @@ readcmd(int argc, char **argv)
                case 'n':
                        nchars = strtol(optionarg, &p, 10);
                        if (*p)
-                               error("invalid count");
+                               sh_error("invalid count");
                        nch_flag = (nchars > 0);
                        break;
                case 's':
@@ -12663,19 +12628,19 @@ readcmd(int argc, char **argv)
                                        int scale;
                                        ts.tv_usec = strtol(p, &p2, 10);
                                        if (*p2)
-                                               error("invalid timeout");
+                                               sh_error("invalid timeout");
                                        scale = p2 - p;
                                        /* normalize to usec */
                                        if (scale > 6)
-                                               error("invalid timeout");
+                                               sh_error("invalid timeout");
                                        while (scale++ < 6)
                                                ts.tv_usec *= 10;
                                }
                        } else if (*p) {
-                               error("invalid timeout");
+                               sh_error("invalid timeout");
                        }
                        if ( ! ts.tv_sec && ! ts.tv_usec)
-                               error("invalid timeout");
+                               sh_error("invalid timeout");
                        break;
 #endif
                case 'r':
@@ -12689,7 +12654,7 @@ readcmd(int argc, char **argv)
                out2str(prompt);
        }
        if (*(ap = argptr) == NULL)
-               error("arg count");
+               sh_error("arg count");
        if ((ifs = bltinlookup("IFS")) == NULL)
                ifs = defifs;
 #if defined(CONFIG_ASH_READ_NCHARS)
@@ -12832,14 +12797,14 @@ static int umaskcmd(int argc, char **argv)
                        mask = 0;
                        do {
                                if (*ap >= '8' || *ap < '0')
-                                       error(illnum, argv[1]);
+                                       sh_error(illnum, argv[1]);
                                mask = (mask << 3) + (*ap - '0');
                        } while (*++ap != '\0');
                        umask(mask);
                } else {
                        mask = ~mask & 0777;
                        if (!bb_parse_mode(ap, &mask)) {
-                               error("Illegal mode: %s", ap);
+                               sh_error("Illegal mode: %s", ap);
                        }
                        umask(~mask & 0777);
                }
@@ -12989,7 +12954,7 @@ ulimitcmd(int argc, char **argv)
                char *p = *argptr;
 
                if (all || argptr[1])
-                       error("too many arguments");
+                       sh_error("too many arguments");
                if (strncmp(p, "unlimited\n", 9) == 0)
                        val = RLIM_INFINITY;
                else {
@@ -13002,7 +12967,7 @@ ulimitcmd(int argc, char **argv)
                                        break;
                        }
                        if (c)
-                               error("bad number");
+                               sh_error("bad number");
                        val *= l->factor;
                }
        }
@@ -13022,7 +12987,7 @@ ulimitcmd(int argc, char **argv)
                if (how & SOFT)
                        limit.rlim_cur = val;
                if (setrlimit(l->cmd, &limit) < 0)
-                       error("error setting limit (%m)");
+                       sh_error("error setting limit (%m)");
        } else {
                printlim(how, &limit, l);
        }