use EC_NO_ID instead of NULL
[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 struct ec_node_int {
49         struct ec_node gen;
50         bool is_signed;
51         bool check_min;
52         bool check_max;
53         union {
54                 int64_t min;
55                 uint64_t umin;
56         };
57         union {
58                 int64_t max;
59                 uint64_t umax;
60         };
61         unsigned int base;
62 };
63
64 static int parse_llint(struct ec_node_int *node, const char *str,
65         int64_t *val)
66 {
67         char *endptr;
68
69         errno = 0;
70         *val = strtoll(str, &endptr, node->base);
71
72         if (errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN))
73                 return -1;
74
75         if (errno != 0 && *val == 0)
76                 return -1;
77
78         if (node->check_min && *val < node->min)
79                 return -1;
80
81         if (node->check_max && *val > node->max)
82                 return -1;
83
84         if (*endptr != 0)
85                 return -1;
86
87         return 0;
88 }
89
90 static int parse_ullint(struct ec_node_int *node, const char *str,
91                         uint64_t *val)
92 {
93         char *endptr;
94
95         /* since a negative input is silently converted to a positive
96          * one by strtoull(), first check that it is positive */
97         if (strchr(str, '-'))
98                 return -1;
99
100         errno = 0;
101         *val = strtoull(str, &endptr, node->base);
102
103         if (errno == ERANGE && *val == ULLONG_MAX)
104                 return -1;
105
106         if (errno != 0 && *val == 0)
107                 return -1;
108
109         if (node->check_min && *val < node->umin)
110                 return -1;
111
112         if (node->check_max && *val > node->umax)
113                 return -1;
114
115         if (*endptr != 0)
116                 return -1;
117
118         return 0;
119 }
120
121 static int ec_node_int_parse(const struct ec_node *gen_node,
122                         struct ec_parsed *state,
123                         const struct ec_strvec *strvec)
124 {
125         struct ec_node_int *node = (struct ec_node_int *)gen_node;
126         const char *str;
127         int64_t val;
128
129         (void)state;
130
131         if (ec_strvec_len(strvec) == 0)
132                 return EC_PARSED_NOMATCH;
133
134         str = ec_strvec_val(strvec, 0);
135         if (parse_llint(node, str, &val) < 0)
136                 return EC_PARSED_NOMATCH;
137
138         return 1;
139 }
140
141 static size_t ec_node_int_get_max_parse_len(const struct ec_node *gen_node)
142 {
143         (void)gen_node;
144         return 1;
145 }
146
147 static struct ec_node_type ec_node_int_type = {
148         .name = "int",
149         .parse = ec_node_int_parse,
150         .complete = ec_node_default_complete,
151         .get_max_parse_len = ec_node_int_get_max_parse_len,
152         .size = sizeof(struct ec_node_int),
153 };
154
155 EC_NODE_TYPE_REGISTER(ec_node_int_type);
156
157 struct ec_node *ec_node_int(const char *id, int64_t min,
158         int64_t max, unsigned int base)
159 {
160         struct ec_node *gen_node = NULL;
161         struct ec_node_int *node = NULL;
162
163         if (min > max)
164                 return NULL;
165
166         gen_node = __ec_node(&ec_node_int_type, id);
167         if (gen_node == NULL)
168                 return NULL;
169         node = (struct ec_node_int *)gen_node;
170
171         node->check_min = true;
172         node->min = min;
173         node->check_max = true;
174         node->max = max;
175         node->base = base;
176         node->is_signed = true;
177
178         return &node->gen;
179 }
180
181 struct ec_node *ec_node_uint(const char *id, uint64_t min,
182         uint64_t max, unsigned int base)
183 {
184         struct ec_node *gen_node = NULL;
185         struct ec_node_int *node = NULL;
186
187         if (min > max)
188                 return NULL;
189
190         gen_node = __ec_node(&ec_node_int_type, id);
191         if (gen_node == NULL)
192                 return NULL;
193         node = (struct ec_node_int *)gen_node;
194
195         node->check_min = true;
196         node->min = min;
197         node->check_max = true;
198         node->max = max;
199         node->base = base;
200         node->is_signed = true;
201
202         return &node->gen;
203 }
204
205 int64_t ec_node_int_getval(struct ec_node *gen_node, const char *str)
206 {
207         struct ec_node_int *node = (struct ec_node_int *)gen_node;
208         int64_t val = 0;
209
210         // XXX check type here
211         // if gen_node->type != int fail
212         // we may need to change the API (return int + val in a ptr ?)
213
214         parse_llint(node, str, &val);
215
216         return val;
217 }
218
219 uint64_t ec_node_uint_getval(struct ec_node *gen_node, const char *str)
220 {
221         struct ec_node_int *node = (struct ec_node_int *)gen_node;
222         uint64_t val = 0;
223
224         // XXX check type here
225         // if gen_node->type != int fail
226
227         parse_ullint(node, str, &val);
228
229         return val;
230 }
231
232 /* LCOV_EXCL_START */
233 static int ec_node_int_testcase(void)
234 {
235         struct ec_parsed *p;
236         struct ec_node *node;
237         const char *s;
238         int ret = 0;
239
240         node = ec_node_uint(EC_NO_ID, 0, 256, 0);
241         if (node == NULL) {
242                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
243                 return -1;
244         }
245         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
246         ret |= EC_TEST_CHECK_PARSE(node, 1, "256", "foo");
247         ret |= EC_TEST_CHECK_PARSE(node, 1, "0x100");
248         ret |= EC_TEST_CHECK_PARSE(node, 1, " 1");
249         ret |= EC_TEST_CHECK_PARSE(node, -1, "-1");
250         ret |= EC_TEST_CHECK_PARSE(node, -1, "0x101");
251
252         p = ec_node_parse(node, "0");
253         s = ec_strvec_val(ec_parsed_strvec(p), 0);
254         EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 0);
255         ec_parsed_free(p);
256
257         p = ec_node_parse(node, "10");
258         s = ec_strvec_val(ec_parsed_strvec(p), 0);
259         EC_TEST_ASSERT(s != NULL && ec_node_uint_getval(node, s) == 10);
260         ec_parsed_free(p);
261         ec_node_free(node);
262
263         node = ec_node_int(EC_NO_ID, -1, LLONG_MAX, 16);
264         if (node == NULL) {
265                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
266                 return -1;
267         }
268         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
269         ret |= EC_TEST_CHECK_PARSE(node, 1, "-1");
270         ret |= EC_TEST_CHECK_PARSE(node, 1, "7fffffffffffffff");
271         ret |= EC_TEST_CHECK_PARSE(node, 1, "0x7fffffffffffffff");
272         ret |= EC_TEST_CHECK_PARSE(node, -1, "-2");
273
274         p = ec_node_parse(node, "10");
275         s = ec_strvec_val(ec_parsed_strvec(p), 0);
276         EC_TEST_ASSERT(s != NULL && ec_node_int_getval(node, s) == 16);
277         ec_parsed_free(p);
278         ec_node_free(node);
279
280         node = ec_node_int(EC_NO_ID, LLONG_MIN, 0, 10);
281         if (node == NULL) {
282                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
283                 return -1;
284         }
285         ret |= EC_TEST_CHECK_PARSE(node, 1, "0");
286         ret |= EC_TEST_CHECK_PARSE(node, 1, "-1");
287         ret |= EC_TEST_CHECK_PARSE(node, 1, "-9223372036854775808");
288         ret |= EC_TEST_CHECK_PARSE(node, -1, "0x0");
289         ret |= EC_TEST_CHECK_PARSE(node, -1, "1");
290         ec_node_free(node);
291
292         /* test completion */
293         node = ec_node_int(EC_NO_ID, 0, 10, 0);
294         if (node == NULL) {
295                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
296                 return -1;
297         }
298         ret |= EC_TEST_CHECK_COMPLETE(node,
299                 "", EC_NODE_ENDLIST,
300                 EC_NODE_ENDLIST);
301         ret |= EC_TEST_CHECK_COMPLETE(node,
302                 "x", EC_NODE_ENDLIST,
303                 EC_NODE_ENDLIST);
304         ret |= EC_TEST_CHECK_COMPLETE(node,
305                 "1", EC_NODE_ENDLIST,
306                 EC_NODE_ENDLIST);
307         ec_node_free(node);
308
309         return ret;
310 }
311 /* LCOV_EXCL_STOP */
312
313 static struct ec_test ec_node_int_test = {
314         .name = "node_int",
315         .test = ec_node_int_testcase,
316 };
317
318 EC_TEST_REGISTER(ec_node_int_test);