* This file provides a shell like 'expr' function to return.
*/
-#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 <vsprintf.h>
+#include <linux/errno.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);
- return val;
+ 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
* @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
+ * 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,
}
len = strlen(data);
- if (s == NULL)
- nlen = 0;
- else
- nlen = strlen(s);
-
for (loop = 0;; loop++) {
struct cap caps[slre.num_caps + 2];
const char *old;
(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", data);
- return 1;
+ debug("%s: No match\n", data);
} else {
- break;
+ debug("## MATCH ## %s\n", data);
}
+ break;
}
- debug("## MATCH ## %s\n", data);
-
if (!s)
return 1;
old = caps[0].ptr;
olen = caps[0].len;
+ nlen = strlen(s);
if (nlen + 1 >= nbuf_size) {
printf("## error: pattern buffer overflow: have %d, need %d\n",
break;
np = substitute(np, &nlen,
- nbuf_size,
+ nbuf_size - (np - nbuf),
backref, 2,
caps[i].ptr, caps[i].len);
}
debug("## SUBST(2) ## %s\n", nbuf);
- datap = substitute(datap, &len, data_size, old, olen,
- nbuf, nlen);
+ datap = substitute(datap, &len, data_size - (datap - data),
+ old, olen, nbuf, nlen);
if (datap == NULL)
return 1;
if (ret)
return 1;
- printf("%s=%s\n", name, data);
+ debug("%s=%s\n", name, data);
return env_set(name, data);
}
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) */
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"
" - For each substring matching the regular expression <r> in the\n"
" string <t>, substitute the string <s>. The result is\n"
" assigned to <name>. If <t> is not supplied, use the old\n"
- " value of <name>\n"
+ " value of <name>. If no substring matching <r> is found in <t>,\n"
+ " assign <t> to <name>.\n"
"setexpr name sub r s [t]\n"
" - Just like gsub(), but replace only the first matching substring"
#endif