init
[protos/libecoli.git] / lib / ecoli_tk_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 <limits.h>
32 #include <ctype.h>
33 #include <errno.h>
34
35 #include <ecoli_tk.h>
36 #include <ecoli_tk_int.h>
37 #include <ecoli_test.h>
38
39 static size_t parse_llint(struct ec_tk_int *tk, const char *str,
40         long long *val)
41 {
42         char *endptr;
43
44         errno = 0;
45         *val = strtoll(str, &endptr, tk->base);
46
47         /* starts with a space */
48         if (isspace(str[0]))
49                 return 0;
50
51         /* out of range */
52         if ((errno == ERANGE && (*val == LLONG_MAX || *val == LLONG_MIN)) ||
53                         (errno != 0 && *val == 0))
54                 return 0;
55
56         if (*val < tk->min || *val > tk->max)
57                 return 0;
58
59         return endptr - str;
60 }
61
62 static struct ec_parsed_tk *parse(const struct ec_tk *gen_tk,
63         const char *str, size_t off)
64 {
65         struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk;
66         struct ec_parsed_tk *parsed_tk;
67         long long val;
68         size_t len;
69
70         len = parse_llint(tk, str + off, &val);
71         if (len == 0)
72                 return NULL;
73
74         parsed_tk = ec_parsed_tk_new(gen_tk);
75         if (parsed_tk == NULL)
76                 return NULL;
77
78         parsed_tk->str = strndup(str + off, len);
79
80         return parsed_tk;
81 }
82
83 static struct ec_tk_ops int_ops = {
84         .parse = parse,
85 };
86
87 struct ec_tk *ec_tk_int_new(const char *id, long long int min,
88         long long int max, unsigned int base)
89 {
90         struct ec_tk_int *tk = NULL;
91
92         tk = (struct ec_tk_int *)ec_tk_new(id, &int_ops, sizeof(*tk));
93         if (tk == NULL)
94                 return NULL;
95
96         tk->min = min;
97         tk->max = max;
98         tk->base = base;
99
100         return &tk->gen;
101 }
102
103 long long ec_tk_int_getval(struct ec_tk *gen_tk, const char *str)
104 {
105         struct ec_tk_int *tk = (struct ec_tk_int *)gen_tk;
106         long long val = 0;
107
108         // XXX check type here
109         // if gen_tk->type != int fail
110
111         parse_llint(tk, str, &val);
112
113         return val;
114 }
115
116 static int testcase(void)
117 {
118         struct ec_parsed_tk *p;
119         struct ec_tk *tk;
120         const char *s;
121         int ret = 0;
122
123         tk = ec_tk_int_new(NULL, 0, 256, 0);
124         if (tk == NULL) {
125                 printf("cannot create tk\n");
126                 return -1;
127         }
128         ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
129         ret |= EC_TEST_CHECK_TK_PARSE(tk, "256", "256");
130         ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x100", "0x100");
131         ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", NULL);
132         ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x101", NULL);
133         ret |= EC_TEST_CHECK_TK_PARSE(tk, " 1", NULL);
134
135         p = ec_tk_parse(tk, "0", 0);
136         s = ec_parsed_tk_to_string(p);
137         if (s == NULL) {
138                 TEST_ERR();
139         } else {
140                 if (ec_tk_int_getval(tk, s) != 0)
141                         TEST_ERR();
142         }
143
144         p = ec_tk_parse(tk, "10", 0);
145         s = ec_parsed_tk_to_string(p);
146         if (s == NULL) {
147                 TEST_ERR();
148         } else {
149                 if (ec_tk_int_getval(tk, s) != 10)
150                         TEST_ERR();
151         }
152
153         ec_tk_free(tk);
154
155         tk = ec_tk_int_new(NULL, -1, LLONG_MAX, 16);
156         if (tk == NULL) {
157                 printf("cannot create tk\n");
158                 return -1;
159         }
160         ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
161         ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", "-1");
162         ret |= EC_TEST_CHECK_TK_PARSE(tk, "7fffffffffffffff",
163                 "7fffffffffffffff");
164         ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x7fffffffffffffff",
165                 "0x7fffffffffffffff");
166         ret |= EC_TEST_CHECK_TK_PARSE(tk, "-2", NULL);
167
168         p = ec_tk_parse(tk, "10", 0);
169         s = ec_parsed_tk_to_string(p);
170         if (s == NULL) {
171                 TEST_ERR();
172         } else {
173                 if (ec_tk_int_getval(tk, s) != 16)
174                         TEST_ERR();
175         }
176
177         ec_tk_free(tk);
178
179         tk = ec_tk_int_new(NULL, LLONG_MIN, 0, 10);
180         if (tk == NULL) {
181                 printf("cannot create tk\n");
182                 return -1;
183         }
184         ret |= EC_TEST_CHECK_TK_PARSE(tk, "0", "0");
185         ret |= EC_TEST_CHECK_TK_PARSE(tk, "-1", "-1");
186         ret |= EC_TEST_CHECK_TK_PARSE(tk, "-9223372036854775808",
187                 "-9223372036854775808");
188         ret |= EC_TEST_CHECK_TK_PARSE(tk, "0x0", "0");
189         ret |= EC_TEST_CHECK_TK_PARSE(tk, "1", NULL);
190         ec_tk_free(tk);
191
192         /* /\* test completion *\/ */
193         /* tk = ec_tk_int_new(NULL, "foo"); */
194         /* if (tk == NULL) { */
195         /*      printf("cannot create tk\n"); */
196         /*      return -1; */
197         /* } */
198         /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "", "foo"); */
199         /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "f", "oo"); */
200         /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "foo", ""); */
201         /* ret |= EC_TEST_CHECK_TK_COMPLETE(tk, "x", NULL); */
202         /* ec_tk_free(tk); */
203
204         return ret;
205 }
206
207 static struct ec_test test = {
208         .name = "tk_int",
209         .test = testcase,
210 };
211
212 EC_REGISTER_TEST(test);