cmd: setexpr: add format string handling

Add format string handling operator to the setexpr command.
It allows to use C or Bash like format string expressions to be
evaluated with the result being stored inside the environment variable
name.

  setexpr <name> fmt <format> [value]...

The following example

  setexpr foo fmt "%d, 0x%x" 0x100 ff

will result in $foo being set to "256, 0xff".

Signed-off-by: Roland Gaudig <roland.gaudig@weidmueller.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/cmd/setexpr.c b/cmd/setexpr.c
index e828be3..1eb67e2 100644
--- a/cmd/setexpr.c
+++ b/cmd/setexpr.c
@@ -11,11 +11,15 @@
 #include <common.h>
 #include <config.h>
 #include <command.h>
+#include <ctype.h>
 #include <env.h>
 #include <log.h>
 #include <malloc.h>
 #include <mapmem.h>
 #include <linux/sizes.h>
+#include "printf.h"
+
+#define MAX_STR_LEN 128
 
 /**
  * struct expr_arg: Holds an argument to an expression
@@ -370,15 +374,16 @@
 	int w;
 
 	/*
-	 * We take 3, 5, or 6 arguments:
+	 * We take 3, 5, or 6 arguments, except fmt operation, which
+	 * takes 4 to 8 arguments (limited by _maxargs):
 	 * 3 : setexpr name value
 	 * 5 : setexpr name val1 op val2
 	 *     setexpr name [g]sub r s
 	 * 6 : setexpr name [g]sub r s t
+	 *     setexpr name fmt format [val1] [val2] [val3] [val4]
 	 */
 
-	/* > 6 already tested by max command args */
-	if ((argc < 3) || (argc == 4))
+	if (argc < 3)
 		return CMD_RET_USAGE;
 
 	w = cmd_get_data_size(argv[0], 4);
@@ -386,6 +391,24 @@
 	if (get_arg(argv[2], w, &aval))
 		return CMD_RET_FAILURE;
 
+	/* format string assignment: "setexpr name fmt %d value" */
+	if (strcmp(argv[2], "fmt") == 0 && IS_ENABLED(CONFIG_CMD_SETEXPR_FMT)) {
+		char str[MAX_STR_LEN];
+		int result;
+
+		if (argc == 3)
+			return CMD_RET_USAGE;
+
+		result = printf_setexpr(str, sizeof(str), argc - 3, &argv[3]);
+		if (result)
+			return result;
+
+		return env_set(argv[1], str);
+	}
+
+	if (argc == 4 || argc > 6)
+		return CMD_RET_USAGE;
+
 	/* plain assignment: "setexpr name value" */
 	if (argc == 3) {
 		if (w == CMD_DATA_SIZE_STR) {
@@ -495,7 +518,7 @@
 }
 
 U_BOOT_CMD(
-	setexpr, 6, 0, do_setexpr,
+	setexpr, 8, 0, do_setexpr,
 	"set environment variable as the result of eval expression",
 	"[.b, .w, .l, .s] name [*]value1 <op> [*]value2\n"
 	"    - set environment variable 'name' to the result of the evaluated\n"
@@ -505,6 +528,12 @@
 	"      memory addresses (*)\n"
 	"setexpr[.b, .w, .l] name [*]value\n"
 	"    - load a value into a variable"
+#ifdef CONFIG_CMD_SETEXPR_FMT
+	"\n"
+	"setexpr name fmt <format> [value1] [value2] [value3] [value4]\n"
+	"    - set environment variable 'name' to the result of the bash like\n"
+	"      format string evaluation of value."
+#endif
 #ifdef CONFIG_REGEX
 	"\n"
 	"setexpr name gsub r s [t]\n"