1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation.
3  * Copyright (c) 2009, Olivier MATZ <[email protected]>
4  * All rights reserved.
5  */
6 
7 #include <stdio.h>
8 #include <stdint.h>
9 #include <inttypes.h>
10 #include <ctype.h>
11 #include <string.h>
12 #include <stdarg.h>
13 #include <errno.h>
14 #include <rte_string_fns.h>
15 
16 #include "cmdline_parse.h"
17 #include "cmdline_parse_num.h"
18 
19 #ifdef RTE_LIBRTE_CMDLINE_DEBUG
20 #define debug_printf(args...) printf(args)
21 #else
22 #define debug_printf(args...) do {} while(0)
23 #endif
24 
25 struct cmdline_token_ops cmdline_token_num_ops = {
26 	.parse = cmdline_parse_num,
27 	.complete_get_nb = NULL,
28 	.complete_get_elt = NULL,
29 	.get_help = cmdline_get_help_num,
30 };
31 
32 
33 enum num_parse_state_t {
34 	START,
35 	DEC_NEG,
36 	BIN,
37 	HEX,
38 
39 	ERROR,
40 
41 	FIRST_OK, /* not used */
42 	ZERO_OK,
43 	HEX_OK,
44 	OCTAL_OK,
45 	BIN_OK,
46 	DEC_NEG_OK,
47 	DEC_POS_OK,
48 };
49 
50 /* Keep it sync with enum in .h */
51 static const char * num_help[] = {
52 	"UINT8", "UINT16", "UINT32", "UINT64",
53 	"INT8", "INT16", "INT32", "INT64",
54 };
55 
56 static inline int
57 add_to_res(unsigned int c, uint64_t *res, unsigned int base)
58 {
59 	/* overflow */
60 	if ( (UINT64_MAX - c) / base < *res ) {
61 		return -1;
62 	}
63 
64 	*res = (uint64_t) (*res * base + c);
65 	return 0;
66 }
67 
68 static int
69 check_res_size(struct cmdline_token_num_data *nd, unsigned ressize)
70 {
71 	switch (nd->type) {
72 	case INT8:
73 	case UINT8:
74 		if (ressize < sizeof(int8_t))
75 			return -1;
76 		break;
77 	case INT16:
78 	case UINT16:
79 		if (ressize < sizeof(int16_t))
80 			return -1;
81 		break;
82 	case INT32:
83 	case UINT32:
84 		if (ressize < sizeof(int32_t))
85 			return -1;
86 		break;
87 	case INT64:
88 	case UINT64:
89 		if (ressize < sizeof(int64_t))
90 			return -1;
91 		break;
92 	default:
93 		return -1;
94 	}
95 	return 0;
96 }
97 
98 /* parse an int */
99 int
100 cmdline_parse_num(cmdline_parse_token_hdr_t *tk, const char *srcbuf, void *res,
101 	unsigned ressize)
102 {
103 	struct cmdline_token_num_data nd;
104 	enum num_parse_state_t st = START;
105 	const char * buf;
106 	char c;
107 	uint64_t res1 = 0;
108 
109 	if (!tk)
110 		return -1;
111 
112 	if (!srcbuf || !*srcbuf)
113 		return -1;
114 
115 	buf = srcbuf;
116 	c = *buf;
117 
118 	memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
119 
120 	/* check that we have enough room in res */
121 	if (res) {
122 		if (check_res_size(&nd, ressize) < 0)
123 			return -1;
124 	}
125 
126 	while ( st != ERROR && c && ! cmdline_isendoftoken(c) ) {
127 		debug_printf("%c %x -> ", c, c);
128 		switch (st) {
129 		case START:
130 			if (c == '-') {
131 				st = DEC_NEG;
132 			}
133 			else if (c == '0') {
134 				st = ZERO_OK;
135 			}
136 			else if (c >= '1' && c <= '9') {
137 				if (add_to_res(c - '0', &res1, 10) < 0)
138 					st = ERROR;
139 				else
140 					st = DEC_POS_OK;
141 			}
142 			else  {
143 				st = ERROR;
144 			}
145 			break;
146 
147 		case ZERO_OK:
148 			if (c == 'x') {
149 				st = HEX;
150 			}
151 			else if (c == 'b') {
152 				st = BIN;
153 			}
154 			else if (c >= '0' && c <= '7') {
155 				if (add_to_res(c - '0', &res1, 10) < 0)
156 					st = ERROR;
157 				else
158 					st = OCTAL_OK;
159 			}
160 			else  {
161 				st = ERROR;
162 			}
163 			break;
164 
165 		case DEC_NEG:
166 			if (c >= '0' && c <= '9') {
167 				if (add_to_res(c - '0', &res1, 10) < 0)
168 					st = ERROR;
169 				else
170 					st = DEC_NEG_OK;
171 			}
172 			else {
173 				st = ERROR;
174 			}
175 			break;
176 
177 		case DEC_NEG_OK:
178 			if (c >= '0' && c <= '9') {
179 				if (add_to_res(c - '0', &res1, 10) < 0)
180 					st = ERROR;
181 			}
182 			else {
183 				st = ERROR;
184 			}
185 			break;
186 
187 		case DEC_POS_OK:
188 			if (c >= '0' && c <= '9') {
189 				if (add_to_res(c - '0', &res1, 10) < 0)
190 					st = ERROR;
191 			}
192 			else {
193 				st = ERROR;
194 			}
195 			break;
196 
197 		case HEX:
198 			st = HEX_OK;
199 			/* fall-through no break */
200 		case HEX_OK:
201 			if (c >= '0' && c <= '9') {
202 				if (add_to_res(c - '0', &res1, 16) < 0)
203 					st = ERROR;
204 			}
205 			else if (c >= 'a' && c <= 'f') {
206 				if (add_to_res(c - 'a' + 10, &res1, 16) < 0)
207 					st = ERROR;
208 			}
209 			else if (c >= 'A' && c <= 'F') {
210 				if (add_to_res(c - 'A' + 10, &res1, 16) < 0)
211 					st = ERROR;
212 			}
213 			else {
214 				st = ERROR;
215 			}
216 			break;
217 
218 
219 		case OCTAL_OK:
220 			if (c >= '0' && c <= '7') {
221 				if (add_to_res(c - '0', &res1, 8) < 0)
222 					st = ERROR;
223 			}
224 			else {
225 				st = ERROR;
226 			}
227 			break;
228 
229 		case BIN:
230 			st = BIN_OK;
231 			/* fall-through */
232 		case BIN_OK:
233 			if (c >= '0' && c <= '1') {
234 				if (add_to_res(c - '0', &res1, 2) < 0)
235 					st = ERROR;
236 			}
237 			else {
238 				st = ERROR;
239 			}
240 			break;
241 		default:
242 			debug_printf("not impl ");
243 
244 		}
245 
246 		debug_printf("(%"PRIu64")\n", res1);
247 
248 		buf ++;
249 		c = *buf;
250 
251 		/* token too long */
252 		if (buf-srcbuf > 127)
253 			return -1;
254 	}
255 
256 	switch (st) {
257 	case ZERO_OK:
258 	case DEC_POS_OK:
259 	case HEX_OK:
260 	case OCTAL_OK:
261 	case BIN_OK:
262 		if ( nd.type == INT8 && res1 <= INT8_MAX ) {
263 			if (res) *(int8_t *)res = (int8_t) res1;
264 			return buf-srcbuf;
265 		}
266 		else if ( nd.type == INT16 && res1 <= INT16_MAX ) {
267 			if (res) *(int16_t *)res = (int16_t) res1;
268 			return buf-srcbuf;
269 		}
270 		else if ( nd.type == INT32 && res1 <= INT32_MAX ) {
271 			if (res) *(int32_t *)res = (int32_t) res1;
272 			return buf-srcbuf;
273 		}
274 		else if ( nd.type == INT64 && res1 <= INT64_MAX ) {
275 			if (res) *(int64_t *)res = (int64_t) res1;
276 			return buf-srcbuf;
277 		}
278 		else if ( nd.type == UINT8 && res1 <= UINT8_MAX ) {
279 			if (res) *(uint8_t *)res = (uint8_t) res1;
280 			return buf-srcbuf;
281 		}
282 		else if (nd.type == UINT16  && res1 <= UINT16_MAX ) {
283 			if (res) *(uint16_t *)res = (uint16_t) res1;
284 			return buf-srcbuf;
285 		}
286 		else if ( nd.type == UINT32 && res1 <= UINT32_MAX ) {
287 			if (res) *(uint32_t *)res = (uint32_t) res1;
288 			return buf-srcbuf;
289 		}
290 		else if ( nd.type == UINT64 ) {
291 			if (res) *(uint64_t *)res = res1;
292 			return buf-srcbuf;
293 		}
294 		else {
295 			return -1;
296 		}
297 		break;
298 
299 	case DEC_NEG_OK:
300 		if ( nd.type == INT8 && res1 <= INT8_MAX + 1 ) {
301 			if (res) *(int8_t *)res = (int8_t) (-res1);
302 			return buf-srcbuf;
303 		}
304 		else if ( nd.type == INT16 && res1 <= (uint16_t)INT16_MAX + 1 ) {
305 			if (res) *(int16_t *)res = (int16_t) (-res1);
306 			return buf-srcbuf;
307 		}
308 		else if ( nd.type == INT32 && res1 <= (uint32_t)INT32_MAX + 1 ) {
309 			if (res) *(int32_t *)res = (int32_t) (-res1);
310 			return buf-srcbuf;
311 		}
312 		else if ( nd.type == INT64 && res1 <= (uint64_t)INT64_MAX + 1 ) {
313 			if (res) *(int64_t *)res = (int64_t) (-res1);
314 			return buf-srcbuf;
315 		}
316 		else {
317 			return -1;
318 		}
319 		break;
320 	default:
321 		debug_printf("error\n");
322 		return -1;
323 	}
324 }
325 
326 
327 /* parse an int */
328 int
329 cmdline_get_help_num(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
330 {
331 	struct cmdline_token_num_data nd;
332 	int ret;
333 
334 	if (!tk)
335 		return -1;
336 
337 	memcpy(&nd, &((struct cmdline_token_num *)tk)->num_data, sizeof(nd));
338 
339 	/* should not happen.... don't so this test */
340 	/* if (nd.type >= (sizeof(num_help)/sizeof(const char *))) */
341 	/* return -1; */
342 
343 	ret = snprintf(dstbuf, size, "%s", num_help[nd.type]);
344 	if (ret < 0)
345 		return -1;
346 	dstbuf[size-1] = '\0';
347 	return 0;
348 }
349