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