1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2012
4  * Joe Hershberger, National Instruments, joe.hershberger@ni.com
5  */
6 
7 #ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <linux/linux_string.h>
11 #else
12 #include <common.h>
13 #include <slre.h>
14 #endif
15 
16 #include <env_attr.h>
17 #include <errno.h>
18 #include <linux/string.h>
19 #include <malloc.h>
20 
21 /*
22  * Iterate through the whole list calling the callback for each found element.
23  * "attr_list" takes the form:
24  *	attributes = [^,:\s]*
25  *	entry = name[:attributes]
26  *	list = entry[,list]
27  */
env_attr_walk(const char * attr_list,int (* callback)(const char * name,const char * attributes,void * priv),void * priv)28 int env_attr_walk(const char *attr_list,
29 	int (*callback)(const char *name, const char *attributes, void *priv),
30 	void *priv)
31 {
32 	const char *entry, *entry_end;
33 	char *name, *attributes;
34 
35 	if (!attr_list)
36 		/* list not found */
37 		return 1;
38 
39 	entry = attr_list;
40 	do {
41 		char *entry_cpy = NULL;
42 
43 		entry_end = strchr(entry, ENV_ATTR_LIST_DELIM);
44 		/* check if this is the last entry in the list */
45 		if (entry_end == NULL) {
46 			int entry_len = strlen(entry);
47 
48 			if (entry_len) {
49 				/*
50 				 * allocate memory to copy the entry into since
51 				 * we will need to inject '\0' chars and squash
52 				 * white-space before calling the callback
53 				 */
54 				entry_cpy = malloc(entry_len + 1);
55 				if (entry_cpy)
56 					/* copy the rest of the list */
57 					strcpy(entry_cpy, entry);
58 				else
59 					return -ENOMEM;
60 			}
61 		} else {
62 			int entry_len = entry_end - entry;
63 
64 			if (entry_len) {
65 				/*
66 				 * allocate memory to copy the entry into since
67 				 * we will need to inject '\0' chars and squash
68 				 * white-space before calling the callback
69 				 */
70 				entry_cpy = malloc(entry_len + 1);
71 				if (entry_cpy) {
72 					/* copy just this entry and null term */
73 					strncpy(entry_cpy, entry, entry_len);
74 					entry_cpy[entry_len] = '\0';
75 				} else
76 					return -ENOMEM;
77 			}
78 		}
79 
80 		/* check if there is anything to process (e.g. not ",,,") */
81 		if (entry_cpy != NULL) {
82 			attributes = strchr(entry_cpy, ENV_ATTR_SEP);
83 			/* check if there is a ':' */
84 			if (attributes != NULL) {
85 				/* replace the ':' with '\0' to term name */
86 				*attributes++ = '\0';
87 				/* remove white-space from attributes */
88 				attributes = strim(attributes);
89 			}
90 			/* remove white-space from name */
91 			name = strim(entry_cpy);
92 
93 			/* only call the callback if there is a name */
94 			if (strlen(name) != 0) {
95 				int retval = 0;
96 
97 				retval = callback(name, attributes, priv);
98 				if (retval) {
99 					free(entry_cpy);
100 					return retval;
101 				}
102 			}
103 		}
104 
105 		free(entry_cpy);
106 		entry = entry_end + 1;
107 	} while (entry_end != NULL);
108 
109 	return 0;
110 }
111 
112 #if defined(CONFIG_REGEX)
113 struct regex_callback_priv {
114 	const char *searched_for;
115 	char *regex;
116 	char *attributes;
117 };
118 
regex_callback(const char * name,const char * attributes,void * priv)119 static int regex_callback(const char *name, const char *attributes, void *priv)
120 {
121 	int retval = 0;
122 	struct regex_callback_priv *cbp = (struct regex_callback_priv *)priv;
123 	struct slre slre;
124 	char regex[strlen(name) + 3];
125 
126 	/* Require the whole string to be described by the regex */
127 	sprintf(regex, "^%s$", name);
128 	if (slre_compile(&slre, regex)) {
129 		struct cap caps[slre.num_caps + 2];
130 
131 		if (slre_match(&slre, cbp->searched_for,
132 			       strlen(cbp->searched_for), caps)) {
133 			free(cbp->regex);
134 			if (!attributes) {
135 				retval = -EINVAL;
136 				goto done;
137 			}
138 			cbp->regex = malloc(strlen(regex) + 1);
139 			if (cbp->regex) {
140 				strcpy(cbp->regex, regex);
141 			} else {
142 				retval = -ENOMEM;
143 				goto done;
144 			}
145 
146 			free(cbp->attributes);
147 			cbp->attributes = malloc(strlen(attributes) + 1);
148 			if (cbp->attributes) {
149 				strcpy(cbp->attributes, attributes);
150 			} else {
151 				retval = -ENOMEM;
152 				free(cbp->regex);
153 				cbp->regex = NULL;
154 				goto done;
155 			}
156 		}
157 	} else {
158 		printf("Error compiling regex: %s\n", slre.err_str);
159 		retval = -EINVAL;
160 	}
161 done:
162 	return retval;
163 }
164 
165 /*
166  * Retrieve the attributes string associated with a single name in the list
167  * There is no protection on attributes being too small for the value
168  */
env_attr_lookup(const char * attr_list,const char * name,char * attributes)169 int env_attr_lookup(const char *attr_list, const char *name, char *attributes)
170 {
171 	if (!attributes)
172 		/* bad parameter */
173 		return -EINVAL;
174 	if (!attr_list)
175 		/* list not found */
176 		return -EINVAL;
177 
178 	struct regex_callback_priv priv;
179 	int retval;
180 
181 	priv.searched_for = name;
182 	priv.regex = NULL;
183 	priv.attributes = NULL;
184 	retval = env_attr_walk(attr_list, regex_callback, &priv);
185 	if (retval)
186 		return retval; /* error */
187 
188 	if (priv.regex) {
189 		strcpy(attributes, priv.attributes);
190 		free(priv.attributes);
191 		free(priv.regex);
192 		/* success */
193 		return 0;
194 	}
195 	return -ENOENT; /* not found in list */
196 }
197 #else
198 
199 /*
200  * Search for the last exactly matching name in an attribute list
201  */
reverse_name_search(const char * searched,const char * search_for,const char ** result)202 static int reverse_name_search(const char *searched, const char *search_for,
203 	const char **result)
204 {
205 	int result_size = 0;
206 	const char *cur_searched = searched;
207 
208 	if (result)
209 		*result = NULL;
210 
211 	if (*search_for == '\0') {
212 		if (result)
213 			*result = searched;
214 		return strlen(searched);
215 	}
216 
217 	for (;;) {
218 		const char *match = strstr(cur_searched, search_for);
219 		const char *prevch;
220 		const char *nextch;
221 
222 		/* Stop looking if no new match is found */
223 		if (match == NULL)
224 			break;
225 
226 		prevch = match - 1;
227 		nextch = match + strlen(search_for);
228 
229 		/* Skip spaces */
230 		while (*prevch == ' ' && prevch >= searched)
231 			prevch--;
232 		while (*nextch == ' ')
233 			nextch++;
234 
235 		/* Start looking past the current match so last is found */
236 		cur_searched = match + 1;
237 		/* Check for an exact match */
238 		if (match != searched &&
239 		    *prevch != ENV_ATTR_LIST_DELIM &&
240 		    prevch != searched - 1)
241 			continue;
242 		if (*nextch != ENV_ATTR_SEP &&
243 		    *nextch != ENV_ATTR_LIST_DELIM &&
244 		    *nextch != '\0')
245 			continue;
246 
247 		if (result)
248 			*result = match;
249 		result_size = strlen(search_for);
250 	}
251 
252 	return result_size;
253 }
254 
255 /*
256  * Retrieve the attributes string associated with a single name in the list
257  * There is no protection on attributes being too small for the value
258  */
env_attr_lookup(const char * attr_list,const char * name,char * attributes)259 int env_attr_lookup(const char *attr_list, const char *name, char *attributes)
260 {
261 	const char *entry = NULL;
262 	int entry_len;
263 
264 	if (!attributes)
265 		/* bad parameter */
266 		return -EINVAL;
267 	if (!attr_list)
268 		/* list not found */
269 		return -EINVAL;
270 
271 	entry_len = reverse_name_search(attr_list, name, &entry);
272 	if (entry != NULL) {
273 		int len;
274 
275 		/* skip the name */
276 		entry += entry_len;
277 		/* skip spaces */
278 		while (*entry == ' ')
279 			entry++;
280 		if (*entry != ENV_ATTR_SEP)
281 			len = 0;
282 		else {
283 			const char *delim;
284 			static const char delims[] = {
285 				ENV_ATTR_LIST_DELIM, ' ', '\0'};
286 
287 			/* skip the attr sep */
288 			entry += 1;
289 			/* skip spaces */
290 			while (*entry == ' ')
291 				entry++;
292 
293 			delim = strpbrk(entry, delims);
294 			if (delim == NULL)
295 				len = strlen(entry);
296 			else
297 				len = delim - entry;
298 			memcpy(attributes, entry, len);
299 		}
300 		attributes[len] = '\0';
301 
302 		/* success */
303 		return 0;
304 	}
305 
306 	/* not found in list */
307 	return -ENOENT;
308 }
309 #endif
310