xref: /openbmc/u-boot/env/attr.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
20649cd0dSSimon Glass /*
30649cd0dSSimon Glass  * (C) Copyright 2012
40649cd0dSSimon Glass  * Joe Hershberger, National Instruments, joe.hershberger@ni.com
50649cd0dSSimon Glass  */
60649cd0dSSimon Glass 
70649cd0dSSimon Glass #ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */
80649cd0dSSimon Glass #include <stdint.h>
90649cd0dSSimon Glass #include <stdio.h>
100649cd0dSSimon Glass #include <linux/linux_string.h>
110649cd0dSSimon Glass #else
120649cd0dSSimon Glass #include <common.h>
130649cd0dSSimon Glass #include <slre.h>
140649cd0dSSimon Glass #endif
150649cd0dSSimon Glass 
160649cd0dSSimon Glass #include <env_attr.h>
170649cd0dSSimon Glass #include <errno.h>
180649cd0dSSimon Glass #include <linux/string.h>
190649cd0dSSimon Glass #include <malloc.h>
200649cd0dSSimon Glass 
210649cd0dSSimon Glass /*
220649cd0dSSimon Glass  * Iterate through the whole list calling the callback for each found element.
230649cd0dSSimon Glass  * "attr_list" takes the form:
240649cd0dSSimon Glass  *	attributes = [^,:\s]*
250649cd0dSSimon Glass  *	entry = name[:attributes]
260649cd0dSSimon Glass  *	list = entry[,list]
270649cd0dSSimon Glass  */
env_attr_walk(const char * attr_list,int (* callback)(const char * name,const char * attributes,void * priv),void * priv)280649cd0dSSimon Glass int env_attr_walk(const char *attr_list,
290649cd0dSSimon Glass 	int (*callback)(const char *name, const char *attributes, void *priv),
300649cd0dSSimon Glass 	void *priv)
310649cd0dSSimon Glass {
320649cd0dSSimon Glass 	const char *entry, *entry_end;
330649cd0dSSimon Glass 	char *name, *attributes;
340649cd0dSSimon Glass 
350649cd0dSSimon Glass 	if (!attr_list)
360649cd0dSSimon Glass 		/* list not found */
370649cd0dSSimon Glass 		return 1;
380649cd0dSSimon Glass 
390649cd0dSSimon Glass 	entry = attr_list;
400649cd0dSSimon Glass 	do {
410649cd0dSSimon Glass 		char *entry_cpy = NULL;
420649cd0dSSimon Glass 
430649cd0dSSimon Glass 		entry_end = strchr(entry, ENV_ATTR_LIST_DELIM);
440649cd0dSSimon Glass 		/* check if this is the last entry in the list */
450649cd0dSSimon Glass 		if (entry_end == NULL) {
460649cd0dSSimon Glass 			int entry_len = strlen(entry);
470649cd0dSSimon Glass 
480649cd0dSSimon Glass 			if (entry_len) {
490649cd0dSSimon Glass 				/*
500649cd0dSSimon Glass 				 * allocate memory to copy the entry into since
510649cd0dSSimon Glass 				 * we will need to inject '\0' chars and squash
520649cd0dSSimon Glass 				 * white-space before calling the callback
530649cd0dSSimon Glass 				 */
540649cd0dSSimon Glass 				entry_cpy = malloc(entry_len + 1);
550649cd0dSSimon Glass 				if (entry_cpy)
560649cd0dSSimon Glass 					/* copy the rest of the list */
570649cd0dSSimon Glass 					strcpy(entry_cpy, entry);
580649cd0dSSimon Glass 				else
590649cd0dSSimon Glass 					return -ENOMEM;
600649cd0dSSimon Glass 			}
610649cd0dSSimon Glass 		} else {
620649cd0dSSimon Glass 			int entry_len = entry_end - entry;
630649cd0dSSimon Glass 
640649cd0dSSimon Glass 			if (entry_len) {
650649cd0dSSimon Glass 				/*
660649cd0dSSimon Glass 				 * allocate memory to copy the entry into since
670649cd0dSSimon Glass 				 * we will need to inject '\0' chars and squash
680649cd0dSSimon Glass 				 * white-space before calling the callback
690649cd0dSSimon Glass 				 */
700649cd0dSSimon Glass 				entry_cpy = malloc(entry_len + 1);
710649cd0dSSimon Glass 				if (entry_cpy) {
720649cd0dSSimon Glass 					/* copy just this entry and null term */
730649cd0dSSimon Glass 					strncpy(entry_cpy, entry, entry_len);
740649cd0dSSimon Glass 					entry_cpy[entry_len] = '\0';
750649cd0dSSimon Glass 				} else
760649cd0dSSimon Glass 					return -ENOMEM;
770649cd0dSSimon Glass 			}
780649cd0dSSimon Glass 		}
790649cd0dSSimon Glass 
800649cd0dSSimon Glass 		/* check if there is anything to process (e.g. not ",,,") */
810649cd0dSSimon Glass 		if (entry_cpy != NULL) {
820649cd0dSSimon Glass 			attributes = strchr(entry_cpy, ENV_ATTR_SEP);
830649cd0dSSimon Glass 			/* check if there is a ':' */
840649cd0dSSimon Glass 			if (attributes != NULL) {
850649cd0dSSimon Glass 				/* replace the ':' with '\0' to term name */
860649cd0dSSimon Glass 				*attributes++ = '\0';
870649cd0dSSimon Glass 				/* remove white-space from attributes */
880649cd0dSSimon Glass 				attributes = strim(attributes);
890649cd0dSSimon Glass 			}
900649cd0dSSimon Glass 			/* remove white-space from name */
910649cd0dSSimon Glass 			name = strim(entry_cpy);
920649cd0dSSimon Glass 
930649cd0dSSimon Glass 			/* only call the callback if there is a name */
940649cd0dSSimon Glass 			if (strlen(name) != 0) {
950649cd0dSSimon Glass 				int retval = 0;
960649cd0dSSimon Glass 
970649cd0dSSimon Glass 				retval = callback(name, attributes, priv);
980649cd0dSSimon Glass 				if (retval) {
990649cd0dSSimon Glass 					free(entry_cpy);
1000649cd0dSSimon Glass 					return retval;
1010649cd0dSSimon Glass 				}
1020649cd0dSSimon Glass 			}
1030649cd0dSSimon Glass 		}
1040649cd0dSSimon Glass 
1050649cd0dSSimon Glass 		free(entry_cpy);
1060649cd0dSSimon Glass 		entry = entry_end + 1;
1070649cd0dSSimon Glass 	} while (entry_end != NULL);
1080649cd0dSSimon Glass 
1090649cd0dSSimon Glass 	return 0;
1100649cd0dSSimon Glass }
1110649cd0dSSimon Glass 
1120649cd0dSSimon Glass #if defined(CONFIG_REGEX)
1130649cd0dSSimon Glass struct regex_callback_priv {
1140649cd0dSSimon Glass 	const char *searched_for;
1150649cd0dSSimon Glass 	char *regex;
1160649cd0dSSimon Glass 	char *attributes;
1170649cd0dSSimon Glass };
1180649cd0dSSimon Glass 
regex_callback(const char * name,const char * attributes,void * priv)1190649cd0dSSimon Glass static int regex_callback(const char *name, const char *attributes, void *priv)
1200649cd0dSSimon Glass {
1210649cd0dSSimon Glass 	int retval = 0;
1220649cd0dSSimon Glass 	struct regex_callback_priv *cbp = (struct regex_callback_priv *)priv;
1230649cd0dSSimon Glass 	struct slre slre;
1240649cd0dSSimon Glass 	char regex[strlen(name) + 3];
1250649cd0dSSimon Glass 
1260649cd0dSSimon Glass 	/* Require the whole string to be described by the regex */
1270649cd0dSSimon Glass 	sprintf(regex, "^%s$", name);
1280649cd0dSSimon Glass 	if (slre_compile(&slre, regex)) {
1290649cd0dSSimon Glass 		struct cap caps[slre.num_caps + 2];
1300649cd0dSSimon Glass 
1310649cd0dSSimon Glass 		if (slre_match(&slre, cbp->searched_for,
1320649cd0dSSimon Glass 			       strlen(cbp->searched_for), caps)) {
1330649cd0dSSimon Glass 			free(cbp->regex);
1340649cd0dSSimon Glass 			if (!attributes) {
1350649cd0dSSimon Glass 				retval = -EINVAL;
1360649cd0dSSimon Glass 				goto done;
1370649cd0dSSimon Glass 			}
1380649cd0dSSimon Glass 			cbp->regex = malloc(strlen(regex) + 1);
1390649cd0dSSimon Glass 			if (cbp->regex) {
1400649cd0dSSimon Glass 				strcpy(cbp->regex, regex);
1410649cd0dSSimon Glass 			} else {
1420649cd0dSSimon Glass 				retval = -ENOMEM;
1430649cd0dSSimon Glass 				goto done;
1440649cd0dSSimon Glass 			}
1450649cd0dSSimon Glass 
1460649cd0dSSimon Glass 			free(cbp->attributes);
1470649cd0dSSimon Glass 			cbp->attributes = malloc(strlen(attributes) + 1);
1480649cd0dSSimon Glass 			if (cbp->attributes) {
1490649cd0dSSimon Glass 				strcpy(cbp->attributes, attributes);
1500649cd0dSSimon Glass 			} else {
1510649cd0dSSimon Glass 				retval = -ENOMEM;
1520649cd0dSSimon Glass 				free(cbp->regex);
1530649cd0dSSimon Glass 				cbp->regex = NULL;
1540649cd0dSSimon Glass 				goto done;
1550649cd0dSSimon Glass 			}
1560649cd0dSSimon Glass 		}
1570649cd0dSSimon Glass 	} else {
1580649cd0dSSimon Glass 		printf("Error compiling regex: %s\n", slre.err_str);
1590649cd0dSSimon Glass 		retval = -EINVAL;
1600649cd0dSSimon Glass 	}
1610649cd0dSSimon Glass done:
1620649cd0dSSimon Glass 	return retval;
1630649cd0dSSimon Glass }
1640649cd0dSSimon Glass 
1650649cd0dSSimon Glass /*
1660649cd0dSSimon Glass  * Retrieve the attributes string associated with a single name in the list
1670649cd0dSSimon Glass  * There is no protection on attributes being too small for the value
1680649cd0dSSimon Glass  */
env_attr_lookup(const char * attr_list,const char * name,char * attributes)1690649cd0dSSimon Glass int env_attr_lookup(const char *attr_list, const char *name, char *attributes)
1700649cd0dSSimon Glass {
1710649cd0dSSimon Glass 	if (!attributes)
1720649cd0dSSimon Glass 		/* bad parameter */
1730649cd0dSSimon Glass 		return -EINVAL;
1740649cd0dSSimon Glass 	if (!attr_list)
1750649cd0dSSimon Glass 		/* list not found */
1760649cd0dSSimon Glass 		return -EINVAL;
1770649cd0dSSimon Glass 
1780649cd0dSSimon Glass 	struct regex_callback_priv priv;
1790649cd0dSSimon Glass 	int retval;
1800649cd0dSSimon Glass 
1810649cd0dSSimon Glass 	priv.searched_for = name;
1820649cd0dSSimon Glass 	priv.regex = NULL;
1830649cd0dSSimon Glass 	priv.attributes = NULL;
1840649cd0dSSimon Glass 	retval = env_attr_walk(attr_list, regex_callback, &priv);
1850649cd0dSSimon Glass 	if (retval)
1860649cd0dSSimon Glass 		return retval; /* error */
1870649cd0dSSimon Glass 
1880649cd0dSSimon Glass 	if (priv.regex) {
1890649cd0dSSimon Glass 		strcpy(attributes, priv.attributes);
1900649cd0dSSimon Glass 		free(priv.attributes);
1910649cd0dSSimon Glass 		free(priv.regex);
1920649cd0dSSimon Glass 		/* success */
1930649cd0dSSimon Glass 		return 0;
1940649cd0dSSimon Glass 	}
1950649cd0dSSimon Glass 	return -ENOENT; /* not found in list */
1960649cd0dSSimon Glass }
1970649cd0dSSimon Glass #else
1980649cd0dSSimon Glass 
1990649cd0dSSimon Glass /*
2000649cd0dSSimon Glass  * Search for the last exactly matching name in an attribute list
2010649cd0dSSimon Glass  */
reverse_name_search(const char * searched,const char * search_for,const char ** result)2020649cd0dSSimon Glass static int reverse_name_search(const char *searched, const char *search_for,
2030649cd0dSSimon Glass 	const char **result)
2040649cd0dSSimon Glass {
2050649cd0dSSimon Glass 	int result_size = 0;
2060649cd0dSSimon Glass 	const char *cur_searched = searched;
2070649cd0dSSimon Glass 
2080649cd0dSSimon Glass 	if (result)
2090649cd0dSSimon Glass 		*result = NULL;
2100649cd0dSSimon Glass 
2110649cd0dSSimon Glass 	if (*search_for == '\0') {
2120649cd0dSSimon Glass 		if (result)
2130649cd0dSSimon Glass 			*result = searched;
2140649cd0dSSimon Glass 		return strlen(searched);
2150649cd0dSSimon Glass 	}
2160649cd0dSSimon Glass 
2170649cd0dSSimon Glass 	for (;;) {
2180649cd0dSSimon Glass 		const char *match = strstr(cur_searched, search_for);
2190649cd0dSSimon Glass 		const char *prevch;
2200649cd0dSSimon Glass 		const char *nextch;
2210649cd0dSSimon Glass 
2220649cd0dSSimon Glass 		/* Stop looking if no new match is found */
2230649cd0dSSimon Glass 		if (match == NULL)
2240649cd0dSSimon Glass 			break;
2250649cd0dSSimon Glass 
2260649cd0dSSimon Glass 		prevch = match - 1;
2270649cd0dSSimon Glass 		nextch = match + strlen(search_for);
2280649cd0dSSimon Glass 
2290649cd0dSSimon Glass 		/* Skip spaces */
2300649cd0dSSimon Glass 		while (*prevch == ' ' && prevch >= searched)
2310649cd0dSSimon Glass 			prevch--;
2320649cd0dSSimon Glass 		while (*nextch == ' ')
2330649cd0dSSimon Glass 			nextch++;
2340649cd0dSSimon Glass 
2350649cd0dSSimon Glass 		/* Start looking past the current match so last is found */
2360649cd0dSSimon Glass 		cur_searched = match + 1;
2370649cd0dSSimon Glass 		/* Check for an exact match */
2380649cd0dSSimon Glass 		if (match != searched &&
2390649cd0dSSimon Glass 		    *prevch != ENV_ATTR_LIST_DELIM &&
2400649cd0dSSimon Glass 		    prevch != searched - 1)
2410649cd0dSSimon Glass 			continue;
2420649cd0dSSimon Glass 		if (*nextch != ENV_ATTR_SEP &&
2430649cd0dSSimon Glass 		    *nextch != ENV_ATTR_LIST_DELIM &&
2440649cd0dSSimon Glass 		    *nextch != '\0')
2450649cd0dSSimon Glass 			continue;
2460649cd0dSSimon Glass 
2470649cd0dSSimon Glass 		if (result)
2480649cd0dSSimon Glass 			*result = match;
2490649cd0dSSimon Glass 		result_size = strlen(search_for);
2500649cd0dSSimon Glass 	}
2510649cd0dSSimon Glass 
2520649cd0dSSimon Glass 	return result_size;
2530649cd0dSSimon Glass }
2540649cd0dSSimon Glass 
2550649cd0dSSimon Glass /*
2560649cd0dSSimon Glass  * Retrieve the attributes string associated with a single name in the list
2570649cd0dSSimon Glass  * There is no protection on attributes being too small for the value
2580649cd0dSSimon Glass  */
env_attr_lookup(const char * attr_list,const char * name,char * attributes)2590649cd0dSSimon Glass int env_attr_lookup(const char *attr_list, const char *name, char *attributes)
2600649cd0dSSimon Glass {
2610649cd0dSSimon Glass 	const char *entry = NULL;
2620649cd0dSSimon Glass 	int entry_len;
2630649cd0dSSimon Glass 
2640649cd0dSSimon Glass 	if (!attributes)
2650649cd0dSSimon Glass 		/* bad parameter */
2660649cd0dSSimon Glass 		return -EINVAL;
2670649cd0dSSimon Glass 	if (!attr_list)
2680649cd0dSSimon Glass 		/* list not found */
2690649cd0dSSimon Glass 		return -EINVAL;
2700649cd0dSSimon Glass 
2710649cd0dSSimon Glass 	entry_len = reverse_name_search(attr_list, name, &entry);
2720649cd0dSSimon Glass 	if (entry != NULL) {
2730649cd0dSSimon Glass 		int len;
2740649cd0dSSimon Glass 
2750649cd0dSSimon Glass 		/* skip the name */
2760649cd0dSSimon Glass 		entry += entry_len;
2770649cd0dSSimon Glass 		/* skip spaces */
2780649cd0dSSimon Glass 		while (*entry == ' ')
2790649cd0dSSimon Glass 			entry++;
2800649cd0dSSimon Glass 		if (*entry != ENV_ATTR_SEP)
2810649cd0dSSimon Glass 			len = 0;
2820649cd0dSSimon Glass 		else {
2830649cd0dSSimon Glass 			const char *delim;
2840649cd0dSSimon Glass 			static const char delims[] = {
2850649cd0dSSimon Glass 				ENV_ATTR_LIST_DELIM, ' ', '\0'};
2860649cd0dSSimon Glass 
2870649cd0dSSimon Glass 			/* skip the attr sep */
2880649cd0dSSimon Glass 			entry += 1;
2890649cd0dSSimon Glass 			/* skip spaces */
2900649cd0dSSimon Glass 			while (*entry == ' ')
2910649cd0dSSimon Glass 				entry++;
2920649cd0dSSimon Glass 
2930649cd0dSSimon Glass 			delim = strpbrk(entry, delims);
2940649cd0dSSimon Glass 			if (delim == NULL)
2950649cd0dSSimon Glass 				len = strlen(entry);
2960649cd0dSSimon Glass 			else
2970649cd0dSSimon Glass 				len = delim - entry;
2980649cd0dSSimon Glass 			memcpy(attributes, entry, len);
2990649cd0dSSimon Glass 		}
3000649cd0dSSimon Glass 		attributes[len] = '\0';
3010649cd0dSSimon Glass 
3020649cd0dSSimon Glass 		/* success */
3030649cd0dSSimon Glass 		return 0;
3040649cd0dSSimon Glass 	}
3050649cd0dSSimon Glass 
3060649cd0dSSimon Glass 	/* not found in list */
3070649cd0dSSimon Glass 	return -ENOENT;
3080649cd0dSSimon Glass }
3090649cd0dSSimon Glass #endif
310