]> git.sur5r.net Git - u-boot/blobdiff - common/hush.c
powerpc mpc85xx: Only clear TSR:WIS in watchdog_reset.
[u-boot] / common / hush.c
index fcc3559235c472876ffa267cea6c002a2e79708e..4c84c2f50153fefe78e814729c5a84c55dc903f2 100644 (file)
@@ -1,4 +1,3 @@
-/* vi: set sw=4 ts=4: */
 /*
  * sh.c -- a prototype Bourne shell grammar parser
  *      Intended to follow the original Thompson and Ritchie
@@ -18,7 +17,6 @@
  *      Erik W. Troan, which they placed in the public domain.  I don't know
  *      how much of the Johnson/Troan code has survived the repeated rewrites.
  * Other credits:
- *      simple_itoa() was lifted from boa-0.93.15
  *      b_addchr() derived from similar w_addchar function in glibc-2.2
  *      setup_redirect(), redirect_opt_num(), and big chunks of main()
  *        and many builtins derived from contributions by Erik Andersen
 #include <common.h>        /* readline */
 #include <hush.h>
 #include <command.h>        /* find_cmd */
-#include <cmd_bootm.h>      /* do_bootd */
+#ifndef CONFIG_SYS_PROMPT_HUSH_PS2
+#define CONFIG_SYS_PROMPT_HUSH_PS2     "> "
+#endif
 #endif
-#ifdef CFG_HUSH_PARSER
 #ifndef __U_BOOT__
 #include <ctype.h>     /* isalpha, isdigit */
 #include <unistd.h>    /* getpid */
 #include <signal.h>
 
 /* #include <dmalloc.h> */
-/* #define DEBUG_SHELL */
 
 #if 1
 #include "busybox.h"
 #endif
 #endif
 #define SPECIAL_VAR_SYMBOL 03
+#define SUBSTED_VAR_SYMBOL 04
 #ifndef __U_BOOT__
 #define FLAG_EXIT_FROM_LOOP 1
 #define FLAG_PARSE_SEMICOLON (1 << 1)          /* symbol ';' is special for parser */
 #endif
 
 #ifdef __U_BOOT__
+DECLARE_GLOBAL_DATA_PTR;
+
 #define EXIT_SUCCESS 0
 #define EOF -1
 #define syntax() syntax_err()
@@ -289,12 +290,13 @@ char **global_argv;
 unsigned int global_argc;
 #endif
 unsigned int last_return_code;
+int nesting_level;
 #ifndef __U_BOOT__
 extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
 #endif
 
 /* "globals" within this file */
-static char *ifs;
+static uchar *ifs;
 static char map[256];
 #ifndef __U_BOOT__
 static int fake_mode;
@@ -312,7 +314,7 @@ struct variables *top_vars = &shell_ver;
 #else
 static int flag_repeat = 0;
 static int do_repeat = 0;
-static struct variables *top_vars ;
+static struct variables *top_vars = NULL ;
 #endif /*__U_BOOT__ */
 
 #define B_CHUNK (100)
@@ -357,6 +359,11 @@ struct built_in_command {
 };
 #endif
 
+/* define DEBUG_SHELL for debugging output (obviously ;-)) */
+#if 0
+#define DEBUG_SHELL
+#endif
+
 /* This should be in utility.c */
 #ifdef DEBUG_SHELL
 #ifndef __U_BOOT__
@@ -368,7 +375,7 @@ static void debug_printf(const char *format, ...)
        va_end(args);
 }
 #else
-#define debug_printf printf             /* U-Boot debug flag */
+#define debug_printf(fmt,args...)      printf (fmt ,##args)
 #endif
 #else
 static inline void debug_printf(const char *format, ...) { }
@@ -415,7 +422,9 @@ static int b_check_space(o_string *o, int len);
 static int b_addchr(o_string *o, int ch);
 static void b_reset(o_string *o);
 static int b_addqchr(o_string *o, int ch, int quote);
+#ifndef __U_BOOT__
 static int b_adduint(o_string *o, unsigned int i);
+#endif
 /*  in_str manipulations: */
 static int static_get(struct in_str *i);
 static int static_peek(struct in_str *i);
@@ -491,11 +500,7 @@ static void remove_bg_job(struct pipe *pi);
 /*     local variable support */
 static char **make_list_in(char **inp, char *name);
 static char *insert_var_value(char *inp);
-static char *get_local_var(const char *var);
-#ifndef __U_BOOT__
-static void  unset_local_var(const char *name);
-#endif
-static int set_local_var(const char *s, int flg_export);
+static char *insert_var_value_sub(char *inp, int tag_subst);
 
 #ifndef __U_BOOT__
 /* Table of built-in functions.  They can be forked or not, depending on
@@ -921,20 +926,7 @@ static int b_addqchr(o_string *o, int ch, int quote)
        return b_addchr(o, ch);
 }
 
-/* belongs in utility.c */
-char *simple_itoa(unsigned int i)
-{
-       /* 21 digits plus null terminator, good for 64-bit or smaller ints */
-       static char local[22];
-       char *p = &local[21];
-       *p-- = '\0';
-       do {
-               *p-- = '0' + i % 10;
-               i /= 10;
-       } while (i > 0);
-       return p + 1;
-}
-
+#ifndef __U_BOOT__
 static int b_adduint(o_string *o, unsigned int i)
 {
        int r;
@@ -943,10 +935,11 @@ static int b_adduint(o_string *o, unsigned int i)
        do r=b_addchr(o, *p++); while (r==0 && *p);
        return r;
 }
+#endif
 
 static int static_get(struct in_str *i)
 {
-       int ch=*i->p++;
+       int ch = *i->p++;
        if (ch=='\0') return EOF;
        return ch;
 }
@@ -1012,16 +1005,31 @@ static void get_user_input(struct in_str *i)
        fflush(stdout);
        i->p = the_command;
 #else
-       extern char console_buffer[CFG_CBSIZE];
        int n;
-       static char the_command[CFG_CBSIZE];
+       static char the_command[CONFIG_SYS_CBSIZE];
 
+#ifdef CONFIG_BOOT_RETRY_TIME
+#  ifndef CONFIG_RESET_TO_RETRY
+#      error "This currently only works with CONFIG_RESET_TO_RETRY enabled"
+#  endif
+       reset_cmd_timeout();
+#endif
        i->__promptme = 1;
        if (i->promptmode == 1) {
-               n = readline(CFG_PROMPT);
+               n = readline(CONFIG_SYS_PROMPT);
        } else {
-               n = readline(CFG_PROMPT_HUSH_PS2);
+               n = readline(CONFIG_SYS_PROMPT_HUSH_PS2);
+       }
+#ifdef CONFIG_BOOT_RETRY_TIME
+       if (n == -2) {
+         puts("\nTimeout waiting for command\n");
+#  ifdef CONFIG_RESET_TO_RETRY
+         do_reset(NULL, 0, 0, NULL);
+#  else
+#      error "This currently only works with CONFIG_RESET_TO_RETRY enabled"
+#  endif
        }
+#endif
        if (n == -1 ) {
                flag_repeat = 0;
                i->__promptme = 0;
@@ -1048,12 +1056,12 @@ static void get_user_input(struct in_str *i)
                i->p = the_command;
        }
        else {
-               if (console_buffer[0] != '\n') {
-                       if (strlen(the_command) + strlen(console_buffer)
-                           < CFG_CBSIZE) {
-                               n = strlen(the_command);
-                               the_command[n-1] = ' ';
-                               strcpy(&the_command[n],console_buffer);
+               if (console_buffer[0] != '\n') {
+                       if (strlen(the_command) + strlen(console_buffer)
+                           < CONFIG_SYS_CBSIZE) {
+                               n = strlen(the_command);
+                               the_command[n-1] = ' ';
+                               strcpy(&the_command[n],console_buffer);
                        }
                        else {
                                the_command[0] = '\n';
@@ -1079,7 +1087,7 @@ static int file_get(struct in_str *i)
        ch = 0;
        /* If there is data waiting, eat it up */
        if (i->p && *i->p) {
-               ch=*i->p++;
+               ch = *i->p++;
        } else {
                /* need to double check i->file because we might be doing something
                 * more complicated by now, like sourcing or substituting. */
@@ -1096,7 +1104,7 @@ static int file_get(struct in_str *i)
                        i->__promptme = 0;
 #endif
                        if (i->p && *i->p) {
-                               ch=*i->p++;
+                               ch = *i->p++;
                        }
 #ifndef __U_BOOT__
                } else {
@@ -1257,8 +1265,8 @@ static void pseudo_exec(struct child_prog *child)
                        if (p != child->argv[i]) free(p);
                }
                child->argv+=i;  /* XXX this hack isn't so horrible, since we are about
-                                       to exit, and therefore don't need to keep data
-                                       structures consistent for free() use. */
+                                       to exit, and therefore don't need to keep data
+                                       structures consistent for free() use. */
                /* If a variable is assigned in a forest, and nobody listens,
                 * was it ever really set?
                 */
@@ -1535,7 +1543,6 @@ static int run_pipe_real(struct pipe *pi)
        int nextin;
        int flag = do_repeat ? CMD_FLAG_REPEAT : 0;
        struct child_prog *child;
-       cmd_tbl_t *cmdtp;
        char *p;
 # if __GNUC__
        /* Avoid longjmp clobbering */
@@ -1639,57 +1646,30 @@ static int run_pipe_real(struct pipe *pi)
                                 * Is it really safe for inline use?  Experimentally,
                                 * things seem to work with glibc. */
                                setup_redirects(child, squirrel);
-#else
-                       /* check ";", because ,example , argv consist from
-                        * "help;flinfo" must not execute
-                        */
-                       if (strchr(child->argv[i], ';')) {
-                               printf ("Unknown command '%s' - try 'help' or use 'run' command\n",
-                                       child->argv[i]);
-                               return -1;
-                       }
-                       /* Look up command in command table */
-                       if ((cmdtp = find_cmd(child->argv[i])) == NULL) {
-                               printf ("Unknown command '%s' - try 'help'\n", child->argv[i]);
-                               return -1;      /* give up after bad command */
-                       } else {
-                               int rcode;
-#if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
-                               /* avoid "bootd" recursion */
-                               if (cmdtp->cmd == do_bootd) {
-                                       if (flag & CMD_FLAG_BOOTD) {
-                                               printf ("'bootd' recursion detected\n");
-                                               return -1;
-                                       }
-                               else
-                                       flag |= CMD_FLAG_BOOTD;
-                               }
-#endif /* CFG_CMD_BOOTD */
-                               /* found - check max args */
-                               if ((child->argc - i) > cmdtp->maxargs) {
-                                       printf ("Usage:\n%s\n", cmdtp->usage);
-                                       return -1;
-                               }
-#endif
-                               child->argv+=i;  /* XXX horrible hack */
-#ifndef __U_BOOT__
+
+                               child->argv += i;  /* XXX horrible hack */
                                rcode = x->function(child);
-#else
-                               /* OK - call function to do the command */
-                               rcode = (cmdtp->cmd)
-                                       (cmdtp, flag,child->argc-i,&child->argv[i]);
-                               if ( !cmdtp->repeatable )
-                                       flag_repeat = 0;
-#endif
-                               child->argv-=i;  /* XXX restore hack so free() can work right */
-#ifndef __U_BOOT__
+                               /* XXX restore hack so free() can work right */
+                               child->argv -= i;
                                restore_redirects(squirrel);
-#endif
-                               return rcode;
                        }
+                       return rcode;
                }
-#ifndef __U_BOOT__
+#else
+               /* check ";", because ,example , argv consist from
+                * "help;flinfo" must not execute
+                */
+               if (strchr(child->argv[i], ';')) {
+                       printf("Unknown command '%s' - try 'help' or use "
+                                       "'run' command\n", child->argv[i]);
+                       return -1;
+               }
+               /* Process the command */
+               return cmd_process(flag, child->argc, child->argv,
+                                  &flag_repeat);
+#endif
        }
+#ifndef __U_BOOT__
 
        for (i = 0; i < pi->num_progs; i++) {
                child = & (pi->progs[i]);
@@ -1833,7 +1813,7 @@ static int run_list_real(struct pipe *pi)
                if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code;
                if (rmode == RES_THEN &&  if_code) continue;
                if (rmode == RES_ELSE && !if_code) continue;
-               if (rmode == RES_ELIF && !if_code) continue;
+               if (rmode == RES_ELIF && !if_code) break;
                if (rmode == RES_FOR && pi->num_progs) {
                        if (!list) {
                                /* if no variable values after "in" we skip "for" */
@@ -1911,6 +1891,10 @@ static int run_list_real(struct pipe *pi)
                }
                last_return_code=rcode;
 #else
+               if (rcode < -1) {
+                       last_return_code = -rcode - 2;
+                       return -2;      /* exit */
+               }
                last_return_code=(rcode == 0) ? 0 : 1;
 #endif
 #ifndef __U_BOOT__
@@ -1965,11 +1949,11 @@ static int free_pipe(struct pipe *pi, int indent)
 #ifndef __U_BOOT__
                        globfree(&child->glob_result);
 #else
-                       for (a = child->argc;a >= 0;a--) {
-                               free(child->argv[a]);
-                       }
+                       for (a = 0; a < child->argc; a++) {
+                               free(child->argv[a]);
+                       }
                                        free(child->argv);
-                       child->argc = 0;
+                       child->argc = 0;
 #endif
                        child->argv=NULL;
                } else if (child->group) {
@@ -2103,17 +2087,17 @@ static int xglob(o_string *dest, int flags, glob_t *pglob)
 {
        int gr;
 
-       /* short-circuit for null word */
+       /* short-circuit for null word */
        /* we can code this better when the debug_printf's are gone */
-       if (dest->length == 0) {
-               if (dest->nonnull) {
-                       /* bash man page calls this an "explicit" null */
-                       gr = globhack(dest->data, flags, pglob);
-                       debug_printf("globhack returned %d\n",gr);
-               } else {
+       if (dest->length == 0) {
+               if (dest->nonnull) {
+                       /* bash man page calls this an "explicit" null */
+                       gr = globhack(dest->data, flags, pglob);
+                       debug_printf("globhack returned %d\n",gr);
+               } else {
                        return 0;
                }
-       } else if (glob_needed(dest->data)) {
+       } else if (glob_needed(dest->data)) {
                gr = glob(dest->data, flags, NULL, pglob);
                debug_printf("glob returned %d\n",gr);
                if (gr == GLOB_NOMATCH) {
@@ -2135,13 +2119,23 @@ static int xglob(o_string *dest, int flags, glob_t *pglob)
 }
 #endif
 
+#ifdef __U_BOOT__
+static char *get_dollar_var(char ch);
+#endif
+
 /* This is used to get/check local shell variables */
-static char *get_local_var(const char *s)
+char *get_local_var(const char *s)
 {
        struct variables *cur;
 
        if (!s)
                return NULL;
+
+#ifdef __U_BOOT__
+       if (*s == '$')
+               return get_dollar_var(s[1]);
+#endif
+
        for (cur = top_vars; cur; cur=cur->next)
                if(strcmp(cur->name, s)==0)
                        return cur->value;
@@ -2152,18 +2146,25 @@ static char *get_local_var(const char *s)
    flg_export==0 if only local (not exporting) variable
    flg_export==1 if "new" exporting environ
    flg_export>1  if current startup environ (not call putenv()) */
-static int set_local_var(const char *s, int flg_export)
+int set_local_var(const char *s, int flg_export)
 {
        char *name, *value;
        int result=0;
        struct variables *cur;
 
+#ifdef __U_BOOT__
+       /* might be possible! */
+       if (!isalpha(*s))
+               return -1;
+#endif
+
        name=strdup(s);
 
 #ifdef __U_BOOT__
        if (getenv(name) != NULL) {
                printf ("ERROR: "
-                               "There is a global environmet variable with the same name.\n");
+                               "There is a global environment variable with the same name.\n");
+               free(name);
                return -1;
        }
 #endif
@@ -2236,8 +2237,7 @@ static int set_local_var(const char *s, int flg_export)
        return result;
 }
 
-#ifndef __U_BOOT__
-static void unset_local_var(const char *name)
+void unset_local_var(const char *name)
 {
        struct variables *cur;
 
@@ -2252,8 +2252,10 @@ static void unset_local_var(const char *name)
                                error_msg("%s: readonly variable", name);
                                return;
                        } else {
+#ifndef __U_BOOT__
                                if(cur->flg_export)
                                        unsetenv(cur->name);
+#endif
                                free(cur->name);
                                free(cur->value);
                                while (next->next != cur)
@@ -2264,11 +2266,13 @@ static void unset_local_var(const char *name)
                }
        }
 }
-#endif
 
 static int is_assignment(const char *s)
 {
-       if (s==NULL || !isalpha(*s)) return 0;
+       if (s == NULL)
+               return 0;
+
+       if (!isalpha(*s)) return 0;
        ++s;
        while(isalnum(*s) || *s=='_') ++s;
        return *s=='=';
@@ -2332,6 +2336,7 @@ struct pipe *new_pipe(void) {
        pi->progs = NULL;
        pi->next = NULL;
        pi->followup = 0;  /* invalid */
+       pi->r_mode = RES_NONE;
        return pi;
 }
 
@@ -2357,34 +2362,35 @@ static void initialize_context(struct p_context *ctx)
  * should handle if, then, elif, else, fi, for, while, until, do, done.
  * case, function, and select are obnoxious, save those for later.
  */
+struct reserved_combo {
+       char *literal;
+       int code;
+       long flag;
+};
+/* Mostly a list of accepted follow-up reserved words.
+ * FLAG_END means we are done with the sequence, and are ready
+ * to turn the compound list into a command.
+ * FLAG_START means the word must start a new compound list.
+ */
+static struct reserved_combo reserved_list[] = {
+       { "if",    RES_IF,    FLAG_THEN | FLAG_START },
+       { "then",  RES_THEN,  FLAG_ELIF | FLAG_ELSE | FLAG_FI },
+       { "elif",  RES_ELIF,  FLAG_THEN },
+       { "else",  RES_ELSE,  FLAG_FI   },
+       { "fi",    RES_FI,    FLAG_END  },
+       { "for",   RES_FOR,   FLAG_IN   | FLAG_START },
+       { "while", RES_WHILE, FLAG_DO   | FLAG_START },
+       { "until", RES_UNTIL, FLAG_DO   | FLAG_START },
+       { "in",    RES_IN,    FLAG_DO   },
+       { "do",    RES_DO,    FLAG_DONE },
+       { "done",  RES_DONE,  FLAG_END  }
+};
+#define NRES (sizeof(reserved_list)/sizeof(struct reserved_combo))
+
 int reserved_word(o_string *dest, struct p_context *ctx)
 {
-       struct reserved_combo {
-               char *literal;
-               int code;
-               long flag;
-       };
-       /* Mostly a list of accepted follow-up reserved words.
-        * FLAG_END means we are done with the sequence, and are ready
-        * to turn the compound list into a command.
-        * FLAG_START means the word must start a new compound list.
-        */
-       static struct reserved_combo reserved_list[] = {
-               { "if",    RES_IF,    FLAG_THEN | FLAG_START },
-               { "then",  RES_THEN,  FLAG_ELIF | FLAG_ELSE | FLAG_FI },
-               { "elif",  RES_ELIF,  FLAG_THEN },
-               { "else",  RES_ELSE,  FLAG_FI   },
-               { "fi",    RES_FI,    FLAG_END  },
-               { "for",   RES_FOR,   FLAG_IN   | FLAG_START },
-               { "while", RES_WHILE, FLAG_DO   | FLAG_START },
-               { "until", RES_UNTIL, FLAG_DO   | FLAG_START },
-               { "in",    RES_IN,    FLAG_DO   },
-               { "do",    RES_DO,    FLAG_DONE },
-               { "done",  RES_DONE,  FLAG_END  }
-       };
        struct reserved_combo *r;
        for (r=reserved_list;
-#define NRES sizeof(reserved_list)/sizeof(struct reserved_combo)
                r<reserved_list+NRES; r++) {
                if (strcmp(dest->data, r->literal) == 0) {
                        debug_printf("found reserved word %s, code %d\n",r->literal,r->code);
@@ -2461,7 +2467,7 @@ static int done_word(o_string *dest, struct p_context *ctx)
                }
 #ifndef __U_BOOT__
                glob_target = &child->glob_result;
-               if (child->argv) flags |= GLOB_APPEND;
+               if (child->argv) flags |= GLOB_APPEND;
 #else
                for (cnt = 1, s = dest->data; s && *s; s++) {
                        if (*s == '\\') s++;
@@ -2521,9 +2527,9 @@ static int done_command(struct p_context *ctx)
        struct child_prog *prog=ctx->child;
 
        if (prog && prog->group == NULL
-                && prog->argv == NULL
+                && prog->argv == NULL
 #ifndef __U_BOOT__
-                && prog->redirects == NULL) {
+                && prog->redirects == NULL) {
 #else
                                                                                ) {
 #endif
@@ -2738,15 +2744,72 @@ static int parse_group(o_string *dest, struct p_context *ctx,
  * see the bash man page under "Parameter Expansion" */
 static char *lookup_param(char *src)
 {
-       char *p=NULL;
-       if (src) {
-               p = getenv(src);
-               if (!p)
-                       p = get_local_var(src);
+       char *p;
+       char *sep;
+       char *default_val = NULL;
+       int assign = 0;
+       int expand_empty = 0;
+
+       if (!src)
+               return NULL;
+
+       sep = strchr(src, ':');
+
+       if (sep) {
+               *sep = '\0';
+               if (*(sep + 1) == '-')
+                       default_val = sep+2;
+               if (*(sep + 1) == '=') {
+                       default_val = sep+2;
+                       assign = 1;
+               }
+               if (*(sep + 1) == '+') {
+                       default_val = sep+2;
+                       expand_empty = 1;
+               }
        }
+
+       p = getenv(src);
+       if (!p)
+               p = get_local_var(src);
+
+       if (!p || strlen(p) == 0) {
+               p = default_val;
+               if (assign) {
+                       char *var = malloc(strlen(src)+strlen(default_val)+2);
+                       if (var) {
+                               sprintf(var, "%s=%s", src, default_val);
+                               set_local_var(var, 0);
+                       }
+                       free(var);
+               }
+       } else if (expand_empty) {
+               p += strlen(p);
+       }
+
+       if (sep)
+               *sep = ':';
+
        return p;
 }
 
+#ifdef __U_BOOT__
+static char *get_dollar_var(char ch)
+{
+       static char buf[40];
+
+       buf[0] = '\0';
+       switch (ch) {
+               case '?':
+                       sprintf(buf, "%u", (unsigned int)last_return_code);
+                       break;
+               default:
+                       return NULL;
+       }
+       return buf;
+}
+#endif
+
 /* return code: 0 for OK, 1 for syntax error */
 static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
 {
@@ -2788,7 +2851,15 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i
                        break;
 #endif
                case '?':
+#ifndef __U_BOOT__
                        b_adduint(dest,last_return_code);
+#else
+                       ctx->child->sp++;
+                       b_addchr(dest, SPECIAL_VAR_SYMBOL);
+                       b_addchr(dest, '$');
+                       b_addchr(dest, '?');
+                       b_addchr(dest, SPECIAL_VAR_SYMBOL);
+#endif
                        advance = 1;
                        break;
 #ifndef __U_BOOT__
@@ -2874,8 +2945,11 @@ int parse_stream(o_string *dest, struct p_context *ctx,
                if (input->__promptme == 0) return 1;
 #endif
                next = (ch == '\n') ? 0 : b_peek(input);
-               debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d\n",
-                       ch,ch,m,dest->quote);
+
+               debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d - %c\n",
+                       ch >= ' ' ? ch : '.', ch, m,
+                       dest->quote, ctx->stack == NULL ? '*' : '.');
+
                if (m==0 || ((m==1 || m==2) && dest->quote)) {
                        b_addqchr(dest, ch, dest->quote);
                } else {
@@ -3016,6 +3090,21 @@ int parse_stream(o_string *dest, struct p_context *ctx,
                        return 1;
                        break;
 #endif
+               case SUBSTED_VAR_SYMBOL:
+                       dest->nonnull = 1;
+                       while (ch = b_getch(input), ch != EOF &&
+                           ch != SUBSTED_VAR_SYMBOL) {
+                               debug_printf("subst, pass=%d\n", ch);
+                               if (input->__promptme == 0)
+                                       return 1;
+                               b_addchr(dest, ch);
+                       }
+                       debug_printf("subst, term=%d\n", ch);
+                       if (ch == EOF) {
+                               syntax();
+                               return 1;
+                       }
+                       break;
                default:
                        syntax();   /* this is really an internal logic error */
                        return 1;
@@ -3044,8 +3133,8 @@ void mapset(const unsigned char *set, int code)
 void update_ifs_map(void)
 {
        /* char *ifs and char map[256] are both globals. */
-       ifs = getenv("IFS");
-       if (ifs == NULL) ifs=" \t\n";
+       ifs = (uchar *)getenv("IFS");
+       if (ifs == NULL) ifs=(uchar *)" \t\n";
        /* Precompute a list of 'flow through' behavior so it can be treated
         * quickly up front.  Computation is necessary because of IFS.
         * Special case handling of IFS == " \t\n" is not implemented.
@@ -3054,11 +3143,15 @@ void update_ifs_map(void)
         */
        memset(map,0,sizeof(map)); /* most characters flow through always */
 #ifndef __U_BOOT__
-       mapset("\\$'\"`", 3);      /* never flow through */
-       mapset("<>;&|(){}#", 1);   /* flow through if quoted */
+       mapset((uchar *)"\\$'\"`", 3);      /* never flow through */
+       mapset((uchar *)"<>;&|(){}#", 1);   /* flow through if quoted */
 #else
-       mapset("\\$'\"", 3);       /* never flow through */
-       mapset(";&|#", 1);         /* flow through if quoted */
+       {
+               uchar subst[2] = {SUBSTED_VAR_SYMBOL, 0};
+               mapset(subst, 3);       /* never flow through */
+       }
+       mapset((uchar *)"\\$'\"", 3);       /* never flow through */
+       mapset((uchar *)";&|#", 1);         /* flow through if quoted */
 #endif
        mapset(ifs, 2);            /* also flow through if quoted */
 }
@@ -3078,7 +3171,7 @@ int parse_stream_outer(struct in_str *inp, int flag)
                ctx.type = flag;
                initialize_context(&ctx);
                update_ifs_map();
-               if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset(";$&|", 0);
+               if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0);
                inp->promptmode=1;
                rcode = parse_stream(&temp, &ctx, inp, '\n');
 #ifdef __U_BOOT__
@@ -3096,7 +3189,18 @@ int parse_stream_outer(struct in_str *inp, int flag)
 #ifndef __U_BOOT__
                        run_list(ctx.list_head);
 #else
-                       if (((code = run_list(ctx.list_head)) == -1))
+                       code = run_list(ctx.list_head);
+                       if (code == -2) {       /* exit */
+                               b_free(&temp);
+                               code = 0;
+                               /* XXX hackish way to not allow exit from main loop */
+                               if (inp->peek == file_peek) {
+                                       printf("exit not allowed from main input shell.\n");
+                                       continue;
+                               }
+                               break;
+                       }
+                       if (code == -1)
                            flag_repeat = 0;
 #endif
                } else {
@@ -3125,7 +3229,7 @@ int parse_stream_outer(struct in_str *inp, int flag)
 #ifndef __U_BOOT__
 static int parse_string_outer(const char *s, int flag)
 #else
-int parse_string_outer(char *s, int flag)
+int parse_string_outer(const char *s, int flag)
 #endif /* __U_BOOT__ */
 {
        struct in_str input;
@@ -3169,14 +3273,32 @@ int parse_file_outer(void)
 }
 
 #ifdef __U_BOOT__
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+static void u_boot_hush_reloc(void)
+{
+       unsigned long addr;
+       struct reserved_combo *r;
+
+       for (r=reserved_list; r<reserved_list+NRES; r++) {
+               addr = (ulong) (r->literal) + gd->reloc_off;
+               r->literal = (char *)addr;
+       }
+}
+#endif
+
 int u_boot_hush_start(void)
 {
-       top_vars = malloc(sizeof(struct variables));
-       top_vars->name = "HUSH_VERSION";
-       top_vars->value = "0.01";
-       top_vars->next = 0;
-       top_vars->flg_export = 0;
-       top_vars->flg_read_only = 1;
+       if (top_vars == NULL) {
+               top_vars = malloc(sizeof(struct variables));
+               top_vars->name = "HUSH_VERSION";
+               top_vars->value = "0.01";
+               top_vars->next = 0;
+               top_vars->flg_export = 0;
+               top_vars->flg_read_only = 1;
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
+               u_boot_hush_reloc();
+#endif
+       }
        return 0;
 }
 
@@ -3232,7 +3354,7 @@ static void setup_job_control(void)
        tcsetpgrp(shell_terminal, shell_pgrp);
 }
 
-int hush_main(int argc, char **argv)
+int hush_main(int argc, char * const *argv)
 {
        int opt;
        FILE *input;
@@ -3325,7 +3447,7 @@ int hush_main(int argc, char **argv)
        debug_printf("\ninteractive=%d\n", interactive);
        if (interactive) {
                /* Looks like they want an interactive shell */
-#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET 
+#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
                printf( "\n\n" BB_BANNER " hush - the humble shell v0.01 (testing)\n");
                printf( "Enter 'help' for a list of built-in commands.\n\n");
 #endif
@@ -3366,6 +3488,11 @@ final_return:
 #endif
 
 static char *insert_var_value(char *inp)
+{
+       return insert_var_value_sub(inp, 0);
+}
+
+static char *insert_var_value_sub(char *inp, int tag_subst)
 {
        int res_str_len = 0;
        int len;
@@ -3373,19 +3500,46 @@ static char *insert_var_value(char *inp)
        char *p, *p1, *res_str = NULL;
 
        while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
+               /* check the beginning of the string for normal charachters */
                if (p != inp) {
+                       /* copy any charachters to the result string */
                        len = p - inp;
                        res_str = xrealloc(res_str, (res_str_len + len));
                        strncpy((res_str + res_str_len), inp, len);
                        res_str_len += len;
                }
                inp = ++p;
+               /* find the ending marker */
                p = strchr(inp, SPECIAL_VAR_SYMBOL);
                *p = '\0';
+               /* look up the value to substitute */
                if ((p1 = lookup_param(inp))) {
-                       len = res_str_len + strlen(p1);
+                       if (tag_subst)
+                               len = res_str_len + strlen(p1) + 2;
+                       else
+                               len = res_str_len + strlen(p1);
                        res_str = xrealloc(res_str, (1 + len));
-                       strcpy((res_str + res_str_len), p1);
+                       if (tag_subst) {
+                               /*
+                                * copy the variable value to the result
+                                * string
+                                */
+                               strcpy((res_str + res_str_len + 1), p1);
+
+                               /*
+                                * mark the replaced text to be accepted as
+                                * is
+                                */
+                               res_str[res_str_len] = SUBSTED_VAR_SYMBOL;
+                               res_str[res_str_len + 1 + strlen(p1)] =
+                                       SUBSTED_VAR_SYMBOL;
+                       } else
+                               /*
+                                * copy the variable value to the result
+                                * string
+                                */
+                               strcpy((res_str + res_str_len), p1);
+
                        res_str_len = len;
                }
                *p = SPECIAL_VAR_SYMBOL;
@@ -3449,9 +3603,14 @@ static char * make_string(char ** inp)
        char *str = NULL;
        int n;
        int len = 2;
+       char *noeval_str;
+       int noeval = 0;
 
+       noeval_str = get_local_var("HUSH_NO_EVAL");
+       if (noeval_str != NULL && *noeval_str != '0' && *noeval_str != '\0')
+               noeval = 1;
        for (n = 0; inp[n]; n++) {
-               p = insert_var_value(inp[n]);
+               p = insert_var_value_sub(inp[n], noeval);
                str = xrealloc(str, (len + strlen(p)));
                if (n) {
                        strcat(str, " ");
@@ -3468,5 +3627,52 @@ static char * make_string(char ** inp)
        return str;
 }
 
-#endif /* CFG_HUSH_PARSER */
+#ifdef __U_BOOT__
+int do_showvar (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       int i, k;
+       int rcode = 0;
+       struct variables *cur;
+
+       if (argc == 1) {                /* Print all env variables      */
+               for (cur = top_vars; cur; cur = cur->next) {
+                       printf ("%s=%s\n", cur->name, cur->value);
+                       if (ctrlc ()) {
+                               puts ("\n ** Abort\n");
+                               return 1;
+                       }
+               }
+               return 0;
+       }
+       for (i = 1; i < argc; ++i) {    /* print single env variables   */
+               char *name = argv[i];
+
+               k = -1;
+               for (cur = top_vars; cur; cur = cur->next) {
+                       if(strcmp (cur->name, name) == 0) {
+                               k = 0;
+                               printf ("%s=%s\n", cur->name, cur->value);
+                       }
+                       if (ctrlc ()) {
+                               puts ("\n ** Abort\n");
+                               return 1;
+                       }
+               }
+               if (k < 0) {
+                       printf ("## Error: \"%s\" not defined\n", name);
+                       rcode ++;
+               }
+       }
+       return rcode;
+}
+
+U_BOOT_CMD(
+       showvar, CONFIG_SYS_MAXARGS, 1, do_showvar,
+       "print local hushshell variables",
+       "\n    - print values of all hushshell variables\n"
+       "showvar name ...\n"
+       "    - print value of hushshell variable 'name'"
+);
+
+#endif
 /****************************************************************************/