Get rid of all "#if 0" content.
[oweals/busybox.git] / shell / ash.c
index e2205efd24f95de01593ad4016afe6ffa1830af3..cc46881c996bedc93116955f52e991b493d6a59d 100644 (file)
@@ -5,26 +5,14 @@
  * Copyright (c) 1989, 1991, 1993, 1994
  *      The Regents of the University of California.  All rights reserved.
  *
- * Copyright (c) 1997-2003 Herbert Xu <herbert@debian.org>
+ * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
  * was re-ported from NetBSD and debianized.
  *
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  *
  * Original BSD copyright notice is retained at the end of this file.
  */
@@ -36,7 +24,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.
  */
 
 
-
 #define IFS_BROKEN
 
 #define PROFILE 0
 
+#include "busybox.h"
+
 #ifdef DEBUG
 #define _GNU_SOURCE
 #endif
 
 #include <sys/types.h>
-#include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/param.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
-#include <sys/time.h>
 #include <sys/wait.h>
 
 #include <stdio.h>
 #include <paths.h>
 #include <setjmp.h>
 #include <signal.h>
-#include <stdint.h>
-#include <sysexits.h>
+/*#include <stdint.h>*/
 #include <time.h>
 #include <fnmatch.h>
 
-
-#include "busybox.h"
 #include "pwd_.h"
 
 #ifdef CONFIG_ASH_JOB_CONTROL
 #undef JOBS
 #endif
 
-#if JOBS
+#if JOBS || defined(CONFIG_ASH_READ_NCHARS)
 #include <termios.h>
 #endif
 
@@ -130,7 +114,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 +134,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 +173,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 */
@@ -212,24 +194,24 @@ static volatile sig_atomic_t pendingsigs;
  * more fun than worrying about efficiency and portability. :-))
  */
 
-#define barrier() ({ __asm__ __volatile__ ("": : :"memory"); })
+#define xbarrier() ({ __asm__ __volatile__ ("": : :"memory"); })
 #define INTOFF \
        ({ \
                suppressint++; \
-               barrier(); \
+               xbarrier(); \
                0; \
        })
 #define SAVEINT(v) ((v) = suppressint)
 #define RESTOREINT(v) \
        ({ \
-               barrier(); \
+               xbarrier(); \
                if ((suppressint = (v)) == 0 && intpending) onint(); \
                0; \
        })
 #define EXSIGON() \
        ({ \
                exsig++; \
-               barrier(); \
+               xbarrier(); \
                if (pendingsigs) \
                        exraise(EXSIG); \
                0; \
@@ -237,11 +219,11 @@ static volatile sig_atomic_t pendingsigs;
 /* EXSIG is turned off by evalbltin(). */
 
 
-static void exraise(int) __attribute__((__noreturn__));
-static void onint(void) __attribute__((__noreturn__));
+static void exraise(int) ATTRIBUTE_NORETURN;
+static void onint(void) ATTRIBUTE_NORETURN;
 
-static void error(const char *, ...) __attribute__((__noreturn__));
-static void exerror(int, 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 *, ...);
 
@@ -263,30 +245,20 @@ static void forceinton(void)
 #else
 #define INTON \
        ({ \
-               barrier(); \
+               xbarrier(); \
                if (--suppressint == 0 && intpending) onint(); \
                0; \
        })
 #define FORCEINTON \
        ({ \
-               barrier(); \
+               xbarrier(); \
                suppressint = 0; \
                if (intpending) onint(); \
                0; \
        })
 #endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */
 
-/*
- * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
- * so we use _setjmp instead.
- */
-
-#if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__)
-#define setjmp(jmploc)  _setjmp(jmploc)
-#define longjmp(jmploc, val)    _longjmp(jmploc, val)
-#endif
-
-/*      $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $     */
+/*      expand.h     */
 
 struct strlist {
        struct strlist *next;
@@ -323,7 +295,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 */
@@ -502,7 +474,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 */
@@ -578,7 +550,7 @@ struct parsefile {
 };
 
 static struct parsefile basepf;         /* top level input file */
-static char basebuf[IBUFSIZ];           /* buffer for top level input file */
+#define basebuf bb_common_bufsiz1       /* buffer for top level input file */
 static struct parsefile *parsefile = &basepf;  /* current input file */
 
 
@@ -601,7 +573,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;
 
@@ -624,7 +596,7 @@ static const char homestr[] = "HOME";
 #define __builtin_expect(x, expected_value) (x)
 #endif
 
-#define likely(x)       __builtin_expect((x),1)
+#define xlikely(x)       __builtin_expect((x),1)
 
 
 #define TEOF 0
@@ -703,7 +675,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
@@ -754,6 +726,9 @@ static const char *tokname(int tok)
 #define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
 #define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
 
+/* C99 say: "char" declaration may be signed or unsigned default */
+#define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int)
+
 /*
  * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
  * (assuming ascii char codes, as the original implementation did)
@@ -867,7 +842,7 @@ static int SIT(int c, int syntax)
        return S_I_T[indx][syntax];
 }
 
-#else                                                   /* USE_SIT_FUNCTION */
+#else   /* USE_SIT_FUNCTION */
 
 #define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
 
@@ -1168,9 +1143,9 @@ static const char syntax_index_table[258] = {
        /* 257   127      */ CWORD_CWORD_CWORD_CWORD,
 };
 
-#endif                                                  /* USE_SIT_FUNCTION */
+#endif  /* USE_SIT_FUNCTION */
 
-/*      $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $      */
+/*      alias.c      */
 
 
 #define ATABSIZE 39
@@ -1217,29 +1192,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 **);
@@ -1249,11 +1222,17 @@ static int commandcmd(int, char **);
 #endif
 static int dotcmd(int, char **);
 static int evalcmd(int, char **);
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+static int echocmd(int, char **);
+#endif
+#ifdef CONFIG_ASH_BUILTIN_TEST
+static int testcmd(int, char **);
+#endif
 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
@@ -1263,7 +1242,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
@@ -1283,18 +1262,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 */
@@ -1308,39 +1287,17 @@ struct builtincmd {
        /* unsigned flags; */
 };
 
-#ifdef CONFIG_ASH_CMDCMD
-# ifdef JOBS
-#  ifdef CONFIG_ASH_ALIAS
-#    define COMMANDCMD (builtincmd + 7)
-#    define EXECCMD (builtincmd + 10)
-#  else
-#    define COMMANDCMD (builtincmd + 6)
-#    define EXECCMD (builtincmd + 9)
-#  endif
-# else /* ! JOBS */
-#  ifdef CONFIG_ASH_ALIAS
-#    define COMMANDCMD (builtincmd + 6)
-#    define EXECCMD (builtincmd + 9)
-#  else
-#    define COMMANDCMD (builtincmd + 5)
-#    define EXECCMD (builtincmd + 8)
-#  endif
-# endif /* JOBS */
-#else   /* ! CONFIG_ASH_CMDCMD */
-# ifdef JOBS
-#  ifdef CONFIG_ASH_ALIAS
-#    define EXECCMD (builtincmd + 9)
-#  else
-#    define EXECCMD (builtincmd + 8)
-#  endif
-# else /* ! JOBS */
-#  ifdef CONFIG_ASH_ALIAS
-#    define EXECCMD (builtincmd + 8)
-#  else
-#    define EXECCMD (builtincmd + 7)
-#  endif
-# endif /* JOBS */
-#endif /* CONFIG_ASH_CMDCMD */
+
+#define COMMANDCMD (builtincmd + 5 + \
+       2 * ENABLE_ASH_BUILTIN_TEST + \
+       ENABLE_ASH_ALIAS + \
+       ENABLE_ASH_JOB_CONTROL)
+#define EXECCMD (builtincmd + 7 + \
+       2 * ENABLE_ASH_BUILTIN_TEST + \
+       ENABLE_ASH_ALIAS + \
+       ENABLE_ASH_JOB_CONTROL + \
+       ENABLE_ASH_CMDCMD + \
+       ENABLE_ASH_BUILTIN_ECHO)
 
 #define BUILTIN_NOSPEC  "0"
 #define BUILTIN_SPECIAL "1"
@@ -1353,14 +1310,20 @@ struct builtincmd {
 
 #define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
 #define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
+#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4)
 
+/* make sure to keep these in proper order since it is searched via bsearch() */
 static const struct builtincmd builtincmd[] = {
        { BUILTIN_SPEC_REG      ".", dotcmd },
        { BUILTIN_SPEC_REG      ":", truecmd },
+#ifdef CONFIG_ASH_BUILTIN_TEST
+       { BUILTIN_REGULAR       "[", testcmd },
+       { BUILTIN_REGULAR       "[[", testcmd },
+#endif
 #ifdef CONFIG_ASH_ALIAS
        { BUILTIN_REG_ASSG      "alias", aliascmd },
 #endif
-#ifdef JOBS
+#if JOBS
        { BUILTIN_REGULAR       "bg", bgcmd },
 #endif
        { BUILTIN_SPEC_REG      "break", breakcmd },
@@ -1370,12 +1333,15 @@ static const struct builtincmd builtincmd[] = {
        { BUILTIN_REGULAR       "command", commandcmd },
 #endif
        { BUILTIN_SPEC_REG      "continue", breakcmd },
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+       { BUILTIN_REGULAR       "echo", echocmd },
+#endif
        { BUILTIN_SPEC_REG      "eval", evalcmd },
        { BUILTIN_SPEC_REG      "exec", execcmd },
        { 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
@@ -1385,7 +1351,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
@@ -1399,6 +1365,10 @@ static const struct builtincmd builtincmd[] = {
        { BUILTIN_SPEC_REG      "return", returncmd },
        { BUILTIN_SPEC_REG      "set", setcmd },
        { BUILTIN_SPEC_REG      "shift", shiftcmd },
+       { BUILTIN_SPEC_REG      "source", dotcmd },
+#ifdef CONFIG_ASH_BUILTIN_TEST
+       { BUILTIN_REGULAR       "test", testcmd },
+#endif
        { BUILTIN_SPEC_REG      "times", timescmd },
        { BUILTIN_SPEC_REG      "trap", trapcmd },
        { BUILTIN_REGULAR       "true", truecmd },
@@ -1436,7 +1406,7 @@ struct cmdentry {
 static const char *pathopt;     /* set by padvance */
 
 static void shellexec(char **, const char *, int)
-    __attribute__((__noreturn__));
+    ATTRIBUTE_NORETURN;
 static char *padvance(const char **, const char *);
 static void find_command(char *, struct cmdentry *, int, const char *);
 static struct builtincmd *find_builtin(const char *);
@@ -1445,8 +1415,17 @@ static void changepath(const char *);
 static void defun(char *, union node *);
 static void unsetfunc(const char *);
 
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+typedef int64_t arith_t;
+#define arith_t_type (long long)
+#else
+typedef long arith_t;
+#define arith_t_type (long)
+#endif
+
 #ifdef CONFIG_ASH_MATH_SUPPORT
-static long dash_arith(const char *);
+static arith_t dash_arith(const char *);
+static arith_t arith(const char *expr, int *perrcode);
 #endif
 
 #ifdef CONFIG_ASH_RANDOM_SUPPORT
@@ -1457,11 +1436,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.
@@ -1636,7 +1615,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 *);
@@ -1713,9 +1692,11 @@ init(void)
       {
              char **envp;
              char ppid[32];
+             const char *p;
+             struct stat st1, st2;
 
              initvar();
-             for (envp = environ ; *envp ; envp++) {
+             for (envp = environ ; envp && *envp ; envp++) {
                      if (strchr(*envp, '=')) {
                              setvareq(*envp, VEXPORT|VTEXTFIXED);
                      }
@@ -1723,12 +1704,23 @@ init(void)
 
              snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
              setvar("PPID", ppid, 0);
-             setpwd(0, 0);
+
+             p = lookupvar("PWD");
+             if (p)
+             if (*p != '/' || stat(p, &st1) || stat(".", &st2) ||
+                 st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)
+                     p = 0;
+             setpwd(p, 0);
       }
 }
 
 /* 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
@@ -1741,7 +1733,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);
@@ -1749,7 +1740,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. */
@@ -1816,18 +1807,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 {
@@ -1868,7 +1860,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;
@@ -1900,7 +1892,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
@@ -1914,7 +1906,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) */
@@ -1940,20 +1932,21 @@ struct shparam {
 #define aflag optlist[10]
 #define bflag optlist[11]
 #define uflag optlist[12]
-#define qflag optlist[13]
+#define viflag 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 $ */
+#ifndef CONFIG_FEATURE_COMMAND_EDITING_VI
+#define setvimode(on) viflag = 0   /* forcibly keep the option off */
+#endif
+
+/*      options.c */
 
 
-static const char *const optletters_optnames[NOPTS] = {
+static const char *const optletters_optnames[] = {
        "e"   "errexit",
        "f"   "noglob",
        "I"   "ignoreeof",
@@ -1967,7 +1960,7 @@ static const char *const optletters_optnames[NOPTS] = {
        "a"   "allexport",
        "b"   "notify",
        "u"   "nounset",
-       "q"   "quietprofile",
+       "\0"  "vi",
 #ifdef DEBUG
        "\0"  "nolog",
        "\0"  "debug",
@@ -1977,6 +1970,7 @@ static const char *const optletters_optnames[NOPTS] = {
 #define optletters(n) optletters_optnames[(n)][0]
 #define optnames(n) (&optletters_optnames[(n)][1])
 
+#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
 
 static char optlist[NOPTS];
 
@@ -1998,7 +1992,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 */
@@ -2011,7 +2005,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
@@ -2024,7 +2018,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 */
@@ -2038,9 +2032,9 @@ 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 void exitshell(void) ATTRIBUTE_NORETURN;
 static int decode_signal(const char *, int);
 
 /*
@@ -2055,7 +2049,6 @@ reset(void)
       {
              evalskip = 0;
              loopnest = 0;
-             funcnest = 0;
       }
 
       /* from input.c: */
@@ -2261,7 +2254,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.
@@ -2310,7 +2303,6 @@ cdcmd(int argc, char **argv)
        else if (dest[0] == '-' && dest[1] == '\0') {
                dest = bltinlookup("OLDPWD");
                flags |= CD_PRINT;
-               goto step7;
        }
        if (!dest)
                dest = nullstr;
@@ -2349,7 +2341,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)
@@ -2507,7 +2499,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.
@@ -2520,7 +2512,7 @@ setpwd(const char *val, int setold)
 
 
 static void exverror(int, const char *, va_list)
-    __attribute__((__noreturn__));
+    ATTRIBUTE_NORETURN;
 
 /*
  * Called to raise an exception.  Since C doesn't include exceptions, we
@@ -2555,7 +2547,6 @@ onint(void) {
        int i;
 
        intpending = 0;
-       sigsetmask(0);
        i = EXSIG;
        if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
                if (!(rootshell && iflag)) {
@@ -2571,20 +2562,17 @@ onint(void) {
 static void
 exvwarning(const char *msg, va_list ap)
 {
-       FILE *errs;
-       const char *name;
-       const char *fmt;
+        FILE *errs;
 
-       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);
+        errs = stderr;
+        fprintf(errs, "%s: ", arg0);
+        if (commandname) {
+                const char *fmt = (!iflag || parsefile->fd) ?
+                                       "%s: %d: " : "%s: ";
+                fprintf(errs, fmt, commandname, startlinno);
+        }
+        vfprintf(errs, msg, ap);
+        outcslow('\n', errs);
 }
 
 /*
@@ -2613,7 +2601,7 @@ exverror(int cond, const char *msg, va_list ap)
 
 
 static void
-error(const char *msg, ...)
+sh_error(const char *msg, ...)
 {
        va_list ap;
 
@@ -2667,7 +2655,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.
@@ -2726,7 +2714,8 @@ evalcmd(int argc, char **argv)
                        STPUTC('\0', concat);
                        p = grabstackstr(concat);
                }
-               evalstring(p);
+               evalstring(p, ~SKIPEVAL);
+
        }
        return exitstatus;
 }
@@ -2736,23 +2725,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;
 }
 
 
@@ -2864,10 +2859,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);
+       }
 }
 
 
@@ -3082,7 +3082,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) {
@@ -3143,7 +3143,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;
@@ -3200,7 +3200,20 @@ parse_command_args(char **argv, const char **path)
 }
 #endif
 
+static inline int
+isassignment(const char *p)
+{
+       const char *q = endofname(p);
+       if (p == q)
+               return 0;
+       return *q == '=';
+}
 
+#ifdef CONFIG_ASH_EXPAND_PRMT
+static const char *expandstr(const char *ps);
+#else
+#define expandstr(s) s
+#endif
 
 /*
  * Execute a simple command.
@@ -3224,6 +3237,8 @@ evalcommand(union node *cmd, int flags)
        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));
@@ -3238,11 +3253,21 @@ evalcommand(union node *cmd, int flags)
        *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;
-               expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+               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++;
        }
@@ -3285,7 +3310,7 @@ evalcommand(union node *cmd, int flags)
                const char *p = " %s";
 
                p++;
-               dprintf(preverrout_fd, p, ps4val());
+               dprintf(preverrout_fd, p, expandstr(ps4val()));
 
                sp = varlist.list;
                for(n = 0; n < 2; n++) {
@@ -3471,6 +3496,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;
@@ -3478,11 +3504,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;
@@ -3490,10 +3515,7 @@ funcdone:
        shellparam = saveparam;
        handler = savehandler;
        INTON;
-       if (evalskip == SKIPFUNC) {
-               evalskip = 0;
-               skipcount = 0;
-       }
+       evalskip &= ~SKIPFUNC;
        return e;
 }
 
@@ -3561,7 +3583,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) {
@@ -3579,19 +3601,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;
 }
 
 
@@ -3622,7 +3637,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.
@@ -3668,6 +3683,7 @@ shellexec(char **argv, const char *path, int idx)
        char *cmdname;
        int e;
        char **envp;
+       int exerrno;
 
        clearredir(1);
        envp = environment();
@@ -3702,6 +3718,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));
@@ -3714,27 +3731,11 @@ tryexec(char *cmd, char **argv, char **envp)
 {
        int repeated = 0;
 #ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
-       int flg_bb = 0;
-       char *name = cmd;
-
-       if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
-               flg_bb = 1;
-       }
-       if(flg_bb) {
-               char **ap;
-               char **new;
-
-               *argv = name;
-               if(strcmp(name, "busybox")) {
-                       for (ap = argv; *ap; ap++);
-                       ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
-                       *ap++ = cmd = "/bin/busybox";
-                       while ((*ap++ = *argv++));
-                       argv = new;
-                       repeated++;
-               } else {
-                       cmd = "/bin/busybox";
-               }
+       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
 
@@ -4004,7 +4005,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 +4439,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
@@ -4531,12 +4514,12 @@ static void ifsfree(void);
 static void expandmeta(struct strlist *, int);
 static int patmatch(char *, const char *);
 
-static int cvtnum(long);
+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__));
+       ATTRIBUTE_NORETURN;
 
 
 #define pmatch(a, b) !fnmatch((a), (b), 0)
@@ -4598,11 +4581,12 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
        ifsfirst.next = NULL;
        ifslastp = NULL;
        argstr(arg->narg.text, flag);
+       p = _STPUTC('\0', expdest);
+       expdest = p - 1;
        if (arglist == NULL) {
                return;                 /* here document expanded */
        }
-       STPUTC('\0', expdest);
-       p = grabstackstr(expdest);
+       p = grabstackstr(p);
        exparg.lastp = &exparg.list;
        /*
         * TODO - EXP_REDIR
@@ -4805,14 +4789,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();
@@ -4896,7 +4879,7 @@ expari(int quotes)
                        p--;
 #ifdef DEBUG
                        if (p < start) {
-                               error("missing CTLARI (shouldn't happen)");
+                               sh_error("missing CTLARI (shouldn't happen)");
                        }
 #endif
                }
@@ -5274,7 +5257,7 @@ memtodest(const char *p, size_t len, int syntax, int quotes) {
        q = makestrspace(len * 2, q);
 
        while (len--) {
-               int c = *p++;
+               int c = SC2INT(*p++);
                if (!c)
                        continue;
                if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
@@ -5348,7 +5331,7 @@ numvar:
                        goto param;
                /* fall through */
        case '*':
-               sep = ifsset() ? ifsval()[0] : ' ';
+               sep = ifsset() ? SC2INT(ifsval()[0]) : ' ';
                if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
                        sepq = 1;
 param:
@@ -5358,9 +5341,12 @@ param:
                        size_t partlen;
 
                        partlen = strlen(p);
-
                        len += partlen;
-                       if (len > partlen && sep) {
+
+                       if (!(subtype == VSPLUS || subtype == VSLENGTH))
+                               memtodest(p, partlen, syntax, quotes);
+
+                       if (*ap && sep) {
                                char *q;
 
                                len++;
@@ -5373,9 +5359,6 @@ param:
                                STPUTC(sep, q);
                                expdest = q;
                        }
-
-                       if (!(subtype == VSPLUS || subtype == VSLENGTH))
-                               memtodest(p, partlen, syntax, quotes);
                }
                return len;
        case '0':
@@ -5902,12 +5885,16 @@ casematch(union node *pattern, char *val)
  */
 
 static int
-cvtnum(long num)
+cvtnum(arith_t num)
 {
        int len;
 
        expdest = makestrspace(32, expdest);
+#ifdef CONFIG_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;
 }
@@ -5927,54 +5914,27 @@ 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.
  */
 
 #define EOF_NLEFT -99           /* value of parsenleft when EOF pushed back */
-#define IBUFSIZ (BUFSIZ + 1)
 
 static void pushfile(void);
 
-/*
- * Read a line from the script.
- */
-
-static inline char *
-pfgets(char *line, int len)
-{
-       char *p = line;
-       int nleft = len;
-       int c;
-
-       while (--nleft > 0) {
-               c = pgetc2();
-               if (c == PEOF) {
-                       if (p == line)
-                               return NULL;
-                       break;
-               }
-               *p++ = c;
-               if (c == '\n')
-                       break;
-       }
-       *p = '\0';
-       return line;
-}
-
-
 /*
  * 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? *parsenextc++ : preadbuffer())
+
+#define pgetc_as_macro()   (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
 
 #ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
 #define pgetc_macro() pgetc()
@@ -6013,12 +5973,48 @@ static inline int pgetc2(void)
 }
 #endif
 
+/*
+ * Read a line from the script.
+ */
+
+static inline char *
+pfgets(char *line, int len)
+{
+       char *p = line;
+       int nleft = len;
+       int c;
+
+       while (--nleft > 0) {
+               c = pgetc2();
+               if (c == PEOF) {
+                       if (p == line)
+                               return NULL;
+                       break;
+               }
+               *p++ = c;
+               if (c == '\n')
+                       break;
+       }
+       *p = '\0';
+       return line;
+}
+
+
 
 #ifdef CONFIG_FEATURE_COMMAND_EDITING
+#ifdef CONFIG_ASH_EXPAND_PRMT
+static char *cmdedit_prompt;
+#else
 static const char *cmdedit_prompt;
+#endif
 static inline void putprompt(const char *s)
 {
+#ifdef CONFIG_ASH_EXPAND_PRMT
+       free(cmdedit_prompt);
+       cmdedit_prompt = bb_xstrdup(s);
+#else
        cmdedit_prompt = s;
+#endif
 }
 #else
 static inline void putprompt(const char *s)
@@ -6039,7 +6035,9 @@ retry:
        if (!iflag || parsefile->fd)
                nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
        else {
+#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
                cmdedit_path_lookup = pathval();
+#endif
                nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
                if(nr == 0) {
                        /* Ctrl+C presend */
@@ -6051,7 +6049,7 @@ retry:
                        }
                        goto retry;
                }
-               if(nr < 0) {
+               if(nr < 0 && errno == 0) {
                        /* Ctrl+D presend */
                        nr = 0;
                }
@@ -6088,7 +6086,7 @@ retry:
 int
 preadbuffer(void)
 {
-       char *p, *q;
+       char *q;
        int more;
        char savec;
 
@@ -6101,45 +6099,48 @@ preadbuffer(void)
 #endif
                popstring();
                if (--parsenleft >= 0)
-                       return (*parsenextc++);
+                       return SC2INT(*parsenextc++);
        }
        if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
                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';
@@ -6150,7 +6151,7 @@ check:
 
        *q = savec;
 
-       return *parsenextc++;
+       return SC2INT(*parsenextc++);
 }
 
 /*
@@ -6232,24 +6233,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;
 }
 
 
@@ -6363,7 +6369,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
@@ -6438,14 +6444,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 */
@@ -6534,7 +6540,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]"
                );
@@ -6557,7 +6563,7 @@ usage:
                                case 's':
                                        signo = decode_signal(optionarg, 1);
                                        if (signo < 0) {
-                                               error(
+                                               sh_error(
                                                        "invalid signal number or name: %s",
                                                        optionarg
                                                );
@@ -6591,7 +6597,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;
        }
 
@@ -6600,10 +6606,12 @@ usage:
                if (**argv == '%') {
                        jp = getjob(*argv, 0);
                        pid = -jp->ps[0].pid;
-               } else
-                       pid = number(*argv);
+               } else {
+                       pid = **argv == '-' ?
+                               -number(*argv + 1) : number(*argv);
+               }
                if (kill(pid, signo) != 0) {
-                       sh_warnx("%m\n");
+                       sh_warnx("(%d) - %m", pid);
                        i = 1;
                }
        } while (*++argv);
@@ -6620,7 +6628,7 @@ jobno(const struct job *jp)
 }
 #endif
 
-#ifdef JOBS
+#if JOBS
 static int
 fgcmd(int argc, char **argv)
 {
@@ -7003,7 +7011,7 @@ gotit:
 #endif
        return jp;
 err:
-       error(err_msg, name);
+       sh_error(err_msg, name);
 }
 
 
@@ -7073,7 +7081,7 @@ growjobtab(void)
                        jq--;
 #define joff(p) ((struct job *)((char *)(p) + l))
 #define jmove(p) (p) = (void *)((char *)(p) + offset)
-                       if (likely(joff(jp)->ps == &jq->ps0))
+                       if (xlikely(joff(jp)->ps == &jq->ps0))
                                jmove(joff(jp)->ps);
                        if (joff(jp)->prev_job)
                                jmove(joff(jp)->prev_job);
@@ -7115,18 +7123,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)
@@ -7146,11 +7154,11 @@ forkchild(struct job *jp, union node *n, int mode)
                ignoresig(SIGQUIT);
                if (jp->nprocs == 0) {
                        close(0);
-                       if (open(_PATH_DEVNULL, O_RDONLY) != 0)
-                               error("Can't open %s", _PATH_DEVNULL);
+                       if (open(bb_dev_null, O_RDONLY) != 0)
+                               sh_error("Can't open %s", bb_dev_null);
                }
        }
-       if (wasroot && iflag) {
+       if (!oldlvl && iflag) {
                setsignal(SIGINT);
                setsignal(SIGQUIT);
                setsignal(SIGTERM);
@@ -7208,7 +7216,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);
@@ -7348,7 +7356,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)) {
@@ -7360,7 +7368,7 @@ dowait(int block, struct job *job)
                if (thisjob)
                        goto gotjob;
        }
-#ifdef JOBS
+#if JOBS
        if (!WIFSTOPPED(status))
 #endif
 
@@ -7374,7 +7382,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);
                        }
@@ -7618,11 +7626,10 @@ cmdputs(const char *s)
        char *nextc;
        int subtype = 0;
        int quoted = 0;
-       static const char *const vstype[16] = {
-               nullstr, "}", "-", "+", "?", "=",
-               "%", "%%", "#", "##", nullstr
+       static const char vstype[VSTYPE + 1][4] = {
+               "", "}", "-", "+", "?", "=",
+               "%", "%%", "#", "##"
        };
-
        nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
        p = s;
        while ((c = *p++) != 0) {
@@ -7644,14 +7651,10 @@ cmdputs(const char *s)
                                goto dostr;
                        break;
                case CTLENDVAR:
+                       str = "\"}" + !(quoted & 1);
                        quoted >>= 1;
                        subtype = 0;
-                       if (quoted & 1) {
-                               str = "\"}";
-                               goto dostr;
-                       }
-                       c = '}';
-                       break;
+                       goto dostr;
                case CTLBACKQ:
                        str = "$(...)";
                        goto dostr;
@@ -7673,13 +7676,13 @@ cmdputs(const char *s)
                case '=':
                        if (subtype == 0)
                                break;
+                       if ((subtype & VSTYPE) != VSNORMAL)
+                               quoted <<= 1;
                        str = vstype[subtype & VSTYPE];
                        if (subtype & VSNUL)
                                c = ':';
                        else
-                               c = *str++;
-                       if (c != '}')
-                               quoted <<= 1;
+                               goto checkstr;
                        break;
                case '\'':
                case '\\':
@@ -7694,6 +7697,7 @@ cmdputs(const char *s)
                        break;
                }
                USTPUTC(c, nextc);
+checkstr:
                if (!str)
                        continue;
 dostr:
@@ -7726,7 +7730,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 */
 
@@ -7758,7 +7762,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?)
@@ -7829,7 +7833,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
@@ -7866,28 +7870,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) {
@@ -7895,11 +7887,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;
@@ -7914,7 +7906,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);
@@ -7956,7 +7947,7 @@ state2:
 state3:
        state = 4;
        if (minusc)
-               evalstring(minusc);
+               evalstring(minusc, 0);
 
        if (sflag || minusc == NULL) {
 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
@@ -7989,7 +7980,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;
@@ -7999,9 +7990,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);
@@ -8024,17 +8015,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;
 }
 
 
@@ -8045,31 +8040,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();
 }
 
 
@@ -8080,14 +8060,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();
 }
@@ -8121,7 +8094,7 @@ find_dot_file(char *name)
        }
 
        /* not found in the PATH */
-       error(not_found_msg, name);
+       sh_error(not_found_msg, name);
        /* NOTREACHED */
 }
 
@@ -8129,17 +8102,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 */
+       if (argc >= 2) {        /* That's what SVR2 does */
                char *fullname;
-               struct stackmark smark;
 
-               setstackmark(&smark);
                fullname = find_dot_file(argv[1]);
 
                if (argc > 2) {
@@ -8149,7 +8119,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();
@@ -8158,10 +8128,9 @@ static int dotcmd(int argc, char **argv)
                        freeparam(&shellparam);
                        shellparam = saveparam;
                };
-
-               popstackmark(&smark);
+               status = exitstatus;
        }
-       return exitstatus;
+       return status;
 }
 
 
@@ -8176,7 +8145,23 @@ exitcmd(int argc, char **argv)
        /* NOTREACHED */
 }
 
-/*      $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $        */
+#ifdef CONFIG_ASH_BUILTIN_ECHO
+static int
+echocmd(int argc, char **argv)
+{
+       return bb_echo(argc, argv);
+}
+#endif
+
+#ifdef CONFIG_ASH_BUILTIN_TEST
+static int
+testcmd(int argc, char **argv)
+{
+       return bb_test(argc, argv);
+}
+#endif
+
+/*      memalloc.c        */
 
 /*
  * Same for malloc, realloc, but returns an error when out of space.
@@ -8187,7 +8172,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;
 }
 
@@ -8206,7 +8191,7 @@ savestr(const char *s)
 {
        char *p = strdup(s);
        if (!p)
-               error(bb_msg_memory_exhausted);
+               sh_error(bb_msg_memory_exhausted);
        return p;
 }
 
@@ -8238,7 +8223,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;
@@ -8316,7 +8301,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;
 
@@ -8436,7 +8421,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.
@@ -8470,7 +8455,7 @@ number(const char *s)
 {
 
        if (! is_number(s))
-               error(illnum, s);
+               sh_error(illnum, s);
        return atoi(s);
 }
 
@@ -8795,7 +8780,7 @@ procargs(int argc, char **argv)
        xminusc = minusc;
        if (*xargv == NULL) {
                if (xminusc)
-                       error("-c requires an argument");
+                       sh_error(bb_msg_requires_arg, "-c");
                sflag = 1;
        }
        if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
@@ -8842,6 +8827,7 @@ optschanged(void)
 #endif
        setinteractive(iflag);
        setjobctl(mflag);
+       setvimode(viflag);
 }
 
 static inline void
@@ -8860,7 +8846,7 @@ minus_o(char *name, int val)
                                optlist[i] = val;
                                return;
                        }
-               error("Illegal option -o %s", name);
+               sh_error("Illegal option -o %s", name);
        }
 }
 
@@ -8928,7 +8914,7 @@ setoption(int flag, int val)
                        optlist[i] = val;
                        return;
                }
-       error("Illegal option -%c", flag);
+       sh_error("Illegal option -%c", flag);
        /* NOTREACHED */
 }
 
@@ -8994,7 +8980,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++) {
@@ -9035,8 +9021,7 @@ setcmd(int argc, char **argv)
 
 #ifdef CONFIG_ASH_GETOPTS
 static void
-getoptsreset(value)
-       const char *value;
+getoptsreset(const char *value)
 {
        shellparam.optind = number(value);
        shellparam.optoff = -1;
@@ -9182,7 +9167,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) {
@@ -9232,13 +9217,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;
        }
@@ -9247,7 +9232,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)
@@ -9315,7 +9300,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     */
 
 
 /*
@@ -9350,21 +9335,12 @@ static int readtoken(void);
 static int xxreadtoken(void);
 static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs);
 static int noexpand(char *);
-static void synexpect(int) __attribute__((__noreturn__));
-static void synerror(const char *) __attribute__((__noreturn__));
+static void synexpect(int) ATTRIBUTE_NORETURN;
+static void synerror(const char *) ATTRIBUTE_NORETURN;
 static void setprompt(int);
 
 
 
-static inline int
-isassignment(const char *p)
-{
-       const char *q = endofname(p);
-       if (p == q)
-               return 0;
-       return *q == '=';
-}
-
 
 /*
  * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
@@ -9890,7 +9866,6 @@ parseheredoc(void)
        while (here) {
                if (needprompt) {
                        setprompt(2);
-                       needprompt = 0;
                }
                readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
                                here->eofmark, here->striptabs);
@@ -10013,7 +9988,7 @@ static const char xxreadtoken_tokens[] = {
 #define xxreadtoken_singles \
        (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
 
-static int xxreadtoken()
+static int xxreadtoken(void)
 {
        int c;
 
@@ -10023,7 +9998,6 @@ static int xxreadtoken()
        }
        if (needprompt) {
                setprompt(2);
-               needprompt = 0;
        }
        startlinno = plinno;
        for (;;) {                      /* until token or start of word found */
@@ -10091,7 +10065,6 @@ xxreadtoken(void)
        }
        if (needprompt) {
                setprompt(2);
-               needprompt = 0;
        }
        startlinno = plinno;
        for (;;) {      /* until token or start of word found */
@@ -10177,15 +10150,15 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
        char *out;
        int len;
        char line[EOFMARKLEN + 1];
-       struct nodelist *bqlist;
-       int quotef;
-       int dblquote;
-       int varnest;    /* levels of variables expansion */
-       int arinest;    /* levels of arithmetic expansion */
-       int parenlevel; /* levels of parens in arithmetic */
-       int dqvarnest;  /* levels of variables expansion within double quotes */
-       int oldstyle;
-       int prevsyntax; /* syntax before arithmetic */
+       struct nodelist *bqlist = 0;
+       int quotef = 0;
+       int dblquote = 0;
+       int varnest = 0;    /* levels of variables expansion */
+       int arinest = 0;    /* levels of arithmetic expansion */
+       int parenlevel = 0; /* levels of parens in arithmetic */
+       int dqvarnest = 0;  /* levels of variables expansion within double quotes */
+       int oldstyle = 0;
+       int prevsyntax = 0; /* syntax before arithmetic */
 #if __GNUC__
        /* Avoid longjmp clobbering */
        (void) &out;
@@ -10244,13 +10217,11 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                        if (doprompt)
                                                setprompt(2);
                                } else {
-                                       if (
-                                               dblquote &&
+                                       if (dblquote &&
                                                c != '\\' && c != '`' &&
                                                c != '$' && (
                                                        c != '"' ||
-                                                       eofmark != NULL
-                                               )
+                                                       eofmark != NULL)
                                        ) {
                                                USTPUTC(CTLESC, out);
                                                USTPUTC('\\', out);
@@ -10610,7 +10581,7 @@ parsebackq: {
        struct jmploc jmploc;
        struct jmploc *volatile savehandler;
        size_t savelen;
-       int saveprompt;
+       int saveprompt = 0;
 #ifdef __GNUC__
        (void) &saveprompt;
 #endif
@@ -10647,7 +10618,6 @@ parsebackq: {
                for (;;) {
                        if (needprompt) {
                                setprompt(2);
-                               needprompt = 0;
                        }
                        switch (pc = pgetc()) {
                        case '`':
@@ -10847,7 +10817,7 @@ static void synexpect(int token)
 static void
 synerror(const char *msg)
 {
-       error("Syntax error: %s", msg);
+       sh_error("Syntax error: %s", msg);
        /* NOTREACHED */
 }
 
@@ -10857,9 +10827,35 @@ synerror(const char *msg)
  *    should be added here.
  */
 
+#ifdef CONFIG_ASH_EXPAND_PRMT
+static const char *
+expandstr(const char *ps)
+{
+       union node n;
+
+       /* XXX Fix (char *) cast. */
+       setinputstring((char *)ps);
+       readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
+       popfile();
+
+       n.narg.type = NARG;
+       n.narg.next = NULL;
+       n.narg.text = wordtext;
+       n.narg.backquote = backquotelist;
+
+       expandarg(&n, NULL, 0);
+       return stackblock();
+}
+#endif
+
 static void setprompt(int whichprompt)
 {
        const char *prompt;
+#ifdef CONFIG_ASH_EXPAND_PRMT
+       struct stackmark smark;
+#endif
+
+       needprompt = 0;
 
        switch (whichprompt) {
        case 1:
@@ -10871,7 +10867,14 @@ static void setprompt(int whichprompt)
        default:                        /* 0 */
                prompt = nullstr;
        }
-       putprompt(prompt);
+#ifdef CONFIG_ASH_EXPAND_PRMT
+       setstackmark(&smark);
+       stalloc(stackblocksize());
+#endif
+       putprompt(expandstr(prompt));
+#ifdef CONFIG_ASH_EXPAND_PRMT
+       popstackmark(&smark);
+#endif
 }
 
 
@@ -10882,7 +10885,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.
@@ -10966,7 +10969,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) {
@@ -11047,9 +11050,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
@@ -11123,7 +11126,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 {
@@ -11206,7 +11209,7 @@ copyfd(int from, int to)
                if (errno == EMFILE)
                        return EMPTY;
                else
-                       error("%d: %m", from);
+                       sh_error("%d: %m", from);
        }
        return newfd;
 }
@@ -11232,7 +11235,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*);
@@ -11579,7 +11582,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
@@ -11628,7 +11631,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')
@@ -11793,23 +11796,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, barrier(), (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;
 }
 
 
@@ -11835,8 +11849,9 @@ setinteractive(int on)
 
                                if(!do_banner) {
                                        out1fmt(
-                       "\n\n" BB_BANNER " Built-in shell (ash)\n"
-                       "Enter 'help' for a list of built-in commands.\n\n");
+                       "\n\n%s Built-in shell (ash)\n"
+                       "Enter 'help' for a list of built-in commands.\n\n",
+                                       BB_BANNER);
                                        do_banner++;
                                }
                }
@@ -11890,19 +11905,21 @@ 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);
 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
        if (iflag && rootshell) {
                const char *hp = lookupvar("HISTFILE");
@@ -11924,7 +11941,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];
 
@@ -11980,7 +11997,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;
@@ -11989,9 +12006,8 @@ setvar(const char *name, const char *val, int flags)
        }
        INTOFF;
        p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
-       *p++ = '\0';
-       if (vallen) {
-               p[-1] = '=';
+       if (val) {
+               *p++ = '=';
                p = mempcpy(p, val, vallen);
        }
        *p = '\0';
@@ -12023,7 +12039,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)
@@ -12453,7 +12469,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>
 
@@ -12488,21 +12504,21 @@ static int timescmd(int ac, char **av)
 }
 
 #ifdef CONFIG_ASH_MATH_SUPPORT
-static long
+static arith_t
 dash_arith(const char *s)
 {
-       long result;
+       arith_t result;
        int errcode = 0;
 
        INTOFF;
        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);
        }
@@ -12523,11 +12539,11 @@ static int
 letcmd(int argc, char **argv)
 {
        char **ap;
-       long i;
+       arith_t i;
 
        ap = argv + 1;
        if(!*ap)
-               error("expression expected");
+               sh_error("expression expected");
        for (ap = argv + 1; *ap; ap++) {
                i = dash_arith(*ap);
        }
@@ -12536,7 +12552,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.
@@ -12571,27 +12587,125 @@ readcmd(int argc, char **argv)
        int startword;
        int status;
        int i;
+#if defined(CONFIG_ASH_READ_NCHARS)
+       int nch_flag = 0;
+       int nchars = 0;
+       int silent = 0;
+       struct termios tty, old_tty;
+#endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+       fd_set set;
+       struct timeval ts;
+
+       ts.tv_sec = ts.tv_usec = 0;
+#endif
 
        rflag = 0;
        prompt = NULL;
-       while ((i = nextopt("p:r")) != '\0') {
-               if (i == 'p')
+#if defined(CONFIG_ASH_READ_NCHARS) && defined(CONFIG_ASH_READ_TIMEOUT)
+       while ((i = nextopt("p:rt:n:s")) != '\0')
+#elif defined(CONFIG_ASH_READ_NCHARS)
+       while ((i = nextopt("p:rn:s")) != '\0')
+#elif defined(CONFIG_ASH_READ_TIMEOUT)
+       while ((i = nextopt("p:rt:")) != '\0')
+#else
+       while ((i = nextopt("p:r")) != '\0')
+#endif
+       {
+               switch(i) {
+               case 'p':
                        prompt = optionarg;
-               else
+                       break;
+#if defined(CONFIG_ASH_READ_NCHARS)
+               case 'n':
+                       nchars = strtol(optionarg, &p, 10);
+                       if (*p)
+                               sh_error("invalid count");
+                       nch_flag = (nchars > 0);
+                       break;
+               case 's':
+                       silent = 1;
+                       break;
+#endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+               case 't':
+                       ts.tv_sec = strtol(optionarg, &p, 10);
+                       ts.tv_usec = 0;
+                       if (*p == '.') {
+                               char *p2;
+                               if (*++p) {
+                                       int scale;
+                                       ts.tv_usec = strtol(p, &p2, 10);
+                                       if (*p2)
+                                               sh_error("invalid timeout");
+                                       scale = p2 - p;
+                                       /* normalize to usec */
+                                       if (scale > 6)
+                                               sh_error("invalid timeout");
+                                       while (scale++ < 6)
+                                               ts.tv_usec *= 10;
+                               }
+                       } else if (*p) {
+                               sh_error("invalid timeout");
+                       }
+                       if ( ! ts.tv_sec && ! ts.tv_usec)
+                               sh_error("invalid timeout");
+                       break;
+#endif
+               case 'r':
                        rflag = 1;
+                       break;
+               default:
+                       break;
+               }
        }
        if (prompt && isatty(0)) {
                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)
+       if (nch_flag || silent) {
+               tcgetattr(0, &tty);
+               old_tty = tty;
+               if (nch_flag) {
+                   tty.c_lflag &= ~ICANON;
+                   tty.c_cc[VMIN] = nchars;
+               }
+               if (silent) {
+                   tty.c_lflag &= ~(ECHO|ECHOK|ECHONL);
+
+               }
+               tcsetattr(0, TCSANOW, &tty);
+       }
+#endif
+#if defined(CONFIG_ASH_READ_TIMEOUT)
+       if (ts.tv_sec || ts.tv_usec) {
+               FD_ZERO (&set);
+               FD_SET (0, &set);
+
+               i = select (FD_SETSIZE, &set, NULL, NULL, &ts);
+               if (!i) {
+#if defined(CONFIG_ASH_READ_NCHARS)
+                       if (nch_flag)
+                               tcsetattr(0, TCSANOW, &old_tty);
+#endif
+                       return 1;
+               }
+       }
+#endif
        status = 0;
        startword = 1;
        backslash = 0;
        STARTSTACKSTR(p);
-       for (;;) {
+#if defined(CONFIG_ASH_READ_NCHARS)
+       while (!nch_flag || nchars--)
+#else
+       for (;;)
+#endif
+       {
                if (read(0, &c, 1) != 1) {
                        status = 1;
                        break;
@@ -12625,6 +12739,11 @@ put:
                        STPUTC(c, p);
                }
        }
+#if defined(CONFIG_ASH_READ_NCHARS)
+       if (nch_flag || silent)
+               tcsetattr(0, TCSANOW, &old_tty);
+#endif
+
        STACKSTRNUL(p);
        /* Remove trailing blanks */
        while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
@@ -12687,14 +12806,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);
                }
@@ -12844,7 +12963,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 {
@@ -12857,7 +12976,7 @@ ulimitcmd(int argc, char **argv)
                                        break;
                        }
                        if (c)
-                               error("bad number");
+                               sh_error("bad number");
                        val *= l->factor;
                }
        }
@@ -12877,7 +12996,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);
        }
@@ -13094,8 +13213,8 @@ static inline int is_right_associativity(operator prec)
 
 
 typedef struct ARITCH_VAR_NUM {
-       long val;
-       long contidional_second_val;
+       arith_t val;
+       arith_t contidional_second_val;
        char contidional_second_val_initialized;
        char *var;      /* if NULL then is regular number,
                           else is variable name */
@@ -13152,9 +13271,8 @@ static int arith_lookup_val(v_n_t *t)
 static inline int
 arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
 {
-       long numptr_val;
        v_n_t *numptr_m1;
-       long rez;
+       arith_t numptr_val, rez;
        int ret_arith_lookup_val;
 
        if (NUMPTR == numstack) goto err; /* There is no operator that can work
@@ -13257,7 +13375,7 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
                        if(numptr_val < 0)
                                return -3;      /* exponent less than 0 */
                        else {
-                               long c = 1;
+                               arith_t c = 1;
 
                                if(numptr_val)
                                        while(numptr_val--)
@@ -13280,7 +13398,11 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
                        goto err;
                }
                /* save to shell variable */
-               sprintf(buf, "%ld", rez);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+               snprintf(buf, sizeof(buf), "%lld", arith_t_type rez);
+#else
+               snprintf(buf, sizeof(buf), "%ld", arith_t_type rez);
+#endif
                setvar(numptr_m1->var, buf, 0);
                /* after saving, make previous value for v++ or v-- */
                if(op == TOK_POST_INC)
@@ -13343,9 +13465,9 @@ static const char op_tokens[] = {
 #define endexpression &op_tokens[sizeof(op_tokens)-7]
 
 
-extern long arith (const char *expr, int *perrcode)
+static arith_t arith (const char *expr, int *perrcode)
 {
-    register char arithval; /* Current character under analysis */
+    char arithval; /* Current character under analysis */
     operator lasttok, op;
     operator prec;
 
@@ -13406,7 +13528,7 @@ extern long arith (const char *expr, int *perrcode)
                        goto prologue;
                }
                if((p = endofname(expr)) != expr) {
-                       int var_name_size = (p-expr) + 1;  /* trailing zero */
+                       size_t var_name_size = (p-expr) + 1;  /* trailing zero */
 
                        numstackptr->var = alloca(var_name_size);
                        safe_strncpy(numstackptr->var, expr, var_name_size);
@@ -13418,7 +13540,11 @@ extern long arith (const char *expr, int *perrcode)
                        continue;
                } else if (is_digit(arithval)) {
                        numstackptr->var = NULL;
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+                       numstackptr->val = strtoll(expr, (char **) &expr, 0);
+#else
                        numstackptr->val = strtol(expr, (char **) &expr, 0);
+#endif
                        goto num;
                }
                for(p = op_tokens; ; p++) {
@@ -13550,11 +13676,7 @@ int main(int argc, char **argv)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- *
- * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
- *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
- *
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *