diff mbox series

[10/10] setexpr: Add support for strings

Message ID 20201101211544.3579850-11-sjg@chromium.org
State Accepted
Commit 2c02152a8e2d640fe1d93177fbf523d25cd3e8f9
Delegated to: Tom Rini
Headers show
Series setexpr: Correct various bugs and add tests plus string support | expand

Commit Message

Simon Glass Nov. 1, 2020, 9:15 p.m. UTC
Add support for dealing with string operands, including reading a string
from memory into an environment variable and concatenating two strings.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 cmd/setexpr.c      | 82 +++++++++++++++++++++++++++++++++++++++----
 test/cmd/setexpr.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 161 insertions(+), 7 deletions(-)

Comments

Marek Behún Nov. 1, 2020, 11:08 p.m. UTC | #1
What is the purpose of + operator on strings?
Can't we use setenv "${a}${b}" ?
Simon Glass Nov. 3, 2020, 3:12 p.m. UTC | #2
Hi Marek,

On Sun, 1 Nov 2020 at 16:08, Marek Behun <marek.behun@nic.cz> wrote:
>
> What is the purpose of + operator on strings?
> Can't we use setenv "${a}${b}" ?

Yes, that does the same thing, although it is a bit clumsy.

setenv a *10
setenv b *100
setenv c "${a}${b}"

instead of

setexpr c *10 + *100

Regards,
Simon
Marek Behún Nov. 3, 2020, 4:30 p.m. UTC | #3
On Tue, 3 Nov 2020 08:12:17 -0700
Simon Glass <sjg@chromium.org> wrote:

> Hi Marek,
> 
> On Sun, 1 Nov 2020 at 16:08, Marek Behun <marek.behun@nic.cz> wrote:
> >
> > What is the purpose of + operator on strings?
> > Can't we use setenv "${a}${b}" ?  
> 
> Yes, that does the same thing, although it is a bit clumsy.
> 
> setenv a *10
> setenv b *100
> setenv c "${a}${b}"
> 
> instead of
> 
> setexpr c *10 + *100

Hi Simon,

I don't know. It provides the same functionality that exists, but only
adds code.
Is someone really going to use this?

Marek

PS: What I think would be more useful is to add substringing
functionality into hush, so e.g. ${a:3:5}, and pattern substitions:
${parameter/pattern/string} ...
Wolfgang Denk Nov. 5, 2020, 4:47 p.m. UTC | #4
Dear Simon,

In message <CAPnjgZ3SXdKaKmYCw=Q0w-JGhghAPVhwHdtG5q2dNEMNiY60Xg@mail.gmail.com> you wrote:
>
> > What is the purpose of + operator on strings?
> > Can't we use setenv "${a}${b}" ?
>
> Yes, that does the same thing, although it is a bit clumsy.
>
> setenv a *10
> setenv b *100
> setenv c "${a}${b}"
>
> instead of
>
> setexpr c *10 + *100

I don't get it.  The equivalent to "${a}${b}" would be

	setexpr c "*10*100"

which is even simpler?

Best regards,

Wolfgang Denk
Wolfgang Denk Nov. 5, 2020, 4:49 p.m. UTC | #5
Dear Marek,

In message <20201103173011.08e22c11@nic.cz> you wrote:
>
> PS: What I think would be more useful is to add substringing
> functionality into hush, so e.g. ${a:3:5}, and pattern substitions:
> ${parameter/pattern/string} ...

No, NAK, don't.

At least not to the current imple,entation of hush.  First, upgrade
to the recent version from BusyBox, and then we might discuss
extensions.  Actually we should eventually discuss these in BusyBox
to have such stuff in "hush mainline" ...

Best regards,

Wolfgang Denk
Simon Glass Nov. 5, 2020, 5:27 p.m. UTC | #6
Hi Marek,

On Tue, 3 Nov 2020 at 09:30, Marek Behun <marek.behun@nic.cz> wrote:
>
> On Tue, 3 Nov 2020 08:12:17 -0700
> Simon Glass <sjg@chromium.org> wrote:
>
> > Hi Marek,
> >
> > On Sun, 1 Nov 2020 at 16:08, Marek Behun <marek.behun@nic.cz> wrote:
> > >
> > > What is the purpose of + operator on strings?
> > > Can't we use setenv "${a}${b}" ?
> >
> > Yes, that does the same thing, although it is a bit clumsy.
> >
> > setenv a *10
> > setenv b *100
> > setenv c "${a}${b}"
> >
> > instead of
> >
> > setexpr c *10 + *100
>
> Hi Simon,
>
> I don't know. It provides the same functionality that exists, but only
> adds code.
> Is someone really going to use this?

I don't know. Perhaps we can wait and see if anyone cares?

>
> Marek
>
> PS: What I think would be more useful is to add substringing
> functionality into hush, so e.g. ${a:3:5}, and pattern substitions:
> ${parameter/pattern/string} ...

Yes we need to upgrade hush.

Regards,
Simon
Simon Glass Nov. 5, 2020, 5:27 p.m. UTC | #7
Hi Wolfgang,

On Thu, 5 Nov 2020 at 09:47, Wolfgang Denk <wd@denx.de> wrote:
>
> Dear Simon,
>
> In message <CAPnjgZ3SXdKaKmYCw=Q0w-JGhghAPVhwHdtG5q2dNEMNiY60Xg@mail.gmail.com> you wrote:
> >
> > > What is the purpose of + operator on strings?
> > > Can't we use setenv "${a}${b}" ?
> >
> > Yes, that does the same thing, although it is a bit clumsy.
> >
> > setenv a *10
> > setenv b *100
> > setenv c "${a}${b}"
> >
> > instead of
> >
> > setexpr c *10 + *100
>
> I don't get it.  The equivalent to "${a}${b}" would be
>
>         setexpr c "*10*100"
>
> which is even simpler?

I don't see how that works. The *10 thing in my example reads a string
out of address 10.

Regards,
Simon
Marek Behún Nov. 5, 2020, 5:50 p.m. UTC | #8
On Thu, 5 Nov 2020 10:27:28 -0700
Simon Glass <sjg@chromium.org> wrote:

> I don't see how that works. The *10 thing in my example reads a string
> out of address 10.

/o\ ahaaaa, OK, that makes sense. So setexpr can dereference strings.
I didn't know about that, I thouth the resulting string would be
"*10*100".

In this case
Acked-by: Marek Behún <marek.behun@nic.cz>
Wolfgang Denk Nov. 5, 2020, 7:10 p.m. UTC | #9
Dear Simon.

In message <CAPnjgZ3DmeaincEcYkjeTuRijqtMZhJiDnyx_o4eSRE4gJaVFw@mail.gmail.com> you wrote:
>
> > > setexpr c *10 + *100
> >
> > I don't get it.  The equivalent to "${a}${b}" would be
> >
> >         setexpr c "*10*100"
> >
> > which is even simpler?
>
> I don't see how that works. The *10 thing in my example reads a string
> out of address 10.

Ah, got it.  This requires your "[PATCH 10/10] setexpr: Add support
for strings" first...

But then... should there not be some '.s' size specification
somewhere?

Best regards,

Wolfgang Denk
Simon Glass Nov. 5, 2020, 8:15 p.m. UTC | #10
Hi Wolfgang,

On Thu, 5 Nov 2020 at 12:10, Wolfgang Denk <wd@denx.de> wrote:
>
> Dear Simon.
>
> In message <CAPnjgZ3DmeaincEcYkjeTuRijqtMZhJiDnyx_o4eSRE4gJaVFw@mail.gmail.com> you wrote:
> >
> > > > setexpr c *10 + *100
> > >
> > > I don't get it.  The equivalent to "${a}${b}" would be
> > >
> > >         setexpr c "*10*100"
> > >
> > > which is even simpler?
> >
> > I don't see how that works. The *10 thing in my example reads a string
> > out of address 10.
>
> Ah, got it.  This requires your "[PATCH 10/10] setexpr: Add support
> for strings" first...
>
> But then... should there not be some '.s' size specification
> somewhere?

Ooops yes:

setexpr.s c *10 + *100

Regards,
Simon
Tom Rini Nov. 6, 2020, 8:58 p.m. UTC | #11
On Thu, Nov 05, 2020 at 10:27:25AM -0700, Simon Glass wrote:
> Hi Marek,
> 
> On Tue, 3 Nov 2020 at 09:30, Marek Behun <marek.behun@nic.cz> wrote:
> >
> > On Tue, 3 Nov 2020 08:12:17 -0700
> > Simon Glass <sjg@chromium.org> wrote:
> >
> > > Hi Marek,
> > >
> > > On Sun, 1 Nov 2020 at 16:08, Marek Behun <marek.behun@nic.cz> wrote:
> > > >
> > > > What is the purpose of + operator on strings?
> > > > Can't we use setenv "${a}${b}" ?
> > >
> > > Yes, that does the same thing, although it is a bit clumsy.
> > >
> > > setenv a *10
> > > setenv b *100
> > > setenv c "${a}${b}"
> > >
> > > instead of
> > >
> > > setexpr c *10 + *100
> >
> > Hi Simon,
> >
> > I don't know. It provides the same functionality that exists, but only
> > adds code.
> > Is someone really going to use this?
> 
> I don't know. Perhaps we can wait and see if anyone cares?

Sorry, does that mean this support is just speculative?  Or did I
misread this part of the thread?  Thanks.
Simon Glass Nov. 6, 2020, 9:48 p.m. UTC | #12
Hi Tom,

On Fri, 6 Nov 2020 at 13:58, Tom Rini <trini@konsulko.com> wrote:
>
> On Thu, Nov 05, 2020 at 10:27:25AM -0700, Simon Glass wrote:
> > Hi Marek,
> >
> > On Tue, 3 Nov 2020 at 09:30, Marek Behun <marek.behun@nic.cz> wrote:
> > >
> > > On Tue, 3 Nov 2020 08:12:17 -0700
> > > Simon Glass <sjg@chromium.org> wrote:
> > >
> > > > Hi Marek,
> > > >
> > > > On Sun, 1 Nov 2020 at 16:08, Marek Behun <marek.behun@nic.cz> wrote:
> > > > >
> > > > > What is the purpose of + operator on strings?
> > > > > Can't we use setenv "${a}${b}" ?
> > > >
> > > > Yes, that does the same thing, although it is a bit clumsy.
> > > >
> > > > setenv a *10
> > > > setenv b *100
> > > > setenv c "${a}${b}"
> > > >
> > > > instead of
> > > >
> > > > setexpr c *10 + *100
> > >
> > > Hi Simon,
> > >
> > > I don't know. It provides the same functionality that exists, but only
> > > adds code.
> > > Is someone really going to use this?
> >
> > I don't know. Perhaps we can wait and see if anyone cares?
>
> Sorry, does that mean this support is just speculative?  Or did I
> misread this part of the thread?  Thanks.

I think it was a misunderstanding of what it does. Marek acked the
patch and Wolfgang seems happy.

Regards,
SImon
Tom Rini Dec. 2, 2020, 9:23 p.m. UTC | #13
On Sun, Nov 01, 2020 at 02:15:44PM -0700, Simon Glass wrote:

> Add support for dealing with string operands, including reading a string
> from memory into an environment variable and concatenating two strings.
> 
> Signed-off-by: Simon Glass <sjg@chromium.org>
> Acked-by: Marek Behún <marek.behun@nic.cz>

Applied to u-boot/next, thanks!
diff mbox series

Patch

diff --git a/cmd/setexpr.c b/cmd/setexpr.c
index 8a3654505da..e828be39700 100644
--- a/cmd/setexpr.c
+++ b/cmd/setexpr.c
@@ -13,15 +13,21 @@ 
 #include <command.h>
 #include <env.h>
 #include <log.h>
+#include <malloc.h>
 #include <mapmem.h>
+#include <linux/sizes.h>
 
 /**
  * struct expr_arg: Holds an argument to an expression
  *
  * @ival: Integer value (if width is not CMD_DATA_SIZE_STR)
+ * @sval: String value (if width is CMD_DATA_SIZE_STR)
  */
 struct expr_arg {
-	ulong ival;
+	union {
+		ulong ival;
+		char *sval;
+	};
 };
 
 static int get_arg(char *s, int w, struct expr_arg *argp)
@@ -36,6 +42,8 @@  static int get_arg(char *s, int w, struct expr_arg *argp)
 		ulong *p;
 		ulong addr;
 		ulong val;
+		int len;
+		char *str;
 
 		addr = simple_strtoul(&s[1], NULL, 16);
 		switch (w) {
@@ -51,6 +59,21 @@  static int get_arg(char *s, int w, struct expr_arg *argp)
 			unmap_sysmem(p);
 			arg.ival = val;
 			break;
+		case CMD_DATA_SIZE_STR:
+			p = map_sysmem(addr, SZ_64K);
+
+			/* Maximum string length of 64KB plus terminator */
+			len = strnlen((char *)p, SZ_64K) + 1;
+			str = malloc(len);
+			if (!str) {
+				printf("Out of memory\n");
+				return -ENOMEM;
+			}
+			memcpy(str, p, len);
+			str[len - 1] = '\0';
+			unmap_sysmem(p);
+			arg.sval = str;
+			break;
 		case 4:
 			p = map_sysmem(addr, sizeof(u32));
 			val = *(u32 *)p;
@@ -65,6 +88,8 @@  static int get_arg(char *s, int w, struct expr_arg *argp)
 			break;
 		}
 	} else {
+		if (w == CMD_DATA_SIZE_STR)
+			return -EINVAL;
 		arg.ival = simple_strtoul(s, NULL, 16);
 	}
 	*argp = arg;
@@ -341,6 +366,7 @@  static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
 {
 	struct expr_arg aval, bval;
 	ulong value;
+	int ret = 0;
 	int w;
 
 	/*
@@ -361,8 +387,16 @@  static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
 		return CMD_RET_FAILURE;
 
 	/* plain assignment: "setexpr name value" */
-	if (argc == 3)
-		return env_set_hex(argv[1], aval.ival);
+	if (argc == 3) {
+		if (w == CMD_DATA_SIZE_STR) {
+			ret = env_set(argv[1], aval.sval);
+			free(aval.sval);
+		} else {
+			ret = env_set_hex(argv[1], aval.ival);
+		}
+
+		return ret;
+	}
 
 	/* 5 or 6 args (6 args only with [g]sub) */
 #ifdef CONFIG_REGEX
@@ -384,10 +418,38 @@  static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
 	if (strlen(argv[3]) != 1)
 		return CMD_RET_USAGE;
 
-	if (get_arg(argv[4], w, &bval))
+	if (get_arg(argv[4], w, &bval)) {
+		if (w == CMD_DATA_SIZE_STR)
+			free(aval.sval);
 		return CMD_RET_FAILURE;
+	}
+
+	if (w == CMD_DATA_SIZE_STR) {
+		int len;
+		char *str;
 
-	if (w != CMD_DATA_SIZE_STR) {
+		switch (argv[3][0]) {
+		case '+':
+			len = strlen(aval.sval) + strlen(bval.sval) + 1;
+			str = malloc(len);
+			if (!str) {
+				printf("Out of memory\n");
+				ret = CMD_RET_FAILURE;
+			} else {
+				/* These were copied out and checked earlier */
+				strcpy(str, aval.sval);
+				strcat(str, bval.sval);
+				ret = env_set(argv[1], str);
+				if (ret)
+					printf("Could not set var\n");
+				free(str);
+			}
+			break;
+		default:
+			printf("invalid op\n");
+			ret = 1;
+		}
+	} else {
 		ulong a = aval.ival;
 		ulong b = bval.ival;
 
@@ -424,15 +486,21 @@  static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
 		env_set_hex(argv[1], value);
 	}
 
-	return 0;
+	if (w == CMD_DATA_SIZE_STR) {
+		free(aval.sval);
+		free(bval.sval);
+	}
+
+	return ret;
 }
 
 U_BOOT_CMD(
 	setexpr, 6, 0, do_setexpr,
 	"set environment variable as the result of eval expression",
-	"[.b, .w, .l] name [*]value1 <op> [*]value2\n"
+	"[.b, .w, .l, .s] name [*]value1 <op> [*]value2\n"
 	"    - set environment variable 'name' to the result of the evaluated\n"
 	"      expression specified by <op>.  <op> can be &, |, ^, +, -, *, /, %\n"
+	"      (for strings only + is supported)\n"
 	"      size argument is only meaningful if value1 and/or value2 are\n"
 	"      memory addresses (*)\n"
 	"setexpr[.b, .w, .l] name [*]value\n"
diff --git a/test/cmd/setexpr.c b/test/cmd/setexpr.c
index 2a897efd9bd..fd6d869c0ed 100644
--- a/test/cmd/setexpr.c
+++ b/test/cmd/setexpr.c
@@ -287,6 +287,92 @@  static int setexpr_test_backref(struct unit_test_state *uts)
 }
 SETEXPR_TEST(setexpr_test_backref, UT_TESTF_CONSOLE_REC);
 
+/* Test 'setexpr' command with setting strings */
+static int setexpr_test_str(struct unit_test_state *uts)
+{
+	ulong start_mem;
+	char *buf;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\xff', BUF_SIZE);
+
+	/*
+	 * Set 'fred' to the same length as we expect to get below, to avoid a
+	 * new allocation in 'setexpr'. That way we can check for memory leaks.
+	 */
+	ut_assertok(env_set("fred", "x"));
+	start_mem = ut_check_free();
+	strcpy(buf, "hello");
+	ut_asserteq(1, run_command("setexpr.s fred 0", 0));
+	ut_assertok(ut_check_delta(start_mem));
+
+	start_mem = ut_check_free();
+	ut_assertok(env_set("fred", "12345"));
+	ut_assertok(run_command("setexpr.s fred *0", 0));
+	ut_asserteq_str("hello", env_get("fred"));
+	ut_assertok(ut_check_delta(start_mem));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_str, UT_TESTF_CONSOLE_REC);
+
+
+/* Test 'setexpr' command with concatenating strings */
+static int setexpr_test_str_oper(struct unit_test_state *uts)
+{
+	ulong start_mem;
+	char *buf;
+
+	buf = map_sysmem(0, BUF_SIZE);
+	memset(buf, '\xff', BUF_SIZE);
+	strcpy(buf, "hello");
+	strcpy(buf + 0x10, " there");
+
+	ut_assertok(console_record_reset_enable());
+	start_mem = ut_check_free();
+	ut_asserteq(1, run_command("setexpr.s fred *0 * *10", 0));
+	ut_assertok(ut_check_delta(start_mem));
+	ut_assert_nextline("invalid op");
+	ut_assert_console_end();
+
+	/*
+	 * Set 'fred' to the same length as we expect to get below, to avoid a
+	 * new allocation in 'setexpr'. That way we can check for memory leaks.
+	 */
+	ut_assertok(env_set("fred", "12345012345"));
+	start_mem = ut_check_free();
+	ut_assertok(run_command("setexpr.s fred *0 + *10", 0));
+	ut_asserteq_str("hello there", env_get("fred"));
+	ut_assertok(ut_check_delta(start_mem));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_str_oper, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with a string that is too long */
+static int setexpr_test_str_long(struct unit_test_state *uts)
+{
+	const int size = 128 << 10;  /* setexpr strings are a max of 64KB */
+	char *buf, *val;
+
+	buf = map_sysmem(0, size);
+	memset(buf, 'a', size);
+
+	/* String should be truncated to 64KB */
+	ut_assertok(run_command("setexpr.s fred *0", 0));
+	val = env_get("fred");
+	ut_asserteq(64 << 10, strlen(val));
+
+	unmap_sysmem(buf);
+
+	return 0;
+}
+SETEXPR_TEST(setexpr_test_str_long, UT_TESTF_CONSOLE_REC);
+
 int do_ut_setexpr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
 	struct unit_test *tests = ll_entry_start(struct unit_test,