9dae63ae356d84bd34c6aa5a26e812f3f9f45b8c
[protos/libecoli.git] / lib / ecoli_node_int.c
1 /*
2  * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <stdbool.h>
33 #include <limits.h>
34 #include <ctype.h>
35 #include <errno.h>
36
37 #include <ecoli_log.h>
38 #include <ecoli_malloc.h>
39 #include <ecoli_strvec.h>
40 #include <ecoli_node.h>
41 #include <ecoli_parsed.h>
42 #include <ecoli_completed.h>
43 #include <ecoli_node_int.h>
44 #include <ecoli_test.h>
45
46 EC_LOG_TYPE_REGISTER(node_int);
47
48 /* common to int and uint */
49 struct ec_node_int_uint {
50         struct ec_node gen;
51         bool is_signed;
52         bool check_min;
53         bool check_max;
54         union {
55                 int64_t min;
56                 uint64_t umin;
57         };
58         union {
59                 int64_t max;
60                 uint64_t umax;
61         };
62         unsigned int base;
63 };
64
65 static int parse_llint(struct ec_node_int_uint *node, const char *str,
66         int64_t *val)
67 {
68         char *endptr;
69
70         errno = 0;
71         *val = strtoll(str, &endptr, node->base);
72
73         if (errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN))
74                 return -1;
75
76         if (errno != 0 && *val == 0)
77                 return -1;
78
79         if (node->check_min && *val < node->min)
80                 return -1;
81
82         if (node->check_max && *val > node->max)
83                 return -1;
84
85         if (*endptr != 0)
86                 return -1;
87
88         return 0;
89 }
90
91 static int parse_ullint(struct ec_node_int_uint *node, const char *str,
92                         uint64_t *val)
93 {
94         char *endptr;
95
96         /* since a negative input is silently converted to a positive
97          * one by strtoull(), first check that it is positive */
98         if (strchr(str, '-'))
99                 return -1;
100
101         errno = 0;
102         *val = strtoull(str, &endptr, node->base);
103
104         if (errno == ERANGE && *val == ULLONG_MAX)
105                 return -1;
106
107         if (errno != 0 && *val == 0)
108                 return -1;
109
110         if (node->check_min && *val < node->umin)
111                 return -1;
112
113         if (node->check_max && *val > node->umax)
114                 return -1;
115
116         if (*endptr != 0)
117                 return -1;
118
119         return 0;
120 }
121
122 static int ec_node_int_uint_parse(const struct ec_node *gen_node,
123                         struct ec_parsed *state,
124                         const struct ec_strvec *strvec)
125 {
126         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
127         const char *str;
128         int64_t val;
129
130         (void)state;
131
132         if (ec_strvec_len(strvec) == 0)
133                 return EC_PARSED_NOMATCH;
134
135         str = ec_strvec_val(strvec, 0);
136         if (parse_llint(node, str, &val) < 0)
137                 return EC_PARSED_NOMATCH;
138
139         return 1;
140 }
141
142 static size_t ec_node_int_uint_get_max_parse_len(const struct ec_node *gen_node)
143 {
144         (void)gen_node;
145         return 1;
146 }
147
148 static int
149 ec_node_uint_init_priv(struct ec_node *gen_node)
150 {
151         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
152
153         node->is_signed = true;
154
155         return 0;
156 }
157
158 static struct ec_node_type ec_node_int_type = {
159         .name = "int",
160         .parse = ec_node_int_uint_parse,
161         .complete = ec_node_default_complete,
162         .get_max_parse_len = ec_node_int_uint_get_max_parse_len,
163         .size = sizeof(struct ec_node_int_uint),
164         .init_priv = ec_node_uint_init_priv,
165 };
166
167 EC_NODE_TYPE_REGISTER(ec_node_int_type);
168
169 struct ec_node *ec_node_int(const char *id, int64_t min,
170         int64_t max, unsigned int base)
171 {
172         struct ec_node *gen_node = NULL;
173
174         gen_node = __ec_node(&ec_node_int_type, id);
175         if (gen_node == NULL)
176                 return NULL;
177
178         if (ec_node_int_set_limits(gen_node, min, max) < 0)
179                 goto fail;
180         if (ec_node_int_set_base(gen_node, base) < 0)
181                 goto fail;
182
183         return gen_node;
184
185 fail:
186         ec_node_free(gen_node);
187         return NULL;
188 }
189
190 static struct ec_node_type ec_node_uint_type = {
191         .name = "uint",
192         .parse = ec_node_int_uint_parse,
193         .complete = ec_node_default_complete,
194         .get_max_parse_len = ec_node_int_uint_get_max_parse_len,
195         .size = sizeof(struct ec_node_int_uint),
196 };
197
198 EC_NODE_TYPE_REGISTER(ec_node_uint_type);
199
200 struct ec_node *ec_node_uint(const char *id, uint64_t min,
201         uint64_t max, unsigned int base)
202 {
203         struct ec_node *gen_node = NULL;
204
205         gen_node = __ec_node(&ec_node_uint_type, id);
206         if (gen_node == NULL)
207                 return NULL;
208
209         if (ec_node_uint_set_limits(gen_node, min, max) < 0)
210                 goto fail;
211         if (ec_node_uint_set_base(gen_node, base) < 0)
212                 goto fail;
213
214         return gen_node;
215
216 fail:
217         ec_node_free(gen_node);
218         return NULL;
219 }
220
221 int ec_node_int_disable_limits(struct ec_node *gen_node)
222 {
223         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
224         int ret;
225
226         ret = ec_node_check_type(gen_node, &ec_node_int_type);
227         if (ret < 0)
228                 return ret;
229
230         node->check_min = false;
231         node->check_max = false;
232
233         return 0;
234 }
235
236 int ec_node_int_set_limits(struct ec_node *gen_node, int64_t min,
237                         int64_t max)
238 {
239         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
240         int ret;
241
242         ret = ec_node_check_type(gen_node, &ec_node_int_type);
243         if (ret < 0)
244                 return ret;
245
246         if (min > max) {
247                 errno = EINVAL;
248                 return -1;
249         }
250
251         node->check_min = true;
252         node->min = min;
253         node->check_max = true;
254         node->max = max;
255
256         return 0;
257 }
258
259 int ec_node_int_set_base(struct ec_node *gen_node, unsigned int base)
260 {
261         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
262         int ret;
263
264         ret = ec_node_check_type(gen_node, &ec_node_int_type);
265         if (ret < 0)
266                 return ret;
267
268         node->base = base;
269
270         return 0;
271
272 }
273
274 int ec_node_uint_disable_limits(struct ec_node *gen_node)
275 {
276         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
277         int ret;
278
279         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
280         if (ret < 0)
281                 return ret;
282
283         node->check_min = false;
284         node->check_max = false;
285
286         return 0;
287 }
288
289 int ec_node_uint_set_limits(struct ec_node *gen_node, uint64_t min,
290                         uint64_t max)
291 {
292         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
293         int ret;
294
295         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
296         if (ret < 0)
297                 return ret;
298
299         if (min > max) {
300                 errno = EINVAL;
301                 return -1;
302         }
303
304         node->check_min = true;
305         node->min = min;
306         node->check_max = true;
307         node->max = max;
308
309         return 0;
310 }
311
312 int ec_node_uint_set_base(struct ec_node *gen_node, unsigned int base)
313 {
314         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
315         int ret;
316
317         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
318         if (ret < 0)
319                 return ret;
320
321         node->base = base;
322
323         return 0;
324 }
325
326 int ec_node_int_getval(const struct ec_node *gen_node, const char *str,
327                         int64_t *result)
328 {
329         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
330         int ret;
331
332         ret = ec_node_check_type(gen_node, &ec_node_int_type);
333         if (ret < 0)
334                 return ret;
335
336         if (parse_llint(node, str, result) < 0)
337                 return -1;
338
339         return 0;
340 }
341
342 int ec_node_uint_getval(const struct ec_node *gen_node, const char *str,
343                         uint64_t *result)
344 {
345         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
346         int ret;
347
348         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
349         if (ret < 0)
350                 return ret;
351
352         if (parse_ullint(node, str, result) < 0)
353                 return -1;
354
355         return 0;
356 }
357
358 /* LCOV_EXCL_START */
359 static int ec_node_int_testcase(void)
360 {
361         struct ec_parsed *p;
362         struct ec_node *node;
363         const char *s;
364         int ret = 0;
365         uint64_t u64;
366         int64_t i64;
367
368         node = ec_node_uint(EC_NO_ID, 0, 256, 0);
369         if (node == NULL) {
370                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
371                 return -1;
372         }
373         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
374         ret |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo");
375         ret |= EC_TEST_CHECK_PARSE(node, 1, "0x100");
376         ret |= EC_TEST_CHECK_PARSE(node, 1, " 1");
377         ret |= EC_TEST_CHECK_PARSE(node, -1, "-1");
378         ret |= EC_TEST_CHECK_PARSE(node, -1, "0x101");
379
380         p = ec_node_parse(node, "0");
381         s = ec_strvec_val(ec_parsed_strvec(p), 0);
382         EC_TEST_ASSERT(s != NULL);
383         EC_TEST_ASSERT(ec_node_uint_getval(node, s, &u64) == 0);
384         EC_TEST_ASSERT (u64 == 0);
385         ec_parsed_free(p);
386
387         p = ec_node_parse(node, "10");
388         s = ec_strvec_val(ec_parsed_strvec(p), 0);
389         EC_TEST_ASSERT(s != NULL);
390         EC_TEST_ASSERT(ec_node_uint_getval(node, s, &u64) == 0);
391         EC_TEST_ASSERT(u64 == 10);
392         ec_parsed_free(p);
393         ec_node_free(node);
394
395         node = ec_node_int(EC_NO_ID, -1, LLONG_MAX, 16);
396         if (node == NULL) {
397                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
398                 return -1;
399         }
400         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
401         ret |= EC_TEST_CHECK_PARSE(node, 1, "-1");
402         ret |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff");
403         ret |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff");
404         ret |= EC_TEST_CHECK_PARSE(node, -1, "-2");
405
406         p = ec_node_parse(node, "10");
407         s = ec_strvec_val(ec_parsed_strvec(p), 0);
408         EC_TEST_ASSERT(s != NULL);
409         EC_TEST_ASSERT(ec_node_int_getval(node, s, &i64) == 0);
410         EC_TEST_ASSERT(i64 == 16);
411         ec_parsed_free(p);
412         ec_node_free(node);
413
414         node = ec_node_int(EC_NO_ID, LLONG_MIN, 0, 10);
415         if (node == NULL) {
416                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
417                 return -1;
418         }
419         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
420         ret |= EC_TEST_CHECK_PARSE(node, 1, "-1");
421         ret |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808");
422         ret |= EC_TEST_CHECK_PARSE(node, -1, "0x0");
423         ret |= EC_TEST_CHECK_PARSE(node, -1, "1");
424         ec_node_free(node);
425
426         /* test completion */
427         node = ec_node_int(EC_NO_ID, 0, 10, 0);
428         if (node == NULL) {
429                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
430                 return -1;
431         }
432         ret |= EC_TEST_CHECK_COMPLETE(node,
433                 "", EC_NODE_ENDLIST,
434                 EC_NODE_ENDLIST);
435         ret |= EC_TEST_CHECK_COMPLETE(node,
436                 "x", EC_NODE_ENDLIST,
437                 EC_NODE_ENDLIST);
438         ret |= EC_TEST_CHECK_COMPLETE(node,
439                 "1", EC_NODE_ENDLIST,
440                 EC_NODE_ENDLIST);
441         ec_node_free(node);
442
443         return ret;
444 }
445 /* LCOV_EXCL_STOP */
446
447 static struct ec_test ec_node_int_test = {
448         .name = "node_int",
449         .test = ec_node_int_testcase,
450 };
451
452 EC_TEST_REGISTER(ec_node_int_test);