1/** 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#ifndef LIBPANDABASE_UTILS_PANDARGS_H 17#define LIBPANDABASE_UTILS_PANDARGS_H 18 19#include <algorithm> 20#include <array> 21#include <list> 22#include <set> 23#include <map> 24#include <string> 25#include <string_view> 26#include <sstream> 27#include <type_traits> 28#include <vector> 29#include <cerrno> 30#include <optional> 31#include <utility> 32 33#include "macros.h" 34 35namespace panda { 36class PandArgBase; 37using sub_args_t = std::vector<PandArgBase *>; 38using arg_list_t = std::vector<std::string>; 39using std::enable_if_t; 40using std::is_same_v; 41 42enum class PandArgType : uint8_t { STRING, INTEGER, DOUBLE, BOOL, LIST, UINT32, UINT64, COMPOUND, NOTYPE }; 43 44// Base class for panda argument 45class PandArgBase { 46public: 47 explicit PandArgBase(std::string name, std::string desc, PandArgType type = PandArgType::NOTYPE) 48 : name_(std::move(name)), desc_(std::move(desc)), type_(type) 49 { 50 } 51 52 PandArgType GetType() const 53 { 54 return type_; 55 } 56 57 std::string GetName() const 58 { 59 return name_; 60 } 61 62 std::string GetDesc() const 63 { 64 return desc_; 65 } 66 67 bool WasSet() const 68 { 69 return was_set_; 70 } 71 72 void SetWasSet(bool value) 73 { 74 was_set_ = value; 75 } 76 77 virtual void ResetDefaultValue() = 0; 78 79private: 80 std::string name_; 81 std::string desc_; 82 PandArgType type_; 83 bool was_set_ {false}; 84}; 85 86template <typename T, 87 enable_if_t<is_same_v<std::string, T> || is_same_v<double, T> || is_same_v<bool, T> || is_same_v<int, T> || 88 is_same_v<uint32_t, T> || is_same_v<uint64_t, T> || is_same_v<arg_list_t, T>> * = nullptr> 89class PandArg : public PandArgBase { 90public: 91 explicit PandArg(const std::string &name, T default_val, const std::string &desc) 92 : PandArgBase(name, desc, this->EvalType()), default_val_(default_val), real_val_(default_val) 93 { 94 } 95 96 explicit PandArg(const std::string &name, T default_val, const std::string &desc, PandArgType type) 97 : PandArgBase(name, desc, type), default_val_(default_val), real_val_(default_val) 98 { 99 } 100 101 explicit PandArg(const std::string &name, int default_val, const std::string &desc, T min_val, T max_val) 102 : PandArgBase(name, desc, this->EvalType()), 103 default_val_(default_val), 104 real_val_(default_val), 105 min_max_val_(std::pair<T, T>(min_val, max_val)) 106 { 107 } 108 109 explicit PandArg(const std::string &name, const arg_list_t &default_val, const std::string &desc, 110 std::string delimiter) 111 : PandArgBase(name, desc, PandArgType::LIST), 112 default_val_(default_val), 113 real_val_(default_val), 114 delimiter_(std::move(delimiter)) 115 { 116 } 117 118 T GetValue() const 119 { 120 return real_val_; 121 } 122 123 T GetDefaultValue() const 124 { 125 return default_val_; 126 } 127 128 template <bool update_flag = true> 129 void SetValue(T val) 130 { 131 real_val_ = val; 132 // NOLINTNEXTLINE(bugprone-suspicious-semicolon) 133 if constexpr (update_flag) { // NOLINT(readability-braces-around-statements) 134 SetWasSet(true); 135 } 136 } 137 138 void ResetDefaultValue() override 139 { 140 real_val_ = default_val_; 141 } 142 143 std::optional<std::string> GetDelimiter() const 144 { 145 return delimiter_; 146 } 147 std::optional<std::pair<T, T>> GetMinMaxVal() 148 { 149 return min_max_val_; 150 } 151 152private: 153 constexpr PandArgType EvalType() 154 { 155 // NOLINTNEXTLINE(bugprone-branch-clone) 156 if constexpr (is_same_v<std::string, T>) { // NOLINT(readability-braces-around-statements) 157 return PandArgType::STRING; 158 // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation) 159 } else if constexpr (is_same_v<double, T>) { 160 return PandArgType::DOUBLE; 161 // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation) 162 } else if constexpr (is_same_v<bool, T>) { 163 return PandArgType::BOOL; 164 // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation) 165 } else if constexpr (is_same_v<int, T>) { 166 return PandArgType::INTEGER; 167 // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation) 168 } else if constexpr (is_same_v<uint32_t, T>) { 169 return PandArgType::UINT32; 170 // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation) 171 } else if constexpr (is_same_v<uint64_t, T>) { 172 return PandArgType::UINT64; 173 // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation) 174 } else if constexpr (is_same_v<arg_list_t, T>) { 175 return PandArgType::LIST; 176 } 177 UNREACHABLE(); 178 } 179 180 T default_val_; 181 T real_val_; 182 183 // Only for integer arguments with range 184 std::optional<std::pair<T, T>> min_max_val_; 185 186 // Only for strings with delimiter 187 std::optional<std::string> delimiter_; 188}; 189 190class PandArgCompound : public PandArg<bool> { 191public: 192 PandArgCompound(const std::string &name, const std::string &desc, std::initializer_list<PandArgBase *> sub_args) 193 : PandArg<bool>(name, false, desc, PandArgType::COMPOUND), sub_args_(sub_args) 194 { 195 } 196 197 PandArgBase *FindSubArg(std::string_view name) 198 { 199 auto res = std::find_if(sub_args_.begin(), sub_args_.end(), 200 [name](const auto &arg) { return arg->GetName() == name; }); 201 return res == sub_args_.end() ? nullptr : *res; 202 } 203 204 void ResetDefaultValue() override 205 { 206 PandArg<bool>::ResetDefaultValue(); 207 std::for_each(sub_args_.begin(), sub_args_.end(), [](auto &arg) { arg->ResetDefaultValue(); }); 208 } 209 210 const auto &GetSubArgs() const 211 { 212 return sub_args_; 213 } 214 215private: 216 sub_args_t sub_args_; 217}; 218 219class PandArgParser { 220public: 221 bool Add(PandArgBase *arg) 222 { 223 if (arg == nullptr) { 224 errstr_ += "pandargs: Can't add `nullptr` as an argument\n"; 225 return false; 226 } 227 bool success = args_.insert(arg).second; 228 if (!success) { 229 errstr_ += "pandargs: Argument " + arg->GetName() + " has duplicate\n"; 230 } 231 return success; 232 } 233 234 bool PushBackTail(PandArgBase *arg) 235 { 236 if (arg == nullptr) { 237 errstr_ += "pandargs: Can't add `nullptr` as a tail argument\n"; 238 return false; 239 } 240 if (std::find(tail_args_.begin(), tail_args_.end(), arg) != tail_args_.end()) { 241 errstr_ += "pandargs: Tail argument " + arg->GetName() + " is already in tail arguments list\n"; 242 return false; 243 } 244 tail_args_.emplace_back(arg); 245 return true; 246 } 247 248 bool PopBackTail() 249 { 250 if (tail_args_.empty()) { 251 errstr_ += "pandargs: Nothing to pop back from tail arguments\n"; 252 return false; 253 } 254 tail_args_.pop_back(); 255 return true; 256 } 257 258 void EraseTail() 259 { 260 tail_args_.erase(tail_args_.begin(), tail_args_.end()); 261 } 262 263 bool Parse(const std::vector<std::string> &argv_vec) 264 { 265 InitDefault(); 266 std::copy(argv_vec.begin(), argv_vec.end(), std::back_inserter(argv_vec_)); 267 return ParseArgs(); 268 } 269 270 bool Parse(int argc, const char *const argv[]) // NOLINT(modernize-avoid-c-arrays, hicpp-avoid-c-arrays) 271 { 272 InitDefault(); 273 for (int i = 1; i < argc; i++) { 274 argv_vec_.emplace_back(argv[i]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) 275 } 276 return ParseArgs(); 277 } 278 279 /** 280 * Parses a string to the option's value. 281 */ 282 bool ParseSingleArg(PandArgBase *option, const std::string_view &option_value) 283 { 284 ASSERT(option != nullptr); 285 argv_vec_ = {std::string(option_value)}; 286 argv_index_ = 0; 287 argv_index_ += ParseNextParam(option, argv_vec_[argv_index_]); 288 return errstr_.empty(); 289 } 290 291 /** 292 * Parses option's name and returns corresponding pointer. 293 */ 294 PandArgBase *GetPandArg(const std::string_view &arg_name) 295 { 296 auto arg_it = args_.find(arg_name); 297 return (arg_it != args_.end()) ? *arg_it : nullptr; 298 } 299 300 std::string GetErrorString() const 301 { 302 return errstr_; 303 } 304 305 void EnableTail() 306 { 307 tail_flag_ = true; 308 } 309 310 void DisableTail() 311 { 312 tail_flag_ = false; 313 } 314 315 bool IsTailEnabled() const 316 { 317 return tail_flag_; 318 } 319 320 std::size_t GetTailSize() const 321 { 322 return tail_args_.size(); 323 } 324 325 void EnableRemainder() noexcept 326 { 327 remainder_flag_ = true; 328 } 329 330 void DisableRemainder() noexcept 331 { 332 remainder_flag_ = false; 333 } 334 335 bool IsRemainderEnabled() const 336 { 337 return remainder_flag_; 338 } 339 340 arg_list_t GetRemainder() 341 { 342 return remainder_; 343 } 344 345 bool IsArgSet(PandArgBase *arg) const 346 { 347 return args_.find(arg) != args_.end(); 348 } 349 350 bool IsArgSet(const std::string &arg_name) const 351 { 352 return args_.find(arg_name) != args_.end(); 353 } 354 355 std::string GetHelpString() const 356 { 357 std::ostringstream helpstr; 358 for (auto i : args_) { 359 if (i->GetType() == PandArgType::COMPOUND) { 360 auto arg = static_cast<PandArgCompound *>(i); 361 helpstr << DOUBLE_DASH << i->GetName() << ": " << i->GetDesc() << "\n"; 362 helpstr << " Sub arguments:\n"; 363 for (auto sub_arg : arg->GetSubArgs()) { 364 helpstr << " " << sub_arg->GetName() << ": " << sub_arg->GetDesc() << "\n"; 365 } 366 } else { 367 helpstr << DOUBLE_DASH << i->GetName() << ": " << i->GetDesc() << "\n"; 368 } 369 } 370 if (!tail_args_.empty()) { 371 helpstr << "Tail arguments:\n"; 372 for (auto i : tail_args_) { 373 helpstr << i->GetName() << ": " << i->GetDesc() << "\n"; 374 } 375 } 376 return helpstr.str(); 377 } 378 379 std::string GetRegularArgs() 380 { 381 std::string args_str; 382 std::string value; 383 for (auto i : args_) { 384 switch (i->GetType()) { 385 case PandArgType::STRING: 386 value = static_cast<PandArg<std::string> *>(i)->GetValue(); 387 break; 388 case PandArgType::INTEGER: 389 value = std::to_string(static_cast<PandArg<int> *>(i)->GetValue()); 390 break; 391 case PandArgType::DOUBLE: 392 value = std::to_string(static_cast<PandArg<double> *>(i)->GetValue()); 393 break; 394 case PandArgType::BOOL: 395 // NOLINTNEXTLINE(readability-implicit-bool-conversion) 396 value = std::to_string(static_cast<PandArg<bool> *>(i)->GetValue()); 397 break; 398 case PandArgType::UINT32: 399 value = std::to_string(static_cast<PandArg<uint32_t> *>(i)->GetValue()); 400 break; 401 case PandArgType::UINT64: 402 value = std::to_string(static_cast<PandArg<uint64_t> *>(i)->GetValue()); 403 break; 404 case PandArgType::LIST: { 405 value = ""; 406 std::vector<std::string> values_buf = static_cast<PandArg<arg_list_t> *>(i)->GetValue(); 407 for (const auto &j : values_buf) { 408 value += j + ", "; 409 } 410 break; 411 } 412 default: 413 errstr_ += "Invalid argument type \"" + i->GetName() + "\"\n"; 414 break; 415 } 416 args_str += DOUBLE_DASH + i->GetName() + "=" + value + "\n"; 417 } 418 return args_str; 419 } 420 421private: 422 struct PandArgPtrComparator { 423 using is_transparent = void; 424 bool operator()(const PandArgBase *lhs, const PandArgBase *rhs) const 425 { 426 return lhs->GetName() < rhs->GetName(); 427 } 428 bool operator()(std::string_view lhs, const PandArgBase *rhs) const 429 { 430 return lhs < rhs->GetName(); 431 } 432 bool operator()(const PandArgBase *lhs, std::string_view rhs) const 433 { 434 return lhs->GetName() < rhs; 435 } 436 }; 437 438 bool ParseArgs() 439 { 440 while (argv_index_ < argv_vec_.size()) { 441 PandArgBase *parsed_arg = ParseNextArg(); 442 if (!errstr_.empty()) { 443 return false; 444 } 445 if (argv_index_ < argv_vec_.size()) { 446 argv_index_ += ParseNextParam(parsed_arg, argv_vec_[argv_index_]); 447 if (!errstr_.empty()) { 448 return false; 449 } 450 } 451 } 452 return true; 453 } 454 455 void InitDefault() 456 { 457 equal_flag_ = false; 458 tail_parsed_flag_ = false; 459 argv_vec_.clear(); 460 argv_index_ = 0; 461 errstr_ = ""; 462 // reset tail 463 for (auto tail_arg : tail_args_) { 464 switch (tail_arg->GetType()) { 465 case PandArgType::STRING: 466 static_cast<PandArg<std::string> *>(tail_arg)->ResetDefaultValue(); 467 break; 468 case PandArgType::INTEGER: 469 static_cast<PandArg<int> *>(tail_arg)->ResetDefaultValue(); 470 break; 471 case PandArgType::DOUBLE: 472 static_cast<PandArg<double> *>(tail_arg)->ResetDefaultValue(); 473 break; 474 case PandArgType::BOOL: 475 static_cast<PandArg<bool> *>(tail_arg)->ResetDefaultValue(); 476 break; 477 case PandArgType::UINT32: 478 static_cast<PandArg<uint32_t> *>(tail_arg)->ResetDefaultValue(); 479 break; 480 case PandArgType::UINT64: 481 static_cast<PandArg<uint64_t> *>(tail_arg)->ResetDefaultValue(); 482 break; 483 case PandArgType::LIST: 484 static_cast<PandArg<arg_list_t> *>(tail_arg)->ResetDefaultValue(); 485 break; 486 default: 487 break; 488 } 489 } 490 // reset remainder 491 remainder_ = arg_list_t(); 492 } 493 494 PandArgBase *FindArg(std::string_view arg_name) 495 { 496 auto arg_it = args_.find(arg_name); 497 if (arg_it == args_.end()) { 498 errstr_.append("pandargs: Invalid option \""); 499 errstr_.append(arg_name); 500 errstr_.append("\"\n"); 501 return nullptr; 502 } 503 return *arg_it; 504 } 505 506 bool ParseSubArgument(PandArgCompound *parent_arg, std::string_view str) 507 { 508 size_t assign_pos = str.find('='); 509 std::string_view arg_name = str.substr(0, assign_pos); 510 auto arg = parent_arg->FindSubArg(arg_name); 511 if (arg == nullptr) { 512 errstr_.append("pandargs: Invalid sub-argument \""); 513 errstr_.append(arg_name); 514 errstr_.append("\"\n"); 515 return false; 516 } 517 518 if (assign_pos != std::string_view::npos) { 519 std::string_view value_str = str.substr(assign_pos + 1); 520 ParseNextParam(arg, value_str); 521 if (!errstr_.empty()) { 522 return false; 523 } 524 } else { 525 if (arg->GetType() != PandArgType::BOOL) { 526 errstr_.append("pandargs: Only boolean arguments might have no value \""); 527 errstr_.append(arg_name); 528 errstr_.append("\"\n"); 529 return false; 530 } 531 static_cast<PandArg<bool> *>(arg)->SetValue(true); 532 } 533 534 return true; 535 } 536 537 PandArgBase *ParseCompoundArg(std::string_view argstr, size_t sep_pos) 538 { 539 auto arg_name = argstr.substr(0, sep_pos); 540 541 auto arg = static_cast<PandArgCompound *>(FindArg(arg_name)); 542 if (arg == nullptr) { 543 return nullptr; 544 } 545 546 arg->SetValue(true); 547 548 auto sub_args_str = argstr.substr(sep_pos + 1); 549 size_t start = 0; 550 for (size_t pos = sub_args_str.find(',', 0); pos != std::string_view::npos; 551 start = pos + 1, pos = sub_args_str.find(',', start)) { 552 auto arg_str = sub_args_str.substr(start, pos - start); 553 if (!ParseSubArgument(arg, arg_str)) { 554 return nullptr; 555 } 556 } 557 if (start < sub_args_str.size()) { 558 if (!ParseSubArgument(arg, sub_args_str.substr(start))) { 559 return nullptr; 560 } 561 } 562 argv_index_++; 563 return arg; 564 } 565 566 PandArgBase *ParseNextRegularArg() 567 { 568 std::string argstr = argv_vec_[argv_index_]; 569 570 const std::size_t SEP_FOUND = NextSeparator(argstr); 571 572 const std::size_t SEP_COMPOUND = argstr.find_first_of(':', 0); 573 if (SEP_COMPOUND != std::string::npos && (SEP_FOUND == std::string::npos || SEP_COMPOUND < SEP_FOUND)) { 574 return ParseCompoundArg(std::string_view(argstr).substr(DASH_COUNT), SEP_COMPOUND - DASH_COUNT); 575 } 576 577 std::string arg_name; 578 579 if (SEP_FOUND != std::string::npos) { 580 equal_flag_ = true; 581 argv_vec_[argv_index_] = argstr.substr(SEP_FOUND + 1); 582 arg_name = argstr.substr(DASH_COUNT, SEP_FOUND - DASH_COUNT); 583 } else { 584 arg_name = argstr.substr(DASH_COUNT, SEP_FOUND); 585 // check if there is next argv element to iterate into 586 if (argv_index_ + 1 < argv_vec_.size()) { 587 argv_index_++; 588 } else { 589 argv_vec_[argv_index_] = ""; 590 } 591 } 592 593 PandArgBase *arg = FindArg(arg_name); 594 if (arg == nullptr) { 595 return nullptr; 596 } 597 598 if (arg->GetType() == PandArgType::COMPOUND) { 599 // It is forbidden to explicitly set compound option, e.g. `--compound=true`, must be `--compound`. 600 if (SEP_FOUND != std::string::npos) { 601 errstr_.append("pandargs: Compound option can not be explicitly set \""); 602 errstr_.append(arg_name); 603 errstr_.append("\"\n"); 604 return nullptr; 605 } 606 static_cast<PandArgCompound *>(arg)->SetValue(true); 607 } 608 609 return arg; 610 } 611 612 PandArgBase *ParseNextArg() 613 { 614 PandArgBase *arg = nullptr; 615 std::string argstr = argv_vec_[argv_index_]; 616 equal_flag_ = false; 617 618 // NOTE: currently we have only double dash argument prefix 619 std::size_t dashes_found = argstr.find(DOUBLE_DASH); 620 if (dashes_found == 0 && argstr.size() > DASH_COUNT) { 621 // regular argument 622 return ParseNextRegularArg(); 623 } 624 625 if (dashes_found == 0 && argstr.size() == DASH_COUNT) { 626 // remainder argument 627 if (!remainder_flag_) { 628 errstr_.append("pandargs: Remainder arguments are not enabled\n"); 629 errstr_.append("pandargs: Remainder found at literal \""); 630 errstr_.append(argstr); 631 errstr_.append("\"\n"); 632 return nullptr; 633 } 634 635 argv_index_++; 636 ParseRemainder(); 637 } else if (dashes_found > 0) { 638 // tail argument, N.B. std::string::npos > 0 639 if (!tail_flag_) { 640 errstr_.append("pandargs: Tail arguments are not enabled\n"); 641 errstr_.append("pandargs: Tail found at literal \""); 642 errstr_.append(argstr); 643 errstr_.append("\"\n"); 644 return nullptr; 645 } 646 if (tail_parsed_flag_) { 647 errstr_.append("pandargs: Too many tail arguments\n"); 648 return nullptr; 649 } 650 ParseTail(); 651 652 if (argv_index_ < argv_vec_.size()) { 653 if (argv_vec_[argv_index_] != DOUBLE_DASH && !remainder_flag_) { 654 errstr_ += "pandargs: Too many tail arguments given\n"; 655 } 656 } 657 } else { 658 errstr_.append("pandargs: Invalid option \""); 659 errstr_.append(argstr); 660 errstr_.append("\"\n"); 661 UNREACHABLE(); 662 } 663 return arg; 664 } 665 666 void ParseTail() 667 { 668 for (auto &tail_arg : tail_args_) { 669 switch (tail_arg->GetType()) { 670 case PandArgType::STRING: 671 argv_index_ += 672 ParseStringArgParam(static_cast<PandArg<std::string> *>(tail_arg), argv_vec_[argv_index_]); 673 break; 674 case PandArgType::INTEGER: 675 argv_index_ += ParseIntArgParam(static_cast<PandArg<int> *>(tail_arg), argv_vec_[argv_index_]); 676 break; 677 case PandArgType::DOUBLE: 678 argv_index_ += 679 ParseDoubleArgParam(static_cast<PandArg<double> *>(tail_arg), argv_vec_[argv_index_]); 680 break; 681 case PandArgType::BOOL: 682 argv_index_ += 683 ParseBoolArgParam(static_cast<PandArg<bool> *>(tail_arg), argv_vec_[argv_index_], true); 684 break; 685 case PandArgType::UINT32: 686 argv_index_ += 687 ParseUint32ArgParam(static_cast<PandArg<uint32_t> *>(tail_arg), argv_vec_[argv_index_]); 688 break; 689 case PandArgType::UINT64: 690 argv_index_ += 691 ParseUint64ArgParam(static_cast<PandArg<uint64_t> *>(tail_arg), argv_vec_[argv_index_]); 692 break; 693 case PandArgType::LIST: 694 argv_index_ += 695 ParseListArgParam(static_cast<PandArg<arg_list_t> *>(tail_arg), argv_vec_[argv_index_]); 696 break; 697 default: 698 errstr_.append("pandargs: Invalid tail option type: \""); 699 errstr_.append(tail_arg->GetName()); 700 errstr_.append("\"\n"); 701 UNREACHABLE(); 702 break; 703 } 704 if (argv_index_ >= argv_vec_.size() || !errstr_.empty()) { 705 break; 706 } 707 } 708 tail_parsed_flag_ = true; 709 } 710 711 void ParseRemainder() 712 { 713 remainder_ = arg_list_t(argv_vec_.begin() + argv_index_, argv_vec_.end()); 714 argv_index_ = argv_vec_.size(); 715 } 716 717 size_t ParseNextParam(PandArgBase *arg, std::string_view argstr) 718 { 719 if (argv_index_ >= argv_vec_.size() || arg == nullptr) { 720 return 0; 721 } 722 switch (arg->GetType()) { 723 case PandArgType::STRING: 724 return ParseStringArgParam(static_cast<PandArg<std::string> *>(arg), argstr); 725 case PandArgType::INTEGER: 726 return ParseIntArgParam(static_cast<PandArg<int> *>(arg), argstr); 727 case PandArgType::DOUBLE: 728 return ParseDoubleArgParam(static_cast<PandArg<double> *>(arg), argstr); 729 case PandArgType::BOOL: 730 return ParseBoolArgParam(static_cast<PandArg<bool> *>(arg), argstr); 731 case PandArgType::UINT32: 732 return ParseUint32ArgParam(static_cast<PandArg<uint32_t> *>(arg), argstr); 733 case PandArgType::UINT64: 734 return ParseUint64ArgParam(static_cast<PandArg<uint64_t> *>(arg), argstr); 735 case PandArgType::LIST: 736 return ParseListArgParam(static_cast<PandArg<arg_list_t> *>(arg), argstr); 737 case PandArgType::COMPOUND: 738 return argstr.empty() ? 1 : 0; 739 case PandArgType::NOTYPE: 740 errstr_.append("pandargs: Invalid option type: \""); 741 errstr_.append(arg->GetName()); 742 errstr_.append("\"\n"); 743 UNREACHABLE(); 744 break; 745 default: 746 UNREACHABLE(); 747 break; 748 } 749 return 0; 750 } 751 752 std::size_t ParseStringArgParam(PandArg<std::string> *arg, std::string_view argstr) 753 { 754 arg->SetValue(std::string(argstr)); 755 return 1; 756 } 757 758 std::size_t ParseIntArgParam(PandArg<int> *arg, std::string_view argstr) 759 { 760 std::string param_str(argstr); 761 if (IsIntegerNumber(param_str)) { 762 int num; 763 errno = 0; 764 if (StartsWith(param_str, "0x")) { 765 const int HEX = 16; 766 num = std::stoi(param_str, nullptr, HEX); 767 } else { 768 num = std::stoi(param_str); 769 } 770 771 if (errno == ERANGE) { 772 errstr_ += 773 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n"; 774 } 775 776 if (IsIntegerArgInRange(arg, num)) { 777 arg->SetValue(num); 778 } else { 779 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" + 780 param_str + "\"\n"; 781 } 782 } else { 783 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" + param_str + 784 "\"\n"; 785 } 786 return 1; 787 } 788 789 std::size_t ParseDoubleArgParam(PandArg<double> *arg, std::string_view argstr) 790 { 791 std::string param_str(argstr); 792 if (IsRationalNumber(param_str)) { 793 arg->SetValue(std::stod(param_str)); 794 } else { 795 errstr_ += 796 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n"; 797 } 798 return 1; 799 } 800 801 std::size_t ParseBoolArgParam(PandArg<bool> *arg, std::string_view argstr, bool is_tail_param = false) 802 { 803 std::string param_str(argstr); 804 805 // if not a tail argument, assume two following cases 806 if (!is_tail_param) { 807 arg->SetValue(true); 808 // bool with no param, next argument comes right after 809 if (StartsWith(param_str, DOUBLE_DASH)) { 810 // check that bool param comes without "=" 811 if (equal_flag_) { 812 SetBoolUnexpectedValueError(arg, param_str); 813 } 814 return 0; 815 } 816 // OR bool arg at the end of arguments line 817 if (param_str.empty()) { 818 // check that bool param comes without "=" 819 if (equal_flag_) { 820 SetBoolUnexpectedValueError(arg, param_str); 821 } 822 return 1; 823 } 824 } 825 826 constexpr std::array<std::string_view, 3> TRUE_VALUES = {"on", "true", "1"}; 827 constexpr std::array<std::string_view, 3> FALSE_VALUES = {"off", "false", "0"}; 828 829 for (const auto &i : TRUE_VALUES) { 830 if (param_str == i) { 831 arg->SetValue(true); 832 return 1; 833 } 834 } 835 for (const auto &i : FALSE_VALUES) { 836 if (param_str == i) { 837 arg->SetValue(false); 838 return 1; 839 } 840 } 841 842 // if it's not a part of tail argument, 843 // assume that it's bool with no param, 844 // preceiding tail argument 845 if (!is_tail_param) { 846 // check that bool param came without "=" 847 if (equal_flag_) { 848 SetBoolUnexpectedValueError(arg, param_str); 849 } else { 850 arg->SetValue(true); 851 } 852 } else { 853 errstr_ += 854 "pandargs: Tail argument " + arg->GetName() + " has unexpected parameter value " + param_str + "\n"; 855 arg->ResetDefaultValue(); 856 } 857 858 return 0; 859 } 860 861 std::size_t ParseUint64ArgParam(PandArg<uint64_t> *arg, std::string_view argstr) 862 { 863 std::string param_str(argstr); 864 if (IsUintNumber(param_str)) { 865 errno = 0; 866 uint64_t num; 867 if (StartsWith(param_str, "0x")) { 868 const int HEX = 16; 869 num = std::strtoull(param_str.c_str(), nullptr, HEX); 870 } else { 871 const int DEC = 10; 872 num = std::strtoull(param_str.c_str(), nullptr, DEC); 873 } 874 if (errno == ERANGE) { 875 errstr_ += 876 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n"; 877 } 878 879 if (IsIntegerArgInRange<uint64_t>(arg, num)) { 880 arg->SetValue(num); 881 } else { 882 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" + 883 param_str + "\"\n"; 884 } 885 } else { 886 errstr_ += 887 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n"; 888 } 889 return 1; 890 } 891 892 std::size_t ParseUint32ArgParam(PandArg<uint32_t> *arg, std::string_view argstr) 893 { 894 std::string param_str(argstr); 895 if (IsUintNumber(param_str)) { 896 errno = 0; 897 uint32_t num; 898 if (StartsWith(param_str, "0x")) { 899 const int HEX = 16; 900 num = std::strtoull(param_str.c_str(), nullptr, HEX); 901 } else { 902 const int DEC = 10; 903 num = std::strtoull(param_str.c_str(), nullptr, DEC); 904 } 905 if (errno == ERANGE) { 906 errstr_ += 907 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n"; 908 } 909 910 if (IsIntegerArgInRange<uint32_t>(arg, num)) { 911 arg->SetValue(num); 912 } else { 913 errstr_ += "pandargs: \"" + arg->GetName() + "\" argument has out of range parameter value \"" + 914 param_str + "\"\n"; 915 } 916 } else { 917 errstr_ += 918 "pandargs: \"" + arg->GetName() + "\" argument has invalid parameter value \"" + param_str + "\"\n"; 919 } 920 return 1; 921 } 922 923 std::size_t ParseListArgParam(PandArg<arg_list_t> *arg, std::string_view argstr) 924 { 925 std::string param_str(argstr); 926 arg_list_t value; 927 if (arg->WasSet()) { 928 value = arg->GetValue(); 929 } else { 930 value = arg_list_t(); 931 } 932 if (!arg->GetDelimiter().has_value()) { 933 value.push_back(param_str); 934 arg->SetValue(value); 935 return 1; 936 } 937 std::string delimiter = arg->GetDelimiter().value(); 938 std::size_t param_str_index = 0; 939 std::size_t pos = param_str.find_first_of(delimiter, param_str_index); 940 while (pos < param_str.size()) { 941 value.push_back(param_str.substr(param_str_index, pos - param_str_index)); 942 param_str_index = pos; 943 param_str_index = param_str.find_first_not_of(delimiter, param_str_index); 944 pos = param_str.find_first_of(delimiter, param_str_index); 945 } 946 947 value.push_back(param_str.substr(param_str_index, pos - param_str_index)); 948 arg->SetValue(value); 949 return 1; 950 } 951 952 static std::size_t NextSeparator(std::string_view argstr, std::size_t pos = 0, 953 const std::string &separarors = EQ_SEPARATOR) 954 { 955 return argstr.find_first_of(separarors, pos); 956 } 957 958 static bool IsIntegerNumber(const std::string_view &str) 959 { 960 if (str.empty()) { 961 return false; 962 } 963 std::size_t pos = 0; 964 // look for dash if it's negative one 965 if (str[0] == '-') { 966 pos++; 967 } 968 // look for hex-style integer 969 if ((str.size() > pos + 2U) && (str.compare(pos, 2U, "0x") == 0)) { 970 pos += HEX_PREFIX_WIDTH; 971 return str.find_first_not_of("0123456789abcdefABCDEF", pos) == std::string::npos; 972 } 973 return str.find_first_not_of("0123456789", pos) == std::string::npos; 974 } 975 976 static bool IsRationalNumber(const std::string_view &str) 977 { 978 if (str.empty()) { 979 return false; 980 } 981 std::size_t pos = 0; 982 // look for dash if it's negative one 983 if (str[0] == '-') { 984 pos++; 985 } 986 return str.find_first_not_of(".0123456789", pos) == std::string::npos; 987 } 988 989 static bool IsUintNumber(const std::string_view &str) 990 { 991 if (str.empty()) { 992 return false; 993 } 994 995 constexpr std::string_view char_search = "0123456789"; 996 constexpr std::string_view char_search_hex = "0123456789abcdef"; 997 std::size_t pos = 0; 998 // look for hex-style uint_t integer 999 if (str[0] == '0' && str[1] == 'x') { 1000 pos += HEX_PREFIX_WIDTH; 1001 } 1002 return str.find_first_not_of((pos != 0) ? char_search_hex.data() : char_search.data(), pos) == 1003 std::string::npos; 1004 } 1005 1006 template <typename T, 1007 enable_if_t<is_same_v<T, int> || is_same_v<T, uint32_t> || is_same_v<T, uint64_t>> * = nullptr> 1008 bool IsIntegerArgInRange(PandArg<T> *arg, T num) 1009 { 1010 if (!(arg->GetMinMaxVal().has_value())) { 1011 return true; 1012 } 1013 std::pair<T, T> min_max = arg->GetMinMaxVal().value(); 1014 return ((num >= std::get<0>(min_max)) && (num <= std::get<1>(min_max))); 1015 } 1016 1017 static bool StartsWith(const std::string &haystack, const std::string &needle) 1018 { 1019 return std::equal(needle.begin(), needle.end(), haystack.begin()); 1020 } 1021 1022 void SetBoolUnexpectedValueError(PandArg<bool> *arg, const std::string &wrongvalue) 1023 { 1024 errstr_ += "pandargs: Bool argument " + arg->GetName() + " has unexpected parameter value " + wrongvalue + "\n"; 1025 arg->ResetDefaultValue(); 1026 } 1027 1028 constexpr static size_t HEX_PREFIX_WIDTH = 2; 1029 constexpr static unsigned int DASH_COUNT = 2; 1030 1031 std::vector<std::string> argv_vec_; 1032 std::size_t argv_index_ = 0; 1033 std::string errstr_ = ""; 1034 bool tail_flag_ = false; 1035 bool remainder_flag_ = false; 1036 bool equal_flag_ = false; 1037 bool tail_parsed_flag_ = false; 1038 static constexpr const char *DOUBLE_DASH = "--"; 1039 static constexpr const char *EQ_SEPARATOR = "="; 1040 std::set<PandArgBase *, PandArgPtrComparator> args_; 1041 std::vector<PandArgBase *> tail_args_; 1042 arg_list_t remainder_ = arg_list_t(); 1043}; 1044 1045} // namespace panda 1046 1047#endif // LIBPANDABASE_UTILS_PANDARGS_H 1048