api documentation for ec_parse
[protos/libecoli.git] / src / ecoli_node_int.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #include <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <limits.h>
11 #include <ctype.h>
12 #include <errno.h>
13
14 #include <ecoli_log.h>
15 #include <ecoli_malloc.h>
16 #include <ecoli_strvec.h>
17 #include <ecoli_node.h>
18 #include <ecoli_config.h>
19 #include <ecoli_parse.h>
20 #include <ecoli_complete.h>
21 #include <ecoli_string.h>
22 #include <ecoli_test.h>
23 #include <ecoli_node_int.h>
24
25 EC_LOG_TYPE_REGISTER(node_int);
26
27 /* common to int and uint */
28 struct ec_node_int_uint {
29         bool is_signed;
30         bool check_min;
31         bool check_max;
32         union {
33                 int64_t min;
34                 uint64_t umin;
35         };
36         union {
37                 int64_t max;
38                 uint64_t umax;
39         };
40         unsigned int base;
41 };
42
43 static int parse_llint(struct ec_node_int_uint *priv, const char *str,
44         int64_t *val)
45 {
46         int64_t min, max;
47
48         if (priv->check_min)
49                 min = priv->min;
50         else
51                 min = LLONG_MIN;
52         if (priv->check_max)
53                 max = priv->max;
54         else
55                 max = LLONG_MAX;
56
57         return ec_str_parse_llint(str, priv->base, min, max, val);
58 }
59
60 static int parse_ullint(struct ec_node_int_uint *priv, const char *str,
61                         uint64_t *val)
62 {
63         uint64_t min, max;
64
65         if (priv->check_min)
66                 min = priv->min;
67         else
68                 min = 0;
69         if (priv->check_max)
70                 max = priv->max;
71         else
72                 max = ULLONG_MAX;
73
74         return ec_str_parse_ullint(str, priv->base, min, max, val);
75 }
76
77 static int ec_node_int_uint_parse(const struct ec_node *node,
78                         struct ec_pnode *pstate,
79                         const struct ec_strvec *strvec)
80 {
81         struct ec_node_int_uint *priv = ec_node_priv(node);
82         const char *str;
83         uint64_t u64;
84         int64_t i64;
85
86         (void)pstate;
87
88         if (ec_strvec_len(strvec) == 0)
89                 return EC_PARSE_NOMATCH;
90
91         str = ec_strvec_val(strvec, 0);
92         if (priv->is_signed) {
93                 if (parse_llint(priv, str, &i64) < 0)
94                         return EC_PARSE_NOMATCH;
95         } else {
96                 if (parse_ullint(priv, str, &u64) < 0)
97                         return EC_PARSE_NOMATCH;
98         }
99         return 1;
100 }
101
102 static int
103 ec_node_uint_init_priv(struct ec_node *node)
104 {
105         struct ec_node_int_uint *priv = ec_node_priv(node);
106
107         priv->is_signed = true;
108
109         return 0;
110 }
111
112 static const struct ec_config_schema ec_node_int_schema[] = {
113         {
114                 .key = "min",
115                 .desc = "The minimum valid value (included).",
116                 .type = EC_CONFIG_TYPE_INT64,
117         },
118         {
119                 .key = "max",
120                 .desc = "The maximum valid value (included).",
121                 .type = EC_CONFIG_TYPE_INT64,
122         },
123         {
124                 .key = "base",
125                 .desc = "The base to use. If unset or 0, try to guess.",
126                 .type = EC_CONFIG_TYPE_UINT64,
127         },
128         {
129                 .type = EC_CONFIG_TYPE_NONE,
130         },
131 };
132
133 static int ec_node_int_set_config(struct ec_node *node,
134                                 const struct ec_config *config)
135 {
136         struct ec_node_int_uint *priv = ec_node_priv(node);
137         const struct ec_config *min_value = NULL;
138         const struct ec_config *max_value = NULL;
139         const struct ec_config *base_value = NULL;
140         char *s = NULL;
141
142         min_value = ec_config_dict_get(config, "min");
143         max_value = ec_config_dict_get(config, "max");
144         base_value = ec_config_dict_get(config, "base");
145
146         if (min_value && max_value && min_value->i64 > max_value->i64) {
147                 errno = EINVAL;
148                 goto fail;
149         }
150
151         if (min_value != NULL) {
152                 priv->check_min = true;
153                 priv->min = min_value->i64;
154         } else {
155                 priv->check_min = false;
156         }
157         if (max_value != NULL) {
158                 priv->check_max = true;
159                 priv->max = max_value->i64;
160         } else {
161                 priv->check_min = false;
162         }
163         if (base_value != NULL)
164                 priv->base = base_value->u64;
165         else
166                 priv->base = 0;
167
168         return 0;
169
170 fail:
171         ec_free(s);
172         return -1;
173 }
174
175 static struct ec_node_type ec_node_int_type = {
176         .name = "int",
177         .schema = ec_node_int_schema,
178         .set_config = ec_node_int_set_config,
179         .parse = ec_node_int_uint_parse,
180         .size = sizeof(struct ec_node_int_uint),
181         .init_priv = ec_node_uint_init_priv,
182 };
183
184 EC_NODE_TYPE_REGISTER(ec_node_int_type);
185
186 struct ec_node *ec_node_int(const char *id, int64_t min,
187         int64_t max, unsigned int base)
188 {
189         struct ec_config *config = NULL;
190         struct ec_node *node = NULL;
191         int ret;
192
193         node = ec_node_from_type(&ec_node_int_type, id);
194         if (node == NULL)
195                 return NULL;
196
197         config = ec_config_dict();
198         if (config == NULL)
199                 goto fail;
200
201         ret = ec_config_dict_set(config, "min", ec_config_i64(min));
202         if (ret < 0)
203                 goto fail;
204         ret = ec_config_dict_set(config, "max", ec_config_i64(max));
205         if (ret < 0)
206                 goto fail;
207         ret = ec_config_dict_set(config, "base", ec_config_u64(base));
208         if (ret < 0)
209                 goto fail;
210
211         ret = ec_node_set_config(node, config);
212         config = NULL;
213         if (ret < 0)
214                 goto fail;
215
216         return node;
217
218 fail:
219         ec_config_free(config);
220         ec_node_free(node);
221         return NULL;
222 }
223
224 static const struct ec_config_schema ec_node_uint_schema[] = {
225         {
226                 .key = "min",
227                 .desc = "The minimum valid value (included).",
228                 .type = EC_CONFIG_TYPE_UINT64,
229         },
230         {
231                 .key = "max",
232                 .desc = "The maximum valid value (included).",
233                 .type = EC_CONFIG_TYPE_UINT64,
234         },
235         {
236                 .key = "base",
237                 .desc = "The base to use. If unset or 0, try to guess.",
238                 .type = EC_CONFIG_TYPE_UINT64,
239         },
240         {
241                 .type = EC_CONFIG_TYPE_NONE,
242         },
243 };
244
245 static int ec_node_uint_set_config(struct ec_node *node,
246                                 const struct ec_config *config)
247 {
248         struct ec_node_int_uint *priv = ec_node_priv(node);
249         const struct ec_config *min_value = NULL;
250         const struct ec_config *max_value = NULL;
251         const struct ec_config *base_value = NULL;
252         char *s = NULL;
253
254         min_value = ec_config_dict_get(config, "min");
255         max_value = ec_config_dict_get(config, "max");
256         base_value = ec_config_dict_get(config, "base");
257
258         if (min_value && max_value && min_value->u64 > max_value->u64) {
259                 errno = EINVAL;
260                 goto fail;
261         }
262
263         if (min_value != NULL) {
264                 priv->check_min = true;
265                 priv->min = min_value->u64;
266         } else {
267                 priv->check_min = false;
268         }
269         if (max_value != NULL) {
270                 priv->check_max = true;
271                 priv->max = max_value->u64;
272         } else {
273                 priv->check_min = false;
274         }
275         if (base_value != NULL)
276                 priv->base = base_value->u64;
277         else
278                 priv->base = 0;
279
280         return 0;
281
282 fail:
283         ec_free(s);
284         return -1;
285 }
286
287 static struct ec_node_type ec_node_uint_type = {
288         .name = "uint",
289         .schema = ec_node_uint_schema,
290         .set_config = ec_node_uint_set_config,
291         .parse = ec_node_int_uint_parse,
292         .size = sizeof(struct ec_node_int_uint),
293 };
294
295 EC_NODE_TYPE_REGISTER(ec_node_uint_type);
296
297 struct ec_node *ec_node_uint(const char *id, uint64_t min,
298         uint64_t max, unsigned int base)
299 {
300         struct ec_config *config = NULL;
301         struct ec_node *node = NULL;
302         int ret;
303
304         node = ec_node_from_type(&ec_node_uint_type, id);
305         if (node == NULL)
306                 return NULL;
307
308         config = ec_config_dict();
309         if (config == NULL)
310                 goto fail;
311
312         ret = ec_config_dict_set(config, "min", ec_config_u64(min));
313         if (ret < 0)
314                 goto fail;
315         ret = ec_config_dict_set(config, "max", ec_config_u64(max));
316         if (ret < 0)
317                 goto fail;
318         ret = ec_config_dict_set(config, "base", ec_config_u64(base));
319         if (ret < 0)
320                 goto fail;
321
322         ret = ec_node_set_config(node, config);
323         config = NULL;
324         if (ret < 0)
325                 goto fail;
326
327         return node;
328
329 fail:
330         ec_config_free(config);
331         ec_node_free(node);
332         return NULL;
333 }
334
335 int ec_node_int_getval(const struct ec_node *node, const char *str,
336                         int64_t *result)
337 {
338         struct ec_node_int_uint *priv = ec_node_priv(node);
339         int ret;
340
341         ret = ec_node_check_type(node, &ec_node_int_type);
342         if (ret < 0)
343                 return ret;
344
345         if (parse_llint(priv, str, result) < 0)
346                 return -1;
347
348         return 0;
349 }
350
351 int ec_node_uint_getval(const struct ec_node *node, const char *str,
352                         uint64_t *result)
353 {
354         struct ec_node_int_uint *priv = ec_node_priv(node);
355         int ret;
356
357         ret = ec_node_check_type(node, &ec_node_uint_type);
358         if (ret < 0)
359                 return ret;
360
361         if (parse_ullint(priv, str, result) < 0)
362                 return -1;
363
364         return 0;
365 }
366
367 /* LCOV_EXCL_START */
368 static int ec_node_int_testcase(void)
369 {
370         struct ec_pnode *p;
371         struct ec_node *node;
372         const char *s;
373         int testres = 0;
374         uint64_t u64;
375         int64_t i64;
376
377         node = ec_node_uint(EC_NO_ID, 1, 256, 0);
378         if (node == NULL) {
379                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
380                 return -1;
381         }
382         testres |= EC_TEST_CHECK_PARSE(node, -1, "");
383         testres |= EC_TEST_CHECK_PARSE(node, -1, "0");
384         testres |= EC_TEST_CHECK_PARSE(node, 1, "1");
385         testres |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo");
386         testres |= EC_TEST_CHECK_PARSE(node, 1, "0x100");
387         testres |= EC_TEST_CHECK_PARSE(node, 1, " 1");
388         testres |= EC_TEST_CHECK_PARSE(node, -1, "-1");
389         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x101");
390         testres |= EC_TEST_CHECK_PARSE(node, -1, "zzz");
391         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x100000000000000000");
392         testres |= EC_TEST_CHECK_PARSE(node, -1, "4r");
393
394         p = ec_parse(node, "1");
395         s = ec_strvec_val(ec_pnode_get_strvec(p), 0);
396         testres |= EC_TEST_CHECK(s != NULL &&
397                 ec_node_uint_getval(node, s, &u64) == 0 &&
398                 u64 == 1, "bad integer value");
399         ec_pnode_free(p);
400
401         p = ec_parse(node, "10");
402         s = ec_strvec_val(ec_pnode_get_strvec(p), 0);
403         testres |= EC_TEST_CHECK(s != NULL &&
404                 ec_node_uint_getval(node, s, &u64) == 0 &&
405                 u64 == 10, "bad integer value");
406         ec_pnode_free(p);
407         ec_node_free(node);
408
409         node = ec_node_int(EC_NO_ID, -1, LLONG_MAX, 16);
410         if (node == NULL) {
411                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
412                 return -1;
413         }
414         testres |= EC_TEST_CHECK_PARSE(node, 1, "0");
415         testres |= EC_TEST_CHECK_PARSE(node, 1, "-1");
416         testres |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff");
417         testres |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff");
418         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x8000000000000000");
419         testres |= EC_TEST_CHECK_PARSE(node, -1, "-2");
420         testres |= EC_TEST_CHECK_PARSE(node, -1, "zzz");
421         testres |= EC_TEST_CHECK_PARSE(node, -1, "4r");
422
423         p = ec_parse(node, "10");
424         s = ec_strvec_val(ec_pnode_get_strvec(p), 0);
425         testres |= EC_TEST_CHECK(s != NULL &&
426                 ec_node_int_getval(node, s, &i64) == 0 &&
427                 i64 == 16, "bad integer value");
428         ec_pnode_free(p);
429         ec_node_free(node);
430
431         node = ec_node_int(EC_NO_ID, LLONG_MIN, 0, 10);
432         if (node == NULL) {
433                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
434                 return -1;
435         }
436         testres |= EC_TEST_CHECK_PARSE(node, 1, "0");
437         testres |= EC_TEST_CHECK_PARSE(node, 1, "-1");
438         testres |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808");
439         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x0");
440         testres |= EC_TEST_CHECK_PARSE(node, -1, "1");
441         ec_node_free(node);
442
443         /* test completion */
444         node = ec_node_int(EC_NO_ID, 0, 10, 0);
445         if (node == NULL) {
446                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
447                 return -1;
448         }
449         testres |= EC_TEST_CHECK_COMPLETE(node,
450                 "", EC_VA_END,
451                 EC_VA_END);
452         testres |= EC_TEST_CHECK_COMPLETE(node,
453                 "x", EC_VA_END,
454                 EC_VA_END);
455         testres |= EC_TEST_CHECK_COMPLETE(node,
456                 "1", EC_VA_END,
457                 EC_VA_END);
458         ec_node_free(node);
459
460         return testres;
461 }
462 /* LCOV_EXCL_STOP */
463
464 static struct ec_test ec_node_int_test = {
465         .name = "node_int",
466         .test = ec_node_int_testcase,
467 };
468
469 EC_TEST_REGISTER(ec_node_int_test);