-/* 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 */
-/*cmd_boot.c*/
-extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); /* 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()
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;
};
#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);
/* 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
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;
}
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;
else {
if (console_buffer[0] != '\n') {
if (strlen(the_command) + strlen(console_buffer)
- < CFG_CBSIZE) {
+ < CONFIG_SYS_CBSIZE) {
n = strlen(the_command);
the_command[n-1] = ' ';
strcpy(&the_command[n],console_buffer);
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 {
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 */
* 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)
- extern int do_bootd (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
-
- /* 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--) {
+ for (a = 0; a < child->argc; a++) {
free(child->argv[a]);
}
free(child->argv);
}
#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 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;
}
* 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)
{
- DECLARE_GLOBAL_DATA_PTR;
unsigned long addr;
struct reserved_combo *r;
r->literal = (char *)addr;
}
}
+#endif
int u_boot_hush_start(void)
{
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;
}
tcsetpgrp(shell_terminal, shell_pgrp);
}
-int hush_main(int argc, char **argv)
+int hush_main(int argc, char * const *argv)
{
int opt;
FILE *input;
#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
/****************************************************************************/