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
rte_kvargs_tokenize(struct rte_kvargs * kvlist,const char * params)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
is_valid_key(const char * const valid[],const char * key_match)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
check_for_valid_keys(struct rte_kvargs * kvlist,const char * const valid[])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
rte_kvargs_count(const struct rte_kvargs * kvlist,const char * key_match)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
rte_kvargs_process(const struct rte_kvargs * kvlist,const char * key_match,arg_handler_t handler,void * opaque_arg)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
rte_kvargs_free(struct rte_kvargs * kvlist)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 *
rte_kvargs_get_with_value(const struct rte_kvargs * kvlist,const char * key,const char * value)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 *
rte_kvargs_get(const struct rte_kvargs * kvlist,const char * key)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 *
rte_kvargs_parse(const char * args,const char * const valid_keys[])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 *
rte_kvargs_parse_delim(const char * args,const char * const valid_keys[],const char * valid_ends)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