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