cd3ca1e612eaafb457f3d200b9e3e6ee4aab601a
[libcmdline.git] / src / lib / cmdline_parse_num.c
1 /*-
2  * Copyright (c) <2010>, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in
14  *   the documentation and/or other materials provided with the
15  *   distribution.
16  *
17  * - Neither the name of Intel Corporation nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
32  * OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 /*
36  * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
37  * All rights reserved.
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions are met:
40  *
41  *     * Redistributions of source code must retain the above copyright
42  *       notice, this list of conditions and the following disclaimer.
43  *     * Redistributions in binary form must reproduce the above copyright
44  *       notice, this list of conditions and the following disclaimer in the
45  *       documentation and/or other materials provided with the distribution.
46  *     * Neither the name of the University of California, Berkeley nor the
47  *       names of its contributors may be used to endorse or promote products
48  *       derived from this software without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
51  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
52  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
53  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
54  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
55  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
56  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
57  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60  */
61
62 #include <stdio.h>
63 #include <stdint.h>
64 #include <inttypes.h>
65 #include <ctype.h>
66 #include <string.h>
67
68 #include "cmdline_parse.h"
69 #include "cmdline_parse_num.h"
70
71 //#define debug_printf(args...) printf(args)
72 #define debug_printf(args...) do {} while(0)
73
74 struct cmdline_token_ops cmdline_token_num_ops = {
75         .parse = cmdline_parse_num,
76         .complete_get_nb = NULL,
77         .complete_get_elt = NULL,
78         .get_help = cmdline_get_help_num,
79 };
80
81
82 enum num_parse_state_t {
83         START,
84         DEC_NEG,
85         BIN,
86         HEX,
87         FLOAT_POS,
88         FLOAT_NEG,
89
90         ERROR,
91
92         FIRST_OK, /* not used */
93         ZERO_OK,
94         HEX_OK,
95         OCTAL_OK,
96         BIN_OK,
97         DEC_NEG_OK,
98         DEC_POS_OK,
99         FLOAT_POS_OK,
100         FLOAT_NEG_OK
101 };
102
103 /* Keep it sync with enum in .h */
104 static const char * num_help[] = {
105         "UINT8", "UINT16", "UINT32", "UINT64",
106         "INT8", "INT16", "INT32", "INT64",
107 #ifdef CMDLINE_HAVE_FLOAT
108         "FLOAT",
109 #endif
110 };
111
112 static inline int
113 add_to_res(unsigned int c, uint64_t *res, unsigned int base)
114 {
115         /* overflow */
116         if ( (UINT64_MAX - c) / base < *res ) {
117                 return -1;
118         }
119
120         *res = (uint64_t) (*res * base + c);
121         return 0;
122 }
123
124
125 /* parse an int or a float */
126 int
127 cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res)
128 {
129         struct cmdline_token_num_data nd;
130         enum num_parse_state_t st = START;
131         const char * buf = srcbuf;
132         char c = *buf;
133         uint64_t res1 = 0;
134 #ifdef CMDLINE_HAVE_FLOAT
135         uint64_t res2 = 0, res3 = 1;
136 #endif
137
138         memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
139
140         while (st != ERROR && c != '\0') {
141                 debug_printf("%c %x -> ", c, c);
142                 switch (st) {
143                 case START:
144                         if (c == '-') {
145                                 st = DEC_NEG;
146                         }
147                         else if (c == '0') {
148                                 st = ZERO_OK;
149                         }
150 #ifdef CMDLINE_HAVE_FLOAT
151                         else if (c == '.') {
152                                 st = FLOAT_POS;
153                                 res1 = 0;
154                         }
155 #endif
156                         else if (c >= '1' && c <= '9') {
157                                 if (add_to_res(c - '0', &res1, 10) < 0)
158                                         st = ERROR;
159                                 else
160                                         st = DEC_POS_OK;
161                         }
162                         else  {
163                                 st = ERROR;
164                         }
165                         break;
166
167                 case ZERO_OK:
168                         if (c == 'x') {
169                                 st = HEX;
170                         }
171                         else if (c == 'b') {
172                                 st = BIN;
173                         }
174 #ifdef CMDLINE_HAVE_FLOAT
175                         else if (c == '.') {
176                                 st = FLOAT_POS;
177                                 res1 = 0;
178                         }
179 #endif
180                         else if (c >= '0' && c <= '7') {
181                                 if (add_to_res(c - '0', &res1, 10) < 0)
182                                         st = ERROR;
183                                 else
184                                         st = OCTAL_OK;
185                         }
186                         else  {
187                                 st = ERROR;
188                         }
189                         break;
190
191                 case DEC_NEG:
192                         if (c >= '0' && c <= '9') {
193                                 if (add_to_res(c - '0', &res1, 10) < 0)
194                                         st = ERROR;
195                                 else
196                                         st = DEC_NEG_OK;
197                         }
198 #ifdef CMDLINE_HAVE_FLOAT
199                         else if (c == '.') {
200                                 res1 = 0;
201                                 st = FLOAT_NEG;
202                         }
203 #endif
204                         else {
205                                 st = ERROR;
206                         }
207                         break;
208
209                 case DEC_NEG_OK:
210                         if (c >= '0' && c <= '9') {
211                                 if (add_to_res(c - '0', &res1, 10) < 0)
212                                         st = ERROR;
213                         }
214 #ifdef CMDLINE_HAVE_FLOAT
215                         else if (c == '.') {
216                                 st = FLOAT_NEG;
217                         }
218 #endif
219                         else {
220                                 st = ERROR;
221                         }
222                         break;
223
224                 case DEC_POS_OK:
225                         if (c >= '0' && c <= '9') {
226                                 if (add_to_res(c - '0', &res1, 10) < 0)
227                                         st = ERROR;
228                         }
229 #ifdef CMDLINE_HAVE_FLOAT
230                         else if (c == '.') {
231                                 st = FLOAT_POS;
232                         }
233 #endif
234                         else {
235                                 st = ERROR;
236                         }
237                         break;
238
239                 case HEX:
240                         st = HEX_OK;
241                         /* no break */
242                 case HEX_OK:
243                         if (c >= '0' && c <= '9') {
244                                 if (add_to_res(c - '0', &res1, 16) < 0)
245                                         st = ERROR;
246                         }
247                         else if (c >= 'a' && c <= 'f') {
248                                 if (add_to_res(c - 'a' + 10, &res1, 16) < 0)
249                                         st = ERROR;
250                         }
251                         else if (c >= 'A' && c <= 'F') {
252                                 if (add_to_res(c - 'A' + 10, &res1, 16) < 0)
253                                         st = ERROR;
254                         }
255                         else {
256                                 st = ERROR;
257                         }
258                         break;
259
260
261                 case OCTAL_OK:
262                         if (c >= '0' && c <= '7') {
263                                 if (add_to_res(c - '0', &res1, 8) < 0)
264                                         st = ERROR;
265                         }
266                         else {
267                                 st = ERROR;
268                         }
269                         break;
270
271                 case BIN:
272                         st = BIN_OK;
273                         /* no break */
274                 case BIN_OK:
275                         if (c >= '0' && c <= '1') {
276                                 if (add_to_res(c - '0', &res1, 2) < 0)
277                                         st = ERROR;
278                         }
279                         else {
280                                 st = ERROR;
281                         }
282                         break;
283
284 #ifdef CMDLINE_HAVE_FLOAT
285                 case FLOAT_POS:
286                         if (c >= '0' && c <= '9') {
287                                 if (add_to_res(c - '0', &res2, 10) < 0)
288                                         st = ERROR;
289                                 else
290                                         st = FLOAT_POS_OK;
291                                 res3 = 10;
292                         }
293                         else {
294                                 st = ERROR;
295                         }
296                         break;
297
298                 case FLOAT_NEG:
299                         if (c >= '0' && c <= '9') {
300                                 if (add_to_res(c - '0', &res2, 10) < 0)
301                                         st = ERROR;
302                                 else
303                                         st = FLOAT_NEG_OK;
304                                 res3 = 10;
305                         }
306                         else {
307                                 st = ERROR;
308                         }
309                         break;
310
311                 case FLOAT_POS_OK:
312                         if (c >= '0' && c <= '9') {
313                                 if (add_to_res(c - '0', &res2, 10) < 0)
314                                         st = ERROR;
315                                 if (add_to_res(0, &res3, 10) < 0)
316                                         st = ERROR;
317                         }
318                         else {
319                                 st = ERROR;
320                         }
321                         break;
322
323                 case FLOAT_NEG_OK:
324                         if (c >= '0' && c <= '9') {
325                                 if (add_to_res(c - '0', &res2, 10) < 0)
326                                         st = ERROR;
327                                 if (add_to_res(0, &res3, 10) < 0)
328                                         st = ERROR;
329                         }
330                         else {
331                                 st = ERROR;
332                         }
333                         break;
334 #endif
335
336                 default:
337                         debug_printf("not impl ");
338
339                 }
340
341 #ifdef CMDLINE_HAVE_FLOAT
342                 debug_printf("(%"PRIu32")  (%"PRIu32")  (%"PRIu32")\n",
343                              res1, res2, res3);
344 #else
345                 debug_printf("(%"PRIu32")\n", res1);
346 #endif
347
348                 buf ++;
349                 c = *buf;
350
351                 /* token too long */
352                 if (buf-srcbuf > 127)
353                         return -1;
354         }
355
356         switch (st) {
357         case ZERO_OK:
358         case DEC_POS_OK:
359         case HEX_OK:
360         case OCTAL_OK:
361         case BIN_OK:
362                 if ( nd.type == INT8 && res1 <= INT8_MAX ) {
363                         if (res)
364                                 *(int8_t *)res = (int8_t) res1;
365                         return (buf-srcbuf);
366                 }
367                 else if ( nd.type == INT16 && res1 <= INT16_MAX ) {
368                         if (res)
369                                 *(int16_t *)res = (int16_t) res1;
370                         return (buf-srcbuf);
371                 }
372                 else if ( nd.type == INT32 && res1 <= INT32_MAX ) {
373                         if (res)
374                                 *(int32_t *)res = (int32_t) res1;
375                         return (buf-srcbuf);
376                 }
377                 else if ( nd.type == UINT8 && res1 <= UINT8_MAX ) {
378                         if (res)
379                                 *(uint8_t *)res = (uint8_t) res1;
380                         return (buf-srcbuf);
381                 }
382                 else if (nd.type == UINT16  && res1 <= UINT16_MAX ) {
383                         if (res)
384                                 *(uint16_t *)res = (uint16_t) res1;
385                         return (buf-srcbuf);
386                 }
387                 else if ( nd.type == UINT32 ) {
388                         if (res)
389                                 *(uint32_t *)res = (uint32_t) res1;
390                         return (buf-srcbuf);
391                 }
392                 else if ( nd.type == UINT64 ) {
393                         if (res)
394                                 *(uint64_t *)res = res1;
395                         return (buf-srcbuf);
396                 }
397 #ifdef CMDLINE_HAVE_FLOAT
398                 else if ( nd.type == FLOAT ) {
399                         if (res)
400                                 *(float *)res = (float)res1;
401                         return (buf-srcbuf);
402                 }
403 #endif
404                 else {
405                         return -1;
406                 }
407                 break;
408
409         case DEC_NEG_OK:
410                 if ( nd.type == INT8 && res1 <= INT8_MAX + 1 ) {
411                         if (res)
412                                 *(int8_t *)res = (int8_t) (-res1);
413                         return (buf-srcbuf);
414                 }
415                 else if ( nd.type == INT16 && res1 <= (uint16_t)INT16_MAX + 1 ) {
416                         if (res)
417                                 *(int16_t *)res = (int16_t) (-res1);
418                         return (buf-srcbuf);
419                 }
420                 else if ( nd.type == INT32 && res1 <= (uint32_t)INT32_MAX + 1 ) {
421                         if (res)
422                                 *(int32_t *)res = (int32_t) (-res1);
423                         return (buf-srcbuf);
424                 }
425 #ifdef CMDLINE_HAVE_FLOAT
426                 else if ( nd.type == FLOAT ) {
427                         if (res)
428                                 *(float *)res = - (float)res1;
429                         return (buf-srcbuf);
430                 }
431 #endif
432                 else {
433                         return -1;
434                 }
435                 break;
436
437 #ifdef CMDLINE_HAVE_FLOAT
438         case FLOAT_POS:
439         case FLOAT_POS_OK:
440                 if ( nd.type == FLOAT ) {
441                         if (res)
442                                 *(float *)res = (float)res1 + ((float)res2 / (float)res3);
443                         return (buf-srcbuf);
444
445                 }
446                 else {
447                         return -1;
448                 }
449                 break;
450
451         case FLOAT_NEG:
452         case FLOAT_NEG_OK:
453                 if ( nd.type == FLOAT ) {
454                         if (res)
455                                 *(float *)res = - ((float)res1 + ((float)res2 / (float)res3));
456                         return (buf-srcbuf);
457
458                 }
459                 else {
460                         return -1;
461                 }
462                 break;
463 #endif
464         default:
465                 debug_printf("error\n");
466                 return -1;
467         }
468 }
469
470
471 /* parse an int or a float */
472 int
473 cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
474 {
475         struct cmdline_token_num_data nd;
476
477         memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
478
479         /* should not happen.... don't so this test */
480         /* if (nd.type >= (sizeof(num_help)/sizeof(const char *))) */
481         /* return -1; */
482
483         strncpy(dstbuf, num_help[nd.type], size);
484         dstbuf[size-1] = '\0';
485         return 0;
486 }