xref: /dpdk/lib/kvargs/rte_kvargs.c (revision b19f366c)
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. */
208 const char *
209 rte_kvargs_get(const struct rte_kvargs *kvlist, const char *key)
210 {
211 	unsigned int i;
212 
213 	if (kvlist == NULL || key == NULL)
214 		return NULL;
215 	for (i = 0; i < kvlist->count; ++i) {
216 		if (strcmp(kvlist->pairs[i].key, key) == 0)
217 			return kvlist->pairs[i].value;
218 	}
219 	return NULL;
220 }
221 
222 /*
223  * Parse the arguments "key=value,key=value,..." string and return
224  * an allocated structure that contains a key/value list. Also
225  * check if only valid keys were used.
226  */
227 struct rte_kvargs *
228 rte_kvargs_parse(const char *args, const char * const valid_keys[])
229 {
230 	struct rte_kvargs *kvlist;
231 
232 	kvlist = malloc(sizeof(*kvlist));
233 	if (kvlist == NULL)
234 		return NULL;
235 	memset(kvlist, 0, sizeof(*kvlist));
236 
237 	if (rte_kvargs_tokenize(kvlist, args) < 0) {
238 		rte_kvargs_free(kvlist);
239 		return NULL;
240 	}
241 
242 	if (valid_keys != NULL && check_for_valid_keys(kvlist, valid_keys) < 0) {
243 		rte_kvargs_free(kvlist);
244 		return NULL;
245 	}
246 
247 	return kvlist;
248 }
249 
250 struct rte_kvargs *
251 rte_kvargs_parse_delim(const char *args, const char * const valid_keys[],
252 		       const char *valid_ends)
253 {
254 	struct rte_kvargs *kvlist = NULL;
255 	char *copy;
256 	size_t len;
257 
258 	if (valid_ends == NULL)
259 		return rte_kvargs_parse(args, valid_keys);
260 
261 	copy = strdup(args);
262 	if (copy == NULL)
263 		return NULL;
264 
265 	len = strcspn(copy, valid_ends);
266 	copy[len] = '\0';
267 
268 	kvlist = rte_kvargs_parse(copy, valid_keys);
269 
270 	free(copy);
271 	return kvlist;
272 }
273 
274 int
275 rte_kvargs_strcmp(const char *key __rte_unused,
276 		  const char *value, void *opaque)
277 {
278 	const char *str = opaque;
279 
280 	return -abs(strcmp(str, value));
281 }
282