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