xref: /dpdk/lib/argparse/rte_argparse.c (revision 31ed9f9f43bb03129aa9c5134e8e4fd20a4ce762)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2024 HiSilicon Limited
3  */
4 
5 #include <errno.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #include <rte_log.h>
10 
11 #include "rte_argparse.h"
12 
13 RTE_LOG_REGISTER_DEFAULT(rte_argparse_logtype, INFO);
14 #define RTE_LOGTYPE_ARGPARSE rte_argparse_logtype
15 #define ARGPARSE_LOG(level, ...) \
16 	RTE_LOG_LINE(level, ARGPARSE, "" __VA_ARGS__)
17 
18 #define ARG_ATTR_HAS_VAL_MASK		RTE_GENMASK64(1, 0)
19 #define ARG_ATTR_VAL_TYPE_MASK		RTE_GENMASK64(9, 2)
20 #define ARG_ATTR_SUPPORT_MULTI_MASK	RTE_BIT64(10)
21 #define ARG_ATTR_FLAG_PARSED_MASK	RTE_BIT64(63)
22 
23 static inline bool
24 is_arg_optional(const struct rte_argparse_arg *arg)
25 {
26 	return arg->name_long[0] == '-';
27 }
28 
29 static inline bool
30 is_arg_positional(const struct rte_argparse_arg *arg)
31 {
32 	return arg->name_long[0] != '-';
33 }
34 
35 static inline uint32_t
36 arg_attr_has_val(const struct rte_argparse_arg *arg)
37 {
38 	return RTE_FIELD_GET64(ARG_ATTR_HAS_VAL_MASK, arg->flags);
39 }
40 
41 static inline uint32_t
42 arg_attr_val_type(const struct rte_argparse_arg *arg)
43 {
44 	return RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, arg->flags);
45 }
46 
47 static inline bool
48 arg_attr_flag_multi(const struct rte_argparse_arg *arg)
49 {
50 	return RTE_FIELD_GET64(ARG_ATTR_SUPPORT_MULTI_MASK, arg->flags);
51 }
52 
53 static inline uint32_t
54 arg_attr_unused_bits(const struct rte_argparse_arg *arg)
55 {
56 #define USED_BIT_MASK	(ARG_ATTR_HAS_VAL_MASK | ARG_ATTR_VAL_TYPE_MASK | \
57 			 ARG_ATTR_SUPPORT_MULTI_MASK)
58 	return arg->flags & ~USED_BIT_MASK;
59 }
60 
61 static int
62 verify_arg_name(const struct rte_argparse_arg *arg)
63 {
64 	if (is_arg_optional(arg)) {
65 		if (strlen(arg->name_long) <= 3) {
66 			ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long);
67 			return -EINVAL;
68 		}
69 		if (arg->name_long[1] != '-') {
70 			ARGPARSE_LOG(ERR, "optional long name %s must only start with '--'",
71 				     arg->name_long);
72 			return -EINVAL;
73 		}
74 		if (arg->name_long[2] == '-') {
75 			ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'",
76 				     arg->name_long);
77 			return -EINVAL;
78 		}
79 	}
80 
81 	if (arg->name_short == NULL)
82 		return 0;
83 
84 	if (!is_arg_optional(arg)) {
85 		ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!",
86 			     arg->name_short);
87 		return -EINVAL;
88 	}
89 
90 	if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' ||
91 		arg->name_short[1] == '-') {
92 		ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter",
93 			     arg->name_short);
94 		return -EINVAL;
95 	}
96 
97 	return 0;
98 }
99 
100 static int
101 verify_arg_help(const struct rte_argparse_arg *arg)
102 {
103 	if (arg->help == NULL) {
104 		ARGPARSE_LOG(ERR, "argument %s must have help info!", arg->name_long);
105 		return -EINVAL;
106 	}
107 
108 	return 0;
109 }
110 
111 static int
112 verify_arg_has_val(const struct rte_argparse_arg *arg)
113 {
114 	uint32_t has_val = arg_attr_has_val(arg);
115 
116 	if (is_arg_positional(arg)) {
117 		if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE)
118 			return 0;
119 		ARGPARSE_LOG(ERR, "argument %s is positional, should has zero or required-val!",
120 			     arg->name_long);
121 		return -EINVAL;
122 	}
123 
124 	if (has_val == 0) {
125 		ARGPARSE_LOG(ERR, "argument %s is optional, has-val config wrong!",
126 			     arg->name_long);
127 		return -EINVAL;
128 	}
129 
130 	return 0;
131 }
132 
133 static int
134 verify_arg_saver(const struct rte_argparse *obj, uint32_t index)
135 {
136 	uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX);
137 	const struct rte_argparse_arg *arg = &obj->args[index];
138 	uint32_t val_type = arg_attr_val_type(arg);
139 	uint32_t has_val = arg_attr_has_val(arg);
140 
141 	if (arg->val_saver == NULL) {
142 		if (val_type != 0) {
143 			ARGPARSE_LOG(ERR, "argument %s parse by callback, val-type must be zero!",
144 				     arg->name_long);
145 			return -EINVAL;
146 		}
147 
148 		if (obj->callback == NULL) {
149 			ARGPARSE_LOG(ERR, "argument %s parse by callback, but callback is NULL!",
150 				     arg->name_long);
151 			return -EINVAL;
152 		}
153 
154 		return 0;
155 	}
156 
157 	if (val_type == 0 || val_type >= cmp_max) {
158 		ARGPARSE_LOG(ERR, "argument %s val-type config wrong!", arg->name_long);
159 		return -EINVAL;
160 	}
161 
162 	if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) {
163 		ARGPARSE_LOG(ERR, "argument %s has required value, val-set should be NULL!",
164 			     arg->name_long);
165 		return -EINVAL;
166 	}
167 
168 	return 0;
169 }
170 
171 static int
172 verify_arg_flags(const struct rte_argparse *obj, uint32_t index)
173 {
174 	const struct rte_argparse_arg *arg = &obj->args[index];
175 	uint32_t unused_bits = arg_attr_unused_bits(arg);
176 
177 	if (unused_bits != 0) {
178 		ARGPARSE_LOG(ERR, "argument %s flags set wrong!", arg->name_long);
179 		return -EINVAL;
180 	}
181 
182 	if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI))
183 		return 0;
184 
185 	if (is_arg_positional(arg)) {
186 		ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!",
187 			     arg->name_long);
188 		return -EINVAL;
189 	}
190 
191 	if (arg->val_saver != NULL) {
192 		ARGPARSE_LOG(ERR, "argument %s could occur multiple times, should use callback to parse!",
193 			     arg->name_long);
194 		return -EINVAL;
195 	}
196 
197 	if (obj->callback == NULL) {
198 		ARGPARSE_LOG(ERR, "argument %s should use callback to parse, but callback is NULL!",
199 			     arg->name_long);
200 		return -EINVAL;
201 	}
202 
203 	return 0;
204 }
205 
206 static int
207 verify_arg_repeat(const struct rte_argparse *self, uint32_t index)
208 {
209 	const struct rte_argparse_arg *arg = &self->args[index];
210 	uint32_t i;
211 
212 	for (i = 0; i < index; i++) {
213 		if (!strcmp(arg->name_long, self->args[i].name_long)) {
214 			ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long);
215 			return -EINVAL;
216 		}
217 	}
218 
219 	if (arg->name_short == NULL)
220 		return 0;
221 
222 	for (i = 0; i < index; i++) {
223 		if (self->args[i].name_short == NULL)
224 			continue;
225 		if (!strcmp(arg->name_short, self->args[i].name_short)) {
226 			ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short);
227 			return -EINVAL;
228 		}
229 	}
230 
231 	return 0;
232 }
233 
234 static int
235 verify_argparse_arg(const struct rte_argparse *obj, uint32_t index)
236 {
237 	const struct rte_argparse_arg *arg = &obj->args[index];
238 	int ret;
239 
240 	ret = verify_arg_name(arg);
241 	if (ret != 0)
242 		return ret;
243 
244 	ret = verify_arg_help(arg);
245 	if (ret != 0)
246 		return ret;
247 
248 	ret = verify_arg_has_val(arg);
249 	if (ret != 0)
250 		return ret;
251 
252 	ret = verify_arg_saver(obj, index);
253 	if (ret != 0)
254 		return ret;
255 
256 	ret = verify_arg_flags(obj, index);
257 	if (ret != 0)
258 		return ret;
259 
260 	ret = verify_arg_repeat(obj, index);
261 	if (ret != 0)
262 		return ret;
263 
264 	return 0;
265 }
266 
267 static int
268 verify_argparse(const struct rte_argparse *obj)
269 {
270 	uint32_t idx;
271 	int ret;
272 
273 	if (obj->prog_name == NULL) {
274 		ARGPARSE_LOG(ERR, "program name is NULL!");
275 		return -EINVAL;
276 	}
277 
278 	if (obj->usage == NULL) {
279 		ARGPARSE_LOG(ERR, "usage is NULL!");
280 		return -EINVAL;
281 	}
282 
283 	for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) {
284 		if (obj->reserved[idx] != 0) {
285 			ARGPARSE_LOG(ERR, "reserved field must be zero!");
286 			return -EINVAL;
287 		}
288 	}
289 
290 	idx = 0;
291 	while (obj->args[idx].name_long != NULL) {
292 		ret = verify_argparse_arg(obj, idx);
293 		if (ret != 0)
294 			return ret;
295 		idx++;
296 	}
297 
298 	return 0;
299 }
300 
301 static uint32_t
302 calc_position_count(const struct rte_argparse *obj)
303 {
304 	const struct rte_argparse_arg *arg;
305 	uint32_t count = 0;
306 	uint32_t i;
307 
308 	for (i = 0; /* NULL */; i++) {
309 		arg = &obj->args[i];
310 		if (obj->args[i].name_long == NULL)
311 			break;
312 		if (is_arg_positional(arg))
313 			count++;
314 	}
315 
316 	return count;
317 }
318 
319 static struct rte_argparse_arg *
320 find_position_arg(struct rte_argparse *obj, uint32_t index)
321 {
322 	struct rte_argparse_arg *arg;
323 	uint32_t count = 0;
324 	uint32_t i;
325 
326 	for (i = 0; /* NULL */; i++) {
327 		arg = &obj->args[i];
328 		if (arg->name_long == NULL)
329 			break;
330 		if (!is_arg_positional(arg))
331 			continue;
332 		count++;
333 		if (count == index)
334 			return arg;
335 	}
336 
337 	return NULL;
338 }
339 
340 static bool
341 is_arg_match(struct rte_argparse_arg *arg, const char *curr_argv, uint32_t len)
342 {
343 	if (strlen(arg->name_long) == len && strncmp(arg->name_long, curr_argv, len) == 0)
344 		return true;
345 
346 	if (arg->name_short == NULL)
347 		return false;
348 
349 	if (strlen(arg->name_short) == len && strncmp(arg->name_short, curr_argv, len) == 0)
350 		return true;
351 
352 	return false;
353 }
354 
355 static struct rte_argparse_arg *
356 find_option_arg(struct rte_argparse *obj, const char *curr_argv, const char *has_equal,
357 		const char **arg_name)
358 {
359 	uint32_t len = strlen(curr_argv) - (has_equal != NULL ? strlen(has_equal) : 0);
360 	struct rte_argparse_arg *arg;
361 	uint32_t i;
362 	bool match;
363 
364 	for (i = 0; /* nothing */; i++) {
365 		arg = &obj->args[i];
366 		if (arg->name_long == NULL)
367 			break;
368 		match = is_arg_match(arg, curr_argv, len);
369 		if (match) {
370 			/* Obtains the exact matching name (long or short). */
371 			*arg_name = len > 2 ? arg->name_long : arg->name_short;
372 			return arg;
373 		}
374 	}
375 
376 	return NULL;
377 }
378 
379 static int
380 parse_arg_int(struct rte_argparse_arg *arg, const char *value)
381 {
382 	char *s = NULL;
383 
384 	if (value == NULL) {
385 		*(int *)arg->val_saver = (int)(intptr_t)arg->val_set;
386 		return 0;
387 	}
388 
389 	errno = 0;
390 	*(int *)arg->val_saver = strtol(value, &s, 0);
391 	if (errno == ERANGE) {
392 		ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long);
393 		return -EINVAL;
394 	}
395 
396 	if (s[0] != '\0') {
397 		ARGPARSE_LOG(ERR, "argument %s expect an integer value!", arg->name_long);
398 		return -EINVAL;
399 	}
400 
401 	return 0;
402 }
403 
404 static int
405 parse_arg_autosave(struct rte_argparse_arg *arg, const char *value)
406 {
407 	static struct {
408 		int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value);
409 	} map[] = {
410 		/* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */
411 		{ NULL          },
412 		{ parse_arg_int },
413 	};
414 	uint32_t index = arg_attr_val_type(arg);
415 	int ret = -EINVAL;
416 
417 	if (index > 0 && index < RTE_DIM(map))
418 		ret = map[index].f_parse_type(arg, value);
419 
420 	return ret;
421 }
422 
423 static int
424 parse_arg_val(struct rte_argparse *obj, struct rte_argparse_arg *arg, char *value)
425 {
426 	int ret;
427 
428 	if (arg->val_saver == NULL)
429 		ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque);
430 	else
431 		ret = parse_arg_autosave(arg, value);
432 	if (ret != 0) {
433 		ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg->name_long);
434 		return ret;
435 	}
436 
437 	return 0;
438 }
439 
440 static bool
441 is_help(const char *curr_argv)
442 {
443 	return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0;
444 }
445 
446 static int
447 parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help)
448 {
449 	uint32_t position_count = calc_position_count(obj);
450 	struct rte_argparse_arg *arg;
451 	uint32_t position_index = 0;
452 	const char *arg_name;
453 	char *curr_argv;
454 	char *has_equal;
455 	char *value;
456 	int ret;
457 	int i;
458 
459 	for (i = 1; i < argc; i++) {
460 		curr_argv = argv[i];
461 		if (curr_argv[0] != '-') {
462 			/* process positional parameters. */
463 			position_index++;
464 			if (position_index > position_count) {
465 				ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv);
466 				return -EINVAL;
467 			}
468 			arg = find_position_arg(obj, position_index);
469 			ret = parse_arg_val(obj, arg, curr_argv);
470 			if (ret != 0)
471 				return ret;
472 			continue;
473 		}
474 
475 		/* process optional parameters. */
476 		if (is_help(curr_argv)) {
477 			*show_help = true;
478 			continue;
479 		}
480 
481 		has_equal = strchr(curr_argv, '=');
482 		arg_name = NULL;
483 		arg = find_option_arg(obj, curr_argv, has_equal, &arg_name);
484 		if (arg == NULL || arg_name == NULL) {
485 			ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv);
486 			return -EINVAL;
487 		}
488 
489 		if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) {
490 			ARGPARSE_LOG(ERR, "argument %s should not occur multiple!",
491 				     arg_name);
492 			return -EINVAL;
493 		}
494 
495 		value = (has_equal != NULL ? has_equal + 1 : NULL);
496 		if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) {
497 			if (value != NULL) {
498 				ARGPARSE_LOG(ERR, "argument %s should not take value!",
499 					     arg_name);
500 				return -EINVAL;
501 			}
502 		} else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) {
503 			if (value == NULL) {
504 				if (i >= argc - 1) {
505 					ARGPARSE_LOG(ERR, "argument %s doesn't have value!",
506 						     arg_name);
507 					return -EINVAL;
508 				}
509 				/* Set value and make i move next. */
510 				value = argv[++i];
511 			}
512 		} else {
513 			/* Do nothing, because it's optional value, only support arg=val or arg. */
514 		}
515 
516 		ret = parse_arg_val(obj, arg, value);
517 		if (ret != 0)
518 			return ret;
519 
520 		/* This argument parsed success! then mark it parsed. */
521 		arg->flags |= ARG_ATTR_FLAG_PARSED_MASK;
522 	}
523 
524 	return 0;
525 }
526 
527 static void
528 show_args_pos_help(const struct rte_argparse *obj)
529 {
530 	uint32_t position_count = calc_position_count(obj);
531 	const struct rte_argparse_arg *arg;
532 	uint32_t i;
533 
534 	if (position_count == 0)
535 		return;
536 
537 	printf("\npositional arguments:\n");
538 	for (i = 0; /* NULL */; i++) {
539 		arg = &obj->args[i];
540 		if (arg->name_long == NULL)
541 			break;
542 		if (!is_arg_positional(arg))
543 			continue;
544 		printf(" %s: %s\n", arg->name_long, arg->help);
545 	}
546 }
547 
548 static void
549 show_args_opt_help(const struct rte_argparse *obj)
550 {
551 	const struct rte_argparse_arg *arg;
552 	uint32_t i;
553 
554 	printf("\noptions:\n"
555 	       " -h, --help: show this help message and exit.\n");
556 	for (i = 0; /* NULL */; i++) {
557 		arg = &obj->args[i];
558 		if (arg->name_long == NULL)
559 			break;
560 		if (!is_arg_optional(arg))
561 			continue;
562 		if (arg->name_short != NULL)
563 			printf(" %s, %s: %s\n", arg->name_short, arg->name_long, arg->help);
564 		else
565 			printf(" %s: %s\n", arg->name_long, arg->help);
566 	}
567 }
568 
569 static void
570 show_args_help(const struct rte_argparse *obj)
571 {
572 	printf("usage: %s %s\n", obj->prog_name, obj->usage);
573 	if (obj->descriptor != NULL)
574 		printf("\ndescriptor: %s\n", obj->descriptor);
575 
576 	show_args_pos_help(obj);
577 	show_args_opt_help(obj);
578 
579 	if (obj->epilog != NULL)
580 		printf("\n%s\n", obj->epilog);
581 }
582 
583 int
584 rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv)
585 {
586 	bool show_help = false;
587 	int ret;
588 
589 	ret = verify_argparse(obj);
590 	if (ret != 0)
591 		goto error;
592 
593 	ret = parse_args(obj, argc, argv, &show_help);
594 	if (ret != 0)
595 		goto error;
596 
597 	if (show_help) {
598 		show_args_help(obj);
599 		exit(0);
600 	}
601 
602 	return 0;
603 
604 error:
605 	if (obj->exit_on_error)
606 		exit(ret);
607 	return ret;
608 }
609