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