aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/cmd/setexpr.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/u-boot/cmd/setexpr.c')
-rw-r--r--roms/u-boot/cmd/setexpr.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/roms/u-boot/cmd/setexpr.c b/roms/u-boot/cmd/setexpr.c
new file mode 100644
index 000000000..e828be397
--- /dev/null
+++ b/roms/u-boot/cmd/setexpr.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc.
+ * Copyright 2013 Wolfgang Denk <wd@denx.de>
+ */
+
+/*
+ * This file provides a shell like 'expr' function to return.
+ */
+
+#include <common.h>
+#include <config.h>
+#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 {
+ 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.
+ */
+ if (s[0] == '*') {
+ ulong *p;
+ ulong addr;
+ ulong val;
+ int len;
+ char *str;
+
+ addr = simple_strtoul(&s[1], NULL, 16);
+ switch (w) {
+ case 1:
+ p = map_sysmem(addr, sizeof(uchar));
+ val = (ulong)*(uchar *)p;
+ unmap_sysmem(p);
+ arg.ival = val;
+ break;
+ case 2:
+ p = map_sysmem(addr, sizeof(ushort));
+ val = (ulong)*(ushort *)p;
+ 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;
+ unmap_sysmem(p);
+ arg.ival = val;
+ break;
+ default:
+ p = map_sysmem(addr, sizeof(ulong));
+ val = *p;
+ unmap_sysmem(p);
+ arg.ival = val;
+ break;
+ }
+ } else {
+ if (w == CMD_DATA_SIZE_STR)
+ return -EINVAL;
+ arg.ival = simple_strtoul(s, NULL, 16);
+ }
+ *argp = arg;
+
+ return 0;
+}
+
+#ifdef CONFIG_REGEX
+
+#include <slre.h>
+
+/*
+ * memstr - Find the first substring in memory
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ *
+ * Similar to and based on strstr(),
+ * but strings do not need to be NUL terminated.
+ */
+static char *memstr(const char *s1, int l1, const char *s2, int l2)
+{
+ if (!l2)
+ return (char *)s1;
+
+ while (l1 >= l2) {
+ l1--;
+ if (!memcmp(s1, s2, l2))
+ return (char *)s1;
+ s1++;
+ }
+ return NULL;
+}
+
+/**
+ * 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);
+
+ if (p == NULL)
+ return NULL;
+
+ debug("## Match at pos %ld: match len %d, subst len %d\n",
+ (long)(p - string), olen, nlen);
+
+ /* make sure replacement matches */
+ if (*slen + nlen - olen > ssize) {
+ printf("## error: substitution buffer overflow\n");
+ return NULL;
+ }
+
+ /* move tail if needed */
+ if (olen != nlen) {
+ int tail, len;
+
+ len = (olen > nlen) ? olen : nlen;
+
+ tail = ssize - (p + len - string);
+
+ debug("## tail len %d\n", tail);
+
+ memmove(p + nlen, p + olen, tail);
+ }
+
+ /* insert substitute */
+ memcpy(p, new, nlen);
+
+ *slen += nlen - olen;
+
+ return p + nlen;
+}
+
+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 *datap = data;
+ int res, len, nlen, loop;
+
+ if (slre_compile(&slre, r) == 0) {
+ printf("Error compiling regex: %s\n", slre.err_str);
+ return 1;
+ }
+
+ len = strlen(data);
+ for (loop = 0;; loop++) {
+ struct cap caps[slre.num_caps + 2];
+ const char *old;
+ char *np;
+ int i, olen;
+
+ (void) memset(caps, 0, sizeof(caps));
+
+ res = slre_match(&slre, datap, len - (datap - data), caps);
+
+ debug("Result: %d\n", res);
+
+ 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;
+ } else {
+ 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",
+ nbuf_size, nlen + 1);
+ return 1;
+ }
+ strcpy(nbuf, s);
+
+ debug("## SUBST(1) ## %s\n", nbuf);
+
+ /*
+ * Handle back references
+ *
+ * Support for \0 ... \9, where \0 is the
+ * whole matched pattern (similar to &).
+ *
+ * Implementation is a bit simpleminded as
+ * backrefs are substituted sequentially, one
+ * by one. This will lead to somewhat
+ * unexpected results if the replacement
+ * strings contain any \N strings then then
+ * may get substitued, too. We accept this
+ * restriction for the sake of simplicity.
+ */
+ for (i = 0; i < 10; ++i) {
+ char backref[2] = {
+ '\\',
+ '0',
+ };
+
+ if (caps[i].len == 0)
+ break;
+
+ backref[1] += i;
+
+ debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n",
+ i,
+ 2, backref,
+ caps[i].len, caps[i].ptr,
+ nbuf);
+
+ for (np = nbuf;;) {
+ char *p = memstr(np, nlen, backref, 2);
+
+ if (p == NULL)
+ break;
+
+ np = substitute(np, &nlen,
+ nbuf_size - (np - nbuf),
+ backref, 2,
+ caps[i].ptr, caps[i].len);
+
+ if (np == NULL)
+ return 1;
+ }
+ }
+ debug("## SUBST(2) ## %s\n", nbuf);
+
+ datap = substitute(datap, &len, data_size - (datap - data),
+ old, olen, nbuf, nlen);
+
+ if (datap == NULL)
+ return 1;
+
+ debug("## REMAINDER: %s\n", datap);
+
+ debug("## RESULT: %s\n", data);
+
+ if (!global)
+ break;
+ }
+ 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(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct expr_arg aval, bval;
+ ulong value;
+ int ret = 0;
+ int w;
+
+ /*
+ * We take 3, 5, or 6 arguments:
+ * 3 : setexpr name value
+ * 5 : setexpr name val1 op val2
+ * setexpr name [g]sub r s
+ * 6 : setexpr name [g]sub r s t
+ */
+
+ /* > 6 already tested by max command args */
+ if ((argc < 3) || (argc == 4))
+ return CMD_RET_USAGE;
+
+ w = cmd_get_data_size(argv[0], 4);
+
+ if (get_arg(argv[2], w, &aval))
+ return CMD_RET_FAILURE;
+
+ /* plain assignment: "setexpr name value" */
+ 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
+ /*
+ * rexep handling: "setexpr name [g]sub r s [t]"
+ * with 5 args, "t" will be NULL
+ */
+ if (strcmp(argv[2], "gsub") == 0)
+ return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
+
+ if (strcmp(argv[2], "sub") == 0)
+ return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
+#endif
+
+ /* standard operators: "setexpr name val1 op val2" */
+ if (argc != 5)
+ return CMD_RET_USAGE;
+
+ if (strlen(argv[3]) != 1)
+ return CMD_RET_USAGE;
+
+ 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;
+
+ 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;
+
+ 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,
+ "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"
+ " 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_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"
+ "setexpr name sub r s [t]\n"
+ " - Just like gsub(), but replace only the first matching substring"
+#endif
+);