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