-/* vi: set sw=4 ts=4: */
/*
* sh.c -- a prototype Bourne shell grammar parser
* Intended to follow the original Thompson and Ritchie
#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 */
#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
#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);
#ifndef __U_BOOT__
/* Table of built-in functions. They can be forked or not, depending on
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];
+ extern char console_buffer[];
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 {
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[]);
-
+#if defined(CONFIG_CMD_BOOTD)
/* avoid "bootd" recursion */
if (cmdtp->cmd == do_bootd) {
if (flag & CMD_FLAG_BOOTD) {
else
flag |= CMD_FLAG_BOOTD;
}
-#endif /* CFG_CMD_BOOTD */
+#endif
/* found - check max args */
- if ((child->argc - i) > cmdtp->maxargs) {
- printf ("Usage:\n%s\n", cmdtp->usage);
- return -1;
- }
+ if ((child->argc - i) > cmdtp->maxargs)
+ return cmd_usage(cmdtp);
#endif
child->argv+=i; /* XXX horrible hack */
#ifndef __U_BOOT__
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 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;
}
* see the bash man page under "Parameter Expansion" */
static char *lookup_param(char *src)
{
- char *p=NULL;
- if (src) {
+ char *p;
+
+ if (!src)
+ return NULL;
+
p = getenv(src);
if (!p)
p = get_local_var(src);
- }
+
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 {
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 */
+ 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 {
}
#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;
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
/****************************************************************************/