* 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
* maybe change map[] to use 2-bit entries
* (eventually) remove all the printf's
*
- * 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
+ * SPDX-License-Identifier: GPL-2.0+
*/
+
#define __U_BOOT__
#ifdef __U_BOOT__
#include <malloc.h> /* malloc, free, realloc*/
#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
#ifndef __U_BOOT__
#include <ctype.h> /* isalpha, isdigit */
#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 */
char **global_argv;
unsigned int global_argc;
#endif
-unsigned int last_return_code;
-int nesting_level;
+static unsigned int last_return_code;
#ifndef __U_BOOT__
extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
#endif
/* 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);
+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)
{
fflush(stdout);
i->p = the_command;
#else
- extern char console_buffer[];
int n;
static char the_command[CONFIG_SYS_CBSIZE];
#ifdef CONFIG_BOOT_RETRY_TIME
-# ifdef CONFIG_RESET_TO_RETRY
- extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
-# else
+# ifndef CONFIG_RESET_TO_RETRY
# error "This currently only works with CONFIG_RESET_TO_RETRY enabled"
# endif
reset_cmd_timeout();
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 defined(CONFIG_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
- /* found - check max args */
- if ((child->argc - i) > cmdtp->maxargs) {
- cmd_usage(cmdtp);
- 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, NULL);
+#endif
}
+#ifndef __U_BOOT__
for (i = 0; i < pi->num_progs; i++) {
child = & (pi->progs[i]);
#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;
* NAME=VALUE format. So the first order of business is to
* split 's' on the '=' into 'name' and 'value' */
value = strchr(name, '=');
- if (value==0 && ++value==0) {
+ if (value == NULL && ++value == NULL) {
free(name);
return -1;
}
result = -1;
} else {
cur->name = strdup(name);
- if(cur->name == 0) {
+ if (cur->name == NULL) {
free(cur);
result = -1;
} else {
struct variables *bottom = top_vars;
cur->value = strdup(value);
- cur->next = 0;
+ cur->next = NULL;
cur->flg_export = flg_export;
cur->flg_read_only = 0;
while(bottom->next) bottom=bottom->next;
if(strcmp(cur->name, name)==0)
break;
}
- if(cur!=0) {
+ if (cur != NULL) {
struct variables *next = top_vars;
if(cur->flg_read_only) {
error_msg("%s: readonly variable", name);
}
#endif
-struct pipe *new_pipe(void) {
+static struct pipe *new_pipe(void)
+{
struct pipe *pi;
pi = xmalloc(sizeof(struct pipe));
pi->num_progs = 0;
};
#define NRES (sizeof(reserved_list)/sizeof(struct reserved_combo))
-int reserved_word(o_string *dest, struct p_context *ctx)
+static int reserved_word(o_string *dest, struct p_context *ctx)
{
struct reserved_combo *r;
for (r=reserved_list;
static char *lookup_param(char *src)
{
char *p;
+ char *sep;
+ char *default_val = NULL;
+ int assign = 0;
+ int expand_empty = 0;
if (!src)
return NULL;
- p = getenv(src);
- if (!p)
- p = get_local_var(src);
+ 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;
}
#endif
/* return code is 0 for normal exit, 1 for syntax error */
-int parse_stream(o_string *dest, struct p_context *ctx,
- struct in_str *input, int end_trigger)
+static int parse_stream(o_string *dest, struct p_context *ctx,
+ struct in_str *input, int end_trigger)
{
unsigned int ch, m;
#ifndef __U_BOOT__
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;
return 0;
}
-void mapset(const unsigned char *set, int code)
+static void mapset(const unsigned char *set, int code)
{
const unsigned char *s;
for (s=set; *s; s++) map[*s] = code;
}
-void update_ifs_map(void)
+static void update_ifs_map(void)
{
/* char *ifs and char map[256] are both globals. */
ifs = (uchar *)getenv("IFS");
mapset((uchar *)"\\$'\"`", 3); /* never flow through */
mapset((uchar *)"<>;&|(){}#", 1); /* flow through if quoted */
#else
+ {
+ 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
/* most recursion does not come through here, the exeception is
* from builtin_source() */
-int parse_stream_outer(struct in_str *inp, int flag)
+static int parse_stream_outer(struct in_str *inp, int flag)
{
struct p_context ctx;
#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__
-#ifndef CONFIG_RELOC_FIXUP_WORKS
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
static void u_boot_hush_reloc(void)
{
unsigned long addr;
top_vars = malloc(sizeof(struct variables));
top_vars->name = "HUSH_VERSION";
top_vars->value = "0.01";
- top_vars->next = 0;
+ top_vars->next = NULL;
top_vars->flg_export = 0;
top_vars->flg_read_only = 1;
-#ifndef CONFIG_RELOC_FIXUP_WORKS
+#ifdef CONFIG_NEEDS_MANUAL_RELOC
u_boot_hush_reloc();
#endif
}
tcsetpgrp(shell_terminal, shell_pgrp);
}
-int hush_main(int argc, char **argv)
+int hush_main(int argc, char * const *argv)
{
int opt;
FILE *input;
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;
int done = 0;
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, " ");
}
#ifdef __U_BOOT__
-int do_showvar (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+static int do_showvar(cmd_tbl_t *cmdtp, int flag, int argc,
+ char * const argv[])
{
int i, k;
int rcode = 0;