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 <mapmem.h> 15 16 static ulong get_arg(char *s, int w) 17 { 18 /* 19 * If the parameter starts with a '*' then assume it is a pointer to 20 * the value we want. 21 */ 22 if (s[0] == '*') { 23 ulong *p; 24 ulong addr; 25 ulong val; 26 27 addr = simple_strtoul(&s[1], NULL, 16); 28 switch (w) { 29 case 1: 30 p = map_sysmem(addr, sizeof(uchar)); 31 val = (ulong)*(uchar *)p; 32 unmap_sysmem(p); 33 return val; 34 case 2: 35 p = map_sysmem(addr, sizeof(ushort)); 36 val = (ulong)*(ushort *)p; 37 unmap_sysmem(p); 38 return val; 39 case 4: 40 default: 41 p = map_sysmem(addr, sizeof(ulong)); 42 val = *p; 43 unmap_sysmem(p); 44 return val; 45 } 46 } else { 47 return simple_strtoul(s, NULL, 16); 48 } 49 } 50 51 #ifdef CONFIG_REGEX 52 53 #include <slre.h> 54 55 #define SLRE_BUFSZ 16384 56 #define SLRE_PATSZ 4096 57 58 /* 59 * memstr - Find the first substring in memory 60 * @s1: The string to be searched 61 * @s2: The string to search for 62 * 63 * Similar to and based on strstr(), 64 * but strings do not need to be NUL terminated. 65 */ 66 static char *memstr(const char *s1, int l1, const char *s2, int l2) 67 { 68 if (!l2) 69 return (char *)s1; 70 71 while (l1 >= l2) { 72 l1--; 73 if (!memcmp(s1, s2, l2)) 74 return (char *)s1; 75 s1++; 76 } 77 return NULL; 78 } 79 80 static char *substitute(char *string, /* string buffer */ 81 int *slen, /* current string length */ 82 int ssize, /* string bufer size */ 83 const char *old,/* old (replaced) string */ 84 int olen, /* length of old string */ 85 const char *new,/* new (replacement) string */ 86 int nlen) /* length of new string */ 87 { 88 char *p = memstr(string, *slen, old, olen); 89 90 if (p == NULL) 91 return NULL; 92 93 debug("## Match at pos %ld: match len %d, subst len %d\n", 94 (long)(p - string), olen, nlen); 95 96 /* make sure replacement matches */ 97 if (*slen + nlen - olen > ssize) { 98 printf("## error: substitution buffer overflow\n"); 99 return NULL; 100 } 101 102 /* move tail if needed */ 103 if (olen != nlen) { 104 int tail, len; 105 106 len = (olen > nlen) ? olen : nlen; 107 108 tail = ssize - (p + len - string); 109 110 debug("## tail len %d\n", tail); 111 112 memmove(p + nlen, p + olen, tail); 113 } 114 115 /* insert substitue */ 116 memcpy(p, new, nlen); 117 118 *slen += nlen - olen; 119 120 return p + nlen; 121 } 122 123 /* 124 * Perform regex operations on a environment variable 125 * 126 * Returns 0 if OK, 1 in case of errors. 127 */ 128 static int regex_sub(const char *name, 129 const char *r, const char *s, const char *t, 130 int global) 131 { 132 struct slre slre; 133 char data[SLRE_BUFSZ]; 134 char *datap = data; 135 const char *value; 136 int res, len, nlen, loop; 137 138 if (name == NULL) 139 return 1; 140 141 if (slre_compile(&slre, r) == 0) { 142 printf("Error compiling regex: %s\n", slre.err_str); 143 return 1; 144 } 145 146 if (t == NULL) { 147 value = env_get(name); 148 149 if (value == NULL) { 150 printf("## Error: variable \"%s\" not defined\n", name); 151 return 1; 152 } 153 t = value; 154 } 155 156 debug("REGEX on %s=%s\n", name, t); 157 debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", 158 r, s ? s : "<NULL>", global); 159 160 len = strlen(t); 161 if (len + 1 > SLRE_BUFSZ) { 162 printf("## error: subst buffer overflow: have %d, need %d\n", 163 SLRE_BUFSZ, len + 1); 164 return 1; 165 } 166 167 strcpy(data, t); 168 169 if (s == NULL) 170 nlen = 0; 171 else 172 nlen = strlen(s); 173 174 for (loop = 0;; loop++) { 175 struct cap caps[slre.num_caps + 2]; 176 char nbuf[SLRE_PATSZ]; 177 const char *old; 178 char *np; 179 int i, olen; 180 181 (void) memset(caps, 0, sizeof(caps)); 182 183 res = slre_match(&slre, datap, len, caps); 184 185 debug("Result: %d\n", res); 186 187 for (i = 0; i < slre.num_caps; i++) { 188 if (caps[i].len > 0) { 189 debug("Substring %d: [%.*s]\n", i, 190 caps[i].len, caps[i].ptr); 191 } 192 } 193 194 if (res == 0) { 195 if (loop == 0) { 196 printf("%s: No match\n", t); 197 return 1; 198 } else { 199 break; 200 } 201 } 202 203 debug("## MATCH ## %s\n", data); 204 205 if (s == NULL) { 206 printf("%s=%s\n", name, t); 207 return 1; 208 } 209 210 old = caps[0].ptr; 211 olen = caps[0].len; 212 213 if (nlen + 1 >= SLRE_PATSZ) { 214 printf("## error: pattern buffer overflow: have %d, need %d\n", 215 SLRE_BUFSZ, nlen + 1); 216 return 1; 217 } 218 strcpy(nbuf, s); 219 220 debug("## SUBST(1) ## %s\n", nbuf); 221 222 /* 223 * Handle back references 224 * 225 * Support for \0 ... \9, where \0 is the 226 * whole matched pattern (similar to &). 227 * 228 * Implementation is a bit simpleminded as 229 * backrefs are substituted sequentially, one 230 * by one. This will lead to somewhat 231 * unexpected results if the replacement 232 * strings contain any \N strings then then 233 * may get substitued, too. We accept this 234 * restriction for the sake of simplicity. 235 */ 236 for (i = 0; i < 10; ++i) { 237 char backref[2] = { 238 '\\', 239 '0', 240 }; 241 242 if (caps[i].len == 0) 243 break; 244 245 backref[1] += i; 246 247 debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n", 248 i, 249 2, backref, 250 caps[i].len, caps[i].ptr, 251 nbuf); 252 253 for (np = nbuf;;) { 254 char *p = memstr(np, nlen, backref, 2); 255 256 if (p == NULL) 257 break; 258 259 np = substitute(np, &nlen, 260 SLRE_PATSZ, 261 backref, 2, 262 caps[i].ptr, caps[i].len); 263 264 if (np == NULL) 265 return 1; 266 } 267 } 268 debug("## SUBST(2) ## %s\n", nbuf); 269 270 datap = substitute(datap, &len, SLRE_BUFSZ, 271 old, olen, 272 nbuf, nlen); 273 274 if (datap == NULL) 275 return 1; 276 277 debug("## REMAINDER: %s\n", datap); 278 279 debug("## RESULT: %s\n", data); 280 281 if (!global) 282 break; 283 } 284 debug("## FINAL (now env_set()) : %s\n", data); 285 286 printf("%s=%s\n", name, data); 287 288 return env_set(name, data); 289 } 290 #endif 291 292 static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 293 { 294 ulong a, b; 295 ulong value; 296 int w; 297 298 /* 299 * We take 3, 5, or 6 arguments: 300 * 3 : setexpr name value 301 * 5 : setexpr name val1 op val2 302 * setexpr name [g]sub r s 303 * 6 : setexpr name [g]sub r s t 304 */ 305 306 /* > 6 already tested by max command args */ 307 if ((argc < 3) || (argc == 4)) 308 return CMD_RET_USAGE; 309 310 w = cmd_get_data_size(argv[0], 4); 311 312 a = get_arg(argv[2], w); 313 314 /* plain assignment: "setexpr name value" */ 315 if (argc == 3) { 316 env_set_hex(argv[1], a); 317 return 0; 318 } 319 320 /* 5 or 6 args (6 args only with [g]sub) */ 321 #ifdef CONFIG_REGEX 322 /* 323 * rexep handling: "setexpr name [g]sub r s [t]" 324 * with 5 args, "t" will be NULL 325 */ 326 if (strcmp(argv[2], "gsub") == 0) 327 return regex_sub(argv[1], argv[3], argv[4], argv[5], 1); 328 329 if (strcmp(argv[2], "sub") == 0) 330 return regex_sub(argv[1], argv[3], argv[4], argv[5], 0); 331 #endif 332 333 /* standard operators: "setexpr name val1 op val2" */ 334 if (argc != 5) 335 return CMD_RET_USAGE; 336 337 if (strlen(argv[3]) != 1) 338 return CMD_RET_USAGE; 339 340 b = get_arg(argv[4], w); 341 342 switch (argv[3][0]) { 343 case '|': 344 value = a | b; 345 break; 346 case '&': 347 value = a & b; 348 break; 349 case '+': 350 value = a + b; 351 break; 352 case '^': 353 value = a ^ b; 354 break; 355 case '-': 356 value = a - b; 357 break; 358 case '*': 359 value = a * b; 360 break; 361 case '/': 362 value = a / b; 363 break; 364 case '%': 365 value = a % b; 366 break; 367 default: 368 printf("invalid op\n"); 369 return 1; 370 } 371 372 env_set_hex(argv[1], value); 373 374 return 0; 375 } 376 377 U_BOOT_CMD( 378 setexpr, 6, 0, do_setexpr, 379 "set environment variable as the result of eval expression", 380 "[.b, .w, .l] name [*]value1 <op> [*]value2\n" 381 " - set environment variable 'name' to the result of the evaluated\n" 382 " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n" 383 " size argument is only meaningful if value1 and/or value2 are\n" 384 " memory addresses (*)\n" 385 "setexpr[.b, .w, .l] name [*]value\n" 386 " - load a value into a variable" 387 #ifdef CONFIG_REGEX 388 "\n" 389 "setexpr name gsub r s [t]\n" 390 " - For each substring matching the regular expression <r> in the\n" 391 " string <t>, substitute the string <s>. The result is\n" 392 " assigned to <name>. If <t> is not supplied, use the old\n" 393 " value of <name>\n" 394 "setexpr name sub r s [t]\n" 395 " - Just like gsub(), but replace only the first matching substring" 396 #endif 397 ); 398