xref: /dpdk/lib/kvargs/rte_kvargs.c (revision de89988365a7ca4087dd451c675320c993910332)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2013 Intel Corporation.
3  * Copyright(c) 2014 6WIND S.A.
4  */
5 
6 #include <string.h>
7 #include <stdlib.h>
8 #include <stdbool.h>
9 
10 #include <rte_os_shim.h>
11 
12 #include "rte_kvargs.h"
13 
14 /*
15  * Receive a string with a list of arguments following the pattern
16  * key=value,key=value,... and insert them into the list.
17  * Params string will be copied to be modified.
18  * list "[]" and list element splitter ",", "-" is treated as value.
19  * Supported examples:
20  *   k1=v1,k2=v2
21  *   k1
22  *   k1=x[0-1]y[1,3-5,9]z
23  */
24 static int
25 rte_kvargs_tokenize(struct rte_kvargs *kvlist, const char *params)
26 {
27 	char *str, *start;
28 	bool in_list = false, end_key = false, end_value = false;
29 	bool save = false, end_pair = false;
30 
31 	/* Copy the const char *params to a modifiable string
32 	 * to pass to rte_strsplit
33 	 */
34 	kvlist->str = strdup(params);
35 	if (kvlist->str == NULL)
36 		return -1;
37 
38 	/* browse each key/value pair and add it in kvlist */
39 	str = kvlist->str;
40 	start = str; /* start of current key or value */
41 	while (1) {
42 		switch (*str) {
43 		case '=': /* End of key. */
44 			end_key = true;
45 			save = true;
46 			break;
47 		case ',':
48 			/* End of value, skip comma in middle of range */
49 			if (!in_list) {
50 				if (end_key)
51 					end_value = true;
52 				else
53 					end_key = true;
54 				save = true;
55 				end_pair = true;
56 			}
57 			break;
58 		case '[': /* Start of list. */
59 			in_list = true;
60 			break;
61 		case ']': /* End of list.  */
62 			if (in_list)
63 				in_list = false;
64 			break;
65 		case '\0': /* End of string */
66 			if (end_key)
67 				end_value = true;
68 			else
69 				end_key = true;
70 			save = true;
71 			end_pair = true;
72 			break;
73 		default:
74 			break;
75 		}
76 
77 		if (!save) {
78 			/* Continue if not end of key or value. */
79 			str++;
80 			continue;
81 		}
82 
83 		if (kvlist->count >= RTE_KVARGS_MAX)
84 			return -1;
85 
86 		if (end_value)
87 			/* Value parsed */
88 			kvlist->pairs[kvlist->count].value = start;
89 		else if (end_key)
90 			/* Key parsed. */
91 			kvlist->pairs[kvlist->count].key = start;
92 
93 		if (end_pair) {
94 			if (end_value || str != start)
95 				/* Ignore empty pair. */
96 				kvlist->count++;
97 			end_key = false;
98 			end_value = false;
99 			end_pair = false;
100 		}
101 
102 		if (*str == '\0') /* End of string. */
103 			break;
104 		*str = '\0';
105 		str++;
106 		start = str;
107 		save = false;
108 	}
109 
110 	return 0;
111 }
112 
113 /*
114  * Determine whether a key is valid or not by looking
115  * into a list of valid keys.
116  */
117 static int
118 is_valid_key(const char * const valid[], const char *key_match)
119 {
120 	const char * const *valid_ptr;
121 
122 	for (valid_ptr = valid; *valid_ptr != NULL; valid_ptr++) {
123 		if (strcmp(key_match, *valid_ptr) == 0)
124 			return 1;
125 	}
126 	return 0;
127 }
128 
129 /*
130  * Determine whether all keys are valid or not by looking
131  * into a list of valid keys.
132  */
133 static int
134 check_for_valid_keys(struct rte_kvargs *kvlist,
135 		const char * const valid[])
136 {
137 	unsigned i, ret;
138 	struct rte_kvargs_pair *pair;
139 
140 	for (i = 0; i < kvlist->count; i++) {
141 		pair = &kvlist->pairs[i];
142 		ret = is_valid_key(valid, pair->key);
143 		if (!ret)
144 			return -1;
145 	}
146 	return 0;
147 }
148 
149 /*
150  * Return the number of times a given arg_name exists in the key/value list.
151  * E.g. given a list = { rx = 0, rx = 1, tx = 2 } the number of args for
152  * arg "rx" will be 2.
153  */
154 unsigned
155 rte_kvargs_count(const struct rte_kvargs *kvlist, const char *key_match)
156 {
157 	const struct rte_kvargs_pair *pair;
158 	unsigned i, ret;
159 
160 	ret = 0;
161 	for (i = 0; i < kvlist->count; i++) {
162 		pair = &kvlist->pairs[i];
163 		if (key_match == NULL || strcmp(pair->key, key_match) == 0)
164 			ret++;
165 	}
166 
167 	return ret;
168 }
169 
170 static int
171 kvargs_process_common(const struct rte_kvargs *kvlist, const char *key_match,
172 		      arg_handler_t handler, void *opaque_arg, bool support_only_key)
173 {
174 	const struct rte_kvargs_pair *pair;
175 	unsigned i;
176 
177 	if (kvlist == NULL)
178 		return -1;
179 
180 	for (i = 0; i < kvlist->count; i++) {
181 		pair = &kvlist->pairs[i];
182 		if (key_match == NULL || strcmp(pair->key, key_match) == 0) {
183 			if (!support_only_key && pair->value == NULL)
184 				return -1;
185 			if ((*handler)(pair->key, pair->value, opaque_arg) < 0)
186 				return -1;
187 		}
188 	}
189 
190 	return 0;
191 }
192 
193 /*
194  * For each matching key in key=value, call the given handler function.
195  */
196 int
197 rte_kvargs_process(const struct rte_kvargs *kvlist, const char *key_match, arg_handler_t handler,
198 		   void *opaque_arg)
199 {
200 	return kvargs_process_common(kvlist, key_match, handler, opaque_arg, false);
201 }
202 
203 /*
204  * For each matching key in key=value or only-key, call the given handler function.
205  */
206 int
207 rte_kvargs_process_opt(const struct rte_kvargs *kvlist, const char *key_match,
208 		       arg_handler_t handler, void *opaque_arg)
209 {
210 	return kvargs_process_common(kvlist, key_match, handler, opaque_arg, true);
211 }
212 
213 /* free the rte_kvargs structure */
214 void
215 rte_kvargs_free(struct rte_kvargs *kvlist)
216 {
217 	if (!kvlist)
218 		return;
219 
220 	free(kvlist->str);
221 	free(kvlist);
222 }
223 
224 /* Lookup a value in an rte_kvargs list by its key and value. */
225 const char *
226 rte_kvargs_get_with_value(const struct rte_kvargs *kvlist, const char *key,
227 			  const char *value)
228 {
229 	unsigned int i;
230 
231 	if (kvlist == NULL)
232 		return NULL;
233 	for (i = 0; i < kvlist->count; ++i) {
234 		if (key != NULL && strcmp(kvlist->pairs[i].key, key) != 0)
235 			continue;
236 		if (value != NULL && strcmp(kvlist->pairs[i].value, value) != 0)
237 			continue;
238 		return kvlist->pairs[i].value;
239 	}
240 	return NULL;
241 }
242 
243 /* Lookup a value in an rte_kvargs list by its key. */
244 const char *
245 rte_kvargs_get(const struct rte_kvargs *kvlist, const char *key)
246 {
247 	if (kvlist == NULL || key == NULL)
248 		return NULL;
249 	return rte_kvargs_get_with_value(kvlist, key, NULL);
250 }
251 
252 /*
253  * Parse the arguments "key=value,key=value,..." string and return
254  * an allocated structure that contains a key/value list. Also
255  * check if only valid keys were used.
256  */
257 struct rte_kvargs *
258 rte_kvargs_parse(const char *args, const char * const valid_keys[])
259 {
260 	struct rte_kvargs *kvlist;
261 
262 	kvlist = malloc(sizeof(*kvlist));
263 	if (kvlist == NULL)
264 		return NULL;
265 	memset(kvlist, 0, sizeof(*kvlist));
266 
267 	if (rte_kvargs_tokenize(kvlist, args) < 0) {
268 		rte_kvargs_free(kvlist);
269 		return NULL;
270 	}
271 
272 	if (valid_keys != NULL && check_for_valid_keys(kvlist, valid_keys) < 0) {
273 		rte_kvargs_free(kvlist);
274 		return NULL;
275 	}
276 
277 	return kvlist;
278 }
279 
280 struct rte_kvargs *
281 rte_kvargs_parse_delim(const char *args, const char * const valid_keys[],
282 		       const char *valid_ends)
283 {
284 	struct rte_kvargs *kvlist = NULL;
285 	char *copy;
286 	size_t len;
287 
288 	if (valid_ends == NULL)
289 		return rte_kvargs_parse(args, valid_keys);
290 
291 	copy = strdup(args);
292 	if (copy == NULL)
293 		return NULL;
294 
295 	len = strcspn(copy, valid_ends);
296 	copy[len] = '\0';
297 
298 	kvlist = rte_kvargs_parse(copy, valid_keys);
299 
300 	free(copy);
301 	return kvlist;
302 }
303