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