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