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