blob: d364dbc2bc536a474a2b60bf172c9460b2cdeacc [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 Glass00e44192020-11-01 14:15:40 -0700137int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
138 const char *r, const char *s, bool global)
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000139{
140 struct slre slre;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000141 char *datap = data;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000142 int res, len, nlen, loop;
143
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000144 if (slre_compile(&slre, r) == 0) {
145 printf("Error compiling regex: %s\n", slre.err_str);
146 return 1;
147 }
148
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700149 len = strlen(data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000150 for (loop = 0;; loop++) {
151 struct cap caps[slre.num_caps + 2];
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000152 const char *old;
153 char *np;
154 int i, olen;
155
156 (void) memset(caps, 0, sizeof(caps));
157
Simon Glass7a114862020-11-01 14:15:42 -0700158 res = slre_match(&slre, datap, len - (datap - data), caps);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000159
160 debug("Result: %d\n", res);
161
Simon Glass7a114862020-11-01 14:15:42 -0700162 for (i = 0; i <= slre.num_caps; i++) {
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000163 if (caps[i].len > 0) {
164 debug("Substring %d: [%.*s]\n", i,
165 caps[i].len, caps[i].ptr);
166 }
167 }
168
169 if (res == 0) {
170 if (loop == 0) {
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700171 printf("%s: No match\n", data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000172 return 1;
173 } else {
174 break;
175 }
176 }
177
178 debug("## MATCH ## %s\n", data);
179
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700180 if (!s)
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000181 return 1;
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000182
183 old = caps[0].ptr;
184 olen = caps[0].len;
Simon Glass28ae25a2020-11-01 14:15:41 -0700185 nlen = strlen(s);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000186
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700187 if (nlen + 1 >= nbuf_size) {
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000188 printf("## error: pattern buffer overflow: have %d, need %d\n",
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700189 nbuf_size, nlen + 1);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000190 return 1;
191 }
192 strcpy(nbuf, s);
193
194 debug("## SUBST(1) ## %s\n", nbuf);
195
196 /*
197 * Handle back references
198 *
199 * Support for \0 ... \9, where \0 is the
200 * whole matched pattern (similar to &).
201 *
202 * Implementation is a bit simpleminded as
203 * backrefs are substituted sequentially, one
204 * by one. This will lead to somewhat
205 * unexpected results if the replacement
206 * strings contain any \N strings then then
207 * may get substitued, too. We accept this
208 * restriction for the sake of simplicity.
209 */
210 for (i = 0; i < 10; ++i) {
211 char backref[2] = {
212 '\\',
213 '0',
214 };
215
216 if (caps[i].len == 0)
217 break;
218
219 backref[1] += i;
220
221 debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n",
222 i,
223 2, backref,
224 caps[i].len, caps[i].ptr,
225 nbuf);
226
227 for (np = nbuf;;) {
228 char *p = memstr(np, nlen, backref, 2);
229
230 if (p == NULL)
231 break;
232
233 np = substitute(np, &nlen,
Simon Glass7a114862020-11-01 14:15:42 -0700234 nbuf_size - (np - nbuf),
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000235 backref, 2,
236 caps[i].ptr, caps[i].len);
237
238 if (np == NULL)
239 return 1;
240 }
241 }
242 debug("## SUBST(2) ## %s\n", nbuf);
243
Simon Glass7a114862020-11-01 14:15:42 -0700244 datap = substitute(datap, &len, data_size - (datap - data),
245 old, olen, nbuf, nlen);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000246
247 if (datap == NULL)
248 return 1;
249
250 debug("## REMAINDER: %s\n", datap);
251
252 debug("## RESULT: %s\n", data);
253
254 if (!global)
255 break;
256 }
Simon Glass6a38e412017-08-03 12:22:09 -0600257 debug("## FINAL (now env_set()) : %s\n", data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000258
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700259 return 0;
260}
261
262#define SLRE_BUFSZ 16384
263#define SLRE_PATSZ 4096
264
265/*
266 * Perform regex operations on a environment variable
267 *
268 * Returns 0 if OK, 1 in case of errors.
269 */
270static int regex_sub_var(const char *name, const char *r, const char *s,
271 const char *t, int global)
272{
273 struct slre slre;
274 char data[SLRE_BUFSZ];
275 char nbuf[SLRE_PATSZ];
276 const char *value;
277 int len;
278 int ret;
279
280 if (!name)
281 return 1;
282
283 if (slre_compile(&slre, r) == 0) {
284 printf("Error compiling regex: %s\n", slre.err_str);
285 return 1;
286 }
287
288 if (!t) {
289 value = env_get(name);
290 if (!value) {
291 printf("## Error: variable \"%s\" not defined\n", name);
292 return 1;
293 }
294 t = value;
295 }
296
297 debug("REGEX on %s=%s\n", name, t);
298 debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
299 global);
300
301 len = strlen(t);
302 if (len + 1 > SLRE_BUFSZ) {
303 printf("## error: subst buffer overflow: have %d, need %d\n",
304 SLRE_BUFSZ, len + 1);
305 return 1;
306 }
307
308 strcpy(data, t);
309
Simon Glass00e44192020-11-01 14:15:40 -0700310 ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s,
311 global);
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700312 if (ret)
313 return 1;
314
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000315 printf("%s=%s\n", name, data);
316
Simon Glass6a38e412017-08-03 12:22:09 -0600317 return env_set(name, data);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000318}
319#endif
320
Simon Glassed38aef2020-05-10 11:40:03 -0600321static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
322 char *const argv[])
Kumar Galaaed83342008-02-14 20:44:42 -0600323{
324 ulong a, b;
Simon Glass103789d2013-02-24 17:33:22 +0000325 ulong value;
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +0100326 int w;
Kumar Galaaed83342008-02-14 20:44:42 -0600327
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000328 /*
329 * We take 3, 5, or 6 arguments:
330 * 3 : setexpr name value
331 * 5 : setexpr name val1 op val2
332 * setexpr name [g]sub r s
333 * 6 : setexpr name [g]sub r s t
334 */
335
336 /* > 6 already tested by max command args */
337 if ((argc < 3) || (argc == 4))
Simon Glassa06dfc72011-12-10 08:44:01 +0000338 return CMD_RET_USAGE;
Kumar Galaaed83342008-02-14 20:44:42 -0600339
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +0100340 w = cmd_get_data_size(argv[0], 4);
341
342 a = get_arg(argv[2], w);
Joe Hershberger0e640e62012-11-01 16:21:14 +0000343
Wolfgang Denkb2234d62013-03-23 23:50:33 +0000344 /* plain assignment: "setexpr name value" */
Joe Hershberger0e640e62012-11-01 16:21:14 +0000345 if (argc == 3) {
Simon Glass4d949a22017-08-03 12:22:10 -0600346 env_set_hex(argv[1], a);
Joe Hershberger0e640e62012-11-01 16:21:14 +0000347 return 0;
348 }
349
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000350 /* 5 or 6 args (6 args only with [g]sub) */
351#ifdef CONFIG_REGEX
352 /*
353 * rexep handling: "setexpr name [g]sub r s [t]"
354 * with 5 args, "t" will be NULL
355 */
356 if (strcmp(argv[2], "gsub") == 0)
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700357 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000358
359 if (strcmp(argv[2], "sub") == 0)
Simon Glassb8e9f2d2020-11-01 14:15:39 -0700360 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000361#endif
362
Wolfgang Denkb2234d62013-03-23 23:50:33 +0000363 /* standard operators: "setexpr name val1 op val2" */
364 if (argc != 5)
365 return CMD_RET_USAGE;
366
367 if (strlen(argv[3]) != 1)
368 return CMD_RET_USAGE;
369
Frans Meulenbroeks2347d4e2010-02-26 14:00:19 +0100370 b = get_arg(argv[4], w);
Kumar Galaaed83342008-02-14 20:44:42 -0600371
372 switch (argv[3][0]) {
Simon Glass103789d2013-02-24 17:33:22 +0000373 case '|':
374 value = a | b;
375 break;
376 case '&':
377 value = a & b;
378 break;
379 case '+':
380 value = a + b;
381 break;
382 case '^':
383 value = a ^ b;
384 break;
385 case '-':
386 value = a - b;
387 break;
388 case '*':
389 value = a * b;
390 break;
391 case '/':
392 value = a / b;
393 break;
394 case '%':
395 value = a % b;
396 break;
Kumar Galaaed83342008-02-14 20:44:42 -0600397 default:
398 printf("invalid op\n");
399 return 1;
400 }
401
Simon Glass4d949a22017-08-03 12:22:10 -0600402 env_set_hex(argv[1], value);
Kumar Galaaed83342008-02-14 20:44:42 -0600403
404 return 0;
405}
406
407U_BOOT_CMD(
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000408 setexpr, 6, 0, do_setexpr,
Peter Tyserdfb72b82009-01-27 18:03:12 -0600409 "set environment variable as the result of eval expression",
Joe Hershberger0e640e62012-11-01 16:21:14 +0000410 "[.b, .w, .l] name [*]value1 <op> [*]value2\n"
Kumar Galaaed83342008-02-14 20:44:42 -0600411 " - set environment variable 'name' to the result of the evaluated\n"
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000412 " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n"
Joe Hershberger0e640e62012-11-01 16:21:14 +0000413 " size argument is only meaningful if value1 and/or value2 are\n"
414 " memory addresses (*)\n"
Wolfgang Denkb2234d62013-03-23 23:50:33 +0000415 "setexpr[.b, .w, .l] name [*]value\n"
416 " - load a value into a variable"
Wolfgang Denkb04ae782013-03-23 23:50:34 +0000417#ifdef CONFIG_REGEX
418 "\n"
419 "setexpr name gsub r s [t]\n"
420 " - For each substring matching the regular expression <r> in the\n"
421 " string <t>, substitute the string <s>. The result is\n"
422 " assigned to <name>. If <t> is not supplied, use the old\n"
423 " value of <name>\n"
424 "setexpr name sub r s [t]\n"
425 " - Just like gsub(), but replace only the first matching substring"
426#endif
Kumar Galaaed83342008-02-14 20:44:42 -0600427);