f8ba567c350c47e1571068e418e812081535cad1
[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 int
143 ec_node_uint_init_priv(struct ec_node *gen_node)
144 {
145         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
146
147         node->is_signed = true;
148
149         return 0;
150 }
151
152 static struct ec_node_type ec_node_int_type = {
153         .name = "int",
154         .parse = ec_node_int_uint_parse,
155         .complete = ec_node_default_complete,
156         .size = sizeof(struct ec_node_int_uint),
157         .init_priv = ec_node_uint_init_priv,
158 };
159
160 EC_NODE_TYPE_REGISTER(ec_node_int_type);
161
162 struct ec_node *ec_node_int(const char *id, int64_t min,
163         int64_t max, unsigned int base)
164 {
165         struct ec_node *gen_node = NULL;
166
167         gen_node = __ec_node(&ec_node_int_type, id);
168         if (gen_node == NULL)
169                 return NULL;
170
171         if (ec_node_int_set_limits(gen_node, min, max) < 0)
172                 goto fail;
173         if (ec_node_int_set_base(gen_node, base) < 0)
174                 goto fail;
175
176         return gen_node;
177
178 fail:
179         ec_node_free(gen_node);
180         return NULL;
181 }
182
183 static struct ec_node_type ec_node_uint_type = {
184         .name = "uint",
185         .parse = ec_node_int_uint_parse,
186         .complete = ec_node_default_complete,
187         .size = sizeof(struct ec_node_int_uint),
188 };
189
190 EC_NODE_TYPE_REGISTER(ec_node_uint_type);
191
192 struct ec_node *ec_node_uint(const char *id, uint64_t min,
193         uint64_t max, unsigned int base)
194 {
195         struct ec_node *gen_node = NULL;
196
197         gen_node = __ec_node(&ec_node_uint_type, id);
198         if (gen_node == NULL)
199                 return NULL;
200
201         if (ec_node_uint_set_limits(gen_node, min, max) < 0)
202                 goto fail;
203         if (ec_node_uint_set_base(gen_node, base) < 0)
204                 goto fail;
205
206         return gen_node;
207
208 fail:
209         ec_node_free(gen_node);
210         return NULL;
211 }
212
213 int ec_node_int_disable_limits(struct ec_node *gen_node)
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         node->check_min = false;
223         node->check_max = false;
224
225         return 0;
226 }
227
228 int ec_node_int_set_limits(struct ec_node *gen_node, int64_t min,
229                         int64_t max)
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         if (min > max) {
239                 errno = EINVAL;
240                 return -1;
241         }
242
243         node->check_min = true;
244         node->min = min;
245         node->check_max = true;
246         node->max = max;
247
248         return 0;
249 }
250
251 int ec_node_int_set_base(struct ec_node *gen_node, unsigned int base)
252 {
253         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
254         int ret;
255
256         ret = ec_node_check_type(gen_node, &ec_node_int_type);
257         if (ret < 0)
258                 return ret;
259
260         node->base = base;
261
262         return 0;
263
264 }
265
266 int ec_node_uint_disable_limits(struct ec_node *gen_node)
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         node->check_min = false;
276         node->check_max = false;
277
278         return 0;
279 }
280
281 int ec_node_uint_set_limits(struct ec_node *gen_node, uint64_t min,
282                         uint64_t max)
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         if (min > max) {
292                 errno = EINVAL;
293                 return -1;
294         }
295
296         node->check_min = true;
297         node->min = min;
298         node->check_max = true;
299         node->max = max;
300
301         return 0;
302 }
303
304 int ec_node_uint_set_base(struct ec_node *gen_node, unsigned int base)
305 {
306         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
307         int ret;
308
309         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
310         if (ret < 0)
311                 return ret;
312
313         node->base = base;
314
315         return 0;
316 }
317
318 int ec_node_int_getval(const struct ec_node *gen_node, const char *str,
319                         int64_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_int_type);
325         if (ret < 0)
326                 return ret;
327
328         if (parse_llint(node, str, result) < 0)
329                 return -1;
330
331         return 0;
332 }
333
334 int ec_node_uint_getval(const struct ec_node *gen_node, const char *str,
335                         uint64_t *result)
336 {
337         struct ec_node_int_uint *node = (struct ec_node_int_uint *)gen_node;
338         int ret;
339
340         ret = ec_node_check_type(gen_node, &ec_node_uint_type);
341         if (ret < 0)
342                 return ret;
343
344         if (parse_ullint(node, str, result) < 0)
345                 return -1;
346
347         return 0;
348 }
349
350 /* LCOV_EXCL_START */
351 static int ec_node_int_testcase(void)
352 {
353         struct ec_parsed *p;
354         struct ec_node *node;
355         const char *s;
356         int ret = 0;
357         uint64_t u64;
358         int64_t i64;
359
360         node = ec_node_uint(EC_NO_ID, 0, 256, 0);
361         if (node == NULL) {
362                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
363                 return -1;
364         }
365         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
366         ret |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo");
367         ret |= EC_TEST_CHECK_PARSE(node, 1, "0x100");
368         ret |= EC_TEST_CHECK_PARSE(node, 1, " 1");
369         ret |= EC_TEST_CHECK_PARSE(node, -1, "-1");
370         ret |= EC_TEST_CHECK_PARSE(node, -1, "0x101");
371
372         p = ec_node_parse(node, "0");
373         s = ec_strvec_val(ec_parsed_strvec(p), 0);
374         EC_TEST_ASSERT(s != NULL &&
375                 ec_node_uint_getval(node, s, &u64) == 0 &&
376                 u64 == 0);
377         ec_parsed_free(p);
378
379         p = ec_node_parse(node, "10");
380         s = ec_strvec_val(ec_parsed_strvec(p), 0);
381         EC_TEST_ASSERT(s != NULL &&
382                 ec_node_uint_getval(node, s, &u64) == 0 &&
383                 u64 == 10);
384         ec_parsed_free(p);
385         ec_node_free(node);
386
387         node = ec_node_int(EC_NO_ID, -1, LLONG_MAX, 16);
388         if (node == NULL) {
389                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
390                 return -1;
391         }
392         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
393         ret |= EC_TEST_CHECK_PARSE(node, 1, "-1");
394         ret |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff");
395         ret |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff");
396         ret |= EC_TEST_CHECK_PARSE(node, -1, "-2");
397
398         p = ec_node_parse(node, "10");
399         s = ec_strvec_val(ec_parsed_strvec(p), 0);
400         EC_TEST_ASSERT(s != NULL &&
401                 ec_node_int_getval(node, s, &i64) == 0 &&
402                 i64 == 16);
403         ec_parsed_free(p);
404         ec_node_free(node);
405
406         node = ec_node_int(EC_NO_ID, LLONG_MIN, 0, 10);
407         if (node == NULL) {
408                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
409                 return -1;
410         }
411         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
412         ret |= EC_TEST_CHECK_PARSE(node, 1, "-1");
413         ret |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808");
414         ret |= EC_TEST_CHECK_PARSE(node, -1, "0x0");
415         ret |= EC_TEST_CHECK_PARSE(node, -1, "1");
416         ec_node_free(node);
417
418         /* test completion */
419         node = ec_node_int(EC_NO_ID, 0, 10, 0);
420         if (node == NULL) {
421                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
422                 return -1;
423         }
424         ret |= EC_TEST_CHECK_COMPLETE(node,
425                 "", EC_NODE_ENDLIST,
426                 EC_NODE_ENDLIST);
427         ret |= EC_TEST_CHECK_COMPLETE(node,
428                 "x", EC_NODE_ENDLIST,
429                 EC_NODE_ENDLIST);
430         ret |= EC_TEST_CHECK_COMPLETE(node,
431                 "1", EC_NODE_ENDLIST,
432                 EC_NODE_ENDLIST);
433         ec_node_free(node);
434
435         return ret;
436 }
437 /* LCOV_EXCL_STOP */
438
439 static struct ec_test ec_node_int_test = {
440         .name = "node_int",
441         .test = ec_node_int_testcase,
442 };
443
444 EC_TEST_REGISTER(ec_node_int_test);