blob: dbb43b3be2fad98358eca60464431c36355d2349 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Kumar Galad0586982008-02-14 20:44:42 -06002/*
3 * Copyright 2008 Freescale Semiconductor, Inc.
Wolfgang Denk855f18e2013-03-23 23:50:34 +00004 * Copyright 2013 Wolfgang Denk <wd@denx.de>
Kumar Galad0586982008-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 Glassc7694dd2019-08-01 09:46:46 -060014#include <env.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060015#include <log.h>
Joe Hershberger2068cea2015-05-11 13:53:13 -050016#include <mapmem.h>
Kumar Galad0586982008-02-14 20:44:42 -060017
Frans Meulenbroeks47ab5ad2010-02-26 14:00:19 +010018static ulong get_arg(char *s, int w)
19{
Wolfgang Denk482126e2010-06-23 20:50:54 +020020 /*
Joe Hershberger2068cea2015-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 Meulenbroeks47ab5ad2010-02-26 14:00:19 +010023 */
Frans Meulenbroeks47ab5ad2010-02-26 14:00:19 +010024 if (s[0] == '*') {
Joe Hershberger2068cea2015-05-11 13:53:13 -050025 ulong *p;
26 ulong addr;
27 ulong val;
28
29 addr = simple_strtoul(&s[1], NULL, 16);
Frans Meulenbroeks47ab5ad2010-02-26 14:00:19 +010030 switch (w) {
Joe Hershberger2068cea2015-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 Meulenbroeks47ab5ad2010-02-26 14:00:19 +010041 case 4:
Simon Glass25a43ac2020-11-01 14:15:37 -070042 p = map_sysmem(addr, sizeof(u32));
43 val = *(u32 *)p;
44 unmap_sysmem(p);
45 return val;
Joe Hershberger2068cea2015-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 Meulenbroeks47ab5ad2010-02-26 14:00:19 +010051 }
52 } else {
53 return simple_strtoul(s, NULL, 16);
54 }
55}
56
Wolfgang Denk855f18e2013-03-23 23:50:34 +000057#ifdef CONFIG_REGEX
58
59#include <slre.h>
60
Wolfgang Denk855f18e2013-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 Glass56331b22020-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 Denk855f18e2013-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 Glass56331b22020-11-01 14:15:39 -0700129 /* insert substitute */
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000130 memcpy(p, new, nlen);
131
132 *slen += nlen - olen;
133
134 return p + nlen;
135}
136
Simon Glassd422c772020-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 Denk855f18e2013-03-23 23:50:34 +0000139{
140 struct slre slre;
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000141 char *datap = data;
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000142 int res, len, nlen, loop;
143
Wolfgang Denk855f18e2013-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 Glass56331b22020-11-01 14:15:39 -0700149 len = strlen(data);
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000150 if (s == NULL)
151 nlen = 0;
152 else
153 nlen = strlen(s);
154
155 for (loop = 0;; loop++) {
156 struct cap caps[slre.num_caps + 2];
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000157 const char *old;
158 char *np;
159 int i, olen;
160
161 (void) memset(caps, 0, sizeof(caps));
162
163 res = slre_match(&slre, datap, len, caps);
164
165 debug("Result: %d\n", res);
166
167 for (i = 0; i < slre.num_caps; i++) {
168 if (caps[i].len > 0) {
169 debug("Substring %d: [%.*s]\n", i,
170 caps[i].len, caps[i].ptr);
171 }
172 }
173
174 if (res == 0) {
175 if (loop == 0) {
Simon Glass56331b22020-11-01 14:15:39 -0700176 printf("%s: No match\n", data);
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000177 return 1;
178 } else {
179 break;
180 }
181 }
182
183 debug("## MATCH ## %s\n", data);
184
Simon Glass56331b22020-11-01 14:15:39 -0700185 if (!s)
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000186 return 1;
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000187
188 old = caps[0].ptr;
189 olen = caps[0].len;
190
Simon Glass56331b22020-11-01 14:15:39 -0700191 if (nlen + 1 >= nbuf_size) {
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000192 printf("## error: pattern buffer overflow: have %d, need %d\n",
Simon Glass56331b22020-11-01 14:15:39 -0700193 nbuf_size, nlen + 1);
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000194 return 1;
195 }
196 strcpy(nbuf, s);
197
198 debug("## SUBST(1) ## %s\n", nbuf);
199
200 /*
201 * Handle back references
202 *
203 * Support for \0 ... \9, where \0 is the
204 * whole matched pattern (similar to &).
205 *
206 * Implementation is a bit simpleminded as
207 * backrefs are substituted sequentially, one
208 * by one. This will lead to somewhat
209 * unexpected results if the replacement
210 * strings contain any \N strings then then
211 * may get substitued, too. We accept this
212 * restriction for the sake of simplicity.
213 */
214 for (i = 0; i < 10; ++i) {
215 char backref[2] = {
216 '\\',
217 '0',
218 };
219
220 if (caps[i].len == 0)
221 break;
222
223 backref[1] += i;
224
225 debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n",
226 i,
227 2, backref,
228 caps[i].len, caps[i].ptr,
229 nbuf);
230
231 for (np = nbuf;;) {
232 char *p = memstr(np, nlen, backref, 2);
233
234 if (p == NULL)
235 break;
236
237 np = substitute(np, &nlen,
Simon Glass56331b22020-11-01 14:15:39 -0700238 nbuf_size,
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000239 backref, 2,
240 caps[i].ptr, caps[i].len);
241
242 if (np == NULL)
243 return 1;
244 }
245 }
246 debug("## SUBST(2) ## %s\n", nbuf);
247
Simon Glass56331b22020-11-01 14:15:39 -0700248 datap = substitute(datap, &len, data_size, old, olen,
249 nbuf, nlen);
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000250
251 if (datap == NULL)
252 return 1;
253
254 debug("## REMAINDER: %s\n", datap);
255
256 debug("## RESULT: %s\n", data);
257
258 if (!global)
259 break;
260 }
Simon Glass382bee52017-08-03 12:22:09 -0600261 debug("## FINAL (now env_set()) : %s\n", data);
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000262
Simon Glass56331b22020-11-01 14:15:39 -0700263 return 0;
264}
265
266#define SLRE_BUFSZ 16384
267#define SLRE_PATSZ 4096
268
269/*
270 * Perform regex operations on a environment variable
271 *
272 * Returns 0 if OK, 1 in case of errors.
273 */
274static int regex_sub_var(const char *name, const char *r, const char *s,
275 const char *t, int global)
276{
277 struct slre slre;
278 char data[SLRE_BUFSZ];
279 char nbuf[SLRE_PATSZ];
280 const char *value;
281 int len;
282 int ret;
283
284 if (!name)
285 return 1;
286
287 if (slre_compile(&slre, r) == 0) {
288 printf("Error compiling regex: %s\n", slre.err_str);
289 return 1;
290 }
291
292 if (!t) {
293 value = env_get(name);
294 if (!value) {
295 printf("## Error: variable \"%s\" not defined\n", name);
296 return 1;
297 }
298 t = value;
299 }
300
301 debug("REGEX on %s=%s\n", name, t);
302 debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
303 global);
304
305 len = strlen(t);
306 if (len + 1 > SLRE_BUFSZ) {
307 printf("## error: subst buffer overflow: have %d, need %d\n",
308 SLRE_BUFSZ, len + 1);
309 return 1;
310 }
311
312 strcpy(data, t);
313
Simon Glassd422c772020-11-01 14:15:40 -0700314 ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s,
315 global);
Simon Glass56331b22020-11-01 14:15:39 -0700316 if (ret)
317 return 1;
318
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000319 printf("%s=%s\n", name, data);
320
Simon Glass382bee52017-08-03 12:22:09 -0600321 return env_set(name, data);
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000322}
323#endif
324
Simon Glass09140112020-05-10 11:40:03 -0600325static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
326 char *const argv[])
Kumar Galad0586982008-02-14 20:44:42 -0600327{
328 ulong a, b;
Simon Glass41ef3722013-02-24 17:33:22 +0000329 ulong value;
Frans Meulenbroeks47ab5ad2010-02-26 14:00:19 +0100330 int w;
Kumar Galad0586982008-02-14 20:44:42 -0600331
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000332 /*
333 * We take 3, 5, or 6 arguments:
334 * 3 : setexpr name value
335 * 5 : setexpr name val1 op val2
336 * setexpr name [g]sub r s
337 * 6 : setexpr name [g]sub r s t
338 */
339
340 /* > 6 already tested by max command args */
341 if ((argc < 3) || (argc == 4))
Simon Glass4c12eeb2011-12-10 08:44:01 +0000342 return CMD_RET_USAGE;
Kumar Galad0586982008-02-14 20:44:42 -0600343
Frans Meulenbroeks47ab5ad2010-02-26 14:00:19 +0100344 w = cmd_get_data_size(argv[0], 4);
345
346 a = get_arg(argv[2], w);
Joe Hershberger4823b452012-11-01 16:21:14 +0000347
Wolfgang Denk103c94b2013-03-23 23:50:33 +0000348 /* plain assignment: "setexpr name value" */
Joe Hershberger4823b452012-11-01 16:21:14 +0000349 if (argc == 3) {
Simon Glass018f5302017-08-03 12:22:10 -0600350 env_set_hex(argv[1], a);
Joe Hershberger4823b452012-11-01 16:21:14 +0000351 return 0;
352 }
353
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000354 /* 5 or 6 args (6 args only with [g]sub) */
355#ifdef CONFIG_REGEX
356 /*
357 * rexep handling: "setexpr name [g]sub r s [t]"
358 * with 5 args, "t" will be NULL
359 */
360 if (strcmp(argv[2], "gsub") == 0)
Simon Glass56331b22020-11-01 14:15:39 -0700361 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000362
363 if (strcmp(argv[2], "sub") == 0)
Simon Glass56331b22020-11-01 14:15:39 -0700364 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000365#endif
366
Wolfgang Denk103c94b2013-03-23 23:50:33 +0000367 /* standard operators: "setexpr name val1 op val2" */
368 if (argc != 5)
369 return CMD_RET_USAGE;
370
371 if (strlen(argv[3]) != 1)
372 return CMD_RET_USAGE;
373
Frans Meulenbroeks47ab5ad2010-02-26 14:00:19 +0100374 b = get_arg(argv[4], w);
Kumar Galad0586982008-02-14 20:44:42 -0600375
376 switch (argv[3][0]) {
Simon Glass41ef3722013-02-24 17:33:22 +0000377 case '|':
378 value = a | b;
379 break;
380 case '&':
381 value = a & b;
382 break;
383 case '+':
384 value = a + b;
385 break;
386 case '^':
387 value = a ^ b;
388 break;
389 case '-':
390 value = a - b;
391 break;
392 case '*':
393 value = a * b;
394 break;
395 case '/':
396 value = a / b;
397 break;
398 case '%':
399 value = a % b;
400 break;
Kumar Galad0586982008-02-14 20:44:42 -0600401 default:
402 printf("invalid op\n");
403 return 1;
404 }
405
Simon Glass018f5302017-08-03 12:22:10 -0600406 env_set_hex(argv[1], value);
Kumar Galad0586982008-02-14 20:44:42 -0600407
408 return 0;
409}
410
411U_BOOT_CMD(
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000412 setexpr, 6, 0, do_setexpr,
Peter Tyser2fb26042009-01-27 18:03:12 -0600413 "set environment variable as the result of eval expression",
Joe Hershberger4823b452012-11-01 16:21:14 +0000414 "[.b, .w, .l] name [*]value1 <op> [*]value2\n"
Kumar Galad0586982008-02-14 20:44:42 -0600415 " - set environment variable 'name' to the result of the evaluated\n"
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000416 " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n"
Joe Hershberger4823b452012-11-01 16:21:14 +0000417 " size argument is only meaningful if value1 and/or value2 are\n"
418 " memory addresses (*)\n"
Wolfgang Denk103c94b2013-03-23 23:50:33 +0000419 "setexpr[.b, .w, .l] name [*]value\n"
420 " - load a value into a variable"
Wolfgang Denk855f18e2013-03-23 23:50:34 +0000421#ifdef CONFIG_REGEX
422 "\n"
423 "setexpr name gsub r s [t]\n"
424 " - For each substring matching the regular expression <r> in the\n"
425 " string <t>, substitute the string <s>. The result is\n"
426 " assigned to <name>. If <t> is not supplied, use the old\n"
427 " value of <name>\n"
428 "setexpr name sub r s [t]\n"
429 " - Just like gsub(), but replace only the first matching substring"
430#endif
Kumar Galad0586982008-02-14 20:44:42 -0600431);