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