xref: /dpdk/lib/kvargs/rte_kvargs.c (revision 97b914f4e715565d53d38ac6e04815b9be5e58a9)
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 /*
171  * For each matching key, call the given handler function.
172  */
173 int
174 rte_kvargs_process(const struct rte_kvargs *kvlist,
175 		const char *key_match,
176 		arg_handler_t handler,
177 		void *opaque_arg)
178 {
179 	const struct rte_kvargs_pair *pair;
180 	unsigned i;
181 
182 	if (kvlist == NULL)
183 		return 0;
184 
185 	for (i = 0; i < kvlist->count; i++) {
186 		pair = &kvlist->pairs[i];
187 		if (key_match == NULL || strcmp(pair->key, key_match) == 0) {
188 			if ((*handler)(pair->key, pair->value, opaque_arg) < 0)
189 				return -1;
190 		}
191 	}
192 	return 0;
193 }
194 
195 /* free the rte_kvargs structure */
196 void
197 rte_kvargs_free(struct rte_kvargs *kvlist)
198 {
199 	if (!kvlist)
200 		return;
201 
202 	free(kvlist->str);
203 	free(kvlist);
204 }
205 
206 /* Lookup a value in an rte_kvargs list by its key and value. */
207 const char *
208 rte_kvargs_get_with_value(const struct rte_kvargs *kvlist, const char *key,
209 			  const char *value)
210 {
211 	unsigned int i;
212 
213 	if (kvlist == NULL)
214 		return NULL;
215 	for (i = 0; i < kvlist->count; ++i) {
216 		if (key != NULL && strcmp(kvlist->pairs[i].key, key) != 0)
217 			continue;
218 		if (value != NULL && strcmp(kvlist->pairs[i].value, value) != 0)
219 			continue;
220 		return kvlist->pairs[i].value;
221 	}
222 	return NULL;
223 }
224 
225 /* Lookup a value in an rte_kvargs list by its key. */
226 const char *
227 rte_kvargs_get(const struct rte_kvargs *kvlist, const char *key)
228 {
229 	if (kvlist == NULL || key == NULL)
230 		return NULL;
231 	return rte_kvargs_get_with_value(kvlist, key, NULL);
232 }
233 
234 /*
235  * Parse the arguments "key=value,key=value,..." string and return
236  * an allocated structure that contains a key/value list. Also
237  * check if only valid keys were used.
238  */
239 struct rte_kvargs *
240 rte_kvargs_parse(const char *args, const char * const valid_keys[])
241 {
242 	struct rte_kvargs *kvlist;
243 
244 	kvlist = malloc(sizeof(*kvlist));
245 	if (kvlist == NULL)
246 		return NULL;
247 	memset(kvlist, 0, sizeof(*kvlist));
248 
249 	if (rte_kvargs_tokenize(kvlist, args) < 0) {
250 		rte_kvargs_free(kvlist);
251 		return NULL;
252 	}
253 
254 	if (valid_keys != NULL && check_for_valid_keys(kvlist, valid_keys) < 0) {
255 		rte_kvargs_free(kvlist);
256 		return NULL;
257 	}
258 
259 	return kvlist;
260 }
261 
262 struct rte_kvargs *
263 rte_kvargs_parse_delim(const char *args, const char * const valid_keys[],
264 		       const char *valid_ends)
265 {
266 	struct rte_kvargs *kvlist = NULL;
267 	char *copy;
268 	size_t len;
269 
270 	if (valid_ends == NULL)
271 		return rte_kvargs_parse(args, valid_keys);
272 
273 	copy = strdup(args);
274 	if (copy == NULL)
275 		return NULL;
276 
277 	len = strcspn(copy, valid_ends);
278 	copy[len] = '\0';
279 
280 	kvlist = rte_kvargs_parse(copy, valid_keys);
281 
282 	free(copy);
283 	return kvlist;
284 }
285