cmdline (merge-intel): use maximum int values defines from stdint.h
[libcmdline.git] / src / lib / cmdline_parse_num.c
1 /*
2  * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
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 <stdint.h>
30 #include <inttypes.h>
31 #include <ctype.h>
32 #include <string.h>
33
34 #include "cmdline_parse.h"
35 #include "cmdline_parse_num.h"
36
37 //#define debug_printf(args...) printf(args)
38 #define debug_printf(args...) do {} while(0)
39
40 struct cmdline_token_ops cmdline_token_num_ops = {
41         .parse = cmdline_parse_num,
42         .complete_get_nb = NULL,
43         .complete_get_elt = NULL,
44         .get_help = cmdline_get_help_num,
45 };
46
47
48 enum num_parse_state_t {
49         START,
50         DEC_NEG,
51         BIN,
52         HEX,
53         FLOAT_POS,
54         FLOAT_NEG,
55
56         ERROR,
57
58         FIRST_OK, /* not used */
59         ZERO_OK,
60         HEX_OK,
61         OCTAL_OK,
62         BIN_OK,
63         DEC_NEG_OK,
64         DEC_POS_OK,
65         FLOAT_POS_OK,
66         FLOAT_NEG_OK,
67 };
68
69 /* Keep it sync with enum in .h */
70 static const char * num_help[] = {
71         "UINT8", "UINT16", "UINT32",
72         "INT8", "INT16", "INT32",
73 #ifndef CMDLINE_NO_FLOAT
74         "FLOAT",
75 #endif
76 };
77
78 static inline int
79 add_to_res(unsigned int c, unsigned int *res, unsigned int base)
80 {
81         /* overflow */
82         if ( (UINT32_MAX - c) / base < *res ) {
83                 return -1;
84         }
85
86         *res = *res * base + c ;
87         return 0;
88 }
89
90
91 /* parse an int or a float */
92 int
93 cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res)
94 {
95         struct cmdline_token_num_data nd;
96         enum num_parse_state_t st = START;
97         const char * buf = srcbuf;
98         char c = *buf;
99         uint32_t res1 = 0;
100 #ifndef CMDLINE_NO_FLOAT
101         uint32_t res2 = 0, res3 = 1;
102 #endif
103
104         memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
105
106         while ( st != ERROR && c && ! cmdline_isendoftoken(c) ) {
107                 debug_printf("%c %x -> ", c, c);
108                 switch (st) {
109                 case START:
110                         if (c == '-') {
111                                 st = DEC_NEG;
112                         }
113                         else if (c == '0') {
114                                 st = ZERO_OK;
115                         }
116 #ifndef CMDLINE_NO_FLOAT
117                         else if (c == '.') {
118                                 st = FLOAT_POS;
119                                 res1 = 0;
120                         }
121 #endif
122                         else if (c >= '1' && c <= '9') {
123                                 if (add_to_res(c - '0', &res1, 10) < 0)
124                                         st = ERROR;
125                                 else
126                                         st = DEC_POS_OK;
127                         }
128                         else  {
129                                 st = ERROR;
130                         }
131                         break;
132
133                 case ZERO_OK:
134                         if (c == 'x') {
135                                 st = HEX;
136                         }
137                         else if (c == 'b') {
138                                 st = BIN;
139                         }
140 #ifndef CMDLINE_NO_FLOAT
141                         else if (c == '.') {
142                                 st = FLOAT_POS;
143                                 res1 = 0;
144                         }
145 #endif
146                         else if (c >= '0' && c <= '7') {
147                                 if (add_to_res(c - '0', &res1, 10) < 0)
148                                         st = ERROR;
149                                 else
150                                         st = OCTAL_OK;
151                         }
152                         else  {
153                                 st = ERROR;
154                         }
155                         break;
156
157                 case DEC_NEG:
158                         if (c >= '0' && c <= '9') {
159                                 if (add_to_res(c - '0', &res1, 10) < 0)
160                                         st = ERROR;
161                                 else
162                                         st = DEC_NEG_OK;
163                         }
164 #ifndef CMDLINE_NO_FLOAT
165                         else if (c == '.') {
166                                 res1 = 0;
167                                 st = FLOAT_NEG;
168                         }
169 #endif
170                         else {
171                                 st = ERROR;
172                         }
173                         break;
174
175                 case DEC_NEG_OK:
176                         if (c >= '0' && c <= '9') {
177                                 if (add_to_res(c - '0', &res1, 10) < 0)
178                                         st = ERROR;
179                         }
180 #ifndef CMDLINE_NO_FLOAT
181                         else if (c == '.') {
182                                 st = FLOAT_NEG;
183                         }
184 #endif
185                         else {
186                                 st = ERROR;
187                         }
188                         break;
189
190                 case DEC_POS_OK:
191                         if (c >= '0' && c <= '9') {
192                                 if (add_to_res(c - '0', &res1, 10) < 0)
193                                         st = ERROR;
194                         }
195 #ifndef CMDLINE_NO_FLOAT
196                         else if (c == '.') {
197                                 st = FLOAT_POS;
198                         }
199 #endif
200                         else {
201                                 st = ERROR;
202                         }
203                         break;
204
205                 case HEX:
206                         st = HEX_OK;
207                         /* no break */
208                 case HEX_OK:
209                         if (c >= '0' && c <= '9') {
210                                 if (add_to_res(c - '0', &res1, 16) < 0)
211                                         st = ERROR;
212                         }
213                         else if (c >= 'a' && c <= 'f') {
214                                 if (add_to_res(c - 'a' + 10, &res1, 16) < 0)
215                                         st = ERROR;
216                         }
217                         else if (c >= 'A' && c <= 'F') {
218                                 if (add_to_res(c - 'A' + 10, &res1, 16) < 0)
219                                         st = ERROR;
220                         }
221                         else {
222                                 st = ERROR;
223                         }
224                         break;
225
226
227                 case OCTAL_OK:
228                         if (c >= '0' && c <= '7') {
229                                 if (add_to_res(c - '0', &res1, 8) < 0)
230                                         st = ERROR;
231                         }
232                         else {
233                                 st = ERROR;
234                         }
235                         break;
236
237                 case BIN:
238                         st = BIN_OK;
239                         /* no break */
240                 case BIN_OK:
241                         if (c >= '0' && c <= '1') {
242                                 if (add_to_res(c - '0', &res1, 2) < 0)
243                                         st = ERROR;
244                         }
245                         else {
246                                 st = ERROR;
247                         }
248                         break;
249
250 #ifndef CMDLINE_NO_FLOAT
251                 case FLOAT_POS:
252                         if (c >= '0' && c <= '9') {
253                                 if (add_to_res(c - '0', &res2, 10) < 0)
254                                         st = ERROR;
255                                 else
256                                         st = FLOAT_POS_OK;
257                                 res3 = 10;
258                         }
259                         else {
260                                 st = ERROR;
261                         }
262                         break;
263
264                 case FLOAT_NEG:
265                         if (c >= '0' && c <= '9') {
266                                 if (add_to_res(c - '0', &res2, 10) < 0)
267                                         st = ERROR;
268                                 else
269                                         st = FLOAT_NEG_OK;
270                                 res3 = 10;
271                         }
272                         else {
273                                 st = ERROR;
274                         }
275                         break;
276
277                 case FLOAT_POS_OK:
278                         if (c >= '0' && c <= '9') {
279                                 if (add_to_res(c - '0', &res2, 10) < 0)
280                                         st = ERROR;
281                                 if (add_to_res(0, &res3, 10) < 0)
282                                         st = ERROR;
283                         }
284                         else {
285                                 st = ERROR;
286                         }
287                         break;
288
289                 case FLOAT_NEG_OK:
290                         if (c >= '0' && c <= '9') {
291                                 if (add_to_res(c - '0', &res2, 10) < 0)
292                                         st = ERROR;
293                                 if (add_to_res(0, &res3, 10) < 0)
294                                         st = ERROR;
295                         }
296                         else {
297                                 st = ERROR;
298                         }
299                         break;
300 #endif
301
302                 default:
303                         debug_printf("not impl ");
304
305                 }
306
307 #ifdef CMDLINE_NO_FLOAT
308                 debug_printf("(%"PRIu32")\n", res1);
309 #else
310                 debug_printf("(%"PRIu32")  (%"PRIu32")  (%"PRIu32")\n",
311                              res1, res2, res3);
312 #endif
313
314                 buf ++;
315                 c = *buf;
316
317                 /* token too long */
318                 if (buf-srcbuf > 127)
319                         return -1;
320         }
321
322         switch (st) {
323         case ZERO_OK:
324         case DEC_POS_OK:
325         case HEX_OK:
326         case OCTAL_OK:
327         case BIN_OK:
328                 if ( nd.type == INT8 && res1 <= INT8_MAX ) {
329                         if (res)
330                                 *(int8_t *)res = (int8_t) res1;
331                         return (buf-srcbuf);
332                 }
333                 else if ( nd.type == INT16 && res1 <= INT16_MAX ) {
334                         if (res)
335                                 *(int16_t *)res = (int16_t) res1;
336                         return (buf-srcbuf);
337                 }
338                 else if ( nd.type == INT32 && res1 <= INT32_MAX ) {
339                         if (res)
340                                 *(int32_t *)res = (int32_t) res1;
341                         return (buf-srcbuf);
342                 }
343                 else if ( nd.type == UINT8 && res1 <= UINT8_MAX ) {
344                         if (res)
345                                 *(uint8_t *)res = (uint8_t) res1;
346                         return (buf-srcbuf);
347                 }
348                 else if (nd.type == UINT16  && res1 <= UINT16_MAX ) {
349                         if (res)
350                                 *(uint16_t *)res = (uint16_t) res1;
351                         return (buf-srcbuf);
352                 }
353                 else if ( nd.type == UINT32 ) {
354                         if (res)
355                                 *(uint32_t *)res = (uint32_t) res1;
356                         return (buf-srcbuf);
357                 }
358 #ifndef CMDLINE_NO_FLOAT
359                 else if ( nd.type == FLOAT ) {
360                         if (res)
361                                 *(float *)res = (float)res1;
362                         return (buf-srcbuf);
363                 }
364 #endif
365                 else {
366                         return -1;
367                 }
368                 break;
369
370         case DEC_NEG_OK:
371                 if ( nd.type == INT8 && res1 <= INT8_MAX + 1 ) {
372                         if (res)
373                                 *(int8_t *)res = - (int8_t) res1;
374                         return (buf-srcbuf);
375                 }
376                 else if ( nd.type == INT16 && res1 <= (uint16_t)INT16_MAX + 1 ) {
377                         if (res)
378                                 *(int16_t *)res = - (int16_t) res1;
379                         return (buf-srcbuf);
380                 }
381                 else if ( nd.type == INT32 && res1 <= (uint32_t)INT32_MAX + 1 ) {
382                         if (res)
383                                 *(int32_t *)res = - (int32_t) res1;
384                         return (buf-srcbuf);
385                 }
386 #ifndef CMDLINE_NO_FLOAT
387                 else if ( nd.type == FLOAT ) {
388                         if (res)
389                                 *(float *)res = - (float)res1;
390                         return (buf-srcbuf);
391                 }
392 #endif
393                 else {
394                         return -1;
395                 }
396                 break;
397
398 #ifndef CMDLINE_NO_FLOAT
399         case FLOAT_POS:
400         case FLOAT_POS_OK:
401                 if ( nd.type == FLOAT ) {
402                         if (res)
403                                 *(float *)res = (float)res1 + ((float)res2 / (float)res3);
404                         return (buf-srcbuf);
405
406                 }
407                 else {
408                         return -1;
409                 }
410                 break;
411
412         case FLOAT_NEG:
413         case FLOAT_NEG_OK:
414                 if ( nd.type == FLOAT ) {
415                         if (res)
416                                 *(float *)res = - ((float)res1 + ((float)res2 / (float)res3));
417                         return (buf-srcbuf);
418
419                 }
420                 else {
421                         return -1;
422                 }
423                 break;
424 #endif
425         default:
426                 debug_printf("error\n");
427                 return -1;
428         }
429         return -1;
430 }
431
432
433 /* parse an int or a float */
434 int
435 cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
436 {
437         struct cmdline_token_num_data nd;
438
439         memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
440
441         /* should not happen.... don't so this test */
442         /* if (nd.type >= (sizeof(num_help)/sizeof(const char *))) */
443         /* return -1; */
444
445         strncpy(dstbuf, num_help[nd.type], size);
446         dstbuf[size-1] = '\0';
447         return 0;
448 }