[PATCH] RFC: nvedit: support doing one (extra) expansion of the value in "env set"

Rasmus Villemoes rasmus.villemoes at prevas.dk
Wed Feb 5 02:08:20 CET 2020


Currently, there's no way to fetch the value of an environment
variable whose name is stored in some other variable, or generated from
such - in non-working pseudo-code,

  ${${varname}}
  ${array${index}}

This forces some scripts to needlessly duplicate logic and hardcode
assumptions. For example, in an A/B scheme with three variables

BOOT_ORDER # Either "A B" or "B A" depending on which slot was last updated
BOOT_A_LEFT # 0..3
BOOT_B_LEFT # 0..3

when one needs to determine the slot to boot from, one does something
like

setenv found
for slot in $BOOT_ORDER ; do
  if test "x$found" != "x" ; then
    # work around lack of break
  elif test "x$slot" = "xA" ; then
    if test $BOOT_A_LEFT -gt 0 ; then
      setexpr BOOT_A_LEFT $BOOT_A_LEFT - 1
      setenv found A
      setenv bootargs ${bootargs_A}
      setenv ubivol ${ubivol_A}
      # more setup based on A
    fi
  elif test "x$slot" = "xB" ; then
    if test $BOOT_B_LEFT -gt 0 ; then
      # the same ...
    fi
  fi
done

This is already bad enough, but extending that to A/B/C is tedious and
prone to copy-pastos.

So this is an attempt at allowing one to do "env set -E var value1 value2"
with the effect that, of course, normal variable expansion happens on
the command line, the valueX are joined with spaces as usual, and then
one more pass is done over that string replacing occurrences of
${foo}.

The above would become

setenv found
for slot in $BOOT_ORDER ; do
  if test "x$found" != "x" ; then
    # work around lack of break
  else
    env set -E boot_left "\${BOOT_${slot}_LEFT}"
    if test $boot_left -gt 0 ; then
      setexpr BOOT_${slot}_LEFT $boot_left - 1
      env set found $slot
      env set -E bootargs "\${bootargs_${slot}}"
      env set -E ubivol "\${ubivol_${slot}}"
    fi
  fi
done

I'm pleasantly surprised it was that easy to implement, but of course
I'm cheating a bit (cli_simple_process_macros is only available if
CONFIG_CMDLINE, though I think cli_simple.o could be unconditionally
built and then link-time GC should get rid of the excess
functions).

This has been lightly tested in the sandbox. I'll add some proper unit
tests, update the help texts and try to handle the Kconfig issue if
this is something that might be accepted.

Signed-off-by: Rasmus Villemoes <rasmus.villemoes at prevas.dk>
---
 cmd/nvedit.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/cmd/nvedit.c b/cmd/nvedit.c
index 81d94cd193..ff6ffcb674 100644
--- a/cmd/nvedit.c
+++ b/cmd/nvedit.c
@@ -224,7 +224,7 @@ DONE:
  */
 static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
 {
-	int   i, len;
+	int   i, len, expand = 0;
 	char  *name, *value, *s;
 	struct env_entry e, *ep;
 
@@ -244,6 +244,9 @@ static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
 			case 'f':		/* force */
 				env_flag |= H_FORCE;
 				break;
+			case 'E':
+				expand = 1;
+				break;
 			default:
 				return CMD_RET_USAGE;
 			}
@@ -287,6 +290,18 @@ static int _do_env_set(int flag, int argc, char * const argv[], int env_flag)
 	if (s != value)
 		*--s = '\0';
 
+	if (expand) {
+		char *expanded = malloc(CONFIG_SYS_CBSIZE);
+
+		if (expanded == NULL) {
+			printf("## Can't malloc %d bytes\n", CONFIG_SYS_CBSIZE);
+			free(value);
+			return 1;
+		}
+		cli_simple_process_macros(value, expanded);
+		free(value);
+		value = expanded;
+	}
 	e.key	= name;
 	e.data	= value;
 	hsearch_r(e, ENV_ENTER, &ep, &env_htab, env_flag);
-- 
2.23.0



More information about the U-Boot mailing list