#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"
-static ulong get_arg(char *s, int w)
+#define MAX_STR_LEN 128
+
+/**
+ * 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 {
+ union {
+ ulong ival;
+ char *sval;
+ };
+};
+
+static int get_arg(char *s, int w, struct expr_arg *argp)
{
+ struct expr_arg arg;
+
/*
* If the parameter starts with a '*' then assume it is a pointer to
* the value we want.
ulong *p;
ulong addr;
ulong val;
+ int len;
+ char *str;
- addr = simple_strtoul(&s[1], NULL, 16);
+ addr = hextoul(&s[1], NULL);
switch (w) {
case 1:
p = map_sysmem(addr, sizeof(uchar));
val = (ulong)*(uchar *)p;
unmap_sysmem(p);
- return val;
+ arg.ival = val;
+ break;
case 2:
p = map_sysmem(addr, sizeof(ushort));
val = (ulong)*(ushort *)p;
unmap_sysmem(p);
- return val;
+ 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;
+ unmap_sysmem(p);
+ arg.ival = val;
+ break;
default:
p = map_sysmem(addr, sizeof(ulong));
val = *p;
unmap_sysmem(p);
- return val;
+ arg.ival = val;
+ break;
}
} else {
- return simple_strtoul(s, NULL, 16);
+ if (w == CMD_DATA_SIZE_STR)
+ return -EINVAL;
+ arg.ival = hextoul(s, NULL);
}
+ *argp = arg;
+
+ return 0;
}
#ifdef CONFIG_REGEX
#include <slre.h>
-#define SLRE_BUFSZ 16384
-#define SLRE_PATSZ 4096
-
/*
* memstr - Find the first substring in memory
* @s1: The string to be searched
return NULL;
}
-static char *substitute(char *string, /* string buffer */
- int *slen, /* current string length */
- int ssize, /* string bufer size */
- const char *old,/* old (replaced) string */
- int olen, /* length of old string */
- const char *new,/* new (replacement) string */
- int nlen) /* length of new string */
+/**
+ * substitute() - Substitute part of one string with another
+ *
+ * This updates @string so that the first occurrence of @old is replaced with
+ * @new
+ *
+ * @string: String buffer containing string to update at the start
+ * @slen: Pointer to current string length, updated on success
+ * @ssize: Size of string buffer
+ * @old: Old string to find in the buffer (no terminator needed)
+ * @olen: Length of @old excluding terminator
+ * @new: New string to replace @old with
+ * @nlen: Length of @new excluding terminator
+ * @return pointer to immediately after the copied @new in @string, or NULL if
+ * no replacement took place
+ */
+static char *substitute(char *string, int *slen, int ssize,
+ const char *old, int olen, const char *new, int nlen)
{
char *p = memstr(string, *slen, old, olen);
memmove(p + nlen, p + olen, tail);
}
- /* insert substitue */
+ /* insert substitute */
memcpy(p, new, nlen);
*slen += nlen - olen;
return p + nlen;
}
-/*
- * Perform regex operations on a environment variable
- *
- * Returns 0 if OK, 1 in case of errors.
- */
-static int regex_sub(const char *name,
- const char *r, const char *s, const char *t,
- int global)
+int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
+ const char *r, const char *s, bool global)
{
struct slre slre;
- char data[SLRE_BUFSZ];
char *datap = data;
- const char *value;
int res, len, nlen, loop;
- if (name == NULL)
- return 1;
-
if (slre_compile(&slre, r) == 0) {
printf("Error compiling regex: %s\n", slre.err_str);
return 1;
}
- if (t == NULL) {
- value = env_get(name);
-
- if (value == NULL) {
- printf("## Error: variable \"%s\" not defined\n", name);
- return 1;
- }
- t = value;
- }
-
- debug("REGEX on %s=%s\n", name, t);
- debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n",
- r, s ? s : "<NULL>", global);
-
- len = strlen(t);
- if (len + 1 > SLRE_BUFSZ) {
- printf("## error: subst buffer overflow: have %d, need %d\n",
- SLRE_BUFSZ, len + 1);
- return 1;
- }
-
- strcpy(data, t);
-
- if (s == NULL)
- nlen = 0;
- else
- nlen = strlen(s);
-
+ len = strlen(data);
for (loop = 0;; loop++) {
struct cap caps[slre.num_caps + 2];
- char nbuf[SLRE_PATSZ];
const char *old;
char *np;
int i, olen;
(void) memset(caps, 0, sizeof(caps));
- res = slre_match(&slre, datap, len, caps);
+ res = slre_match(&slre, datap, len - (datap - data), caps);
debug("Result: %d\n", res);
- for (i = 0; i < slre.num_caps; i++) {
+ for (i = 0; i <= slre.num_caps; i++) {
if (caps[i].len > 0) {
debug("Substring %d: [%.*s]\n", i,
caps[i].len, caps[i].ptr);
if (res == 0) {
if (loop == 0) {
- printf("%s: No match\n", t);
+ printf("%s: No match\n", data);
return 1;
} else {
break;
debug("## MATCH ## %s\n", data);
- if (s == NULL) {
- printf("%s=%s\n", name, t);
+ if (!s)
return 1;
- }
old = caps[0].ptr;
olen = caps[0].len;
+ nlen = strlen(s);
- if (nlen + 1 >= SLRE_PATSZ) {
+ if (nlen + 1 >= nbuf_size) {
printf("## error: pattern buffer overflow: have %d, need %d\n",
- SLRE_BUFSZ, nlen + 1);
+ nbuf_size, nlen + 1);
return 1;
}
strcpy(nbuf, s);
break;
np = substitute(np, &nlen,
- SLRE_PATSZ,
+ nbuf_size - (np - nbuf),
backref, 2,
caps[i].ptr, caps[i].len);
}
debug("## SUBST(2) ## %s\n", nbuf);
- datap = substitute(datap, &len, SLRE_BUFSZ,
- old, olen,
- nbuf, nlen);
+ datap = substitute(datap, &len, data_size - (datap - data),
+ old, olen, nbuf, nlen);
if (datap == NULL)
return 1;
}
debug("## FINAL (now env_set()) : %s\n", data);
+ return 0;
+}
+
+#define SLRE_BUFSZ 16384
+#define SLRE_PATSZ 4096
+
+/*
+ * Perform regex operations on a environment variable
+ *
+ * Returns 0 if OK, 1 in case of errors.
+ */
+static int regex_sub_var(const char *name, const char *r, const char *s,
+ const char *t, int global)
+{
+ struct slre slre;
+ char data[SLRE_BUFSZ];
+ char nbuf[SLRE_PATSZ];
+ const char *value;
+ int len;
+ int ret;
+
+ if (!name)
+ return 1;
+
+ if (slre_compile(&slre, r) == 0) {
+ printf("Error compiling regex: %s\n", slre.err_str);
+ return 1;
+ }
+
+ if (!t) {
+ value = env_get(name);
+ if (!value) {
+ printf("## Error: variable \"%s\" not defined\n", name);
+ return 1;
+ }
+ t = value;
+ }
+
+ debug("REGEX on %s=%s\n", name, t);
+ debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
+ global);
+
+ len = strlen(t);
+ if (len + 1 > SLRE_BUFSZ) {
+ printf("## error: subst buffer overflow: have %d, need %d\n",
+ SLRE_BUFSZ, len + 1);
+ return 1;
+ }
+
+ strcpy(data, t);
+
+ ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s,
+ global);
+ if (ret)
+ return 1;
+
printf("%s=%s\n", name, data);
return env_set(name, data);
}
#endif
-static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
{
- ulong a, b;
+ struct expr_arg aval, bval;
ulong value;
+ int ret = 0;
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);
- a = get_arg(argv[2], w);
+ 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) {
- env_set_hex(argv[1], a);
- return 0;
+ 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) */
* with 5 args, "t" will be NULL
*/
if (strcmp(argv[2], "gsub") == 0)
- return regex_sub(argv[1], argv[3], argv[4], argv[5], 1);
+ return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
if (strcmp(argv[2], "sub") == 0)
- return regex_sub(argv[1], argv[3], argv[4], argv[5], 0);
+ return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
#endif
/* standard operators: "setexpr name val1 op val2" */
if (strlen(argv[3]) != 1)
return CMD_RET_USAGE;
- b = get_arg(argv[4], w);
-
- switch (argv[3][0]) {
- case '|':
- value = a | b;
- break;
- case '&':
- value = a & b;
- break;
- case '+':
- value = a + b;
- break;
- case '^':
- value = a ^ b;
- break;
- case '-':
- value = a - b;
- break;
- case '*':
- value = a * b;
- break;
- case '/':
- value = a / b;
- break;
- case '%':
- value = a % b;
- break;
- default:
- printf("invalid op\n");
- return 1;
+ if (get_arg(argv[4], w, &bval)) {
+ if (w == CMD_DATA_SIZE_STR)
+ free(aval.sval);
+ return CMD_RET_FAILURE;
}
- env_set_hex(argv[1], value);
+ if (w == CMD_DATA_SIZE_STR) {
+ int len;
+ char *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;
- return 0;
+ switch (argv[3][0]) {
+ case '|':
+ value = a | b;
+ break;
+ case '&':
+ value = a & b;
+ break;
+ case '+':
+ value = a + b;
+ break;
+ case '^':
+ value = a ^ b;
+ break;
+ case '-':
+ value = a - b;
+ break;
+ case '*':
+ value = a * b;
+ break;
+ case '/':
+ value = a / b;
+ break;
+ case '%':
+ value = a % b;
+ break;
+ default:
+ printf("invalid op\n");
+ return 1;
+ }
+
+ env_set_hex(argv[1], value);
+ }
+
+ if (w == CMD_DATA_SIZE_STR) {
+ free(aval.sval);
+ free(bval.sval);
+ }
+
+ return ret;
}
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] 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"
" - 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"