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