-/* vi: set sw=4 ts=4: */
/*
* sh.c -- a prototype Bourne shell grammar parser
* Intended to follow the original Thompson and Ritchie
* 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 */
-#ifdef BB_VER
+#if 1
#include "busybox.h"
#include "cmdedit.h"
#else
#define applet_name "hush"
#include "standalone.h"
#define hush_main main
-#undef BB_FEATURE_SH_FANCY_PROMPT
+#undef CONFIG_FEATURE_SH_FANCY_PROMPT
+#define BB_BANNER
#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()
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;
#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)
};
#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__
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, ...) { }
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);
/* close_me manipulations: */
static void mark_open(int fd);
static void mark_closed(int fd);
-static void close_all();
+static void close_all(void);
#endif
/* "run" the final data structures: */
static char *indenter(int i);
/* 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
static void b_free(o_string *o)
{
b_reset(o);
- if (o->data != NULL) free(o->data);
+ free(o->data);
o->data = NULL;
o->maxlen = 0;
}
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;
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;
}
#ifndef __U_BOOT__
static inline void cmdedit_set_initial_prompt(void)
{
-#ifndef BB_FEATURE_SH_FANCY_PROMPT
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
PS1 = NULL;
#else
PS1 = getenv("PS1");
static inline void setup_prompt_string(int promptmode, char **prompt_str)
{
debug_printf("setup_prompt_string %d ",promptmode);
-#ifndef BB_FEATURE_SH_FANCY_PROMPT
+#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
/* Set up the prompt */
if (promptmode == 1) {
- if (PS1)
- free(PS1);
+ free(PS1);
PS1=xmalloc(strlen(cwd)+4);
sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# ");
*prompt_str = PS1;
static char the_command[BUFSIZ];
setup_prompt_string(i->promptmode, &prompt_str);
-#ifdef BB_FEATURE_COMMAND_EDITING
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
/*
** enable command line editing only while a command line
** is actually being read; otherwise, we'll end up bequeathing
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;
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';
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. */
i->__promptme = 0;
#endif
if (i->p && *i->p) {
- ch=*i->p++;
+ ch = *i->p++;
}
#ifndef __U_BOOT__
} else {
free(tmp);
}
-static void close_all()
+static void close_all(void)
{
struct close_me *c;
for (c=close_me_head; c; c=c->next) {
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?
*/
* really dislike relying on /proc for things. We could exec ourself
* from global_argv[0], but if we are in a chroot, we may not be able
* to find ourself... */
-#ifdef BB_FEATURE_SH_STANDALONE_SHELL
+#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
{
int argc_l;
char** argv_l=child->argv;
char *name = child->argv[0];
-#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
+#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
/* Following discussions from November 2000 on the busybox mailing
* list, the default configuration, (without
* get_last_path_component()) lets the user force use of an
* external command by specifying the full (with slashes) filename.
- * If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then applets
+ * If you enable CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN then applets
* _aways_ override external commands, so if you want to run
* /bin/cat, it will use BusyBox cat even if /bin/cat exists on the
* filesystem and is _not_ busybox. Some systems may want this,
struct child_prog *child;
struct built_in_command *x;
char *p;
+# if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &i;
+ (void) &nextin;
+ (void) &nextout;
+ (void) &child;
+# endif
#else
int nextin;
int flag = do_repeat ? CMD_FLAG_REPEAT : 0;
struct child_prog *child;
- cmd_tbl_t *cmdtp;
char *p;
-#endif
+# if __GNUC__
+ /* Avoid longjmp clobbering */
+ (void) &i;
+ (void) &nextin;
+ (void) &child;
+# endif
+#endif /* __U_BOOT__ */
nextin = 0;
#ifndef __U_BOOT__
* 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]);
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" */
}
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__
#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) {
{
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) {
}
#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;
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
return result;
}
-#ifndef __U_BOOT__
-static void unset_local_var(const char *name)
+void unset_local_var(const char *name)
{
struct variables *cur;
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)
}
}
}
-#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=='=';
pi->progs = NULL;
pi->next = NULL;
pi->followup = 0; /* invalid */
+ pi->r_mode = RES_NONE;
return pi;
}
* 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);
}
#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++;
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
* 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)
{
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__
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 {
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;
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.
*/
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 */
}
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__
#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 {
#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;
}
#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;
}
/* Make sure we have a controlling tty. If we get started under a job
* aware app (like bash for example), make sure we are now in charge so
* we don't fight over who gets the foreground */
-static void setup_job_control()
+static void setup_job_control(void)
{
static pid_t shell_pgrp;
/* Loop until we are in the foreground. */
tcsetpgrp(shell_terminal, shell_pgrp);
}
-int hush_main(int argc, char **argv)
+int hush_main(int argc, char * const *argv)
{
int opt;
FILE *input;
/* Initialize some more globals to non-zero values */
set_cwd();
-#ifdef BB_FEATURE_COMMAND_EDITING
+#ifdef CONFIG_FEATURE_COMMAND_EDITING
cmdedit_set_initial_prompt();
#else
PS1 = NULL;
debug_printf("\ninteractive=%d\n", interactive);
if (interactive) {
/* Looks like they want an interactive shell */
- fprintf(stdout, "\nhush -- the humble shell v0.01 (testing)\n\n");
+#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
setup_job_control();
}
input = xfopen(argv[optind], "r");
opt = parse_file_outer(input);
-#ifdef BB_FEATURE_CLEAN_UP
+#ifdef CONFIG_FEATURE_CLEAN_UP
fclose(input);
if (cwd && cwd != unknown)
free((char*)cwd);
#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;
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;
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, " ");
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
/****************************************************************************/