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