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 doesn't 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 doesn't 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, must config 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-value 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 parsed by callback, value-type should not be set!", 144 arg->name_long); 145 return -EINVAL; 146 } 147 148 if (obj->callback == NULL) { 149 ARGPARSE_LOG(ERR, "argument %s parsed 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 value-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, value-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 unused bits should not be set!", 179 arg->name_long); 180 return -EINVAL; 181 } 182 183 if (!(arg->flags & RTE_ARGPARSE_ARG_SUPPORT_MULTI)) 184 return 0; 185 186 if (is_arg_positional(arg)) { 187 ARGPARSE_LOG(ERR, "argument %s is positional, don't support multiple times!", 188 arg->name_long); 189 return -EINVAL; 190 } 191 192 if (arg->val_saver != NULL) { 193 ARGPARSE_LOG(ERR, "argument %s supports multiple times, should use callback to parse!", 194 arg->name_long); 195 return -EINVAL; 196 } 197 198 return 0; 199 } 200 201 static int 202 verify_arg_repeat(const struct rte_argparse *self, uint32_t index) 203 { 204 const struct rte_argparse_arg *arg = &self->args[index]; 205 uint32_t i; 206 207 for (i = 0; i < index; i++) { 208 if (!strcmp(arg->name_long, self->args[i].name_long)) { 209 ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); 210 return -EINVAL; 211 } 212 } 213 214 if (arg->name_short == NULL) 215 return 0; 216 217 for (i = 0; i < index; i++) { 218 if (self->args[i].name_short == NULL) 219 continue; 220 if (!strcmp(arg->name_short, self->args[i].name_short)) { 221 ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); 222 return -EINVAL; 223 } 224 } 225 226 return 0; 227 } 228 229 static int 230 verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) 231 { 232 const struct rte_argparse_arg *arg = &obj->args[index]; 233 int ret; 234 235 ret = verify_arg_name(arg); 236 if (ret != 0) 237 return ret; 238 239 ret = verify_arg_help(arg); 240 if (ret != 0) 241 return ret; 242 243 ret = verify_arg_has_val(arg); 244 if (ret != 0) 245 return ret; 246 247 ret = verify_arg_saver(obj, index); 248 if (ret != 0) 249 return ret; 250 251 ret = verify_arg_flags(obj, index); 252 if (ret != 0) 253 return ret; 254 255 ret = verify_arg_repeat(obj, index); 256 if (ret != 0) 257 return ret; 258 259 return 0; 260 } 261 262 static int 263 verify_argparse(const struct rte_argparse *obj) 264 { 265 uint32_t idx; 266 int ret; 267 268 if (obj->prog_name == NULL) { 269 ARGPARSE_LOG(ERR, "program name is NULL!"); 270 return -EINVAL; 271 } 272 273 if (obj->usage == NULL) { 274 ARGPARSE_LOG(ERR, "usage is NULL!"); 275 return -EINVAL; 276 } 277 278 for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) { 279 if (obj->reserved[idx] != 0) { 280 ARGPARSE_LOG(ERR, "reserved field must be zero!"); 281 return -EINVAL; 282 } 283 } 284 285 idx = 0; 286 while (obj->args[idx].name_long != NULL) { 287 ret = verify_argparse_arg(obj, idx); 288 if (ret != 0) 289 return ret; 290 idx++; 291 } 292 293 return 0; 294 } 295 296 static uint32_t 297 calc_position_count(const struct rte_argparse *obj) 298 { 299 const struct rte_argparse_arg *arg; 300 uint32_t count = 0; 301 uint32_t i; 302 303 for (i = 0; /* NULL */; i++) { 304 arg = &obj->args[i]; 305 if (obj->args[i].name_long == NULL) 306 break; 307 if (is_arg_positional(arg)) 308 count++; 309 } 310 311 return count; 312 } 313 314 static struct rte_argparse_arg * 315 find_position_arg(struct rte_argparse *obj, uint32_t index) 316 { 317 struct rte_argparse_arg *arg; 318 uint32_t count = 0; 319 uint32_t i; 320 321 for (i = 0; /* NULL */; i++) { 322 arg = &obj->args[i]; 323 if (arg->name_long == NULL) 324 break; 325 if (!is_arg_positional(arg)) 326 continue; 327 count++; 328 if (count == index) 329 return arg; 330 } 331 332 return NULL; 333 } 334 335 static bool 336 is_arg_match(struct rte_argparse_arg *arg, const char *curr_argv, uint32_t len) 337 { 338 if (strlen(arg->name_long) == len && strncmp(arg->name_long, curr_argv, len) == 0) 339 return true; 340 341 if (arg->name_short == NULL) 342 return false; 343 344 if (strlen(arg->name_short) == len && strncmp(arg->name_short, curr_argv, len) == 0) 345 return true; 346 347 return false; 348 } 349 350 static struct rte_argparse_arg * 351 find_option_arg(struct rte_argparse *obj, const char *curr_argv, const char *has_equal, 352 const char **arg_name) 353 { 354 uint32_t len = strlen(curr_argv) - (has_equal != NULL ? strlen(has_equal) : 0); 355 struct rte_argparse_arg *arg; 356 uint32_t i; 357 bool match; 358 359 for (i = 0; /* nothing */; i++) { 360 arg = &obj->args[i]; 361 if (arg->name_long == NULL) 362 break; 363 match = is_arg_match(arg, curr_argv, len); 364 if (match) { 365 /* Obtains the exact matching name (long or short). */ 366 *arg_name = len > 2 ? arg->name_long : arg->name_short; 367 return arg; 368 } 369 } 370 371 return NULL; 372 } 373 374 static int 375 parse_arg_int(struct rte_argparse_arg *arg, const char *value) 376 { 377 char *s = NULL; 378 379 if (value == NULL) { 380 *(int *)arg->val_saver = (int)(intptr_t)arg->val_set; 381 return 0; 382 } 383 384 errno = 0; 385 *(int *)arg->val_saver = strtol(value, &s, 0); 386 if (errno == ERANGE) { 387 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 388 return -EINVAL; 389 } 390 391 if (s[0] != '\0') { 392 ARGPARSE_LOG(ERR, "argument %s expect an integer value!", arg->name_long); 393 return -EINVAL; 394 } 395 396 return 0; 397 } 398 399 static int 400 parse_arg_u8(struct rte_argparse_arg *arg, const char *value) 401 { 402 unsigned long val; 403 char *s = NULL; 404 405 if (value == NULL) { 406 *(uint8_t *)arg->val_saver = (uint8_t)(intptr_t)arg->val_set; 407 return 0; 408 } 409 410 errno = 0; 411 val = strtoul(value, &s, 0); 412 if (errno == ERANGE || val > UINT8_MAX) { 413 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 414 return -EINVAL; 415 } 416 417 if (s[0] != '\0') { 418 ARGPARSE_LOG(ERR, "argument %s expect an uint8 value!", arg->name_long); 419 return -EINVAL; 420 } 421 422 *(uint8_t *)arg->val_saver = val; 423 424 return 0; 425 } 426 427 static int 428 parse_arg_u16(struct rte_argparse_arg *arg, const char *value) 429 { 430 unsigned long val; 431 char *s = NULL; 432 433 if (value == NULL) { 434 *(uint16_t *)arg->val_saver = (uint16_t)(intptr_t)arg->val_set; 435 return 0; 436 } 437 438 errno = 0; 439 val = strtoul(value, &s, 0); 440 if (errno == ERANGE || val > UINT16_MAX) { 441 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 442 return -EINVAL; 443 } 444 445 if (s[0] != '\0') { 446 ARGPARSE_LOG(ERR, "argument %s expect an uint16 value!", arg->name_long); 447 return -EINVAL; 448 } 449 450 *(uint16_t *)arg->val_saver = val; 451 452 return 0; 453 } 454 455 static int 456 parse_arg_u32(struct rte_argparse_arg *arg, const char *value) 457 { 458 unsigned long val; 459 char *s = NULL; 460 461 if (value == NULL) { 462 *(uint32_t *)arg->val_saver = (uint32_t)(intptr_t)arg->val_set; 463 return 0; 464 } 465 466 errno = 0; 467 val = strtoul(value, &s, 0); 468 if (errno == ERANGE || val > UINT32_MAX) { 469 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 470 return -EINVAL; 471 } 472 473 if (s[0] != '\0') { 474 ARGPARSE_LOG(ERR, "argument %s expect an uint32 value!", arg->name_long); 475 return -EINVAL; 476 } 477 478 *(uint32_t *)arg->val_saver = val; 479 480 return 0; 481 } 482 483 static int 484 parse_arg_u64(struct rte_argparse_arg *arg, const char *value) 485 { 486 unsigned long val; 487 char *s = NULL; 488 489 if (value == NULL) { 490 *(uint64_t *)arg->val_saver = (uint64_t)(intptr_t)arg->val_set; 491 return 0; 492 } 493 494 errno = 0; 495 val = strtoull(value, &s, 0); 496 if (errno == ERANGE) { 497 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 498 return -EINVAL; 499 } 500 501 if (s[0] != '\0') { 502 ARGPARSE_LOG(ERR, "argument %s expect an uint64 value!", arg->name_long); 503 return -EINVAL; 504 } 505 506 *(uint64_t *)arg->val_saver = val; 507 508 return 0; 509 } 510 511 static int 512 parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) 513 { 514 static struct { 515 int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value); 516 } map[] = { 517 /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ 518 { NULL }, 519 { parse_arg_int }, 520 { parse_arg_u8 }, 521 { parse_arg_u16 }, 522 { parse_arg_u32 }, 523 { parse_arg_u64 }, 524 }; 525 uint32_t index = arg_attr_val_type(arg); 526 int ret = -EINVAL; 527 528 if (index > 0 && index < RTE_DIM(map)) 529 ret = map[index].f_parse_type(arg, value); 530 531 return ret; 532 } 533 534 /* arg_parse indicates the name entered by the user, which can be long-name or short-name. */ 535 static int 536 parse_arg_val(struct rte_argparse *obj, const char *arg_name, 537 struct rte_argparse_arg *arg, char *value) 538 { 539 int ret; 540 541 if (arg->val_saver == NULL) 542 ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque); 543 else 544 ret = parse_arg_autosave(arg, value); 545 if (ret != 0) { 546 ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg_name); 547 return ret; 548 } 549 550 return 0; 551 } 552 553 static bool 554 is_help(const char *curr_argv) 555 { 556 return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0; 557 } 558 559 static int 560 parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) 561 { 562 uint32_t position_count = calc_position_count(obj); 563 struct rte_argparse_arg *arg; 564 uint32_t position_index = 0; 565 const char *arg_name; 566 char *curr_argv; 567 char *has_equal; 568 char *value; 569 int ret; 570 int i; 571 572 for (i = 1; i < argc; i++) { 573 curr_argv = argv[i]; 574 if (curr_argv[0] != '-') { 575 /* process positional parameters. */ 576 position_index++; 577 if (position_index > position_count) { 578 ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv); 579 return -EINVAL; 580 } 581 arg = find_position_arg(obj, position_index); 582 ret = parse_arg_val(obj, arg->name_long, arg, curr_argv); 583 if (ret != 0) 584 return ret; 585 continue; 586 } 587 588 /* process optional parameters. */ 589 if (is_help(curr_argv)) { 590 *show_help = true; 591 continue; 592 } 593 594 has_equal = strchr(curr_argv, '='); 595 arg_name = NULL; 596 arg = find_option_arg(obj, curr_argv, has_equal, &arg_name); 597 if (arg == NULL || arg_name == NULL) { 598 ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv); 599 return -EINVAL; 600 } 601 602 if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) { 603 ARGPARSE_LOG(ERR, "argument %s should not occur multiple!", 604 arg_name); 605 return -EINVAL; 606 } 607 608 value = (has_equal != NULL ? has_equal + 1 : NULL); 609 if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) { 610 if (value != NULL) { 611 ARGPARSE_LOG(ERR, "argument %s should not take value!", 612 arg_name); 613 return -EINVAL; 614 } 615 } else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) { 616 if (value == NULL) { 617 if (i >= argc - 1) { 618 ARGPARSE_LOG(ERR, "argument %s doesn't have value!", 619 arg_name); 620 return -EINVAL; 621 } 622 /* Set value and make i move next. */ 623 value = argv[++i]; 624 } 625 } else { 626 /* Do nothing, because it's optional value, only support arg=val or arg. */ 627 } 628 629 ret = parse_arg_val(obj, arg_name, arg, value); 630 if (ret != 0) 631 return ret; 632 633 /* This argument parsed success! then mark it parsed. */ 634 arg->flags |= ARG_ATTR_FLAG_PARSED_MASK; 635 } 636 637 return 0; 638 } 639 640 static uint32_t 641 calc_help_align(const struct rte_argparse *obj) 642 { 643 const struct rte_argparse_arg *arg; 644 uint32_t width = 12; /* Default "-h, --help " len. */ 645 uint32_t len; 646 uint32_t i; 647 648 for (i = 0; /* NULL */; i++) { 649 arg = &obj->args[i]; 650 if (arg->name_long == NULL) 651 break; 652 len = strlen(arg->name_long); 653 if (is_arg_optional(arg) && arg->name_short != NULL) { 654 len += strlen(", "); 655 len += strlen(arg->name_short); 656 } 657 width = RTE_MAX(width, 1 + len + 2); /* start with 1 & end with 2 space. */ 658 } 659 660 return width; 661 } 662 663 static void 664 show_oneline_help(const struct rte_argparse_arg *arg, uint32_t width) 665 { 666 uint32_t len = 0; 667 uint32_t i; 668 669 if (arg->name_short != NULL) 670 len = printf(" %s,", arg->name_short); 671 len += printf(" %s", arg->name_long); 672 673 for (i = len; i < width; i++) 674 printf(" "); 675 676 printf("%s\n", arg->help); 677 } 678 679 static void 680 show_args_pos_help(const struct rte_argparse *obj, uint32_t align) 681 { 682 uint32_t position_count = calc_position_count(obj); 683 const struct rte_argparse_arg *arg; 684 uint32_t i; 685 686 if (position_count == 0) 687 return; 688 689 printf("\npositional arguments:\n"); 690 for (i = 0; /* NULL */; i++) { 691 arg = &obj->args[i]; 692 if (arg->name_long == NULL) 693 break; 694 if (!is_arg_positional(arg)) 695 continue; 696 show_oneline_help(arg, align); 697 } 698 } 699 700 static void 701 show_args_opt_help(const struct rte_argparse *obj, uint32_t align) 702 { 703 static const struct rte_argparse_arg help = { 704 .name_long = "--help", 705 .name_short = "-h", 706 .help = "show this help message and exit.", 707 }; 708 const struct rte_argparse_arg *arg; 709 uint32_t i; 710 711 printf("\noptions:\n"); 712 show_oneline_help(&help, align); 713 for (i = 0; /* NULL */; i++) { 714 arg = &obj->args[i]; 715 if (arg->name_long == NULL) 716 break; 717 if (!is_arg_optional(arg)) 718 continue; 719 show_oneline_help(arg, align); 720 } 721 } 722 723 static void 724 show_args_help(const struct rte_argparse *obj) 725 { 726 uint32_t align = calc_help_align(obj); 727 728 printf("usage: %s %s\n", obj->prog_name, obj->usage); 729 if (obj->descriptor != NULL) 730 printf("\ndescriptor: %s\n", obj->descriptor); 731 732 show_args_pos_help(obj, align); 733 show_args_opt_help(obj, align); 734 735 if (obj->epilog != NULL) 736 printf("\n%s\n", obj->epilog); 737 else 738 printf("\n"); 739 } 740 741 int 742 rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) 743 { 744 bool show_help = false; 745 int ret; 746 747 ret = verify_argparse(obj); 748 if (ret != 0) 749 goto error; 750 751 ret = parse_args(obj, argc, argv, &show_help); 752 if (ret != 0) 753 goto error; 754 755 if (show_help) { 756 show_args_help(obj); 757 exit(0); 758 } 759 760 return 0; 761 762 error: 763 if (obj->exit_on_error) 764 exit(ret); 765 return ret; 766 } 767 768 int 769 rte_argparse_parse_type(const char *str, uint64_t val_type, void *val) 770 { 771 uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); 772 struct rte_argparse_arg arg = { 773 .name_long = str, 774 .name_short = NULL, 775 .val_saver = val, 776 .val_set = NULL, 777 .flags = val_type, 778 }; 779 uint32_t value_type = arg_attr_val_type(&arg); 780 781 if (value_type == 0 || value_type >= cmp_max) 782 return -EINVAL; 783 784 return parse_arg_autosave(&arg, str); 785 } 786