kvargs: new function to get from key and value
[dpdk.git] / lib / kvargs / rte_kvargs.c
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
287 int
288 rte_kvargs_strcmp(const char *key __rte_unused,
289                   const char *value, void *opaque)
290 {
291         const char *str = opaque;
292
293         return -abs(strcmp(str, value));
294 }