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_FLAG_PARSED_MASK RTE_BIT64(63) 19 20 static inline bool 21 is_arg_optional(const struct rte_argparse_arg *arg) 22 { 23 return arg->name_long[0] == '-'; 24 } 25 26 static inline bool 27 is_arg_positional(const struct rte_argparse_arg *arg) 28 { 29 return arg->name_long[0] != '-'; 30 } 31 32 static inline uint32_t 33 arg_attr_has_val(const struct rte_argparse_arg *arg) 34 { 35 return RTE_FIELD_GET64(RTE_ARGPARSE_HAS_VAL_BITMASK, arg->flags); 36 } 37 38 static inline uint32_t 39 arg_attr_val_type(const struct rte_argparse_arg *arg) 40 { 41 return RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK, arg->flags); 42 } 43 44 static inline bool 45 arg_attr_flag_multi(const struct rte_argparse_arg *arg) 46 { 47 return RTE_FIELD_GET64(RTE_ARGPARSE_ARG_SUPPORT_MULTI, arg->flags); 48 } 49 50 static inline uint64_t 51 arg_attr_unused_bits(const struct rte_argparse_arg *arg) 52 { 53 #define USED_BIT_MASK (RTE_ARGPARSE_HAS_VAL_BITMASK | \ 54 RTE_ARGPARSE_VAL_TYPE_BITMASK | \ 55 RTE_ARGPARSE_ARG_SUPPORT_MULTI) 56 return arg->flags & ~USED_BIT_MASK; 57 } 58 59 static int 60 verify_arg_name(const struct rte_argparse_arg *arg) 61 { 62 if (is_arg_optional(arg)) { 63 if (strlen(arg->name_long) <= 3) { 64 ARGPARSE_LOG(ERR, "optional long name %s too short!", arg->name_long); 65 return -EINVAL; 66 } 67 if (arg->name_long[1] != '-') { 68 ARGPARSE_LOG(ERR, "optional long name %s doesn't start with '--'", 69 arg->name_long); 70 return -EINVAL; 71 } 72 if (arg->name_long[2] == '-') { 73 ARGPARSE_LOG(ERR, "optional long name %s should not start with '---'", 74 arg->name_long); 75 return -EINVAL; 76 } 77 } 78 79 if (arg->name_short == NULL) 80 return 0; 81 82 if (!is_arg_optional(arg)) { 83 ARGPARSE_LOG(ERR, "short name %s corresponding long name must be optional!", 84 arg->name_short); 85 return -EINVAL; 86 } 87 88 if (strlen(arg->name_short) != 2 || arg->name_short[0] != '-' || 89 arg->name_short[1] == '-') { 90 ARGPARSE_LOG(ERR, "short name %s must start with a hyphen (-) followed by an English letter", 91 arg->name_short); 92 return -EINVAL; 93 } 94 95 return 0; 96 } 97 98 static int 99 verify_arg_help(const struct rte_argparse_arg *arg) 100 { 101 if (arg->help == NULL) { 102 ARGPARSE_LOG(ERR, "argument %s doesn't have help info!", arg->name_long); 103 return -EINVAL; 104 } 105 106 return 0; 107 } 108 109 static int 110 verify_arg_has_val(const struct rte_argparse_arg *arg) 111 { 112 uint32_t has_val = arg_attr_has_val(arg); 113 114 if (is_arg_positional(arg)) { 115 if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE) 116 return 0; 117 ARGPARSE_LOG(ERR, "argument %s is positional, must config required-val!", 118 arg->name_long); 119 return -EINVAL; 120 } 121 122 if (has_val == 0) { 123 ARGPARSE_LOG(ERR, "argument %s is optional, has-value config wrong!", 124 arg->name_long); 125 return -EINVAL; 126 } 127 128 return 0; 129 } 130 131 static int 132 verify_arg_saver(const struct rte_argparse *obj, uint32_t index) 133 { 134 uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK, 135 RTE_ARGPARSE_ARG_VALUE_MAX); 136 const struct rte_argparse_arg *arg = &obj->args[index]; 137 uint32_t val_type = arg_attr_val_type(arg); 138 uint32_t has_val = arg_attr_has_val(arg); 139 140 if (arg->val_saver == NULL) { 141 if (val_type != 0) { 142 ARGPARSE_LOG(ERR, "argument %s parsed by callback, value-type should not be set!", 143 arg->name_long); 144 return -EINVAL; 145 } 146 147 if (obj->callback == NULL) { 148 ARGPARSE_LOG(ERR, "argument %s parsed by callback, but callback is NULL!", 149 arg->name_long); 150 return -EINVAL; 151 } 152 153 return 0; 154 } 155 156 if (val_type == 0 || val_type >= cmp_max) { 157 ARGPARSE_LOG(ERR, "argument %s value-type config wrong!", arg->name_long); 158 return -EINVAL; 159 } 160 161 if (has_val == RTE_ARGPARSE_ARG_REQUIRED_VALUE && arg->val_set != NULL) { 162 ARGPARSE_LOG(ERR, "argument %s has required value, value-set should be NULL!", 163 arg->name_long); 164 return -EINVAL; 165 } 166 167 return 0; 168 } 169 170 static int 171 verify_arg_flags(const struct rte_argparse *obj, uint32_t index) 172 { 173 const struct rte_argparse_arg *arg = &obj->args[index]; 174 uint64_t unused_bits = arg_attr_unused_bits(arg); 175 176 if (unused_bits != 0) { 177 ARGPARSE_LOG(ERR, "argument %s flags unused bits should not be set!", 178 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 supports multiple times, should use callback to parse!", 193 arg->name_long); 194 return -EINVAL; 195 } 196 197 return 0; 198 } 199 200 static int 201 verify_arg_repeat(const struct rte_argparse *self, uint32_t index) 202 { 203 const struct rte_argparse_arg *arg = &self->args[index]; 204 uint32_t i; 205 206 for (i = 0; i < index; i++) { 207 if (!strcmp(arg->name_long, self->args[i].name_long)) { 208 ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_long); 209 return -EINVAL; 210 } 211 } 212 213 if (arg->name_short == NULL) 214 return 0; 215 216 for (i = 0; i < index; i++) { 217 if (self->args[i].name_short == NULL) 218 continue; 219 if (!strcmp(arg->name_short, self->args[i].name_short)) { 220 ARGPARSE_LOG(ERR, "argument %s repeat!", arg->name_short); 221 return -EINVAL; 222 } 223 } 224 225 return 0; 226 } 227 228 static int 229 verify_argparse_arg(const struct rte_argparse *obj, uint32_t index) 230 { 231 const struct rte_argparse_arg *arg = &obj->args[index]; 232 int ret; 233 234 ret = verify_arg_name(arg); 235 if (ret != 0) 236 return ret; 237 238 ret = verify_arg_help(arg); 239 if (ret != 0) 240 return ret; 241 242 ret = verify_arg_has_val(arg); 243 if (ret != 0) 244 return ret; 245 246 ret = verify_arg_saver(obj, index); 247 if (ret != 0) 248 return ret; 249 250 ret = verify_arg_flags(obj, index); 251 if (ret != 0) 252 return ret; 253 254 ret = verify_arg_repeat(obj, index); 255 if (ret != 0) 256 return ret; 257 258 return 0; 259 } 260 261 static int 262 verify_argparse(const struct rte_argparse *obj) 263 { 264 uint32_t idx; 265 int ret; 266 267 if (obj->prog_name == NULL) { 268 ARGPARSE_LOG(ERR, "program name is NULL!"); 269 return -EINVAL; 270 } 271 272 if (obj->usage == NULL) { 273 ARGPARSE_LOG(ERR, "usage is NULL!"); 274 return -EINVAL; 275 } 276 277 for (idx = 0; idx < RTE_DIM(obj->reserved); idx++) { 278 if (obj->reserved[idx] != 0) { 279 ARGPARSE_LOG(ERR, "reserved field must be zero!"); 280 return -EINVAL; 281 } 282 } 283 284 idx = 0; 285 while (obj->args[idx].name_long != NULL) { 286 ret = verify_argparse_arg(obj, idx); 287 if (ret != 0) 288 return ret; 289 idx++; 290 } 291 292 return 0; 293 } 294 295 static uint32_t 296 calc_position_count(const struct rte_argparse *obj) 297 { 298 const struct rte_argparse_arg *arg; 299 uint32_t count = 0; 300 uint32_t i; 301 302 for (i = 0; /* NULL */; i++) { 303 arg = &obj->args[i]; 304 if (obj->args[i].name_long == NULL) 305 break; 306 if (is_arg_positional(arg)) 307 count++; 308 } 309 310 return count; 311 } 312 313 static struct rte_argparse_arg * 314 find_position_arg(struct rte_argparse *obj, uint32_t index) 315 { 316 struct rte_argparse_arg *arg; 317 uint32_t count = 0; 318 uint32_t i; 319 320 for (i = 0; /* NULL */; i++) { 321 arg = &obj->args[i]; 322 if (arg->name_long == NULL) 323 break; 324 if (!is_arg_positional(arg)) 325 continue; 326 count++; 327 if (count == index) 328 return arg; 329 } 330 331 return NULL; 332 } 333 334 static bool 335 is_arg_match(struct rte_argparse_arg *arg, const char *curr_argv, uint32_t len) 336 { 337 if (strlen(arg->name_long) == len && strncmp(arg->name_long, curr_argv, len) == 0) 338 return true; 339 340 if (arg->name_short == NULL) 341 return false; 342 343 if (strlen(arg->name_short) == len && strncmp(arg->name_short, curr_argv, len) == 0) 344 return true; 345 346 return false; 347 } 348 349 static struct rte_argparse_arg * 350 find_option_arg(struct rte_argparse *obj, const char *curr_argv, const char *has_equal, 351 const char **arg_name) 352 { 353 uint32_t len = strlen(curr_argv) - (has_equal != NULL ? strlen(has_equal) : 0); 354 struct rte_argparse_arg *arg; 355 uint32_t i; 356 bool match; 357 358 for (i = 0; /* nothing */; i++) { 359 arg = &obj->args[i]; 360 if (arg->name_long == NULL) 361 break; 362 match = is_arg_match(arg, curr_argv, len); 363 if (match) { 364 /* Obtains the exact matching name (long or short). */ 365 *arg_name = len > 2 ? arg->name_long : arg->name_short; 366 return arg; 367 } 368 } 369 370 return NULL; 371 } 372 373 static int 374 parse_arg_int(struct rte_argparse_arg *arg, const char *value) 375 { 376 char *s = NULL; 377 378 if (value == NULL) { 379 *(int *)arg->val_saver = (int)(intptr_t)arg->val_set; 380 return 0; 381 } 382 383 errno = 0; 384 *(int *)arg->val_saver = strtol(value, &s, 0); 385 if (errno == ERANGE) { 386 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 387 return -EINVAL; 388 } 389 390 if (s[0] != '\0') { 391 ARGPARSE_LOG(ERR, "argument %s expect an integer value!", arg->name_long); 392 return -EINVAL; 393 } 394 395 return 0; 396 } 397 398 static int 399 parse_arg_u8(struct rte_argparse_arg *arg, const char *value) 400 { 401 unsigned long val; 402 char *s = NULL; 403 404 if (value == NULL) { 405 *(uint8_t *)arg->val_saver = (uint8_t)(intptr_t)arg->val_set; 406 return 0; 407 } 408 409 errno = 0; 410 val = strtoul(value, &s, 0); 411 if (errno == ERANGE || val > UINT8_MAX) { 412 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 413 return -EINVAL; 414 } 415 416 if (s[0] != '\0') { 417 ARGPARSE_LOG(ERR, "argument %s expect an uint8 value!", arg->name_long); 418 return -EINVAL; 419 } 420 421 *(uint8_t *)arg->val_saver = val; 422 423 return 0; 424 } 425 426 static int 427 parse_arg_u16(struct rte_argparse_arg *arg, const char *value) 428 { 429 unsigned long val; 430 char *s = NULL; 431 432 if (value == NULL) { 433 *(uint16_t *)arg->val_saver = (uint16_t)(intptr_t)arg->val_set; 434 return 0; 435 } 436 437 errno = 0; 438 val = strtoul(value, &s, 0); 439 if (errno == ERANGE || val > UINT16_MAX) { 440 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 441 return -EINVAL; 442 } 443 444 if (s[0] != '\0') { 445 ARGPARSE_LOG(ERR, "argument %s expect an uint16 value!", arg->name_long); 446 return -EINVAL; 447 } 448 449 *(uint16_t *)arg->val_saver = val; 450 451 return 0; 452 } 453 454 static int 455 parse_arg_u32(struct rte_argparse_arg *arg, const char *value) 456 { 457 unsigned long val; 458 char *s = NULL; 459 460 if (value == NULL) { 461 *(uint32_t *)arg->val_saver = (uint32_t)(intptr_t)arg->val_set; 462 return 0; 463 } 464 465 errno = 0; 466 val = strtoul(value, &s, 0); 467 if (errno == ERANGE || val > UINT32_MAX) { 468 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 469 return -EINVAL; 470 } 471 472 if (s[0] != '\0') { 473 ARGPARSE_LOG(ERR, "argument %s expect an uint32 value!", arg->name_long); 474 return -EINVAL; 475 } 476 477 *(uint32_t *)arg->val_saver = val; 478 479 return 0; 480 } 481 482 static int 483 parse_arg_u64(struct rte_argparse_arg *arg, const char *value) 484 { 485 unsigned long val; 486 char *s = NULL; 487 488 if (value == NULL) { 489 *(uint64_t *)arg->val_saver = (uint64_t)(intptr_t)arg->val_set; 490 return 0; 491 } 492 493 errno = 0; 494 val = strtoull(value, &s, 0); 495 if (errno == ERANGE) { 496 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 497 return -EINVAL; 498 } 499 500 if (s[0] != '\0') { 501 ARGPARSE_LOG(ERR, "argument %s expect an uint64 value!", arg->name_long); 502 return -EINVAL; 503 } 504 505 *(uint64_t *)arg->val_saver = val; 506 507 return 0; 508 } 509 510 static int 511 parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) 512 { 513 static struct { 514 int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value); 515 } map[] = { 516 /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ 517 { NULL }, 518 { parse_arg_int }, 519 { parse_arg_u8 }, 520 { parse_arg_u16 }, 521 { parse_arg_u32 }, 522 { parse_arg_u64 }, 523 }; 524 uint32_t index = arg_attr_val_type(arg); 525 int ret = -EINVAL; 526 527 if (index > 0 && index < RTE_DIM(map)) 528 ret = map[index].f_parse_type(arg, value); 529 530 return ret; 531 } 532 533 /* arg_parse indicates the name entered by the user, which can be long-name or short-name. */ 534 static int 535 parse_arg_val(struct rte_argparse *obj, const char *arg_name, 536 struct rte_argparse_arg *arg, char *value) 537 { 538 int ret; 539 540 if (arg->val_saver == NULL) 541 ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque); 542 else 543 ret = parse_arg_autosave(arg, value); 544 if (ret != 0) { 545 ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg_name); 546 return ret; 547 } 548 549 return 0; 550 } 551 552 static bool 553 is_help(const char *curr_argv) 554 { 555 return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0; 556 } 557 558 static int 559 parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) 560 { 561 uint32_t position_count = calc_position_count(obj); 562 struct rte_argparse_arg *arg; 563 uint32_t position_index = 0; 564 const char *arg_name; 565 char *curr_argv; 566 char *has_equal; 567 char *value; 568 int ret; 569 int i; 570 571 for (i = 1; i < argc; i++) { 572 curr_argv = argv[i]; 573 if (curr_argv[0] != '-') { 574 /* process positional parameters. */ 575 position_index++; 576 if (position_index > position_count) { 577 ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv); 578 return -EINVAL; 579 } 580 arg = find_position_arg(obj, position_index); 581 ret = parse_arg_val(obj, arg->name_long, arg, curr_argv); 582 if (ret != 0) 583 return ret; 584 continue; 585 } 586 587 /* process optional parameters. */ 588 if (is_help(curr_argv)) { 589 *show_help = true; 590 continue; 591 } 592 593 has_equal = strchr(curr_argv, '='); 594 arg_name = NULL; 595 arg = find_option_arg(obj, curr_argv, has_equal, &arg_name); 596 if (arg == NULL || arg_name == NULL) { 597 ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv); 598 return -EINVAL; 599 } 600 601 if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) { 602 ARGPARSE_LOG(ERR, "argument %s should not occur multiple!", 603 arg_name); 604 return -EINVAL; 605 } 606 607 value = (has_equal != NULL ? has_equal + 1 : NULL); 608 if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) { 609 if (value != NULL) { 610 ARGPARSE_LOG(ERR, "argument %s should not take value!", 611 arg_name); 612 return -EINVAL; 613 } 614 } else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) { 615 if (value == NULL) { 616 if (i >= argc - 1) { 617 ARGPARSE_LOG(ERR, "argument %s doesn't have value!", 618 arg_name); 619 return -EINVAL; 620 } 621 /* Set value and make i move next. */ 622 value = argv[++i]; 623 } 624 } else { 625 /* Do nothing, because it's optional value, only support arg=val or arg. */ 626 } 627 628 ret = parse_arg_val(obj, arg_name, arg, value); 629 if (ret != 0) 630 return ret; 631 632 /* This argument parsed success! then mark it parsed. */ 633 arg->flags |= ARG_ATTR_FLAG_PARSED_MASK; 634 } 635 636 return 0; 637 } 638 639 static uint32_t 640 calc_help_align(const struct rte_argparse *obj) 641 { 642 const struct rte_argparse_arg *arg; 643 uint32_t width = 12; /* Default "-h, --help " len. */ 644 uint32_t len; 645 uint32_t i; 646 647 for (i = 0; /* NULL */; i++) { 648 arg = &obj->args[i]; 649 if (arg->name_long == NULL) 650 break; 651 len = strlen(arg->name_long); 652 if (is_arg_optional(arg) && arg->name_short != NULL) { 653 len += strlen(", "); 654 len += strlen(arg->name_short); 655 } 656 width = RTE_MAX(width, 1 + len + 2); /* start with 1 & end with 2 space. */ 657 } 658 659 return width; 660 } 661 662 static void 663 show_oneline_help(const struct rte_argparse_arg *arg, uint32_t width) 664 { 665 uint32_t len = 0; 666 uint32_t i; 667 668 if (arg->name_short != NULL) 669 len = printf(" %s,", arg->name_short); 670 len += printf(" %s", arg->name_long); 671 672 for (i = len; i < width; i++) 673 printf(" "); 674 675 printf("%s\n", arg->help); 676 } 677 678 static void 679 show_args_pos_help(const struct rte_argparse *obj, uint32_t align) 680 { 681 uint32_t position_count = calc_position_count(obj); 682 const struct rte_argparse_arg *arg; 683 uint32_t i; 684 685 if (position_count == 0) 686 return; 687 688 printf("\npositional arguments:\n"); 689 for (i = 0; /* NULL */; i++) { 690 arg = &obj->args[i]; 691 if (arg->name_long == NULL) 692 break; 693 if (!is_arg_positional(arg)) 694 continue; 695 show_oneline_help(arg, align); 696 } 697 } 698 699 static void 700 show_args_opt_help(const struct rte_argparse *obj, uint32_t align) 701 { 702 static const struct rte_argparse_arg help = { 703 .name_long = "--help", 704 .name_short = "-h", 705 .help = "show this help message and exit.", 706 }; 707 const struct rte_argparse_arg *arg; 708 uint32_t i; 709 710 printf("\noptions:\n"); 711 show_oneline_help(&help, align); 712 for (i = 0; /* NULL */; i++) { 713 arg = &obj->args[i]; 714 if (arg->name_long == NULL) 715 break; 716 if (!is_arg_optional(arg)) 717 continue; 718 show_oneline_help(arg, align); 719 } 720 } 721 722 static void 723 show_args_help(const struct rte_argparse *obj) 724 { 725 uint32_t align = calc_help_align(obj); 726 727 printf("usage: %s %s\n", obj->prog_name, obj->usage); 728 if (obj->descriptor != NULL) 729 printf("\ndescriptor: %s\n", obj->descriptor); 730 731 show_args_pos_help(obj, align); 732 show_args_opt_help(obj, align); 733 734 if (obj->epilog != NULL) 735 printf("\n%s\n", obj->epilog); 736 else 737 printf("\n"); 738 } 739 740 int 741 rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) 742 { 743 bool show_help = false; 744 int ret; 745 746 ret = verify_argparse(obj); 747 if (ret != 0) 748 goto error; 749 750 ret = parse_args(obj, argc, argv, &show_help); 751 if (ret != 0) 752 goto error; 753 754 if (show_help) { 755 show_args_help(obj); 756 exit(0); 757 } 758 759 return 0; 760 761 error: 762 if (obj->exit_on_error) 763 exit(ret); 764 return ret; 765 } 766 767 int 768 rte_argparse_parse_type(const char *str, uint64_t val_type, void *val) 769 { 770 uint32_t cmp_max = RTE_FIELD_GET64(RTE_ARGPARSE_VAL_TYPE_BITMASK, 771 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