blob: 8a3654505da0737d71af6e59efe6b25aa9323930 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Kumar Galaaed83342008-02-14 20:44:42 -06002/*
3 * Copyright 2008 Freescale Semiconductor, Inc.
Wolfgang Denkb04ae782013-03-23 23:50:34 +00004 * Copyright 2013 Wolfgang Denk <wd@denx.de>
Kumar Galaaed83342008-02-14 20:44:42 -06005 */
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>
Simon Glass313112a2019-08-01 09:46:46 -060014#include <env.h>
Simon Glass0f2af882020-05-10 11:40:05 -060015#include <log.h>
Joe Hershberger9d3f8962015-05-11 13:53:13 -050016#include <mapmem.h>
Kumar Galaaed83342008-02-14 20:44:42 -060017
Simon Glass00547af2020-11-01 14:15:43 -070018/**
19 * struct expr_arg: Holds an argument to an expression
20 *
21 * @ival: Integer value (if width is not CMD_DATA_SIZE_STR)
22 */
23struct expr_arg {
24 ulong ival;
25};
26
27static int get_arg(char *s, int w, struct expr_arg *argp)
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010028{
Simon Glass00547af2020-11-01 14:15:43 -070029 struct expr_arg arg;
30
Wolfgang Denk9a183d22010-06-23 20:50:54 +020031 /*
Joe Hershberger9d3f8962015-05-11 13:53:13 -050032 * If the parameter starts with a '*' then assume it is a pointer to
33 * the value we want.
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010034 */
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010035 if (s[0] == '*') {
Joe Hershberger9d3f8962015-05-11 13:53:13 -050036 ulong *p;
37 ulong addr;
38 ulong val;
39
40 addr = simple_strtoul(&s[1], NULL, 16);
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010041 switch (w) {
Joe Hershberger9d3f8962015-05-11 13:53:13 -050042 case 1:
43 p = map_sysmem(addr, sizeof(uchar));
44 val = (ulong)*(uchar *)p;
45 unmap_sysmem(p);
Simon Glass00547af2020-11-01 14:15:43 -070046 arg.ival = val;
47 break;
Joe Hershberger9d3f8962015-05-11 13:53:13 -050048 case 2:
49 p = map_sysmem(addr, sizeof(ushort));
50 val = (ulong)*(ushort *)p;
51 unmap_sysmem(p);
Simon Glass00547af2020-11-01 14:15:43 -070052 arg.ival = val;
53 break;
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010054 case 4:
Simon Glass7c19edf2020-11-01 14:15:37 -070055 p = map_sysmem(addr, sizeof(u32));
56 val = *(u32 *)p;
57 unmap_sysmem(p);
Simon Glass00547af2020-11-01 14:15:43 -070058 arg.ival = val;
59 break;
Joe Hershberger9d3f8962015-05-11 13:53:13 -050060 default:
61 p = map_sysmem(addr, sizeof(ulong));
62 val = *p;
63 unmap_sysmem(p);
Simon Glass00547af2020-11-01 14:15:43 -070064 arg.ival = val;
65 break;
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010066 }
67 } else {
Simon Glass00547af2020-11-01 14:15:43 -070068 arg.ival = simple_strtoul(s, NULL, 16);
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010069 }
Simon Glass00547af2020-11-01 14:15:43 -070070 *argp = arg;
71
72 return 0;
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010073}
74
Wolfgang Denkb04ae782013-03-23 23:50:34 +000075#ifdef CONFIG_REGEX
76
77#include <slre.h>
78
Wolfgang Denkb04ae782013-03-23 23:50:34 +000079/*
80 * memstr - Find the first substring in memory
81 * @s1: The string to be searched
82 * @s2: The string to search for
83 *
84 * Similar to and based on strstr(),
85 * but strings do not need to be NUL terminated.
86 */
87static char *memstr(const char *s1, int l1, const char *s2, int l2)
88{
89 if (!l2)
90 return (char *)s1;
91
92 while (l1 >= l2) {
93 l1--;
94 if (!memcmp(s1, s2, l2))
95 return (char *)s1;
96 s1++;
97 }
98 return NULL;
99}
100
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700101/**
102 * substitute() - Substitute part of one string with another
103 *
104 * This updates @string so that the first occurrence of @old is replaced with
105 * @new
106 *
107 * @string: String buffer containing string to update at the start
108 * @slen: Pointer to current string length, updated on success
109 * @ssize: Size of string buffer
110 * @old: Old string to find in the buffer (no terminator needed)
111 * @olen: Length of @old excluding terminator
112 * @new: New string to replace @old with
113 * @nlen: Length of @new excluding terminator
114 * @return pointer to immediately after the copied @new in @string, or NULL if
115 * no replacement took place
116 */
117static char *substitute(char *string, int *slen, int ssize,
118 const char *old, int olen, const char *new, int nlen)
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000119{
120 char *p = memstr(string, *slen, old, olen);
121
122 if (p == NULL)
123 return NULL;
124
125 debug("## Match at pos %ld: match len %d, subst len %d\n",
126 (long)(p - string), olen, nlen);
127
128 /* make sure replacement matches */
129 if (*slen + nlen - olen > ssize) {
130 printf("## error: substitution buffer overflow\n");
131 return NULL;
132 }
133
134 /* move tail if needed */
135 if (olen != nlen) {
136 int tail, len;
137
138 len = (olen > nlen) ? olen : nlen;
139
140 tail = ssize - (p + len - string);
141
142 debug("## tail len %d\n", tail);
143
144 memmove(p + nlen, p + olen, tail);
145 }
146
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700147 /* insert substitute */
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000148 memcpy(p, new, nlen);
149
150 *slen += nlen - olen;
151
152 return p + nlen;
153}
154
Simon Glass00e44192020-11-01 14:15:40 -0700155int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
156 const char *r, const char *s, bool global)
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000157{
158 struct slre slre;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000159 char *datap = data;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000160 int res, len, nlen, loop;
161
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000162 if (slre_compile(&slre, r) == 0) {
163 printf("Error compiling regex: %s\n", slre.err_str);
164 return 1;
165 }
166
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700167 len = strlen(data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000168 for (loop = 0;; loop++) {
169 struct cap caps[slre.num_caps + 2];
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000170 const char *old;
171 char *np;
172 int i, olen;
173
174 (void) memset(caps, 0, sizeof(caps));
175
Simon Glass7a114862020-11-01 14:15:42 -0700176 res = slre_match(&slre, datap, len - (datap - data), caps);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000177
178 debug("Result: %d\n", res);
179
Simon Glass7a114862020-11-01 14:15:42 -0700180 for (i = 0; i <= slre.num_caps; i++) {
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000181 if (caps[i].len > 0) {
182 debug("Substring %d: [%.*s]\n", i,
183 caps[i].len, caps[i].ptr);
184 }
185 }
186
187 if (res == 0) {
188 if (loop == 0) {
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700189 printf("%s: No match\n", data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000190 return 1;
191 } else {
192 break;
193 }
194 }
195
196 debug("## MATCH ## %s\n", data);
197
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700198 if (!s)
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000199 return 1;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000200
201 old = caps[0].ptr;
202 olen = caps[0].len;
Simon Glass28ae25a2020-11-01 14:15:41 -0700203 nlen = strlen(s);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000204
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700205 if (nlen + 1 >= nbuf_size) {
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000206 printf("## error: pattern buffer overflow: have %d, need %d\n",
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700207 nbuf_size, nlen + 1);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000208 return 1;
209 }
210 strcpy(nbuf, s);
211
212 debug("## SUBST(1) ## %s\n", nbuf);
213
214 /*
215 * Handle back references
216 *
217 * Support for \0 ... \9, where \0 is the
218 * whole matched pattern (similar to &).
219 *
220 * Implementation is a bit simpleminded as
221 * backrefs are substituted sequentially, one
222 * by one. This will lead to somewhat
223 * unexpected results if the replacement
224 * strings contain any \N strings then then
225 * may get substitued, too. We accept this
226 * restriction for the sake of simplicity.
227 */
228 for (i = 0; i < 10; ++i) {
229 char backref[2] = {
230 '\\',
231 '0',
232 };
233
234 if (caps[i].len == 0)
235 break;
236
237 backref[1] += i;
238
239 debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n",
240 i,
241 2, backref,
242 caps[i].len, caps[i].ptr,
243 nbuf);
244
245 for (np = nbuf;;) {
246 char *p = memstr(np, nlen, backref, 2);
247
248 if (p == NULL)
249 break;
250
251 np = substitute(np, &nlen,
Simon Glass7a114862020-11-01 14:15:42 -0700252 nbuf_size - (np - nbuf),
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000253 backref, 2,
254 caps[i].ptr, caps[i].len);
255
256 if (np == NULL)
257 return 1;
258 }
259 }
260 debug("## SUBST(2) ## %s\n", nbuf);
261
Simon Glass7a114862020-11-01 14:15:42 -0700262 datap = substitute(datap, &len, data_size - (datap - data),
263 old, olen, nbuf, nlen);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000264
265 if (datap == NULL)
266 return 1;
267
268 debug("## REMAINDER: %s\n", datap);
269
270 debug("## RESULT: %s\n", data);
271
272 if (!global)
273 break;
274 }
Simon Glass6a38e412017-08-03 12:22:09 -0600275 debug("## FINAL (now env_set()) : %s\n", data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000276
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700277 return 0;
278}
279
280#define SLRE_BUFSZ 16384
281#define SLRE_PATSZ 4096
282
283/*
284 * Perform regex operations on a environment variable
285 *
286 * Returns 0 if OK, 1 in case of errors.
287 */
288static int regex_sub_var(const char *name, const char *r, const char *s,
289 const char *t, int global)
290{
291 struct slre slre;
292 char data[SLRE_BUFSZ];
293 char nbuf[SLRE_PATSZ];
294 const char *value;
295 int len;
296 int ret;
297
298 if (!name)
299 return 1;
300
301 if (slre_compile(&slre, r) == 0) {
302 printf("Error compiling regex: %s\n", slre.err_str);
303 return 1;
304 }
305
306 if (!t) {
307 value = env_get(name);
308 if (!value) {
309 printf("## Error: variable \"%s\" not defined\n", name);
310 return 1;
311 }
312 t = value;
313 }
314
315 debug("REGEX on %s=%s\n", name, t);
316 debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
317 global);
318
319 len = strlen(t);
320 if (len + 1 > SLRE_BUFSZ) {
321 printf("## error: subst buffer overflow: have %d, need %d\n",
322 SLRE_BUFSZ, len + 1);
323 return 1;
324 }
325
326 strcpy(data, t);
327
Simon Glass00e44192020-11-01 14:15:40 -0700328 ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s,
329 global);
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700330 if (ret)
331 return 1;
332
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000333 printf("%s=%s\n", name, data);
334
Simon Glass6a38e412017-08-03 12:22:09 -0600335 return env_set(name, data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000336}
337#endif
338
Simon Glassed38aef2020-05-10 11:40:03 -0600339static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
340 char *const argv[])
Kumar Galaaed83342008-02-14 20:44:42 -0600341{
Simon Glass00547af2020-11-01 14:15:43 -0700342 struct expr_arg aval, bval;
Simon Glass103789d2013-02-24 17:33:22 +0000343 ulong value;
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +0100344 int w;
Kumar Galaaed83342008-02-14 20:44:42 -0600345
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000346 /*
347 * We take 3, 5, or 6 arguments:
348 * 3 : setexpr name value
349 * 5 : setexpr name val1 op val2
350 * setexpr name [g]sub r s
351 * 6 : setexpr name [g]sub r s t
352 */
353
354 /* > 6 already tested by max command args */
355 if ((argc < 3) || (argc == 4))
Simon Glassa06dfc72011-12-10 08:44:01 +0000356 return CMD_RET_USAGE;
Kumar Galaaed83342008-02-14 20:44:42 -0600357
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +0100358 w = cmd_get_data_size(argv[0], 4);
359
Simon Glass00547af2020-11-01 14:15:43 -0700360 if (get_arg(argv[2], w, &aval))
361 return CMD_RET_FAILURE;
Joe Hershberger0e640e62012-11-01 16:21:14 +0000362
Wolfgang Denkb2234d62013-03-23 23:50:33 +0000363 /* plain assignment: "setexpr name value" */
Simon Glass00547af2020-11-01 14:15:43 -0700364 if (argc == 3)
365 return env_set_hex(argv[1], aval.ival);
Joe Hershberger0e640e62012-11-01 16:21:14 +0000366
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000367 /* 5 or 6 args (6 args only with [g]sub) */
368#ifdef CONFIG_REGEX
369 /*
370 * rexep handling: "setexpr name [g]sub r s [t]"
371 * with 5 args, "t" will be NULL
372 */
373 if (strcmp(argv[2], "gsub") == 0)
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700374 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000375
376 if (strcmp(argv[2], "sub") == 0)
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700377 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000378#endif
379
Wolfgang Denkb2234d62013-03-23 23:50:33 +0000380 /* standard operators: "setexpr name val1 op val2" */
381 if (argc != 5)
382 return CMD_RET_USAGE;
383
384 if (strlen(argv[3]) != 1)
385 return CMD_RET_USAGE;
386
Simon Glass00547af2020-11-01 14:15:43 -0700387 if (get_arg(argv[4], w, &bval))
388 return CMD_RET_FAILURE;
Kumar Galaaed83342008-02-14 20:44:42 -0600389
Simon Glass00547af2020-11-01 14:15:43 -0700390 if (w != CMD_DATA_SIZE_STR) {
391 ulong a = aval.ival;
392 ulong b = bval.ival;
Kumar Galaaed83342008-02-14 20:44:42 -0600393
Simon Glass00547af2020-11-01 14:15:43 -0700394 switch (argv[3][0]) {
395 case '|':
396 value = a | b;
397 break;
398 case '&':
399 value = a & b;
400 break;
401 case '+':
402 value = a + b;
403 break;
404 case '^':
405 value = a ^ b;
406 break;
407 case '-':
408 value = a - b;
409 break;
410 case '*':
411 value = a * b;
412 break;
413 case '/':
414 value = a / b;
415 break;
416 case '%':
417 value = a % b;
418 break;
419 default:
420 printf("invalid op\n");
421 return 1;
422 }
423
424 env_set_hex(argv[1], value);
425 }
Kumar Galaaed83342008-02-14 20:44:42 -0600426
427 return 0;
428}
429
430U_BOOT_CMD(
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000431 setexpr, 6, 0, do_setexpr,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600432 "set environment variable as the result of eval expression",
Joe Hershberger0e640e62012-11-01 16:21:14 +0000433 "[.b, .w, .l] name [*]value1 <op> [*]value2\n"
Kumar Galaaed83342008-02-14 20:44:42 -0600434 " - set environment variable 'name' to the result of the evaluated\n"
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000435 " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n"
Joe Hershberger0e640e62012-11-01 16:21:14 +0000436 " size argument is only meaningful if value1 and/or value2 are\n"
437 " memory addresses (*)\n"
Wolfgang Denkb2234d62013-03-23 23:50:33 +0000438 "setexpr[.b, .w, .l] name [*]value\n"
439 " - load a value into a variable"
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000440#ifdef CONFIG_REGEX
441 "\n"
442 "setexpr name gsub r s [t]\n"
443 " - For each substring matching the regular expression <r> in the\n"
444 " string <t>, substitute the string <s>. The result is\n"
445 " assigned to <name>. If <t> is not supplied, use the old\n"
446 " value of <name>\n"
447 "setexpr name sub r s [t]\n"
448 " - Just like gsub(), but replace only the first matching substring"
449#endif
Kumar Galaaed83342008-02-14 20:44:42 -0600450);