setexpr: Split the core logic into its own function
[platform/kernel/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 <log.h>
16 #include <mapmem.h>
17
18 static ulong get_arg(char *s, int w)
19 {
20         /*
21          * If the parameter starts with a '*' then assume it is a pointer to
22          * the value we want.
23          */
24         if (s[0] == '*') {
25                 ulong *p;
26                 ulong addr;
27                 ulong val;
28
29                 addr = simple_strtoul(&s[1], NULL, 16);
30                 switch (w) {
31                 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;
41                 case 4:
42                         p = map_sysmem(addr, sizeof(u32));
43                         val = *(u32 *)p;
44                         unmap_sysmem(p);
45                         return val;
46                 default:
47                         p = map_sysmem(addr, sizeof(ulong));
48                         val = *p;
49                         unmap_sysmem(p);
50                         return val;
51                 }
52         } else {
53                 return simple_strtoul(s, NULL, 16);
54         }
55 }
56
57 #ifdef CONFIG_REGEX
58
59 #include <slre.h>
60
61 /*
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  */
69 static 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
83 /**
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  */
99 static char *substitute(char *string, int *slen, int ssize,
100                         const char *old, int olen, const char *new, int nlen)
101 {
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
129         /* insert substitute */
130         memcpy(p, new, nlen);
131
132         *slen += nlen - olen;
133
134         return p + nlen;
135 }
136
137 /**
138  * regex_sub() - Replace a regex pattern with a string
139  *
140  * @data: Buffer containing the string to update
141  * @data_size: Size of buffer (must be large enough for the new string)
142  * @nbuf: Back-reference buffer
143  * @nbuf_size: Size of back-reference buffer (must be larger enough for @s plus
144  *      all back-reference expansions)
145  * @r: Regular expression to find
146  * @s: String to replace with
147  * @global: true to replace all matches in @data, false to replace just the
148  *      first
149  * @return 0 if OK, 1 on error
150  */
151 static int regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
152                      const char *r, const char *s, bool global)
153 {
154         struct slre slre;
155         char *datap = data;
156         int res, len, nlen, loop;
157
158         if (slre_compile(&slre, r) == 0) {
159                 printf("Error compiling regex: %s\n", slre.err_str);
160                 return 1;
161         }
162
163         len = strlen(data);
164         if (s == NULL)
165                 nlen = 0;
166         else
167                 nlen = strlen(s);
168
169         for (loop = 0;; loop++) {
170                 struct cap caps[slre.num_caps + 2];
171                 const char *old;
172                 char *np;
173                 int i, olen;
174
175                 (void) memset(caps, 0, sizeof(caps));
176
177                 res = slre_match(&slre, datap, len, caps);
178
179                 debug("Result: %d\n", res);
180
181                 for (i = 0; i < slre.num_caps; i++) {
182                         if (caps[i].len > 0) {
183                                 debug("Substring %d: [%.*s]\n", i,
184                                         caps[i].len, caps[i].ptr);
185                         }
186                 }
187
188                 if (res == 0) {
189                         if (loop == 0) {
190                                 printf("%s: No match\n", data);
191                                 return 1;
192                         } else {
193                                 break;
194                         }
195                 }
196
197                 debug("## MATCH ## %s\n", data);
198
199                 if (!s)
200                         return 1;
201
202                 old = caps[0].ptr;
203                 olen = caps[0].len;
204
205                 if (nlen + 1 >= nbuf_size) {
206                         printf("## error: pattern buffer overflow: have %d, need %d\n",
207                                nbuf_size, nlen + 1);
208                         return 1;
209                 }
210                 strcpy(nbuf, s);
211
212                 debug("## SUBST(1) ## %s\n", nbuf);
213
214                 /*
215                  * Handle back references
216                  *
217                  * Support for \0 ... \9, where \0 is the
218                  * whole matched pattern (similar to &).
219                  *
220                  * Implementation is a bit simpleminded as
221                  * backrefs are substituted sequentially, one
222                  * by one.  This will lead to somewhat
223                  * unexpected results if the replacement
224                  * strings contain any \N strings then then
225                  * may get substitued, too.  We accept this
226                  * restriction for the sake of simplicity.
227                  */
228                 for (i = 0; i < 10; ++i) {
229                         char backref[2] = {
230                                 '\\',
231                                 '0',
232                         };
233
234                         if (caps[i].len == 0)
235                                 break;
236
237                         backref[1] += i;
238
239                         debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n",
240                                 i,
241                                 2, backref,
242                                 caps[i].len, caps[i].ptr,
243                                 nbuf);
244
245                         for (np = nbuf;;) {
246                                 char *p = memstr(np, nlen, backref, 2);
247
248                                 if (p == NULL)
249                                         break;
250
251                                 np = substitute(np, &nlen,
252                                         nbuf_size,
253                                         backref, 2,
254                                         caps[i].ptr, caps[i].len);
255
256                                 if (np == NULL)
257                                         return 1;
258                         }
259                 }
260                 debug("## SUBST(2) ## %s\n", nbuf);
261
262                 datap = substitute(datap, &len, data_size, old, olen,
263                                    nbuf, nlen);
264
265                 if (datap == NULL)
266                         return 1;
267
268                 debug("## REMAINDER: %s\n", datap);
269
270                 debug("## RESULT: %s\n", data);
271
272                 if (!global)
273                         break;
274         }
275         debug("## FINAL (now env_set()) :  %s\n", data);
276
277         return 0;
278 }
279
280 #define SLRE_BUFSZ      16384
281 #define SLRE_PATSZ      4096
282
283 /*
284  * Perform regex operations on a environment variable
285  *
286  * Returns 0 if OK, 1 in case of errors.
287  */
288 static int regex_sub_var(const char *name, const char *r, const char *s,
289                          const char *t, int global)
290 {
291         struct slre slre;
292         char data[SLRE_BUFSZ];
293         char nbuf[SLRE_PATSZ];
294         const char *value;
295         int len;
296         int ret;
297
298         if (!name)
299                 return 1;
300
301         if (slre_compile(&slre, r) == 0) {
302                 printf("Error compiling regex: %s\n", slre.err_str);
303                 return 1;
304         }
305
306         if (!t) {
307                 value = env_get(name);
308                 if (!value) {
309                         printf("## Error: variable \"%s\" not defined\n", name);
310                         return 1;
311                 }
312                 t = value;
313         }
314
315         debug("REGEX on %s=%s\n", name, t);
316         debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
317               global);
318
319         len = strlen(t);
320         if (len + 1 > SLRE_BUFSZ) {
321                 printf("## error: subst buffer overflow: have %d, need %d\n",
322                        SLRE_BUFSZ, len + 1);
323                 return 1;
324         }
325
326         strcpy(data, t);
327
328         ret = regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s, global);
329         if (ret)
330                 return 1;
331
332         printf("%s=%s\n", name, data);
333
334         return env_set(name, data);
335 }
336 #endif
337
338 static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
339                       char *const argv[])
340 {
341         ulong a, b;
342         ulong value;
343         int w;
344
345         /*
346          * We take 3, 5, or 6 arguments:
347          * 3 : setexpr name value
348          * 5 : setexpr name val1 op val2
349          *     setexpr name [g]sub r s
350          * 6 : setexpr name [g]sub r s t
351          */
352
353         /* > 6 already tested by max command args */
354         if ((argc < 3) || (argc == 4))
355                 return CMD_RET_USAGE;
356
357         w = cmd_get_data_size(argv[0], 4);
358
359         a = get_arg(argv[2], w);
360
361         /* plain assignment: "setexpr name value" */
362         if (argc == 3) {
363                 env_set_hex(argv[1], a);
364                 return 0;
365         }
366
367         /* 5 or 6 args (6 args only with [g]sub) */
368 #ifdef CONFIG_REGEX
369         /*
370          * rexep handling: "setexpr name [g]sub r s [t]"
371          * with 5 args, "t" will be NULL
372          */
373         if (strcmp(argv[2], "gsub") == 0)
374                 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
375
376         if (strcmp(argv[2], "sub") == 0)
377                 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
378 #endif
379
380         /* standard operators: "setexpr name val1 op val2" */
381         if (argc != 5)
382                 return CMD_RET_USAGE;
383
384         if (strlen(argv[3]) != 1)
385                 return CMD_RET_USAGE;
386
387         b = get_arg(argv[4], w);
388
389         switch (argv[3][0]) {
390         case '|':
391                 value = a | b;
392                 break;
393         case '&':
394                 value = a & b;
395                 break;
396         case '+':
397                 value = a + b;
398                 break;
399         case '^':
400                 value = a ^ b;
401                 break;
402         case '-':
403                 value = a - b;
404                 break;
405         case '*':
406                 value = a * b;
407                 break;
408         case '/':
409                 value = a / b;
410                 break;
411         case '%':
412                 value = a % b;
413                 break;
414         default:
415                 printf("invalid op\n");
416                 return 1;
417         }
418
419         env_set_hex(argv[1], value);
420
421         return 0;
422 }
423
424 U_BOOT_CMD(
425         setexpr, 6, 0, do_setexpr,
426         "set environment variable as the result of eval expression",
427         "[.b, .w, .l] name [*]value1 <op> [*]value2\n"
428         "    - set environment variable 'name' to the result of the evaluated\n"
429         "      expression specified by <op>.  <op> can be &, |, ^, +, -, *, /, %\n"
430         "      size argument is only meaningful if value1 and/or value2 are\n"
431         "      memory addresses (*)\n"
432         "setexpr[.b, .w, .l] name [*]value\n"
433         "    - load a value into a variable"
434 #ifdef CONFIG_REGEX
435         "\n"
436         "setexpr name gsub r s [t]\n"
437         "    - For each substring matching the regular expression <r> in the\n"
438         "      string <t>, substitute the string <s>.  The result is\n"
439         "      assigned to <name>.  If <t> is not supplied, use the old\n"
440         "      value of <name>\n"
441         "setexpr name sub r s [t]\n"
442         "    - Just like gsub(), but replace only the first matching substring"
443 #endif
444 );