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