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