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