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