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