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