Merge branch 'master' of git://git.denx.de/u-boot-mmc
[kernel/u-boot.git] / common / cmd_setexpr.c
1 /*
2  * Copyright 2008 Freescale Semiconductor, Inc.
3  * Copyright 2013 Wolfgang Denk <wd@denx.de>
4  *
5  * SPDX-License-Identifier:     GPL-2.0+ 
6  */
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
16 static ulong get_arg(char *s, int w)
17 {
18         ulong *p;
19
20         /*
21          * 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
38 #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  */
53 static 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
67 static 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  */
115 static 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
279 static int do_setexpr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
280 {
281         ulong a, b;
282         ulong value;
283         int w;
284
285         /*
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))
295                 return CMD_RET_USAGE;
296
297         w = cmd_get_data_size(argv[0], 4);
298
299         a = get_arg(argv[2], w);
300
301         /* plain assignment: "setexpr name value" */
302         if (argc == 3) {
303                 setenv_hex(argv[1], a);
304                 return 0;
305         }
306
307         /* 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
320         /* 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
327         b = get_arg(argv[4], w);
328
329         switch (argv[3][0]) {
330         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;
354         default:
355                 printf("invalid op\n");
356                 return 1;
357         }
358
359         setenv_hex(argv[1], value);
360
361         return 0;
362 }
363
364 U_BOOT_CMD(
365         setexpr, 6, 0, do_setexpr,
366         "set environment variable as the result of eval expression",
367         "[.b, .w, .l] name [*]value1 <op> [*]value2\n"
368         "    - set environment variable 'name' to the result of the evaluated\n"
369         "      expression specified by <op>.  <op> can be &, |, ^, +, -, *, /, %\n"
370         "      size argument is only meaningful if value1 and/or value2 are\n"
371         "      memory addresses (*)\n"
372         "setexpr[.b, .w, .l] name [*]value\n"
373         "    - load a value into a variable"
374 #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
384 );