arm: a37xx: pci: Fix a3700_fdt_fix_pcie_regions() function
[pandora-u-boot.git] / cmd / setexpr.c
index 0182f19..e828be3 100644 (file)
 #include <config.h>
 #include <command.h>
 #include <env.h>
+#include <log.h>
+#include <malloc.h>
 #include <mapmem.h>
+#include <linux/sizes.h>
 
-static ulong get_arg(char *s, int w)
+/**
+ * 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.
@@ -24,6 +42,8 @@ static ulong get_arg(char *s, int w)
                ulong *p;
                ulong addr;
                ulong val;
+               int len;
+               char *str;
 
                addr = simple_strtoul(&s[1], NULL, 16);
                switch (w) {
@@ -31,31 +51,56 @@ static ulong get_arg(char *s, int w)
                        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 = simple_strtoul(s, NULL, 16);
        }
+       *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
@@ -78,13 +123,24 @@ static char *memstr(const char *s1, int l1, const char *s2, int l2)
        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);
 
@@ -113,7 +169,7 @@ static char *substitute(char *string,       /* string buffer */
                memmove(p + nlen, p + olen, tail);
        }
 
-       /* insert substitue */
+       /* insert substitute */
        memcpy(p, new, nlen);
 
        *slen += nlen - olen;
@@ -121,71 +177,32 @@ static char *substitute(char *string,     /* string buffer */
        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);
@@ -194,7 +211,7 @@ static int regex_sub(const char *name,
 
                if (res == 0) {
                        if (loop == 0) {
-                               printf("%s: No match\n", t);
+                               printf("%s: No match\n", data);
                                return 1;
                        } else {
                                break;
@@ -203,17 +220,16 @@ static int regex_sub(const char *name,
 
                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);
@@ -258,7 +274,7 @@ static int regex_sub(const char *name,
                                        break;
 
                                np = substitute(np, &nlen,
-                                       SLRE_PATSZ,
+                                       nbuf_size - (np - nbuf),
                                        backref, 2,
                                        caps[i].ptr, caps[i].len);
 
@@ -268,9 +284,8 @@ static int regex_sub(const char *name,
                }
                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;
@@ -284,16 +299,74 @@ static int regex_sub(const char *name,
        }
        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;
 
        /*
@@ -310,12 +383,19 @@ static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 
        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;
 
        /* 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) */
@@ -325,10 +405,10 @@ static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
         * 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" */
@@ -338,49 +418,89 @@ static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
        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,
        "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"