Home | History | Annotate | Download | only in cmd
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright 2008 Freescale Semiconductor, Inc.
      4  * Copyright 2013 Wolfgang Denk <wd (at) 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 
     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  */
     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 
     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  */
    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 
    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