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_u8(struct rte_argparse_arg *arg, const char *value) 406 { 407 unsigned long val; 408 char *s = NULL; 409 410 if (value == NULL) { 411 *(uint8_t *)arg->val_saver = (uint8_t)(intptr_t)arg->val_set; 412 return 0; 413 } 414 415 errno = 0; 416 val = strtoul(value, &s, 0); 417 if (errno == ERANGE || val > UINT8_MAX) { 418 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 419 return -EINVAL; 420 } 421 422 if (s[0] != '\0') { 423 ARGPARSE_LOG(ERR, "argument %s expect an uint8 value!", arg->name_long); 424 return -EINVAL; 425 } 426 427 *(uint8_t *)arg->val_saver = val; 428 429 return 0; 430 } 431 432 static int 433 parse_arg_u16(struct rte_argparse_arg *arg, const char *value) 434 { 435 unsigned long val; 436 char *s = NULL; 437 438 if (value == NULL) { 439 *(uint16_t *)arg->val_saver = (uint16_t)(intptr_t)arg->val_set; 440 return 0; 441 } 442 443 errno = 0; 444 val = strtoul(value, &s, 0); 445 if (errno == ERANGE || val > UINT16_MAX) { 446 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 447 return -EINVAL; 448 } 449 450 if (s[0] != '\0') { 451 ARGPARSE_LOG(ERR, "argument %s expect an uint16 value!", arg->name_long); 452 return -EINVAL; 453 } 454 455 *(uint16_t *)arg->val_saver = val; 456 457 return 0; 458 } 459 460 static int 461 parse_arg_u32(struct rte_argparse_arg *arg, const char *value) 462 { 463 unsigned long val; 464 char *s = NULL; 465 466 if (value == NULL) { 467 *(uint32_t *)arg->val_saver = (uint32_t)(intptr_t)arg->val_set; 468 return 0; 469 } 470 471 errno = 0; 472 val = strtoul(value, &s, 0); 473 if (errno == ERANGE || val > UINT32_MAX) { 474 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 475 return -EINVAL; 476 } 477 478 if (s[0] != '\0') { 479 ARGPARSE_LOG(ERR, "argument %s expect an uint32 value!", arg->name_long); 480 return -EINVAL; 481 } 482 483 *(uint32_t *)arg->val_saver = val; 484 485 return 0; 486 } 487 488 static int 489 parse_arg_u64(struct rte_argparse_arg *arg, const char *value) 490 { 491 unsigned long val; 492 char *s = NULL; 493 494 if (value == NULL) { 495 *(uint64_t *)arg->val_saver = (uint64_t)(intptr_t)arg->val_set; 496 return 0; 497 } 498 499 errno = 0; 500 val = strtoull(value, &s, 0); 501 if (errno == ERANGE) { 502 ARGPARSE_LOG(ERR, "argument %s numerical out of range!", arg->name_long); 503 return -EINVAL; 504 } 505 506 if (s[0] != '\0') { 507 ARGPARSE_LOG(ERR, "argument %s expect an uint64 value!", arg->name_long); 508 return -EINVAL; 509 } 510 511 *(uint64_t *)arg->val_saver = val; 512 513 return 0; 514 } 515 516 static int 517 parse_arg_autosave(struct rte_argparse_arg *arg, const char *value) 518 { 519 static struct { 520 int (*f_parse_type)(struct rte_argparse_arg *arg, const char *value); 521 } map[] = { 522 /* Sort by RTE_ARGPARSE_ARG_VALUE_XXX. */ 523 { NULL }, 524 { parse_arg_int }, 525 { parse_arg_u8 }, 526 { parse_arg_u16 }, 527 { parse_arg_u32 }, 528 { parse_arg_u64 }, 529 }; 530 uint32_t index = arg_attr_val_type(arg); 531 int ret = -EINVAL; 532 533 if (index > 0 && index < RTE_DIM(map)) 534 ret = map[index].f_parse_type(arg, value); 535 536 return ret; 537 } 538 539 static int 540 parse_arg_val(struct rte_argparse *obj, struct rte_argparse_arg *arg, char *value) 541 { 542 int ret; 543 544 if (arg->val_saver == NULL) 545 ret = obj->callback((uint32_t)(uintptr_t)arg->val_set, value, obj->opaque); 546 else 547 ret = parse_arg_autosave(arg, value); 548 if (ret != 0) { 549 ARGPARSE_LOG(ERR, "argument %s parse value fail!", arg->name_long); 550 return ret; 551 } 552 553 return 0; 554 } 555 556 static bool 557 is_help(const char *curr_argv) 558 { 559 return strcmp(curr_argv, "-h") == 0 || strcmp(curr_argv, "--help") == 0; 560 } 561 562 static int 563 parse_args(struct rte_argparse *obj, int argc, char **argv, bool *show_help) 564 { 565 uint32_t position_count = calc_position_count(obj); 566 struct rte_argparse_arg *arg; 567 uint32_t position_index = 0; 568 const char *arg_name; 569 char *curr_argv; 570 char *has_equal; 571 char *value; 572 int ret; 573 int i; 574 575 for (i = 1; i < argc; i++) { 576 curr_argv = argv[i]; 577 if (curr_argv[0] != '-') { 578 /* process positional parameters. */ 579 position_index++; 580 if (position_index > position_count) { 581 ARGPARSE_LOG(ERR, "too much positional argument %s!", curr_argv); 582 return -EINVAL; 583 } 584 arg = find_position_arg(obj, position_index); 585 ret = parse_arg_val(obj, arg, curr_argv); 586 if (ret != 0) 587 return ret; 588 continue; 589 } 590 591 /* process optional parameters. */ 592 if (is_help(curr_argv)) { 593 *show_help = true; 594 continue; 595 } 596 597 has_equal = strchr(curr_argv, '='); 598 arg_name = NULL; 599 arg = find_option_arg(obj, curr_argv, has_equal, &arg_name); 600 if (arg == NULL || arg_name == NULL) { 601 ARGPARSE_LOG(ERR, "unknown argument %s!", curr_argv); 602 return -EINVAL; 603 } 604 605 if ((arg->flags & ARG_ATTR_FLAG_PARSED_MASK) && !arg_attr_flag_multi(arg)) { 606 ARGPARSE_LOG(ERR, "argument %s should not occur multiple!", 607 arg_name); 608 return -EINVAL; 609 } 610 611 value = (has_equal != NULL ? has_equal + 1 : NULL); 612 if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_NO_VALUE) { 613 if (value != NULL) { 614 ARGPARSE_LOG(ERR, "argument %s should not take value!", 615 arg_name); 616 return -EINVAL; 617 } 618 } else if (arg_attr_has_val(arg) == RTE_ARGPARSE_ARG_REQUIRED_VALUE) { 619 if (value == NULL) { 620 if (i >= argc - 1) { 621 ARGPARSE_LOG(ERR, "argument %s doesn't have value!", 622 arg_name); 623 return -EINVAL; 624 } 625 /* Set value and make i move next. */ 626 value = argv[++i]; 627 } 628 } else { 629 /* Do nothing, because it's optional value, only support arg=val or arg. */ 630 } 631 632 ret = parse_arg_val(obj, arg, value); 633 if (ret != 0) 634 return ret; 635 636 /* This argument parsed success! then mark it parsed. */ 637 arg->flags |= ARG_ATTR_FLAG_PARSED_MASK; 638 } 639 640 return 0; 641 } 642 643 static uint32_t 644 calc_help_align(const struct rte_argparse *obj) 645 { 646 const struct rte_argparse_arg *arg; 647 uint32_t width = 12; /* Default "-h, --help " len. */ 648 uint32_t len; 649 uint32_t i; 650 651 for (i = 0; /* NULL */; i++) { 652 arg = &obj->args[i]; 653 if (arg->name_long == NULL) 654 break; 655 len = strlen(arg->name_long); 656 if (is_arg_optional(arg) && arg->name_short != NULL) { 657 len += strlen(", "); 658 len += strlen(arg->name_short); 659 } 660 width = RTE_MAX(width, 1 + len + 2); /* start with 1 & end with 2 space. */ 661 } 662 663 return width; 664 } 665 666 static void 667 show_oneline_help(const struct rte_argparse_arg *arg, uint32_t width) 668 { 669 uint32_t len = 0; 670 uint32_t i; 671 672 if (arg->name_short != NULL) 673 len = printf(" %s,", arg->name_short); 674 len += printf(" %s", arg->name_long); 675 676 for (i = len; i < width; i++) 677 printf(" "); 678 679 printf("%s\n", arg->help); 680 } 681 682 static void 683 show_args_pos_help(const struct rte_argparse *obj, uint32_t align) 684 { 685 uint32_t position_count = calc_position_count(obj); 686 const struct rte_argparse_arg *arg; 687 uint32_t i; 688 689 if (position_count == 0) 690 return; 691 692 printf("\npositional arguments:\n"); 693 for (i = 0; /* NULL */; i++) { 694 arg = &obj->args[i]; 695 if (arg->name_long == NULL) 696 break; 697 if (!is_arg_positional(arg)) 698 continue; 699 show_oneline_help(arg, align); 700 } 701 } 702 703 static void 704 show_args_opt_help(const struct rte_argparse *obj, uint32_t align) 705 { 706 static const struct rte_argparse_arg help = { 707 .name_long = "--help", 708 .name_short = "-h", 709 .help = "show this help message and exit.", 710 }; 711 const struct rte_argparse_arg *arg; 712 uint32_t i; 713 714 printf("\noptions:\n"); 715 show_oneline_help(&help, align); 716 for (i = 0; /* NULL */; i++) { 717 arg = &obj->args[i]; 718 if (arg->name_long == NULL) 719 break; 720 if (!is_arg_optional(arg)) 721 continue; 722 show_oneline_help(arg, align); 723 } 724 } 725 726 static void 727 show_args_help(const struct rte_argparse *obj) 728 { 729 uint32_t align = calc_help_align(obj); 730 731 printf("usage: %s %s\n", obj->prog_name, obj->usage); 732 if (obj->descriptor != NULL) 733 printf("\ndescriptor: %s\n", obj->descriptor); 734 735 show_args_pos_help(obj, align); 736 show_args_opt_help(obj, align); 737 738 if (obj->epilog != NULL) 739 printf("\n%s\n", obj->epilog); 740 else 741 printf("\n"); 742 } 743 744 int 745 rte_argparse_parse(struct rte_argparse *obj, int argc, char **argv) 746 { 747 bool show_help = false; 748 int ret; 749 750 ret = verify_argparse(obj); 751 if (ret != 0) 752 goto error; 753 754 ret = parse_args(obj, argc, argv, &show_help); 755 if (ret != 0) 756 goto error; 757 758 if (show_help) { 759 show_args_help(obj); 760 exit(0); 761 } 762 763 return 0; 764 765 error: 766 if (obj->exit_on_error) 767 exit(ret); 768 return ret; 769 } 770 771 int 772 rte_argparse_parse_type(const char *str, uint64_t val_type, void *val) 773 { 774 uint32_t cmp_max = RTE_FIELD_GET64(ARG_ATTR_VAL_TYPE_MASK, RTE_ARGPARSE_ARG_VALUE_MAX); 775 struct rte_argparse_arg arg = { 776 .name_long = str, 777 .name_short = NULL, 778 .val_saver = val, 779 .val_set = NULL, 780 .flags = val_type, 781 }; 782 uint32_t value_type = arg_attr_val_type(&arg); 783 784 if (value_type == 0 || value_type >= cmp_max) 785 return -EINVAL; 786 787 return parse_arg_autosave(&arg, str); 788 } 789