Enables variables expansion for hush 2021, 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.lan...@amarulasolutions.com>
Reviewed-by: Simon Glass <s...@chromium.org>
---
 common/cli_hush_2021.c     | 17 +++++++
 common/cli_hush_upstream.c | 91 +++++++++++++++++++++++++++++++++++---
 2 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index 653ea52929..7dd30ea0ef 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -207,6 +207,23 @@ static const char* endofname(const char *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;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 84227a248e..b806c5c653 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -3486,7 +3486,6 @@ static int o_get_last_ptr(o_string *o, int n)
        return ((int)(uintptr_t)list[n-1]) + string_start;
 }
 
-#ifndef __U_BOOT__
 /*
  * Globbing routines.
  *
@@ -3741,8 +3740,10 @@ static int glob_needed(const char *s)
  */
 static int perform_glob(o_string *o, int n)
 {
+#ifndef __U_BOOT__
        glob_t globdata;
        int gr;
+#endif /* __U_BOOT__ */
        char *pattern;
 
        debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
@@ -3751,13 +3752,16 @@ static int perform_glob(o_string *o, int n)
        pattern = o->data + o_get_last_ptr(o, n);
        debug_printf_glob("glob pattern '%s'\n", pattern);
        if (!glob_needed(pattern)) {
+#ifndef __U_BOOT__
  literal:
+#endif /* __U_BOOT__ */
                /* unbackslash last string in o in place, fix length */
                o->length = unbackslash(pattern) - o->data;
                debug_printf_glob("glob pattern '%s' is literal\n", pattern);
                return o_save_ptr_helper(o, n);
        }
 
+#ifndef __U_BOOT__
        memset(&globdata, 0, sizeof(globdata));
        /* Can't use GLOB_NOCHECK: it does not unescape the string.
         * If we glob "*.\*" and don't find anything, we need
@@ -3793,16 +3797,22 @@ static int perform_glob(o_string *o, int n)
        if (DEBUG_GLOB)
                debug_print_list("perform_glob returning", o, 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 */
 
 /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
  * Otherwise, just finish current list[] and start new */
 static int o_save_ptr(o_string *o, int n)
 {
-#ifndef __U_BOOT__
        if (o->o_expflags & EXP_FLAG_GLOB) {
                /* If o->has_empty_slot, list[n] was already globbed
                 * (if it was requested back then when it was filled)
@@ -3810,7 +3820,6 @@ static int o_save_ptr(o_string *o, int n)
                if (!o->has_empty_slot)
                        return perform_glob(o, n); /* o_save_ptr_helper is 
inside */
        }
-#endif /* !__U_BOOT__ */
        return o_save_ptr_helper(o, n);
 }
 
@@ -5456,7 +5465,20 @@ static int parse_dollar(o_string *as_string,
                        nommu_addchr(as_string, ch);
                        if (ch == '}')
                                break;
+#ifndef __U_BOOT__
                        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;
 #ifndef __U_BOOT__
                                unsigned char last_ch;
@@ -7031,7 +7053,20 @@ static NOINLINE int expand_one_var(o_string *output, int 
n,
                }
 #endif /* !__U_BOOT__ */
                default:
+#ifndef __U_BOOT__
                        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__ */
                }
        }
 
@@ -7376,7 +7411,11 @@ static NOINLINE int expand_vars_to_list(o_string 
*output, int n, char *arg)
                case '*':
                case '@': {
                        int i;
+#ifndef __U_BOOT__
                        if (!G.global_argv[1])
+#else /* __U_BOOT__ */
+                       if (!G.global_argv || !G.global_argv[1])
+#endif /* __U_BOOT__ */
                                break;
                        i = 1;
                        cant_be_null |= first_ch; /* do it for "$@" _now_, when 
we know it's not empty */
@@ -9977,7 +10016,30 @@ static NOINLINE int run_pipe(struct pipe *pi)
 #endif /* !__U_BOOT__ */
                command = &pi->cmds[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",
                                        command->argv[0], command->argv[1]);
                } else {
@@ -10092,6 +10154,25 @@ static NOINLINE int run_pipe(struct pipe *pi)
                rcode = cmd_process(G.do_repeat ? CMD_FLAG_REPEAT : 0,
                                    command->argc, command->argv,
                                    &(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__ */
        }
 
-- 
2.34.1

Reply via email to