uboot/cmd/setexpr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2008 Freescale Semiconductor, Inc.
   4 * Copyright 2013 Wolfgang Denk <wd@denx.de>
   5 */
   6
   7/*
   8 * This file provides a shell like 'expr' function to return.
   9 */
  10
  11#include <common.h>
  12#include <config.h>
  13#include <command.h>
  14#include <ctype.h>
  15#include <env.h>
  16#include <log.h>
  17#include <malloc.h>
  18#include <mapmem.h>
  19#include <linux/sizes.h>
  20#include "printf.h"
  21
  22#define MAX_STR_LEN 128
  23
  24/**
  25 * struct expr_arg: Holds an argument to an expression
  26 *
  27 * @ival: Integer value (if width is not CMD_DATA_SIZE_STR)
  28 * @sval: String value (if width is CMD_DATA_SIZE_STR)
  29 */
  30struct expr_arg {
  31        union {
  32                ulong ival;
  33                char *sval;
  34        };
  35};
  36
  37static int get_arg(char *s, int w, struct expr_arg *argp)
  38{
  39        struct expr_arg arg;
  40
  41        /*
  42         * If the parameter starts with a '*' then assume it is a pointer to
  43         * the value we want.
  44         */
  45        if (s[0] == '*') {
  46                ulong *p;
  47                ulong addr;
  48                ulong val;
  49                int len;
  50                char *str;
  51
  52                addr = hextoul(&s[1], NULL);
  53                switch (w) {
  54                case 1:
  55                        p = map_sysmem(addr, sizeof(uchar));
  56                        val = (ulong)*(uchar *)p;
  57                        unmap_sysmem(p);
  58                        arg.ival = val;
  59                        break;
  60                case 2:
  61                        p = map_sysmem(addr, sizeof(ushort));
  62                        val = (ulong)*(ushort *)p;
  63                        unmap_sysmem(p);
  64                        arg.ival = val;
  65                        break;
  66                case CMD_DATA_SIZE_STR:
  67                        p = map_sysmem(addr, SZ_64K);
  68
  69                        /* Maximum string length of 64KB plus terminator */
  70                        len = strnlen((char *)p, SZ_64K) + 1;
  71                        str = malloc(len);
  72                        if (!str) {
  73                                printf("Out of memory\n");
  74                                return -ENOMEM;
  75                        }
  76                        memcpy(str, p, len);
  77                        str[len - 1] = '\0';
  78                        unmap_sysmem(p);
  79                        arg.sval = str;
  80                        break;
  81                case 4:
  82                        p = map_sysmem(addr, sizeof(u32));
  83                        val = *(u32 *)p;
  84                        unmap_sysmem(p);
  85                        arg.ival = val;
  86                        break;
  87                default:
  88                        p = map_sysmem(addr, sizeof(ulong));
  89                        val = *p;
  90                        unmap_sysmem(p);
  91                        arg.ival = val;
  92                        break;
  93                }
  94        } else {
  95                if (w == CMD_DATA_SIZE_STR)
  96                        return -EINVAL;
  97                arg.ival = hextoul(s, NULL);
  98        }
  99        *argp = arg;
 100
 101        return 0;
 102}
 103
 104#ifdef CONFIG_REGEX
 105
 106#include <slre.h>
 107
 108/*
 109 * memstr - Find the first substring in memory
 110 * @s1: The string to be searched
 111 * @s2: The string to search for
 112 *
 113 * Similar to and based on strstr(),
 114 * but strings do not need to be NUL terminated.
 115 */
 116static char *memstr(const char *s1, int l1, const char *s2, int l2)
 117{
 118        if (!l2)
 119                return (char *)s1;
 120
 121        while (l1 >= l2) {
 122                l1--;
 123                if (!memcmp(s1, s2, l2))
 124                        return (char *)s1;
 125                s1++;
 126        }
 127        return NULL;
 128}
 129
 130/**
 131 * substitute() - Substitute part of one string with another
 132 *
 133 * This updates @string so that the first occurrence of @old is replaced with
 134 * @new
 135 *
 136 * @string: String buffer containing string to update at the start
 137 * @slen: Pointer to current string length, updated on success
 138 * @ssize: Size of string buffer
 139 * @old: Old string to find in the buffer (no terminator needed)
 140 * @olen: Length of @old excluding terminator
 141 * @new: New string to replace @old with
 142 * @nlen: Length of @new excluding terminator
 143 * @return pointer to immediately after the copied @new in @string, or NULL if
 144 *      no replacement took place
 145 */
 146static char *substitute(char *string, int *slen, int ssize,
 147                        const char *old, int olen, const char *new, int nlen)
 148{
 149        char *p = memstr(string, *slen, old, olen);
 150
 151        if (p == NULL)
 152                return NULL;
 153
 154        debug("## Match at pos %ld: match len %d, subst len %d\n",
 155                (long)(p - string), olen, nlen);
 156
 157        /* make sure replacement matches */
 158        if (*slen + nlen - olen > ssize) {
 159                printf("## error: substitution buffer overflow\n");
 160                return NULL;
 161        }
 162
 163        /* move tail if needed */
 164        if (olen != nlen) {
 165                int tail, len;
 166
 167                len = (olen > nlen) ? olen : nlen;
 168
 169                tail = ssize - (p + len - string);
 170
 171                debug("## tail len %d\n", tail);
 172
 173                memmove(p + nlen, p + olen, tail);
 174        }
 175
 176        /* insert substitute */
 177        memcpy(p, new, nlen);
 178
 179        *slen += nlen - olen;
 180
 181        return p + nlen;
 182}
 183
 184int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
 185                      const char *r, const char *s, bool global)
 186{
 187        struct slre slre;
 188        char *datap = data;
 189        int res, len, nlen, loop;
 190
 191        if (slre_compile(&slre, r) == 0) {
 192                printf("Error compiling regex: %s\n", slre.err_str);
 193                return 1;
 194        }
 195
 196        len = strlen(data);
 197        for (loop = 0;; loop++) {
 198                struct cap caps[slre.num_caps + 2];
 199                const char *old;
 200                char *np;
 201                int i, olen;
 202
 203                (void) memset(caps, 0, sizeof(caps));
 204
 205                res = slre_match(&slre, datap, len - (datap - data), caps);
 206
 207                debug("Result: %d\n", res);
 208
 209                for (i = 0; i <= slre.num_caps; i++) {
 210                        if (caps[i].len > 0) {
 211                                debug("Substring %d: [%.*s]\n", i,
 212                                        caps[i].len, caps[i].ptr);
 213                        }
 214                }
 215
 216                if (res == 0) {
 217                        if (loop == 0) {
 218                                printf("%s: No match\n", data);
 219                                return 1;
 220                        } else {
 221                                break;
 222                        }
 223                }
 224
 225                debug("## MATCH ## %s\n", data);
 226
 227                if (!s)
 228                        return 1;
 229
 230                old = caps[0].ptr;
 231                olen = caps[0].len;
 232                nlen = strlen(s);
 233
 234                if (nlen + 1 >= nbuf_size) {
 235                        printf("## error: pattern buffer overflow: have %d, need %d\n",
 236                               nbuf_size, nlen + 1);
 237                        return 1;
 238                }
 239                strcpy(nbuf, s);
 240
 241                debug("## SUBST(1) ## %s\n", nbuf);
 242
 243                /*
 244                 * Handle back references
 245                 *
 246                 * Support for \0 ... \9, where \0 is the
 247                 * whole matched pattern (similar to &).
 248                 *
 249                 * Implementation is a bit simpleminded as
 250                 * backrefs are substituted sequentially, one
 251                 * by one.  This will lead to somewhat
 252                 * unexpected results if the replacement
 253                 * strings contain any \N strings then then
 254                 * may get substitued, too.  We accept this
 255                 * restriction for the sake of simplicity.
 256                 */
 257                for (i = 0; i < 10; ++i) {
 258                        char backref[2] = {
 259                                '\\',
 260                                '0',
 261                        };
 262
 263                        if (caps[i].len == 0)
 264                                break;
 265
 266                        backref[1] += i;
 267
 268                        debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n",
 269                                i,
 270                                2, backref,
 271                                caps[i].len, caps[i].ptr,
 272                                nbuf);
 273
 274                        for (np = nbuf;;) {
 275                                char *p = memstr(np, nlen, backref, 2);
 276
 277                                if (p == NULL)
 278                                        break;
 279
 280                                np = substitute(np, &nlen,
 281                                        nbuf_size - (np - nbuf),
 282                                        backref, 2,
 283                                        caps[i].ptr, caps[i].len);
 284
 285                                if (np == NULL)
 286                                        return 1;
 287                        }
 288                }
 289                debug("## SUBST(2) ## %s\n", nbuf);
 290
 291                datap = substitute(datap, &len, data_size - (datap - data),
 292                                   old, olen, nbuf, nlen);
 293
 294                if (datap == NULL)
 295                        return 1;
 296
 297                debug("## REMAINDER: %s\n", datap);
 298
 299                debug("## RESULT: %s\n", data);
 300
 301                if (!global)
 302                        break;
 303        }
 304        debug("## FINAL (now env_set()) :  %s\n", data);
 305
 306        return 0;
 307}
 308
 309#define SLRE_BUFSZ      16384
 310#define SLRE_PATSZ      4096
 311
 312/*
 313 * Perform regex operations on a environment variable
 314 *
 315 * Returns 0 if OK, 1 in case of errors.
 316 */
 317static int regex_sub_var(const char *name, const char *r, const char *s,
 318                         const char *t, int global)
 319{
 320        struct slre slre;
 321        char data[SLRE_BUFSZ];
 322        char nbuf[SLRE_PATSZ];
 323        const char *value;
 324        int len;
 325        int ret;
 326
 327        if (!name)
 328                return 1;
 329
 330        if (slre_compile(&slre, r) == 0) {
 331                printf("Error compiling regex: %s\n", slre.err_str);
 332                return 1;
 333        }
 334
 335        if (!t) {
 336                value = env_get(name);
 337                if (!value) {
 338                        printf("## Error: variable \"%s\" not defined\n", name);
 339                        return 1;
 340                }
 341                t = value;
 342        }
 343
 344        debug("REGEX on %s=%s\n", name, t);
 345        debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
 346              global);
 347
 348        len = strlen(t);
 349        if (len + 1 > SLRE_BUFSZ) {
 350                printf("## error: subst buffer overflow: have %d, need %d\n",
 351                       SLRE_BUFSZ, len + 1);
 352                return 1;
 353        }
 354
 355        strcpy(data, t);
 356
 357        ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s,
 358                                global);
 359        if (ret)
 360                return 1;
 361
 362        printf("%s=%s\n", name, data);
 363
 364        return env_set(name, data);
 365}
 366#endif
 367
 368static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
 369                      char *const argv[])
 370{
 371        struct expr_arg aval, bval;
 372        ulong value;
 373        int ret = 0;
 374        int w;
 375
 376        /*
 377         * We take 3, 5, or 6 arguments, except fmt operation, which
 378         * takes 4 to 8 arguments (limited by _maxargs):
 379         * 3 : setexpr name value
 380         * 5 : setexpr name val1 op val2
 381         *     setexpr name [g]sub r s
 382         * 6 : setexpr name [g]sub r s t
 383         *     setexpr name fmt format [val1] [val2] [val3] [val4]
 384         */
 385
 386        if (argc < 3)
 387                return CMD_RET_USAGE;
 388
 389        w = cmd_get_data_size(argv[0], 4);
 390
 391        if (get_arg(argv[2], w, &aval))
 392                return CMD_RET_FAILURE;
 393
 394        /* format string assignment: "setexpr name fmt %d value" */
 395        if (strcmp(argv[2], "fmt") == 0 && IS_ENABLED(CONFIG_CMD_SETEXPR_FMT)) {
 396                char str[MAX_STR_LEN];
 397                int result;
 398
 399                if (argc == 3)
 400                        return CMD_RET_USAGE;
 401
 402                result = printf_setexpr(str, sizeof(str), argc - 3, &argv[3]);
 403                if (result)
 404                        return result;
 405
 406                return env_set(argv[1], str);
 407        }
 408
 409        if (argc == 4 || argc > 6)
 410                return CMD_RET_USAGE;
 411
 412        /* plain assignment: "setexpr name value" */
 413        if (argc == 3) {
 414                if (w == CMD_DATA_SIZE_STR) {
 415                        ret = env_set(argv[1], aval.sval);
 416                        free(aval.sval);
 417                } else {
 418                        ret = env_set_hex(argv[1], aval.ival);
 419                }
 420
 421                return ret;
 422        }
 423
 424        /* 5 or 6 args (6 args only with [g]sub) */
 425#ifdef CONFIG_REGEX
 426        /*
 427         * rexep handling: "setexpr name [g]sub r s [t]"
 428         * with 5 args, "t" will be NULL
 429         */
 430        if (strcmp(argv[2], "gsub") == 0)
 431                return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
 432
 433        if (strcmp(argv[2], "sub") == 0)
 434                return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
 435#endif
 436
 437        /* standard operators: "setexpr name val1 op val2" */
 438        if (argc != 5)
 439                return CMD_RET_USAGE;
 440
 441        if (strlen(argv[3]) != 1)
 442                return CMD_RET_USAGE;
 443
 444        if (get_arg(argv[4], w, &bval)) {
 445                if (w == CMD_DATA_SIZE_STR)
 446                        free(aval.sval);
 447                return CMD_RET_FAILURE;
 448        }
 449
 450        if (w == CMD_DATA_SIZE_STR) {
 451                int len;
 452                char *str;
 453
 454                switch (argv[3][0]) {
 455                case '+':
 456                        len = strlen(aval.sval) + strlen(bval.sval) + 1;
 457                        str = malloc(len);
 458                        if (!str) {
 459                                printf("Out of memory\n");
 460                                ret = CMD_RET_FAILURE;
 461                        } else {
 462                                /* These were copied out and checked earlier */
 463                                strcpy(str, aval.sval);
 464                                strcat(str, bval.sval);
 465                                ret = env_set(argv[1], str);
 466                                if (ret)
 467                                        printf("Could not set var\n");
 468                                free(str);
 469                        }
 470                        break;
 471                default:
 472                        printf("invalid op\n");
 473                        ret = 1;
 474                }
 475        } else {
 476                ulong a = aval.ival;
 477                ulong b = bval.ival;
 478
 479                switch (argv[3][0]) {
 480                case '|':
 481                        value = a | b;
 482                        break;
 483                case '&':
 484                        value = a & b;
 485                        break;
 486                case '+':
 487                        value = a + b;
 488                        break;
 489                case '^':
 490                        value = a ^ b;
 491                        break;
 492                case '-':
 493                        value = a - b;
 494                        break;
 495                case '*':
 496                        value = a * b;
 497                        break;
 498                case '/':
 499                        value = a / b;
 500                        break;
 501                case '%':
 502                        value = a % b;
 503                        break;
 504                default:
 505                        printf("invalid op\n");
 506                        return 1;
 507                }
 508
 509                env_set_hex(argv[1], value);
 510        }
 511
 512        if (w == CMD_DATA_SIZE_STR) {
 513                free(aval.sval);
 514                free(bval.sval);
 515        }
 516
 517        return ret;
 518}
 519
 520U_BOOT_CMD(
 521        setexpr, 8, 0, do_setexpr,
 522        "set environment variable as the result of eval expression",
 523        "[.b, .w, .l, .s] name [*]value1 <op> [*]value2\n"
 524        "    - set environment variable 'name' to the result of the evaluated\n"
 525        "      expression specified by <op>.  <op> can be &, |, ^, +, -, *, /, %\n"
 526        "      (for strings only + is supported)\n"
 527        "      size argument is only meaningful if value1 and/or value2 are\n"
 528        "      memory addresses (*)\n"
 529        "setexpr[.b, .w, .l] name [*]value\n"
 530        "    - load a value into a variable"
 531#ifdef CONFIG_CMD_SETEXPR_FMT
 532        "\n"
 533        "setexpr name fmt <format> [value1] [value2] [value3] [value4]\n"
 534        "    - set environment variable 'name' to the result of the bash like\n"
 535        "      format string evaluation of value."
 536#endif
 537#ifdef CONFIG_REGEX
 538        "\n"
 539        "setexpr name gsub r s [t]\n"
 540        "    - For each substring matching the regular expression <r> in the\n"
 541        "      string <t>, substitute the string <s>.  The result is\n"
 542        "      assigned to <name>.  If <t> is not supplied, use the old\n"
 543        "      value of <name>\n"
 544        "setexpr name sub r s [t]\n"
 545        "    - Just like gsub(), but replace only the first matching substring"
 546#endif
 547);
 548