blob: fe3435b4d99a3061562709c59ba8e0b9d0d23a65 [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
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010018static ulong get_arg(char *s, int w)
19{
Wolfgang Denk9a183d22010-06-23 20:50:54 +020020 /*
Joe Hershberger9d3f8962015-05-11 13:53:13 -050021 * If the parameter starts with a '*' then assume it is a pointer to
22 * the value we want.
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010023 */
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010024 if (s[0] == '*') {
Joe Hershberger9d3f8962015-05-11 13:53:13 -050025 ulong *p;
26 ulong addr;
27 ulong val;
28
29 addr = simple_strtoul(&s[1], NULL, 16);
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010030 switch (w) {
Joe Hershberger9d3f8962015-05-11 13:53:13 -050031 case 1:
32 p = map_sysmem(addr, sizeof(uchar));
33 val = (ulong)*(uchar *)p;
34 unmap_sysmem(p);
35 return val;
36 case 2:
37 p = map_sysmem(addr, sizeof(ushort));
38 val = (ulong)*(ushort *)p;
39 unmap_sysmem(p);
40 return val;
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010041 case 4:
Simon Glass7c19edf2020-11-01 14:15:37 -070042 p = map_sysmem(addr, sizeof(u32));
43 val = *(u32 *)p;
44 unmap_sysmem(p);
45 return val;
Joe Hershberger9d3f8962015-05-11 13:53:13 -050046 default:
47 p = map_sysmem(addr, sizeof(ulong));
48 val = *p;
49 unmap_sysmem(p);
50 return val;
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +010051 }
52 } else {
53 return simple_strtoul(s, NULL, 16);
54 }
55}
56
Wolfgang Denkb04ae782013-03-23 23:50:34 +000057#ifdef CONFIG_REGEX
58
59#include <slre.h>
60
Wolfgang Denkb04ae782013-03-23 23:50:34 +000061/*
62 * memstr - Find the first substring in memory
63 * @s1: The string to be searched
64 * @s2: The string to search for
65 *
66 * Similar to and based on strstr(),
67 * but strings do not need to be NUL terminated.
68 */
69static char *memstr(const char *s1, int l1, const char *s2, int l2)
70{
71 if (!l2)
72 return (char *)s1;
73
74 while (l1 >= l2) {
75 l1--;
76 if (!memcmp(s1, s2, l2))
77 return (char *)s1;
78 s1++;
79 }
80 return NULL;
81}
82
Simon Glassb8e9f2d2020-11-01 14:15:39 -070083/**
84 * substitute() - Substitute part of one string with another
85 *
86 * This updates @string so that the first occurrence of @old is replaced with
87 * @new
88 *
89 * @string: String buffer containing string to update at the start
90 * @slen: Pointer to current string length, updated on success
91 * @ssize: Size of string buffer
92 * @old: Old string to find in the buffer (no terminator needed)
93 * @olen: Length of @old excluding terminator
94 * @new: New string to replace @old with
95 * @nlen: Length of @new excluding terminator
96 * @return pointer to immediately after the copied @new in @string, or NULL if
97 * no replacement took place
98 */
99static char *substitute(char *string, int *slen, int ssize,
100 const char *old, int olen, const char *new, int nlen)
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000101{
102 char *p = memstr(string, *slen, old, olen);
103
104 if (p == NULL)
105 return NULL;
106
107 debug("## Match at pos %ld: match len %d, subst len %d\n",
108 (long)(p - string), olen, nlen);
109
110 /* make sure replacement matches */
111 if (*slen + nlen - olen > ssize) {
112 printf("## error: substitution buffer overflow\n");
113 return NULL;
114 }
115
116 /* move tail if needed */
117 if (olen != nlen) {
118 int tail, len;
119
120 len = (olen > nlen) ? olen : nlen;
121
122 tail = ssize - (p + len - string);
123
124 debug("## tail len %d\n", tail);
125
126 memmove(p + nlen, p + olen, tail);
127 }
128
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700129 /* insert substitute */
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000130 memcpy(p, new, nlen);
131
132 *slen += nlen - olen;
133
134 return p + nlen;
135}
136
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700137/**
138 * regex_sub() - Replace a regex pattern with a string
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000139 *
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700140 * @data: Buffer containing the string to update
141 * @data_size: Size of buffer (must be large enough for the new string)
142 * @nbuf: Back-reference buffer
143 * @nbuf_size: Size of back-reference buffer (must be larger enough for @s plus
144 * all back-reference expansions)
145 * @r: Regular expression to find
146 * @s: String to replace with
147 * @global: true to replace all matches in @data, false to replace just the
148 * first
149 * @return 0 if OK, 1 on error
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000150 */
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700151static int regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
152 const char *r, const char *s, bool global)
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000153{
154 struct slre slre;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000155 char *datap = data;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000156 int res, len, nlen, loop;
157
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000158 if (slre_compile(&slre, r) == 0) {
159 printf("Error compiling regex: %s\n", slre.err_str);
160 return 1;
161 }
162
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700163 len = strlen(data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000164 if (s == NULL)
165 nlen = 0;
166 else
167 nlen = strlen(s);
168
169 for (loop = 0;; loop++) {
170 struct cap caps[slre.num_caps + 2];
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000171 const char *old;
172 char *np;
173 int i, olen;
174
175 (void) memset(caps, 0, sizeof(caps));
176
177 res = slre_match(&slre, datap, len, caps);
178
179 debug("Result: %d\n", res);
180
181 for (i = 0; i < slre.num_caps; i++) {
182 if (caps[i].len > 0) {
183 debug("Substring %d: [%.*s]\n", i,
184 caps[i].len, caps[i].ptr);
185 }
186 }
187
188 if (res == 0) {
189 if (loop == 0) {
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700190 printf("%s: No match\n", data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000191 return 1;
192 } else {
193 break;
194 }
195 }
196
197 debug("## MATCH ## %s\n", data);
198
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700199 if (!s)
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000200 return 1;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000201
202 old = caps[0].ptr;
203 olen = caps[0].len;
204
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 Glassb8e9f2d2020-11-01 14:15:39 -0700252 nbuf_size,
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 Glassb8e9f2d2020-11-01 14:15:39 -0700262 datap = substitute(datap, &len, data_size, old, olen,
263 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
328 ret = regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s, global);
329 if (ret)
330 return 1;
331
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000332 printf("%s=%s\n", name, data);
333
Simon Glass6a38e412017-08-03 12:22:09 -0600334 return env_set(name, data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000335}
336#endif
337
Simon Glassed38aef2020-05-10 11:40:03 -0600338static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
339 char *const argv[])
Kumar Galaaed83342008-02-14 20:44:42 -0600340{
341 ulong a, b;
Simon Glass103789d2013-02-24 17:33:22 +0000342 ulong value;
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +0100343 int w;
Kumar Galaaed83342008-02-14 20:44:42 -0600344
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000345 /*
346 * We take 3, 5, or 6 arguments:
347 * 3 : setexpr name value
348 * 5 : setexpr name val1 op val2
349 * setexpr name [g]sub r s
350 * 6 : setexpr name [g]sub r s t
351 */
352
353 /* > 6 already tested by max command args */
354 if ((argc < 3) || (argc == 4))
Simon Glassa06dfc72011-12-10 08:44:01 +0000355 return CMD_RET_USAGE;
Kumar Galaaed83342008-02-14 20:44:42 -0600356
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +0100357 w = cmd_get_data_size(argv[0], 4);
358
359 a = get_arg(argv[2], w);
Joe Hershberger0e640e62012-11-01 16:21:14 +0000360
Wolfgang Denkb2234d62013-03-23 23:50:33 +0000361 /* plain assignment: "setexpr name value" */
Joe Hershberger0e640e62012-11-01 16:21:14 +0000362 if (argc == 3) {
Simon Glass4d949a22017-08-03 12:22:10 -0600363 env_set_hex(argv[1], a);
Joe Hershberger0e640e62012-11-01 16:21:14 +0000364 return 0;
365 }
366
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
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +0100387 b = get_arg(argv[4], w);
Kumar Galaaed83342008-02-14 20:44:42 -0600388
389 switch (argv[3][0]) {
Simon Glass103789d2013-02-24 17:33:22 +0000390 case '|':
391 value = a | b;
392 break;
393 case '&':
394 value = a & b;
395 break;
396 case '+':
397 value = a + b;
398 break;
399 case '^':
400 value = a ^ b;
401 break;
402 case '-':
403 value = a - b;
404 break;
405 case '*':
406 value = a * b;
407 break;
408 case '/':
409 value = a / b;
410 break;
411 case '%':
412 value = a % b;
413 break;
Kumar Galaaed83342008-02-14 20:44:42 -0600414 default:
415 printf("invalid op\n");
416 return 1;
417 }
418
Simon Glass4d949a22017-08-03 12:22:10 -0600419 env_set_hex(argv[1], value);
Kumar Galaaed83342008-02-14 20:44:42 -0600420
421 return 0;
422}
423
424U_BOOT_CMD(
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000425 setexpr, 6, 0, do_setexpr,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600426 "set environment variable as the result of eval expression",
Joe Hershberger0e640e62012-11-01 16:21:14 +0000427 "[.b, .w, .l] name [*]value1 <op> [*]value2\n"
Kumar Galaaed83342008-02-14 20:44:42 -0600428 " - set environment variable 'name' to the result of the evaluated\n"
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000429 " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n"
Joe Hershberger0e640e62012-11-01 16:21:14 +0000430 " size argument is only meaningful if value1 and/or value2 are\n"
431 " memory addresses (*)\n"
Wolfgang Denkb2234d62013-03-23 23:50:33 +0000432 "setexpr[.b, .w, .l] name [*]value\n"
433 " - load a value into a variable"
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000434#ifdef CONFIG_REGEX
435 "\n"
436 "setexpr name gsub r s [t]\n"
437 " - For each substring matching the regular expression <r> in the\n"
438 " string <t>, substitute the string <s>. The result is\n"
439 " assigned to <name>. If <t> is not supplied, use the old\n"
440 " value of <name>\n"
441 "setexpr name sub r s [t]\n"
442 " - Just like gsub(), but replace only the first matching substring"
443#endif
Kumar Galaaed83342008-02-14 20:44:42 -0600444);