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 debug("%s: No match\n", data); 219 } else { 220 debug("## MATCH ## %s\n", data); 221 } 222 break; 223 } 224 225 if (!s) 226 return 1; 227 228 old = caps[0].ptr; 229 olen = caps[0].len; 230 nlen = strlen(s); 231 232 if (nlen + 1 >= nbuf_size) { 233 printf("## error: pattern buffer overflow: have %d, need %d\n", 234 nbuf_size, nlen + 1); 235 return 1; 236 } 237 strcpy(nbuf, s); 238 239 debug("## SUBST(1) ## %s\n", nbuf); 240 241 /* 242 * Handle back references 243 * 244 * Support for \0 ... \9, where \0 is the 245 * whole matched pattern (similar to &). 246 * 247 * Implementation is a bit simpleminded as 248 * backrefs are substituted sequentially, one 249 * by one. This will lead to somewhat 250 * unexpected results if the replacement 251 * strings contain any \N strings then then 252 * may get substitued, too. We accept this 253 * restriction for the sake of simplicity. 254 */ 255 for (i = 0; i < 10; ++i) { 256 char backref[2] = { 257 '\\', 258 '0', 259 }; 260 261 if (caps[i].len == 0) 262 break; 263 264 backref[1] += i; 265 266 debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n", 267 i, 268 2, backref, 269 caps[i].len, caps[i].ptr, 270 nbuf); 271 272 for (np = nbuf;;) { 273 char *p = memstr(np, nlen, backref, 2); 274 275 if (p == NULL) 276 break; 277 278 np = substitute(np, &nlen, 279 nbuf_size - (np - nbuf), 280 backref, 2, 281 caps[i].ptr, caps[i].len); 282 283 if (np == NULL) 284 return 1; 285 } 286 } 287 debug("## SUBST(2) ## %s\n", nbuf); 288 289 datap = substitute(datap, &len, data_size - (datap - data), 290 old, olen, nbuf, nlen); 291 292 if (datap == NULL) 293 return 1; 294 295 debug("## REMAINDER: %s\n", datap); 296 297 debug("## RESULT: %s\n", data); 298 299 if (!global) 300 break; 301 } 302 debug("## FINAL (now env_set()) : %s\n", data); 303 304 return 0; 305} 306 307#define SLRE_BUFSZ 16384 308#define SLRE_PATSZ 4096 309 310/* 311 * Perform regex operations on a environment variable 312 * 313 * Returns 0 if OK, 1 in case of errors. 314 */ 315static int regex_sub_var(const char *name, const char *r, const char *s, 316 const char *t, int global) 317{ 318 struct slre slre; 319 char data[SLRE_BUFSZ]; 320 char nbuf[SLRE_PATSZ]; 321 const char *value; 322 int len; 323 int ret; 324 325 if (!name) 326 return 1; 327 328 if (slre_compile(&slre, r) == 0) { 329 printf("Error compiling regex: %s\n", slre.err_str); 330 return 1; 331 } 332 333 if (!t) { 334 value = env_get(name); 335 if (!value) { 336 printf("## Error: variable \"%s\" not defined\n", name); 337 return 1; 338 } 339 t = value; 340 } 341 342 debug("REGEX on %s=%s\n", name, t); 343 debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>", 344 global); 345 346 len = strlen(t); 347 if (len + 1 > SLRE_BUFSZ) { 348 printf("## error: subst buffer overflow: have %d, need %d\n", 349 SLRE_BUFSZ, len + 1); 350 return 1; 351 } 352 353 strcpy(data, t); 354 355 ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s, 356 global); 357 if (ret) 358 return 1; 359 360 debug("%s=%s\n", name, data); 361 362 return env_set(name, data); 363} 364#endif 365 366static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc, 367 char *const argv[]) 368{ 369 struct expr_arg aval, bval; 370 ulong value; 371 int ret = 0; 372 int w; 373 374 /* 375 * We take 3, 5, or 6 arguments, except fmt operation, which 376 * takes 4 to 8 arguments (limited by _maxargs): 377 * 3 : setexpr name value 378 * 5 : setexpr name val1 op val2 379 * setexpr name [g]sub r s 380 * 6 : setexpr name [g]sub r s t 381 * setexpr name fmt format [val1] [val2] [val3] [val4] 382 */ 383 384 if (argc < 3) 385 return CMD_RET_USAGE; 386 387 w = cmd_get_data_size(argv[0], 4); 388 389 if (get_arg(argv[2], w, &aval)) 390 return CMD_RET_FAILURE; 391 392 /* format string assignment: "setexpr name fmt %d value" */ 393 if (strcmp(argv[2], "fmt") == 0 && IS_ENABLED(CONFIG_CMD_SETEXPR_FMT)) { 394 char str[MAX_STR_LEN]; 395 int result; 396 397 if (argc == 3) 398 return CMD_RET_USAGE; 399 400 result = printf_setexpr(str, sizeof(str), argc - 3, &argv[3]); 401 if (result) 402 return result; 403 404 return env_set(argv[1], str); 405 } 406 407 if (argc == 4 || argc > 6) 408 return CMD_RET_USAGE; 409 410 /* plain assignment: "setexpr name value" */ 411 if (argc == 3) { 412 if (w == CMD_DATA_SIZE_STR) { 413 ret = env_set(argv[1], aval.sval); 414 free(aval.sval); 415 } else { 416 ret = env_set_hex(argv[1], aval.ival); 417 } 418 419 return ret; 420 } 421 422 /* 5 or 6 args (6 args only with [g]sub) */ 423#ifdef CONFIG_REGEX 424 /* 425 * rexep handling: "setexpr name [g]sub r s [t]" 426 * with 5 args, "t" will be NULL 427 */ 428 if (strcmp(argv[2], "gsub") == 0) 429 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1); 430 431 if (strcmp(argv[2], "sub") == 0) 432 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0); 433#endif 434 435 /* standard operators: "setexpr name val1 op val2" */ 436 if (argc != 5) 437 return CMD_RET_USAGE; 438 439 if (strlen(argv[3]) != 1) 440 return CMD_RET_USAGE; 441 442 if (get_arg(argv[4], w, &bval)) { 443 if (w == CMD_DATA_SIZE_STR) 444 free(aval.sval); 445 return CMD_RET_FAILURE; 446 } 447 448 if (w == CMD_DATA_SIZE_STR) { 449 int len; 450 char *str; 451 452 switch (argv[3][0]) { 453 case '+': 454 len = strlen(aval.sval) + strlen(bval.sval) + 1; 455 str = malloc(len); 456 if (!str) { 457 printf("Out of memory\n"); 458 ret = CMD_RET_FAILURE; 459 } else { 460 /* These were copied out and checked earlier */ 461 strcpy(str, aval.sval); 462 strcat(str, bval.sval); 463 ret = env_set(argv[1], str); 464 if (ret) 465 printf("Could not set var\n"); 466 free(str); 467 } 468 break; 469 default: 470 printf("invalid op\n"); 471 ret = 1; 472 } 473 } else { 474 ulong a = aval.ival; 475 ulong b = bval.ival; 476 477 switch (argv[3][0]) { 478 case '|': 479 value = a | b; 480 break; 481 case '&': 482 value = a & b; 483 break; 484 case '+': 485 value = a + b; 486 break; 487 case '^': 488 value = a ^ b; 489 break; 490 case '-': 491 value = a - b; 492 break; 493 case '*': 494 value = a * b; 495 break; 496 case '/': 497 value = a / b; 498 break; 499 case '%': 500 value = a % b; 501 break; 502 default: 503 printf("invalid op\n"); 504 return 1; 505 } 506 507 env_set_hex(argv[1], value); 508 } 509 510 if (w == CMD_DATA_SIZE_STR) { 511 free(aval.sval); 512 free(bval.sval); 513 } 514 515 return ret; 516} 517 518U_BOOT_CMD( 519 setexpr, 8, 0, do_setexpr, 520 "set environment variable as the result of eval expression", 521 "[.b, .w, .l, .s] name [*]value1 <op> [*]value2\n" 522 " - set environment variable 'name' to the result of the evaluated\n" 523 " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n" 524 " (for strings only + is supported)\n" 525 " size argument is only meaningful if value1 and/or value2 are\n" 526 " memory addresses (*)\n" 527 "setexpr[.b, .w, .l] name [*]value\n" 528 " - load a value into a variable" 529#ifdef CONFIG_CMD_SETEXPR_FMT 530 "\n" 531 "setexpr name fmt <format> [value1] [value2] [value3] [value4]\n" 532 " - set environment variable 'name' to the result of the bash like\n" 533 " format string evaluation of value." 534#endif 535#ifdef CONFIG_REGEX 536 "\n" 537 "setexpr name gsub r s [t]\n" 538 " - For each substring matching the regular expression <r> in the\n" 539 " string <t>, substitute the string <s>. The result is\n" 540 " assigned to <name>. If <t> is not supplied, use the old\n" 541 " value of <name>. If no substring matching <r> is found in <t>,\n" 542 " assign <t> to <name>.\n" 543 "setexpr name sub r s [t]\n" 544 " - Just like gsub(), but replace only the first matching substring" 545#endif 546); 547