acad9ed5396343eccea736d55d1fc8df10e839b2
[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 *state,
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)state;
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         .complete = ec_complete_unknown,
181         .size = sizeof(struct ec_node_int_uint),
182         .init_priv = ec_node_uint_init_priv,
183 };
184
185 EC_NODE_TYPE_REGISTER(ec_node_int_type);
186
187 struct ec_node *ec_node_int(const char *id, int64_t min,
188         int64_t max, unsigned int base)
189 {
190         struct ec_config *config = NULL;
191         struct ec_node *node = NULL;
192         int ret;
193
194         node = ec_node_from_type(&ec_node_int_type, id);
195         if (node == NULL)
196                 return NULL;
197
198         config = ec_config_dict();
199         if (config == NULL)
200                 goto fail;
201
202         ret = ec_config_dict_set(config, "min", ec_config_i64(min));
203         if (ret < 0)
204                 goto fail;
205         ret = ec_config_dict_set(config, "max", ec_config_i64(max));
206         if (ret < 0)
207                 goto fail;
208         ret = ec_config_dict_set(config, "base", ec_config_u64(base));
209         if (ret < 0)
210                 goto fail;
211
212         ret = ec_node_set_config(node, config);
213         config = NULL;
214         if (ret < 0)
215                 goto fail;
216
217         return node;
218
219 fail:
220         ec_config_free(config);
221         ec_node_free(node);
222         return NULL;
223 }
224
225 static const struct ec_config_schema ec_node_uint_schema[] = {
226         {
227                 .key = "min",
228                 .desc = "The minimum valid value (included).",
229                 .type = EC_CONFIG_TYPE_UINT64,
230         },
231         {
232                 .key = "max",
233                 .desc = "The maximum valid value (included).",
234                 .type = EC_CONFIG_TYPE_UINT64,
235         },
236         {
237                 .key = "base",
238                 .desc = "The base to use. If unset or 0, try to guess.",
239                 .type = EC_CONFIG_TYPE_UINT64,
240         },
241         {
242                 .type = EC_CONFIG_TYPE_NONE,
243         },
244 };
245
246 static int ec_node_uint_set_config(struct ec_node *node,
247                                 const struct ec_config *config)
248 {
249         struct ec_node_int_uint *priv = ec_node_priv(node);
250         const struct ec_config *min_value = NULL;
251         const struct ec_config *max_value = NULL;
252         const struct ec_config *base_value = NULL;
253         char *s = NULL;
254
255         min_value = ec_config_dict_get(config, "min");
256         max_value = ec_config_dict_get(config, "max");
257         base_value = ec_config_dict_get(config, "base");
258
259         if (min_value && max_value && min_value->u64 > max_value->u64) {
260                 errno = EINVAL;
261                 goto fail;
262         }
263
264         if (min_value != NULL) {
265                 priv->check_min = true;
266                 priv->min = min_value->u64;
267         } else {
268                 priv->check_min = false;
269         }
270         if (max_value != NULL) {
271                 priv->check_max = true;
272                 priv->max = max_value->u64;
273         } else {
274                 priv->check_min = false;
275         }
276         if (base_value != NULL)
277                 priv->base = base_value->u64;
278         else
279                 priv->base = 0;
280
281         return 0;
282
283 fail:
284         ec_free(s);
285         return -1;
286 }
287
288 static struct ec_node_type ec_node_uint_type = {
289         .name = "uint",
290         .schema = ec_node_uint_schema,
291         .set_config = ec_node_uint_set_config,
292         .parse = ec_node_int_uint_parse,
293         .complete = ec_complete_unknown,
294         .size = sizeof(struct ec_node_int_uint),
295 };
296
297 EC_NODE_TYPE_REGISTER(ec_node_uint_type);
298
299 struct ec_node *ec_node_uint(const char *id, uint64_t min,
300         uint64_t max, unsigned int base)
301 {
302         struct ec_config *config = NULL;
303         struct ec_node *node = NULL;
304         int ret;
305
306         node = ec_node_from_type(&ec_node_uint_type, id);
307         if (node == NULL)
308                 return NULL;
309
310         config = ec_config_dict();
311         if (config == NULL)
312                 goto fail;
313
314         ret = ec_config_dict_set(config, "min", ec_config_u64(min));
315         if (ret < 0)
316                 goto fail;
317         ret = ec_config_dict_set(config, "max", ec_config_u64(max));
318         if (ret < 0)
319                 goto fail;
320         ret = ec_config_dict_set(config, "base", ec_config_u64(base));
321         if (ret < 0)
322                 goto fail;
323
324         ret = ec_node_set_config(node, config);
325         config = NULL;
326         if (ret < 0)
327                 goto fail;
328
329         return node;
330
331 fail:
332         ec_config_free(config);
333         ec_node_free(node);
334         return NULL;
335 }
336
337 int ec_node_int_getval(const struct ec_node *node, const char *str,
338                         int64_t *result)
339 {
340         struct ec_node_int_uint *priv = ec_node_priv(node);
341         int ret;
342
343         ret = ec_node_check_type(node, &ec_node_int_type);
344         if (ret < 0)
345                 return ret;
346
347         if (parse_llint(priv, str, result) < 0)
348                 return -1;
349
350         return 0;
351 }
352
353 int ec_node_uint_getval(const struct ec_node *node, const char *str,
354                         uint64_t *result)
355 {
356         struct ec_node_int_uint *priv = ec_node_priv(node);
357         int ret;
358
359         ret = ec_node_check_type(node, &ec_node_uint_type);
360         if (ret < 0)
361                 return ret;
362
363         if (parse_ullint(priv, str, result) < 0)
364                 return -1;
365
366         return 0;
367 }
368
369 /* LCOV_EXCL_START */
370 static int ec_node_int_testcase(void)
371 {
372         struct ec_pnode *p;
373         struct ec_node *node;
374         const char *s;
375         int testres = 0;
376         uint64_t u64;
377         int64_t i64;
378
379         node = ec_node_uint(EC_NO_ID, 1, 256, 0);
380         if (node == NULL) {
381                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
382                 return -1;
383         }
384         testres |= EC_TEST_CHECK_PARSE(node, -1, "");
385         testres |= EC_TEST_CHECK_PARSE(node, -1, "0");
386         testres |= EC_TEST_CHECK_PARSE(node, 1, "1");
387         testres |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo");
388         testres |= EC_TEST_CHECK_PARSE(node, 1, "0x100");
389         testres |= EC_TEST_CHECK_PARSE(node, 1, " 1");
390         testres |= EC_TEST_CHECK_PARSE(node, -1, "-1");
391         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x101");
392         testres |= EC_TEST_CHECK_PARSE(node, -1, "zzz");
393         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x100000000000000000");
394         testres |= EC_TEST_CHECK_PARSE(node, -1, "4r");
395
396         p = ec_parse(node, "1");
397         s = ec_strvec_val(ec_pnode_strvec(p), 0);
398         testres |= EC_TEST_CHECK(s != NULL &&
399                 ec_node_uint_getval(node, s, &u64) == 0 &&
400                 u64 == 1, "bad integer value");
401         ec_pnode_free(p);
402
403         p = ec_parse(node, "10");
404         s = ec_strvec_val(ec_pnode_strvec(p), 0);
405         testres |= EC_TEST_CHECK(s != NULL &&
406                 ec_node_uint_getval(node, s, &u64) == 0 &&
407                 u64 == 10, "bad integer value");
408         ec_pnode_free(p);
409         ec_node_free(node);
410
411         node = ec_node_int(EC_NO_ID, -1, LLONG_MAX, 16);
412         if (node == NULL) {
413                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
414                 return -1;
415         }
416         testres |= EC_TEST_CHECK_PARSE(node, 1, "0");
417         testres |= EC_TEST_CHECK_PARSE(node, 1, "-1");
418         testres |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff");
419         testres |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff");
420         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x8000000000000000");
421         testres |= EC_TEST_CHECK_PARSE(node, -1, "-2");
422         testres |= EC_TEST_CHECK_PARSE(node, -1, "zzz");
423         testres |= EC_TEST_CHECK_PARSE(node, -1, "4r");
424
425         p = ec_parse(node, "10");
426         s = ec_strvec_val(ec_pnode_strvec(p), 0);
427         testres |= EC_TEST_CHECK(s != NULL &&
428                 ec_node_int_getval(node, s, &i64) == 0 &&
429                 i64 == 16, "bad integer value");
430         ec_pnode_free(p);
431         ec_node_free(node);
432
433         node = ec_node_int(EC_NO_ID, LLONG_MIN, 0, 10);
434         if (node == NULL) {
435                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
436                 return -1;
437         }
438         testres |= EC_TEST_CHECK_PARSE(node, 1, "0");
439         testres |= EC_TEST_CHECK_PARSE(node, 1, "-1");
440         testres |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808");
441         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x0");
442         testres |= EC_TEST_CHECK_PARSE(node, -1, "1");
443         ec_node_free(node);
444
445         /* test completion */
446         node = ec_node_int(EC_NO_ID, 0, 10, 0);
447         if (node == NULL) {
448                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
449                 return -1;
450         }
451         testres |= EC_TEST_CHECK_COMPLETE(node,
452                 "", EC_VA_END,
453                 EC_VA_END);
454         testres |= EC_TEST_CHECK_COMPLETE(node,
455                 "x", EC_VA_END,
456                 EC_VA_END);
457         testres |= EC_TEST_CHECK_COMPLETE(node,
458                 "1", EC_VA_END,
459                 EC_VA_END);
460         ec_node_free(node);
461
462         return testres;
463 }
464 /* LCOV_EXCL_STOP */
465
466 static struct ec_test ec_node_int_test = {
467         .name = "node_int",
468         .test = ec_node_int_testcase,
469 };
470
471 EC_TEST_REGISTER(ec_node_int_test);