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