rename default complete as complete unknown
[protos/libecoli.git] / lib / 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_parsed.h>
19 #include <ecoli_completed.h>
20 #include <ecoli_node_int.h>
21 #include <ecoli_test.h>
22
23 EC_LOG_TYPE_REGISTER(node_int);
24
25 /* common to int and uint */
26 struct ec_node_int_uint {
27         struct ec_node gen;
28         bool is_signed;
29         bool check_min;
30         bool check_max;
31         union {
32                 int64_t min;
33                 uint64_t umin;
34         };
35         union {
36                 int64_t max;
37                 uint64_t umax;
38         };
39         unsigned int base;
40 };
41
42 static int parse_llint(struct ec_node_int_uint *node, const char *str,
43         int64_t *val)
44 {
45         char *endptr;
46
47         errno = 0;
48         *val = strtoll(str, &endptr, node->base);
49
50         if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
51                         (errno != 0 && *val == 0))
52                 return -1;
53
54         if (node->check_min && *val < node->min)
55                 return -1;
56
57         if (node->check_max && *val > node->max)
58                 return -1;
59
60         if (*endptr != 0)
61                 return -1;
62
63         return 0;
64 }
65
66 static int parse_ullint(struct ec_node_int_uint *node, const char *str,
67                         uint64_t *val)
68 {
69         char *endptr;
70
71         /* since a negative input is silently converted to a positive
72          * one by strtoull(), first check that it is positive */
73         if (strchr(str, '-'))
74                 return -1;
75
76         errno = 0;
77         *val = strtoull(str, &endptr, node->base);
78
79         if ((errno == ERANGE && *val == ULLONG_MAX) ||
80                         (errno != 0 && *val == 0))
81                 return -1;
82
83         if (node->check_min && *val < node->umin)
84                 return -1;
85
86         if (node->check_max && *val > node->umax)
87                 return -1;
88
89         if (*endptr != 0)
90                 return -1;
91
92         return 0;
93 }
94
95 static int ec_node_int_uint_parse(const struct ec_node *gen_node,
96                         struct ec_parsed *state,
97                         const struct ec_strvec *strvec)
98 {
99         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
100         const char *str;
101         uint64_t u64;
102         int64_t i64;
103
104         (void)state;
105
106         if (ec_strvec_len(strvec) == 0)
107                 return EC_PARSED_NOMATCH;
108
109         str = ec_strvec_val(strvec, 0);
110         if (node->is_signed) {
111                 if (parse_llint(node, str, &i64) < 0)
112                         return EC_PARSED_NOMATCH;
113         } else {
114                 if (parse_ullint(node, str, &u64) < 0)
115                         return EC_PARSED_NOMATCH;
116         }
117         return 1;
118 }
119
120 static int
121 ec_node_uint_init_priv(struct ec_node *gen_node)
122 {
123         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
124
125         node->is_signed = true;
126
127         return 0;
128 }
129
130 static struct ec_node_type ec_node_int_type = {
131         .name = "int",
132         .parse = ec_node_int_uint_parse,
133         .complete = ec_node_complete_unknown,
134         .size = sizeof(struct ec_node_int_uint),
135         .init_priv = ec_node_uint_init_priv,
136 };
137
138 EC_NODE_TYPE_REGISTER(ec_node_int_type);
139
140 struct ec_node *ec_node_int(const char *id, int64_t min,
141         int64_t max, unsigned int base)
142 {
143         struct ec_node *gen_node = NULL;
144
145         gen_node = __ec_node(&ec_node_int_type, id);
146         if (gen_node == NULL)
147                 return NULL;
148
149         if (ec_node_int_set_limits(gen_node, min, max) < 0)
150                 goto fail;
151         if (ec_node_int_set_base(gen_node, base) < 0)
152                 goto fail;
153
154         return gen_node;
155
156 fail:
157         ec_node_free(gen_node);
158         return NULL;
159 }
160
161 static struct ec_node_type ec_node_uint_type = {
162         .name = "uint",
163         .parse = ec_node_int_uint_parse,
164         .complete = ec_node_complete_unknown,
165         .size = sizeof(struct ec_node_int_uint),
166 };
167
168 EC_NODE_TYPE_REGISTER(ec_node_uint_type);
169
170 struct ec_node *ec_node_uint(const char *id, uint64_t min,
171         uint64_t max, unsigned int base)
172 {
173         struct ec_node *gen_node = NULL;
174
175         gen_node = __ec_node(&ec_node_uint_type, id);
176         if (gen_node == NULL)
177                 return NULL;
178
179         if (ec_node_uint_set_limits(gen_node, min, max) < 0)
180                 goto fail;
181         if (ec_node_uint_set_base(gen_node, base) < 0)
182                 goto fail;
183
184         return gen_node;
185
186 fail:
187         ec_node_free(gen_node);
188         return NULL;
189 }
190
191 int ec_node_int_disable_limits(struct ec_node *gen_node)
192 {
193         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
194         int ret;
195
196         ret = ec_node_check_type(gen_node, &ec_node_int_type);
197         if (ret < 0)
198                 return ret;
199
200         node->check_min = false;
201         node->check_max = false;
202
203         return 0;
204 }
205
206 int ec_node_int_set_limits(struct ec_node *gen_node, int64_t min,
207                         int64_t max)
208 {
209         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
210         int ret;
211
212         ret = ec_node_check_type(gen_node, &ec_node_int_type);
213         if (ret < 0)
214                 return ret;
215
216         if (min > max) {
217                 errno = EINVAL;
218                 return -1;
219         }
220
221         node->check_min = true;
222         node->min = min;
223         node->check_max = true;
224         node->max = max;
225
226         return 0;
227 }
228
229 int ec_node_int_set_base(struct ec_node *gen_node, unsigned int base)
230 {
231         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
232         int ret;
233
234         ret = ec_node_check_type(gen_node, &ec_node_int_type);
235         if (ret < 0)
236                 return ret;
237
238         node->base = base;
239
240         return 0;
241
242 }
243
244 int ec_node_uint_disable_limits(struct ec_node *gen_node)
245 {
246         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
247         int ret;
248
249         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
250         if (ret < 0)
251                 return ret;
252
253         node->check_min = false;
254         node->check_max = false;
255
256         return 0;
257 }
258
259 int ec_node_uint_set_limits(struct ec_node *gen_node, uint64_t min,
260                         uint64_t max)
261 {
262         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
263         int ret;
264
265         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
266         if (ret < 0)
267                 return ret;
268
269         if (min > max) {
270                 errno = EINVAL;
271                 return -1;
272         }
273
274         node->check_min = true;
275         node->min = min;
276         node->check_max = true;
277         node->max = max;
278
279         return 0;
280 }
281
282 int ec_node_uint_set_base(struct ec_node *gen_node, unsigned int base)
283 {
284         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
285         int ret;
286
287         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
288         if (ret < 0)
289                 return ret;
290
291         node->base = base;
292
293         return 0;
294 }
295
296 int ec_node_int_getval(const struct ec_node *gen_node, const char *str,
297                         int64_t *result)
298 {
299         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
300         int ret;
301
302         ret = ec_node_check_type(gen_node, &ec_node_int_type);
303         if (ret < 0)
304                 return ret;
305
306         if (parse_llint(node, str, result) < 0)
307                 return -1;
308
309         return 0;
310 }
311
312 int ec_node_uint_getval(const struct ec_node *gen_node, const char *str,
313                         uint64_t *result)
314 {
315         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
316         int ret;
317
318         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
319         if (ret < 0)
320                 return ret;
321
322         if (parse_ullint(node, str, result) < 0)
323                 return -1;
324
325         return 0;
326 }
327
328 /* LCOV_EXCL_START */
329 static int ec_node_int_testcase(void)
330 {
331         struct ec_parsed *p;
332         struct ec_node *node;
333         const char *s;
334         int testres = 0, ret;
335         uint64_t u64;
336         int64_t i64;
337
338         node = ec_node_uint(EC_NO_ID, 1, 256, 0);
339         if (node == NULL) {
340                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
341                 return -1;
342         }
343         testres |= EC_TEST_CHECK_PARSE(node, -1, "");
344         testres |= EC_TEST_CHECK_PARSE(node, -1, "0");
345         testres |= EC_TEST_CHECK_PARSE(node, 1, "1");
346         testres |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo");
347         testres |= EC_TEST_CHECK_PARSE(node, 1, "0x100");
348         testres |= EC_TEST_CHECK_PARSE(node, 1, " 1");
349         testres |= EC_TEST_CHECK_PARSE(node, -1, "-1");
350         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x101");
351         testres |= EC_TEST_CHECK_PARSE(node, -1, "zzz");
352         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x100000000000000000");
353         testres |= EC_TEST_CHECK_PARSE(node, -1, "4r");
354         ret = ec_node_uint_disable_limits(node);
355         testres |= EC_TEST_CHECK(ret == 0, "cannot disable limits");
356         testres |= EC_TEST_CHECK_PARSE(node, 1, "0");
357
358         p = ec_node_parse(node, "1");
359         s = ec_strvec_val(ec_parsed_strvec(p), 0);
360         testres |= EC_TEST_CHECK(s != NULL &&
361                 ec_node_uint_getval(node, s, &u64) == 0 &&
362                 u64 == 1, "bad integer value");
363         ec_parsed_free(p);
364
365         p = ec_node_parse(node, "10");
366         s = ec_strvec_val(ec_parsed_strvec(p), 0);
367         testres |= EC_TEST_CHECK(s != NULL &&
368                 ec_node_uint_getval(node, s, &u64) == 0 &&
369                 u64 == 10, "bad integer value");
370         ec_parsed_free(p);
371         ec_node_free(node);
372
373         node = ec_node_int(EC_NO_ID, -1, LLONG_MAX, 16);
374         if (node == NULL) {
375                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
376                 return -1;
377         }
378         testres |= EC_TEST_CHECK_PARSE(node, 1, "0");
379         testres |= EC_TEST_CHECK_PARSE(node, 1, "-1");
380         testres |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff");
381         testres |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff");
382         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x8000000000000000");
383         testres |= EC_TEST_CHECK_PARSE(node, -1, "-2");
384         testres |= EC_TEST_CHECK_PARSE(node, -1, "zzz");
385         testres |= EC_TEST_CHECK_PARSE(node, -1, "4r");
386         ret = ec_node_int_disable_limits(node);
387         testres |= EC_TEST_CHECK(ret == 0, "cannot disable limits");
388         testres |= EC_TEST_CHECK_PARSE(node, 1, "-2");
389
390         p = ec_node_parse(node, "10");
391         s = ec_strvec_val(ec_parsed_strvec(p), 0);
392         testres |= EC_TEST_CHECK(s != NULL &&
393                 ec_node_int_getval(node, s, &i64) == 0 &&
394                 i64 == 16, "bad integer value");
395         ec_parsed_free(p);
396         ec_node_free(node);
397
398         node = ec_node_int(EC_NO_ID, LLONG_MIN, 0, 10);
399         if (node == NULL) {
400                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
401                 return -1;
402         }
403         testres |= EC_TEST_CHECK_PARSE(node, 1, "0");
404         testres |= EC_TEST_CHECK_PARSE(node, 1, "-1");
405         testres |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808");
406         testres |= EC_TEST_CHECK_PARSE(node, -1, "0x0");
407         testres |= EC_TEST_CHECK_PARSE(node, -1, "1");
408         ec_node_free(node);
409
410         /* test completion */
411         node = ec_node_int(EC_NO_ID, 0, 10, 0);
412         if (node == NULL) {
413                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
414                 return -1;
415         }
416         testres |= EC_TEST_CHECK_COMPLETE(node,
417                 "", EC_NODE_ENDLIST,
418                 EC_NODE_ENDLIST);
419         testres |= EC_TEST_CHECK_COMPLETE(node,
420                 "x", EC_NODE_ENDLIST,
421                 EC_NODE_ENDLIST);
422         testres |= EC_TEST_CHECK_COMPLETE(node,
423                 "1", EC_NODE_ENDLIST,
424                 EC_NODE_ENDLIST);
425         ec_node_free(node);
426
427         return testres;
428 }
429 /* LCOV_EXCL_STOP */
430
431 static struct ec_test ec_node_int_test = {
432         .name = "node_int",
433         .test = ec_node_int_testcase,
434 };
435
436 EC_TEST_REGISTER(ec_node_int_test);