mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
cli: hush_modern: Enable variables expansion for modern hush
Enables variables expansion for modern hush, both for local and environment variables. So the following commands: foo=bar echo $foo setenv bar foo echo $bar leads to "bar" and "foo" being printed on console output. Signed-off-by: Francis Laniel <francis.laniel@amarulasolutions.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
9a06837731
commit
74e425407f
2 changed files with 103 additions and 5 deletions
|
@ -207,6 +207,23 @@ static const char* endofname(const char *name)
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_size() - returns the number of elements in char ** before NULL.
|
||||||
|
*
|
||||||
|
* Argument must contain NULL to signalize its end.
|
||||||
|
*
|
||||||
|
* @list The list to count the number of element.
|
||||||
|
* @return The number of element in list.
|
||||||
|
*/
|
||||||
|
static size_t list_size(char **list)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
for (size = 0; list[size] != NULL; size++);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
struct in_str;
|
struct in_str;
|
||||||
static int u_boot_cli_readline(struct in_str *i);
|
static int u_boot_cli_readline(struct in_str *i);
|
||||||
|
|
||||||
|
|
|
@ -3485,7 +3485,6 @@ static int o_get_last_ptr(o_string *o, int n)
|
||||||
return ((int)(uintptr_t)list[n-1]) + string_start;
|
return ((int)(uintptr_t)list[n-1]) + string_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __U_BOOT__
|
|
||||||
/*
|
/*
|
||||||
* Globbing routines.
|
* Globbing routines.
|
||||||
*
|
*
|
||||||
|
@ -3740,8 +3739,10 @@ static int glob_needed(const char *s)
|
||||||
*/
|
*/
|
||||||
static int perform_glob(o_string *o, int n)
|
static int perform_glob(o_string *o, int n)
|
||||||
{
|
{
|
||||||
|
#ifndef __U_BOOT__
|
||||||
glob_t globdata;
|
glob_t globdata;
|
||||||
int gr;
|
int gr;
|
||||||
|
#endif /* __U_BOOT__ */
|
||||||
char *pattern;
|
char *pattern;
|
||||||
|
|
||||||
debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
|
debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
|
||||||
|
@ -3750,13 +3751,16 @@ static int perform_glob(o_string *o, int n)
|
||||||
pattern = o->data + o_get_last_ptr(o, n);
|
pattern = o->data + o_get_last_ptr(o, n);
|
||||||
debug_printf_glob("glob pattern '%s'\n", pattern);
|
debug_printf_glob("glob pattern '%s'\n", pattern);
|
||||||
if (!glob_needed(pattern)) {
|
if (!glob_needed(pattern)) {
|
||||||
|
#ifndef __U_BOOT__
|
||||||
literal:
|
literal:
|
||||||
|
#endif /* __U_BOOT__ */
|
||||||
/* unbackslash last string in o in place, fix length */
|
/* unbackslash last string in o in place, fix length */
|
||||||
o->length = unbackslash(pattern) - o->data;
|
o->length = unbackslash(pattern) - o->data;
|
||||||
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
|
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
|
||||||
return o_save_ptr_helper(o, n);
|
return o_save_ptr_helper(o, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef __U_BOOT__
|
||||||
memset(&globdata, 0, sizeof(globdata));
|
memset(&globdata, 0, sizeof(globdata));
|
||||||
/* Can't use GLOB_NOCHECK: it does not unescape the string.
|
/* Can't use GLOB_NOCHECK: it does not unescape the string.
|
||||||
* If we glob "*.\*" and don't find anything, we need
|
* If we glob "*.\*" and don't find anything, we need
|
||||||
|
@ -3792,16 +3796,22 @@ static int perform_glob(o_string *o, int n)
|
||||||
if (DEBUG_GLOB)
|
if (DEBUG_GLOB)
|
||||||
debug_print_list("perform_glob returning", o, n);
|
debug_print_list("perform_glob returning", o, n);
|
||||||
return n;
|
return n;
|
||||||
|
#else /* __U_BOOT__ */
|
||||||
|
/*
|
||||||
|
* NOTE We only use perform glob to call unbackslash to remove backslash
|
||||||
|
* from string once expanded.
|
||||||
|
* So, it seems OK to return this if no previous return was done.
|
||||||
|
*/
|
||||||
|
return o_save_ptr_helper(o, n);
|
||||||
|
#endif /* __U_BOOT__ */
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* !__U_BOOT__ */
|
|
||||||
#endif /* !HUSH_BRACE_EXPANSION */
|
#endif /* !HUSH_BRACE_EXPANSION */
|
||||||
|
|
||||||
/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
|
/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
|
||||||
* Otherwise, just finish current list[] and start new */
|
* Otherwise, just finish current list[] and start new */
|
||||||
static int o_save_ptr(o_string *o, int n)
|
static int o_save_ptr(o_string *o, int n)
|
||||||
{
|
{
|
||||||
#ifndef __U_BOOT__
|
|
||||||
if (o->o_expflags & EXP_FLAG_GLOB) {
|
if (o->o_expflags & EXP_FLAG_GLOB) {
|
||||||
/* If o->has_empty_slot, list[n] was already globbed
|
/* If o->has_empty_slot, list[n] was already globbed
|
||||||
* (if it was requested back then when it was filled)
|
* (if it was requested back then when it was filled)
|
||||||
|
@ -3809,7 +3819,6 @@ static int o_save_ptr(o_string *o, int n)
|
||||||
if (!o->has_empty_slot)
|
if (!o->has_empty_slot)
|
||||||
return perform_glob(o, n); /* o_save_ptr_helper is inside */
|
return perform_glob(o, n); /* o_save_ptr_helper is inside */
|
||||||
}
|
}
|
||||||
#endif /* !__U_BOOT__ */
|
|
||||||
return o_save_ptr_helper(o, n);
|
return o_save_ptr_helper(o, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5455,7 +5464,20 @@ static int parse_dollar(o_string *as_string,
|
||||||
nommu_addchr(as_string, ch);
|
nommu_addchr(as_string, ch);
|
||||||
if (ch == '}')
|
if (ch == '}')
|
||||||
break;
|
break;
|
||||||
|
#ifndef __U_BOOT__
|
||||||
if (!isalnum(ch) && ch != '_') {
|
if (!isalnum(ch) && ch != '_') {
|
||||||
|
#else /* __U_BOOT__ */
|
||||||
|
/*
|
||||||
|
* In several places in U-Boot, we use variable like
|
||||||
|
* foo# (e.g. serial#), particularly in env.
|
||||||
|
* So, we need to authorize # to appear inside
|
||||||
|
* variable name and then expand this variable.
|
||||||
|
* NOTE Having # in variable name is not permitted in
|
||||||
|
* upstream hush but expansion will be done (even though
|
||||||
|
* the result will be empty).
|
||||||
|
*/
|
||||||
|
if (!isalnum(ch) && ch != '_' && ch != '#') {
|
||||||
|
#endif /* __U_BOOT__ */
|
||||||
unsigned end_ch;
|
unsigned end_ch;
|
||||||
#ifndef __U_BOOT__
|
#ifndef __U_BOOT__
|
||||||
unsigned char last_ch;
|
unsigned char last_ch;
|
||||||
|
@ -7030,7 +7052,20 @@ static NOINLINE int expand_one_var(o_string *output, int n,
|
||||||
}
|
}
|
||||||
#endif /* !__U_BOOT__ */
|
#endif /* !__U_BOOT__ */
|
||||||
default:
|
default:
|
||||||
|
#ifndef __U_BOOT__
|
||||||
val = get_local_var_value(var);
|
val = get_local_var_value(var);
|
||||||
|
#else /* __U_BOOT__ */
|
||||||
|
/*
|
||||||
|
* Environment variable set with setenv* have to be
|
||||||
|
* expanded.
|
||||||
|
* So, we first search if the variable exists in
|
||||||
|
* environment, if this is not the case, we default to
|
||||||
|
* local value.
|
||||||
|
*/
|
||||||
|
val = env_get(var);
|
||||||
|
if (!val)
|
||||||
|
val = get_local_var_value(var);
|
||||||
|
#endif /* __U_BOOT__ */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7375,7 +7410,11 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
|
||||||
case '*':
|
case '*':
|
||||||
case '@': {
|
case '@': {
|
||||||
int i;
|
int i;
|
||||||
|
#ifndef __U_BOOT__
|
||||||
if (!G.global_argv[1])
|
if (!G.global_argv[1])
|
||||||
|
#else /* __U_BOOT__ */
|
||||||
|
if (!G.global_argv || !G.global_argv[1])
|
||||||
|
#endif /* __U_BOOT__ */
|
||||||
break;
|
break;
|
||||||
i = 1;
|
i = 1;
|
||||||
cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
|
cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
|
||||||
|
@ -9976,7 +10015,30 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
||||||
#endif /* !__U_BOOT__ */
|
#endif /* !__U_BOOT__ */
|
||||||
command = &pi->cmds[cmd_no];
|
command = &pi->cmds[cmd_no];
|
||||||
cmd_no++;
|
cmd_no++;
|
||||||
if (command->argv) {
|
|
||||||
|
#ifdef __U_BOOT__
|
||||||
|
/* Replace argv and argc by expanded if it exists. */
|
||||||
|
if (argv_expanded) {
|
||||||
|
/*
|
||||||
|
* We need to save a pointer to argv, we will restore it
|
||||||
|
* later, so it will be freed when pipe is freed.
|
||||||
|
*/
|
||||||
|
argv = command->argv;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* After expansion, there can be more or less argument, so we need to
|
||||||
|
* update argc, for example:
|
||||||
|
* - More arguments:
|
||||||
|
* foo='bar quuz'
|
||||||
|
* echo $foo
|
||||||
|
* - Less arguments:
|
||||||
|
* echo $foo (if foo was never set)
|
||||||
|
*/
|
||||||
|
command->argc = list_size(argv_expanded);
|
||||||
|
command->argv = argv_expanded;
|
||||||
|
}
|
||||||
|
#endif /* __U_BOOT__ */
|
||||||
|
if (command->argv) {
|
||||||
debug_printf_exec(": pipe member '%s' '%s'...\n",
|
debug_printf_exec(": pipe member '%s' '%s'...\n",
|
||||||
command->argv[0], command->argv[1]);
|
command->argv[0], command->argv[1]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -10091,6 +10153,25 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
||||||
rcode = cmd_process(G.do_repeat ? CMD_FLAG_REPEAT : 0,
|
rcode = cmd_process(G.do_repeat ? CMD_FLAG_REPEAT : 0,
|
||||||
command->argc, command->argv,
|
command->argc, command->argv,
|
||||||
&(G.flag_repeat), NULL);
|
&(G.flag_repeat), NULL);
|
||||||
|
|
||||||
|
if (argv_expanded) {
|
||||||
|
/*
|
||||||
|
* expand_strvec_to_strvec() allocates memory to expand
|
||||||
|
* argv, we need to free it.
|
||||||
|
*/
|
||||||
|
free(argv_expanded);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We also restore command->argv to its original value
|
||||||
|
* so no memory leak happens.
|
||||||
|
*/
|
||||||
|
command->argv = argv;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE argc exists only in U-Boot, so argv freeing does
|
||||||
|
* not rely on it as this code exists in BusyBox.
|
||||||
|
*/
|
||||||
|
}
|
||||||
#endif /* __U_BOOT__ */
|
#endif /* __U_BOOT__ */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue