1/* 2 3Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck 4 5Permission is hereby granted, free of charge, to any person obtaining a copy 6of this software and associated documentation files (the "Software"), to deal 7in the Software without restriction, including without limitation the rights 8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9copies of the Software, and to permit persons to whom the Software is 10furnished to do so, subject to the following conditions: 11 12The above copyright notice and this permission notice shall be included in 13all copies or substantial portions of the Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21THE SOFTWARE. 22 23*/ 24 25#ifndef CXXOPTS_HPP_INCLUDED 26#define CXXOPTS_HPP_INCLUDED 27 28#include <cstring> 29#include <cctype> 30#include <exception> 31#include <iostream> 32#include <limits> 33#include <map> 34#include <memory> 35#include <regex> 36#include <sstream> 37#include <string> 38#include <unordered_map> 39#include <unordered_set> 40#include <vector> 41 42#ifdef __cpp_lib_optional 43#include <optional> 44#define CXXOPTS_HAS_OPTIONAL 45#endif 46 47#ifndef CXXOPTS_VECTOR_DELIMITER 48#define CXXOPTS_VECTOR_DELIMITER ',' 49#endif 50 51#define CXXOPTS__VERSION_MAJOR 2 52#define CXXOPTS__VERSION_MINOR 2 53#define CXXOPTS__VERSION_PATCH 1 54 55namespace cxxopts 56{ 57 static constexpr struct { 58 uint8_t major, minor, patch; 59 } version = { 60 CXXOPTS__VERSION_MAJOR, 61 CXXOPTS__VERSION_MINOR, 62 CXXOPTS__VERSION_PATCH 63 }; 64} 65 66//when we ask cxxopts to use Unicode, help strings are processed using ICU, 67//which results in the correct lengths being computed for strings when they 68//are formatted for the help output 69//it is necessary to make sure that <unicode/unistr.h> can be found by the 70//compiler, and that icu-uc is linked in to the binary. 71 72#ifdef CXXOPTS_USE_UNICODE 73#include <unicode/unistr.h> 74 75namespace cxxopts 76{ 77 typedef icu::UnicodeString String; 78 79 inline 80 String 81 toLocalString(std::string s) 82 { 83 return icu::UnicodeString::fromUTF8(std::move(s)); 84 } 85 86 class UnicodeStringIterator : public 87 std::iterator<std::forward_iterator_tag, int32_t> 88 { 89 public: 90 91 UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) 92 : s(string) 93 , i(pos) 94 { 95 } 96 97 value_type 98 operator*() const 99 { 100 return s->char32At(i); 101 } 102 103 bool 104 operator==(const UnicodeStringIterator& rhs) const 105 { 106 return s == rhs.s && i == rhs.i; 107 } 108 109 bool 110 operator!=(const UnicodeStringIterator& rhs) const 111 { 112 return !(*this == rhs); 113 } 114 115 UnicodeStringIterator& 116 operator++() 117 { 118 ++i; 119 return *this; 120 } 121 122 UnicodeStringIterator 123 operator+(int32_t v) 124 { 125 return UnicodeStringIterator(s, i + v); 126 } 127 128 private: 129 const icu::UnicodeString* s; 130 int32_t i; 131 }; 132 133 inline 134 String& 135 stringAppend(String&s, String a) 136 { 137 return s.append(std::move(a)); 138 } 139 140 inline 141 String& 142 stringAppend(String& s, int n, UChar32 c) 143 { 144 for (int i = 0; i != n; ++i) 145 { 146 s.append(c); 147 } 148 149 return s; 150 } 151 152 template <typename Iterator> 153 String& 154 stringAppend(String& s, Iterator begin, Iterator end) 155 { 156 while (begin != end) 157 { 158 s.append(*begin); 159 ++begin; 160 } 161 162 return s; 163 } 164 165 inline 166 size_t 167 stringLength(const String& s) 168 { 169 return s.length(); 170 } 171 172 inline 173 std::string 174 toUTF8String(const String& s) 175 { 176 std::string result; 177 s.toUTF8String(result); 178 179 return result; 180 } 181 182 inline 183 bool 184 empty(const String& s) 185 { 186 return s.isEmpty(); 187 } 188} 189 190namespace std 191{ 192 inline 193 cxxopts::UnicodeStringIterator 194 begin(const icu::UnicodeString& s) 195 { 196 return cxxopts::UnicodeStringIterator(&s, 0); 197 } 198 199 inline 200 cxxopts::UnicodeStringIterator 201 end(const icu::UnicodeString& s) 202 { 203 return cxxopts::UnicodeStringIterator(&s, s.length()); 204 } 205} 206 207//ifdef CXXOPTS_USE_UNICODE 208#else 209 210namespace cxxopts 211{ 212 typedef std::string String; 213 214 template <typename T> 215 T 216 toLocalString(T&& t) 217 { 218 return std::forward<T>(t); 219 } 220 221 inline 222 size_t 223 stringLength(const String& s) 224 { 225 return s.length(); 226 } 227 228 inline 229 String& 230 stringAppend(String&s, String a) 231 { 232 return s.append(std::move(a)); 233 } 234 235 inline 236 String& 237 stringAppend(String& s, size_t n, char c) 238 { 239 return s.append(n, c); 240 } 241 242 template <typename Iterator> 243 String& 244 stringAppend(String& s, Iterator begin, Iterator end) 245 { 246 return s.append(begin, end); 247 } 248 249 template <typename T> 250 std::string 251 toUTF8String(T&& t) 252 { 253 return std::forward<T>(t); 254 } 255 256 inline 257 bool 258 empty(const std::string& s) 259 { 260 return s.empty(); 261 } 262} 263 264//ifdef CXXOPTS_USE_UNICODE 265#endif 266 267namespace cxxopts 268{ 269 namespace 270 { 271#ifdef _WIN32 272 const std::string LQUOTE("\'"); 273 const std::string RQUOTE("\'"); 274#else 275 const std::string LQUOTE("‘"); 276 const std::string RQUOTE("’"); 277#endif 278 } 279 280 class Value : public std::enable_shared_from_this<Value> 281 { 282 public: 283 284 virtual ~Value() = default; 285 286 virtual 287 std::shared_ptr<Value> 288 clone() const = 0; 289 290 virtual void 291 parse(const std::string& text) const = 0; 292 293 virtual void 294 parse() const = 0; 295 296 virtual bool 297 has_default() const = 0; 298 299 virtual bool 300 is_container() const = 0; 301 302 virtual bool 303 has_implicit() const = 0; 304 305 virtual std::string 306 get_default_value() const = 0; 307 308 virtual std::string 309 get_implicit_value() const = 0; 310 311 virtual std::shared_ptr<Value> 312 default_value(const std::string& value) = 0; 313 314 virtual std::shared_ptr<Value> 315 implicit_value(const std::string& value) = 0; 316 317 virtual std::shared_ptr<Value> 318 no_implicit_value() = 0; 319 320 virtual bool 321 is_boolean() const = 0; 322 }; 323 324 class OptionException : public std::exception 325 { 326 public: 327 OptionException(const std::string& message) 328 : m_message(message) 329 { 330 } 331 332 virtual const char* 333 what() const noexcept 334 { 335 return m_message.c_str(); 336 } 337 338 private: 339 std::string m_message; 340 }; 341 342 class OptionSpecException : public OptionException 343 { 344 public: 345 346 OptionSpecException(const std::string& message) 347 : OptionException(message) 348 { 349 } 350 }; 351 352 class OptionParseException : public OptionException 353 { 354 public: 355 OptionParseException(const std::string& message) 356 : OptionException(message) 357 { 358 } 359 }; 360 361 class option_exists_error : public OptionSpecException 362 { 363 public: 364 option_exists_error(const std::string& option) 365 : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") 366 { 367 } 368 }; 369 370 class invalid_option_format_error : public OptionSpecException 371 { 372 public: 373 invalid_option_format_error(const std::string& format) 374 : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) 375 { 376 } 377 }; 378 379 class option_syntax_exception : public OptionParseException { 380 public: 381 option_syntax_exception(const std::string& text) 382 : OptionParseException("Argument " + LQUOTE + text + RQUOTE + 383 " starts with a - but has incorrect syntax") 384 { 385 } 386 }; 387 388 class option_not_exists_exception : public OptionParseException 389 { 390 public: 391 option_not_exists_exception(const std::string& option) 392 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") 393 { 394 } 395 }; 396 397 class missing_argument_exception : public OptionParseException 398 { 399 public: 400 missing_argument_exception(const std::string& option) 401 : OptionParseException( 402 "Option " + LQUOTE + option + RQUOTE + " is missing an argument" 403 ) 404 { 405 } 406 }; 407 408 class option_requires_argument_exception : public OptionParseException 409 { 410 public: 411 option_requires_argument_exception(const std::string& option) 412 : OptionParseException( 413 "Option " + LQUOTE + option + RQUOTE + " requires an argument" 414 ) 415 { 416 } 417 }; 418 419 class option_not_has_argument_exception : public OptionParseException 420 { 421 public: 422 option_not_has_argument_exception 423 ( 424 const std::string& option, 425 const std::string& arg 426 ) 427 : OptionParseException( 428 "Option " + LQUOTE + option + RQUOTE + 429 " does not take an argument, but argument " + 430 LQUOTE + arg + RQUOTE + " given" 431 ) 432 { 433 } 434 }; 435 436 class option_not_present_exception : public OptionParseException 437 { 438 public: 439 option_not_present_exception(const std::string& option) 440 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") 441 { 442 } 443 }; 444 445 class argument_incorrect_type : public OptionParseException 446 { 447 public: 448 argument_incorrect_type 449 ( 450 const std::string& arg 451 ) 452 : OptionParseException( 453 "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" 454 ) 455 { 456 } 457 }; 458 459 class option_required_exception : public OptionParseException 460 { 461 public: 462 option_required_exception(const std::string& option) 463 : OptionParseException( 464 "Option " + LQUOTE + option + RQUOTE + " is required but not present" 465 ) 466 { 467 } 468 }; 469 470 namespace values 471 { 472 namespace 473 { 474 std::basic_regex<char> integer_pattern 475 ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); 476 std::basic_regex<char> truthy_pattern 477 ("(t|T)(rue)?|1"); 478 std::basic_regex<char> falsy_pattern 479 ("(f|F)(alse)?|0"); 480 } 481 482 namespace detail 483 { 484 template <typename T, bool B> 485 struct SignedCheck; 486 487 template <typename T> 488 struct SignedCheck<T, true> 489 { 490 template <typename U> 491 void 492 operator()(bool negative, U u, const std::string& text) 493 { 494 if (negative) 495 { 496 if (u > static_cast<U>((std::numeric_limits<T>::min)())) 497 { 498 throw argument_incorrect_type(text); 499 } 500 } 501 else 502 { 503 if (u > static_cast<U>((std::numeric_limits<T>::max)())) 504 { 505 throw argument_incorrect_type(text); 506 } 507 } 508 } 509 }; 510 511 template <typename T> 512 struct SignedCheck<T, false> 513 { 514 template <typename U> 515 void 516 operator()(bool, U, const std::string&) {} 517 }; 518 519 template <typename T, typename U> 520 void 521 check_signed_range(bool negative, U value, const std::string& text) 522 { 523 SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text); 524 } 525 } 526 527 template <typename R, typename T> 528 R 529 checked_negate(T&& t, const std::string&, std::true_type) 530 { 531 // if we got to here, then `t` is a positive number that fits into 532 // `R`. So to avoid MSVC C4146, we first cast it to `R`. 533 // See https://github.com/jarro2783/cxxopts/issues/62 for more details. 534 return -static_cast<R>(t-1)-1; 535 } 536 537 template <typename R, typename T> 538 T 539 checked_negate(T&&, const std::string& text, std::false_type) 540 { 541 throw argument_incorrect_type(text); 542 } 543 544 template <typename T> 545 void 546 integer_parser(const std::string& text, T& value) 547 { 548 std::smatch match; 549 std::regex_match(text, match, integer_pattern); 550 551 if (match.length() == 0) 552 { 553 throw argument_incorrect_type(text); 554 } 555 556 if (match.length(4) > 0) 557 { 558 value = 0; 559 return; 560 } 561 562 using US = typename std::make_unsigned<T>::type; 563 564 constexpr bool is_signed = std::numeric_limits<T>::is_signed; 565 const bool negative = match.length(1) > 0; 566 const uint8_t base = match.length(2) > 0 ? 16 : 10; 567 568 auto value_match = match[3]; 569 570 US result = 0; 571 572 for (auto iter = value_match.first; iter != value_match.second; ++iter) 573 { 574 US digit = 0; 575 576 if (*iter >= '0' && *iter <= '9') 577 { 578 digit = static_cast<US>(*iter - '0'); 579 } 580 else if (base == 16 && *iter >= 'a' && *iter <= 'f') 581 { 582 digit = static_cast<US>(*iter - 'a' + 10); 583 } 584 else if (base == 16 && *iter >= 'A' && *iter <= 'F') 585 { 586 digit = static_cast<US>(*iter - 'A' + 10); 587 } 588 else 589 { 590 throw argument_incorrect_type(text); 591 } 592 593 US next = result * base + digit; 594 if (result > next) 595 { 596 throw argument_incorrect_type(text); 597 } 598 599 result = next; 600 } 601 602 detail::check_signed_range<T>(negative, result, text); 603 604 if (negative) 605 { 606 value = checked_negate<T>(result, 607 text, 608 std::integral_constant<bool, is_signed>()); 609 } 610 else 611 { 612 value = static_cast<T>(result); 613 } 614 } 615 616 template <typename T> 617 void stringstream_parser(const std::string& text, T& value) 618 { 619 std::stringstream in(text); 620 in >> value; 621 if (!in) { 622 throw argument_incorrect_type(text); 623 } 624 } 625 626 inline 627 void 628 parse_value(const std::string& text, uint8_t& value) 629 { 630 integer_parser(text, value); 631 } 632 633 inline 634 void 635 parse_value(const std::string& text, int8_t& value) 636 { 637 integer_parser(text, value); 638 } 639 640 inline 641 void 642 parse_value(const std::string& text, uint16_t& value) 643 { 644 integer_parser(text, value); 645 } 646 647 inline 648 void 649 parse_value(const std::string& text, int16_t& value) 650 { 651 integer_parser(text, value); 652 } 653 654 inline 655 void 656 parse_value(const std::string& text, uint32_t& value) 657 { 658 integer_parser(text, value); 659 } 660 661 inline 662 void 663 parse_value(const std::string& text, int32_t& value) 664 { 665 integer_parser(text, value); 666 } 667 668 inline 669 void 670 parse_value(const std::string& text, uint64_t& value) 671 { 672 integer_parser(text, value); 673 } 674 675 inline 676 void 677 parse_value(const std::string& text, int64_t& value) 678 { 679 integer_parser(text, value); 680 } 681 682 inline 683 void 684 parse_value(const std::string& text, bool& value) 685 { 686 std::smatch result; 687 std::regex_match(text, result, truthy_pattern); 688 689 if (!result.empty()) 690 { 691 value = true; 692 return; 693 } 694 695 std::regex_match(text, result, falsy_pattern); 696 if (!result.empty()) 697 { 698 value = false; 699 return; 700 } 701 702 throw argument_incorrect_type(text); 703 } 704 705 inline 706 void 707 parse_value(const std::string& text, std::string& value) 708 { 709 value = text; 710 } 711 712 // The fallback parser. It uses the stringstream parser to parse all types 713 // that have not been overloaded explicitly. It has to be placed in the 714 // source code before all other more specialized templates. 715 template <typename T> 716 void 717 parse_value(const std::string& text, T& value) { 718 stringstream_parser(text, value); 719 } 720 721 template <typename T> 722 void 723 parse_value(const std::string& text, std::vector<T>& value) 724 { 725 std::stringstream in(text); 726 std::string token; 727 while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { 728 T v; 729 parse_value(token, v); 730 value.emplace_back(std::move(v)); 731 } 732 } 733 734#ifdef CXXOPTS_HAS_OPTIONAL 735 template <typename T> 736 void 737 parse_value(const std::string& text, std::optional<T>& value) 738 { 739 T result; 740 parse_value(text, result); 741 value = std::move(result); 742 } 743#endif 744 745 template <typename T> 746 struct type_is_container 747 { 748 static constexpr bool value = false; 749 }; 750 751 template <typename T> 752 struct type_is_container<std::vector<T>> 753 { 754 static constexpr bool value = true; 755 }; 756 757 template <typename T> 758 class abstract_value : public Value 759 { 760 using Self = abstract_value<T>; 761 762 public: 763 abstract_value() 764 : m_result(std::make_shared<T>()) 765 , m_store(m_result.get()) 766 { 767 } 768 769 abstract_value(T* t) 770 : m_store(t) 771 { 772 } 773 774 virtual ~abstract_value() = default; 775 776 abstract_value(const abstract_value& rhs) 777 { 778 if (rhs.m_result) 779 { 780 m_result = std::make_shared<T>(); 781 m_store = m_result.get(); 782 } 783 else 784 { 785 m_store = rhs.m_store; 786 } 787 788 m_default = rhs.m_default; 789 m_implicit = rhs.m_implicit; 790 m_default_value = rhs.m_default_value; 791 m_implicit_value = rhs.m_implicit_value; 792 } 793 794 void 795 parse(const std::string& text) const 796 { 797 parse_value(text, *m_store); 798 } 799 800 bool 801 is_container() const 802 { 803 return type_is_container<T>::value; 804 } 805 806 void 807 parse() const 808 { 809 parse_value(m_default_value, *m_store); 810 } 811 812 bool 813 has_default() const 814 { 815 return m_default; 816 } 817 818 bool 819 has_implicit() const 820 { 821 return m_implicit; 822 } 823 824 std::shared_ptr<Value> 825 default_value(const std::string& value) 826 { 827 m_default = true; 828 m_default_value = value; 829 return shared_from_this(); 830 } 831 832 std::shared_ptr<Value> 833 implicit_value(const std::string& value) 834 { 835 m_implicit = true; 836 m_implicit_value = value; 837 return shared_from_this(); 838 } 839 840 std::shared_ptr<Value> 841 no_implicit_value() 842 { 843 m_implicit = false; 844 return shared_from_this(); 845 } 846 847 std::string 848 get_default_value() const 849 { 850 return m_default_value; 851 } 852 853 std::string 854 get_implicit_value() const 855 { 856 return m_implicit_value; 857 } 858 859 bool 860 is_boolean() const 861 { 862 return std::is_same<T, bool>::value; 863 } 864 865 const T& 866 get() const 867 { 868 if (m_store == nullptr) 869 { 870 return *m_result; 871 } 872 else 873 { 874 return *m_store; 875 } 876 } 877 878 protected: 879 std::shared_ptr<T> m_result; 880 T* m_store; 881 882 bool m_default = false; 883 bool m_implicit = false; 884 885 std::string m_default_value; 886 std::string m_implicit_value; 887 }; 888 889 template <typename T> 890 class standard_value : public abstract_value<T> 891 { 892 public: 893 using abstract_value<T>::abstract_value; 894 895 std::shared_ptr<Value> 896 clone() const 897 { 898 return std::make_shared<standard_value<T>>(*this); 899 } 900 }; 901 902 template <> 903 class standard_value<bool> : public abstract_value<bool> 904 { 905 public: 906 ~standard_value() = default; 907 908 standard_value() 909 { 910 set_default_and_implicit(); 911 } 912 913 standard_value(bool* b) 914 : abstract_value(b) 915 { 916 set_default_and_implicit(); 917 } 918 919 std::shared_ptr<Value> 920 clone() const 921 { 922 return std::make_shared<standard_value<bool>>(*this); 923 } 924 925 private: 926 927 void 928 set_default_and_implicit() 929 { 930 m_default = true; 931 m_default_value = "false"; 932 m_implicit = true; 933 m_implicit_value = "true"; 934 } 935 }; 936 } 937 938 template <typename T> 939 std::shared_ptr<Value> 940 value() 941 { 942 return std::make_shared<values::standard_value<T>>(); 943 } 944 945 template <typename T> 946 std::shared_ptr<Value> 947 value(T& t) 948 { 949 return std::make_shared<values::standard_value<T>>(&t); 950 } 951 952 class OptionAdder; 953 954 class OptionDetails 955 { 956 public: 957 OptionDetails 958 ( 959 const std::string& short_, 960 const std::string& long_, 961 const String& desc, 962 std::shared_ptr<const Value> val 963 ) 964 : m_short(short_) 965 , m_long(long_) 966 , m_desc(desc) 967 , m_value(val) 968 , m_count(0) 969 { 970 } 971 972 OptionDetails(const OptionDetails& rhs) 973 : m_desc(rhs.m_desc) 974 , m_count(rhs.m_count) 975 { 976 m_value = rhs.m_value->clone(); 977 } 978 979 OptionDetails(OptionDetails&& rhs) = default; 980 981 const String& 982 description() const 983 { 984 return m_desc; 985 } 986 987 const Value& value() const { 988 return *m_value; 989 } 990 991 std::shared_ptr<Value> 992 make_storage() const 993 { 994 return m_value->clone(); 995 } 996 997 const std::string& 998 short_name() const 999 { 1000 return m_short; 1001 } 1002 1003 const std::string& 1004 long_name() const 1005 { 1006 return m_long; 1007 } 1008 1009 private: 1010 std::string m_short; 1011 std::string m_long; 1012 String m_desc; 1013 std::shared_ptr<const Value> m_value; 1014 int m_count; 1015 }; 1016 1017 struct HelpOptionDetails 1018 { 1019 std::string s; 1020 std::string l; 1021 String desc; 1022 bool has_default; 1023 std::string default_value; 1024 bool has_implicit; 1025 std::string implicit_value; 1026 std::string arg_help; 1027 bool is_container; 1028 bool is_boolean; 1029 }; 1030 1031 struct HelpGroupDetails 1032 { 1033 std::string name; 1034 std::string description; 1035 std::vector<HelpOptionDetails> options; 1036 }; 1037 1038 class OptionValue 1039 { 1040 public: 1041 void 1042 parse 1043 ( 1044 std::shared_ptr<const OptionDetails> details, 1045 const std::string& text 1046 ) 1047 { 1048 ensure_value(details); 1049 ++m_count; 1050 m_value->parse(text); 1051 } 1052 1053 void 1054 parse_default(std::shared_ptr<const OptionDetails> details) 1055 { 1056 ensure_value(details); 1057 m_default = true; 1058 m_value->parse(); 1059 } 1060 1061 size_t 1062 count() const noexcept 1063 { 1064 return m_count; 1065 } 1066 1067 // TODO: maybe default options should count towards the number of arguments 1068 bool 1069 has_default() const noexcept 1070 { 1071 return m_default; 1072 } 1073 1074 template <typename T> 1075 const T& 1076 as() const 1077 { 1078 if (m_value == nullptr) { 1079 throw std::domain_error("No value"); 1080 } 1081 1082#ifdef CXXOPTS_NO_RTTI 1083 return static_cast<const values::standard_value<T>&>(*m_value).get(); 1084#else 1085 return dynamic_cast<const values::standard_value<T>&>(*m_value).get(); 1086#endif 1087 } 1088 1089 private: 1090 void 1091 ensure_value(std::shared_ptr<const OptionDetails> details) 1092 { 1093 if (m_value == nullptr) 1094 { 1095 m_value = details->make_storage(); 1096 } 1097 } 1098 1099 std::shared_ptr<Value> m_value; 1100 size_t m_count = 0; 1101 bool m_default = false; 1102 }; 1103 1104 class KeyValue 1105 { 1106 public: 1107 KeyValue(std::string key_, std::string value_) 1108 : m_key(std::move(key_)) 1109 , m_value(std::move(value_)) 1110 { 1111 } 1112 1113 const 1114 std::string& 1115 key() const 1116 { 1117 return m_key; 1118 } 1119 1120 const 1121 std::string& 1122 value() const 1123 { 1124 return m_value; 1125 } 1126 1127 template <typename T> 1128 T 1129 as() const 1130 { 1131 T result; 1132 values::parse_value(m_value, result); 1133 return result; 1134 } 1135 1136 private: 1137 std::string m_key; 1138 std::string m_value; 1139 }; 1140 1141 class ParseResult 1142 { 1143 public: 1144 1145 ParseResult( 1146 const std::shared_ptr< 1147 std::unordered_map<std::string, std::shared_ptr<OptionDetails>> 1148 >, 1149 std::vector<std::string>, 1150 bool allow_unrecognised, 1151 int&, char**&); 1152 1153 size_t 1154 count(const std::string& o) const 1155 { 1156 auto iter = m_options->find(o); 1157 if (iter == m_options->end()) 1158 { 1159 return 0; 1160 } 1161 1162 auto riter = m_results.find(iter->second); 1163 1164 return riter->second.count(); 1165 } 1166 1167 const OptionValue& 1168 operator[](const std::string& option) const 1169 { 1170 auto iter = m_options->find(option); 1171 1172 if (iter == m_options->end()) 1173 { 1174 throw option_not_present_exception(option); 1175 } 1176 1177 auto riter = m_results.find(iter->second); 1178 1179 return riter->second; 1180 } 1181 1182 const std::vector<KeyValue>& 1183 arguments() const 1184 { 1185 return m_sequential; 1186 } 1187 1188 private: 1189 1190 void 1191 parse(int& argc, char**& argv); 1192 1193 void 1194 add_to_option(const std::string& option, const std::string& arg); 1195 1196 bool 1197 consume_positional(std::string a); 1198 1199 void 1200 parse_option 1201 ( 1202 std::shared_ptr<OptionDetails> value, 1203 const std::string& name, 1204 const std::string& arg = "" 1205 ); 1206 1207 void 1208 parse_default(std::shared_ptr<OptionDetails> details); 1209 1210 void 1211 checked_parse_arg 1212 ( 1213 int argc, 1214 char* argv[], 1215 int& current, 1216 std::shared_ptr<OptionDetails> value, 1217 const std::string& name 1218 ); 1219 1220 const std::shared_ptr< 1221 std::unordered_map<std::string, std::shared_ptr<OptionDetails>> 1222 > m_options; 1223 std::vector<std::string> m_positional; 1224 std::vector<std::string>::iterator m_next_positional; 1225 std::unordered_set<std::string> m_positional_set; 1226 std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results; 1227 1228 bool m_allow_unrecognised; 1229 1230 std::vector<KeyValue> m_sequential; 1231 }; 1232 1233 class Options 1234 { 1235 typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>> 1236 OptionMap; 1237 public: 1238 1239 Options(std::string program, std::string help_string = "") 1240 : m_program(std::move(program)) 1241 , m_help_string(toLocalString(std::move(help_string))) 1242 , m_custom_help("[OPTION...]") 1243 , m_positional_help("positional parameters") 1244 , m_show_positional(false) 1245 , m_allow_unrecognised(false) 1246 , m_options(std::make_shared<OptionMap>()) 1247 , m_next_positional(m_positional.end()) 1248 { 1249 } 1250 1251 Options& 1252 positional_help(std::string help_text) 1253 { 1254 m_positional_help = std::move(help_text); 1255 return *this; 1256 } 1257 1258 Options& 1259 custom_help(std::string help_text) 1260 { 1261 m_custom_help = std::move(help_text); 1262 return *this; 1263 } 1264 1265 Options& 1266 show_positional_help() 1267 { 1268 m_show_positional = true; 1269 return *this; 1270 } 1271 1272 Options& 1273 allow_unrecognised_options() 1274 { 1275 m_allow_unrecognised = true; 1276 return *this; 1277 } 1278 1279 ParseResult 1280 parse(int& argc, char**& argv); 1281 1282 OptionAdder 1283 add_options(std::string group = ""); 1284 1285 void 1286 add_option 1287 ( 1288 const std::string& group, 1289 const std::string& s, 1290 const std::string& l, 1291 std::string desc, 1292 std::shared_ptr<const Value> value, 1293 std::string arg_help 1294 ); 1295 1296 //parse positional arguments into the given option 1297 void 1298 parse_positional(std::string option); 1299 1300 void 1301 parse_positional(std::vector<std::string> options); 1302 1303 void 1304 parse_positional(std::initializer_list<std::string> options); 1305 1306 template <typename Iterator> 1307 void 1308 parse_positional(Iterator begin, Iterator end) { 1309 parse_positional(std::vector<std::string>{begin, end}); 1310 } 1311 1312 std::string 1313 help(const std::vector<std::string>& groups = {}) const; 1314 1315 const std::vector<std::string> 1316 groups() const; 1317 1318 const HelpGroupDetails& 1319 group_help(const std::string& group) const; 1320 1321 private: 1322 1323 void 1324 add_one_option 1325 ( 1326 const std::string& option, 1327 std::shared_ptr<OptionDetails> details 1328 ); 1329 1330 String 1331 help_one_group(const std::string& group) const; 1332 1333 void 1334 generate_group_help 1335 ( 1336 String& result, 1337 const std::vector<std::string>& groups 1338 ) const; 1339 1340 void 1341 generate_all_groups_help(String& result) const; 1342 1343 std::string m_program; 1344 String m_help_string; 1345 std::string m_custom_help; 1346 std::string m_positional_help; 1347 bool m_show_positional; 1348 bool m_allow_unrecognised; 1349 1350 std::shared_ptr<OptionMap> m_options; 1351 std::vector<std::string> m_positional; 1352 std::vector<std::string>::iterator m_next_positional; 1353 std::unordered_set<std::string> m_positional_set; 1354 1355 //mapping from groups to help options 1356 std::map<std::string, HelpGroupDetails> m_help; 1357 }; 1358 1359 class OptionAdder 1360 { 1361 public: 1362 1363 OptionAdder(Options& options, std::string group) 1364 : m_options(options), m_group(std::move(group)) 1365 { 1366 } 1367 1368 OptionAdder& 1369 operator() 1370 ( 1371 const std::string& opts, 1372 const std::string& desc, 1373 std::shared_ptr<const Value> value 1374 = ::cxxopts::value<bool>(), 1375 std::string arg_help = "" 1376 ); 1377 1378 private: 1379 Options& m_options; 1380 std::string m_group; 1381 }; 1382 1383 namespace 1384 { 1385 constexpr int OPTION_LONGEST = 30; 1386 constexpr int OPTION_DESC_GAP = 2; 1387 1388 std::basic_regex<char> option_matcher 1389 ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); 1390 1391 std::basic_regex<char> option_specifier 1392 ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); 1393 1394 String 1395 format_option 1396 ( 1397 const HelpOptionDetails& o 1398 ) 1399 { 1400 auto& s = o.s; 1401 auto& l = o.l; 1402 1403 String result = " "; 1404 1405 if (s.size() > 0) 1406 { 1407 result += "-" + toLocalString(s) + ","; 1408 } 1409 else 1410 { 1411 result += " "; 1412 } 1413 1414 if (l.size() > 0) 1415 { 1416 result += " --" + toLocalString(l); 1417 } 1418 1419 auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; 1420 1421 if (!o.is_boolean) 1422 { 1423 if (o.has_implicit) 1424 { 1425 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; 1426 } 1427 else 1428 { 1429 result += " " + arg; 1430 } 1431 } 1432 1433 return result; 1434 } 1435 1436 String 1437 format_description 1438 ( 1439 const HelpOptionDetails& o, 1440 size_t start, 1441 size_t width 1442 ) 1443 { 1444 auto desc = o.desc; 1445 1446 if (o.has_default && (!o.is_boolean || o.default_value != "false")) 1447 { 1448 desc += toLocalString(" (default: " + o.default_value + ")"); 1449 } 1450 1451 String result; 1452 1453 auto current = std::begin(desc); 1454 auto startLine = current; 1455 auto lastSpace = current; 1456 1457 auto size = size_t{}; 1458 1459 while (current != std::end(desc)) 1460 { 1461 if (*current == ' ') 1462 { 1463 lastSpace = current; 1464 } 1465 1466 if (*current == '\n') 1467 { 1468 startLine = current + 1; 1469 lastSpace = startLine; 1470 } 1471 else if (size > width) 1472 { 1473 if (lastSpace == startLine) 1474 { 1475 stringAppend(result, startLine, current + 1); 1476 stringAppend(result, "\n"); 1477 stringAppend(result, start, ' '); 1478 startLine = current + 1; 1479 lastSpace = startLine; 1480 } 1481 else 1482 { 1483 stringAppend(result, startLine, lastSpace); 1484 stringAppend(result, "\n"); 1485 stringAppend(result, start, ' '); 1486 startLine = lastSpace + 1; 1487 lastSpace = startLine; 1488 } 1489 size = 0; 1490 } 1491 else 1492 { 1493 ++size; 1494 } 1495 1496 ++current; 1497 } 1498 1499 //append whatever is left 1500 stringAppend(result, startLine, current); 1501 1502 return result; 1503 } 1504 } 1505 1506inline 1507ParseResult::ParseResult 1508( 1509 const std::shared_ptr< 1510 std::unordered_map<std::string, std::shared_ptr<OptionDetails>> 1511 > options, 1512 std::vector<std::string> positional, 1513 bool allow_unrecognised, 1514 int& argc, char**& argv 1515) 1516: m_options(options) 1517, m_positional(std::move(positional)) 1518, m_next_positional(m_positional.begin()) 1519, m_allow_unrecognised(allow_unrecognised) 1520{ 1521 parse(argc, argv); 1522} 1523 1524inline 1525OptionAdder 1526Options::add_options(std::string group) 1527{ 1528 return OptionAdder(*this, std::move(group)); 1529} 1530 1531inline 1532OptionAdder& 1533OptionAdder::operator() 1534( 1535 const std::string& opts, 1536 const std::string& desc, 1537 std::shared_ptr<const Value> value, 1538 std::string arg_help 1539) 1540{ 1541 std::match_results<const char*> result; 1542 std::regex_match(opts.c_str(), result, option_specifier); 1543 1544 if (result.empty()) 1545 { 1546 throw invalid_option_format_error(opts); 1547 } 1548 1549 const auto& short_match = result[2]; 1550 const auto& long_match = result[3]; 1551 1552 if (!short_match.length() && !long_match.length()) 1553 { 1554 throw invalid_option_format_error(opts); 1555 } else if (long_match.length() == 1 && short_match.length()) 1556 { 1557 throw invalid_option_format_error(opts); 1558 } 1559 1560 auto option_names = [] 1561 ( 1562 const std::sub_match<const char*>& short_, 1563 const std::sub_match<const char*>& long_ 1564 ) 1565 { 1566 if (long_.length() == 1) 1567 { 1568 return std::make_tuple(long_.str(), short_.str()); 1569 } 1570 else 1571 { 1572 return std::make_tuple(short_.str(), long_.str()); 1573 } 1574 }(short_match, long_match); 1575 1576 m_options.add_option 1577 ( 1578 m_group, 1579 std::get<0>(option_names), 1580 std::get<1>(option_names), 1581 desc, 1582 value, 1583 std::move(arg_help) 1584 ); 1585 1586 return *this; 1587} 1588 1589inline 1590void 1591ParseResult::parse_default(std::shared_ptr<OptionDetails> details) 1592{ 1593 m_results[details].parse_default(details); 1594} 1595 1596inline 1597void 1598ParseResult::parse_option 1599( 1600 std::shared_ptr<OptionDetails> value, 1601 const std::string& /*name*/, 1602 const std::string& arg 1603) 1604{ 1605 auto& result = m_results[value]; 1606 result.parse(value, arg); 1607 1608 m_sequential.emplace_back(value->long_name(), arg); 1609} 1610 1611inline 1612void 1613ParseResult::checked_parse_arg 1614( 1615 int argc, 1616 char* argv[], 1617 int& current, 1618 std::shared_ptr<OptionDetails> value, 1619 const std::string& name 1620) 1621{ 1622 if (current + 1 >= argc) 1623 { 1624 if (value->value().has_implicit()) 1625 { 1626 parse_option(value, name, value->value().get_implicit_value()); 1627 } 1628 else 1629 { 1630 throw missing_argument_exception(name); 1631 } 1632 } 1633 else 1634 { 1635 if (value->value().has_implicit()) 1636 { 1637 parse_option(value, name, value->value().get_implicit_value()); 1638 } 1639 else 1640 { 1641 parse_option(value, name, argv[current + 1]); 1642 ++current; 1643 } 1644 } 1645} 1646 1647inline 1648void 1649ParseResult::add_to_option(const std::string& option, const std::string& arg) 1650{ 1651 auto iter = m_options->find(option); 1652 1653 if (iter == m_options->end()) 1654 { 1655 throw option_not_exists_exception(option); 1656 } 1657 1658 parse_option(iter->second, option, arg); 1659} 1660 1661inline 1662bool 1663ParseResult::consume_positional(std::string a) 1664{ 1665 while (m_next_positional != m_positional.end()) 1666 { 1667 auto iter = m_options->find(*m_next_positional); 1668 if (iter != m_options->end()) 1669 { 1670 auto& result = m_results[iter->second]; 1671 if (!iter->second->value().is_container()) 1672 { 1673 if (result.count() == 0) 1674 { 1675 add_to_option(*m_next_positional, a); 1676 ++m_next_positional; 1677 return true; 1678 } 1679 else 1680 { 1681 ++m_next_positional; 1682 continue; 1683 } 1684 } 1685 else 1686 { 1687 add_to_option(*m_next_positional, a); 1688 return true; 1689 } 1690 } 1691 else 1692 { 1693 throw option_not_exists_exception(*m_next_positional); 1694 } 1695 } 1696 1697 return false; 1698} 1699 1700inline 1701void 1702Options::parse_positional(std::string option) 1703{ 1704 parse_positional(std::vector<std::string>{std::move(option)}); 1705} 1706 1707inline 1708void 1709Options::parse_positional(std::vector<std::string> options) 1710{ 1711 m_positional = std::move(options); 1712 m_next_positional = m_positional.begin(); 1713 1714 m_positional_set.insert(m_positional.begin(), m_positional.end()); 1715} 1716 1717inline 1718void 1719Options::parse_positional(std::initializer_list<std::string> options) 1720{ 1721 parse_positional(std::vector<std::string>(std::move(options))); 1722} 1723 1724inline 1725ParseResult 1726Options::parse(int& argc, char**& argv) 1727{ 1728 ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv); 1729 return result; 1730} 1731 1732inline 1733void 1734ParseResult::parse(int& argc, char**& argv) 1735{ 1736 int current = 1; 1737 1738 int nextKeep = 1; 1739 1740 bool consume_remaining = false; 1741 1742 while (current != argc) 1743 { 1744 if (strcmp(argv[current], "--") == 0) 1745 { 1746 consume_remaining = true; 1747 ++current; 1748 break; 1749 } 1750 1751 std::match_results<const char*> result; 1752 std::regex_match(argv[current], result, option_matcher); 1753 1754 if (result.empty()) 1755 { 1756 //not a flag 1757 1758 // but if it starts with a `-`, then it's an error 1759 if (argv[current][0] == '-' && argv[current][1] != '\0') { 1760 if (!m_allow_unrecognised) { 1761 throw option_syntax_exception(argv[current]); 1762 } 1763 } 1764 1765 //if true is returned here then it was consumed, otherwise it is 1766 //ignored 1767 if (consume_positional(argv[current])) 1768 { 1769 } 1770 else 1771 { 1772 argv[nextKeep] = argv[current]; 1773 ++nextKeep; 1774 } 1775 //if we return from here then it was parsed successfully, so continue 1776 } 1777 else 1778 { 1779 //short or long option? 1780 if (result[4].length() != 0) 1781 { 1782 const std::string& s = result[4]; 1783 1784 for (std::size_t i = 0; i != s.size(); ++i) 1785 { 1786 std::string name(1, s[i]); 1787 auto iter = m_options->find(name); 1788 1789 if (iter == m_options->end()) 1790 { 1791 if (m_allow_unrecognised) 1792 { 1793 continue; 1794 } 1795 else 1796 { 1797 //error 1798 throw option_not_exists_exception(name); 1799 } 1800 } 1801 1802 auto value = iter->second; 1803 1804 if (i + 1 == s.size()) 1805 { 1806 //it must be the last argument 1807 checked_parse_arg(argc, argv, current, value, name); 1808 } 1809 else if (value->value().has_implicit()) 1810 { 1811 parse_option(value, name, value->value().get_implicit_value()); 1812 } 1813 else 1814 { 1815 //error 1816 throw option_requires_argument_exception(name); 1817 } 1818 } 1819 } 1820 else if (result[1].length() != 0) 1821 { 1822 const std::string& name = result[1]; 1823 1824 auto iter = m_options->find(name); 1825 1826 if (iter == m_options->end()) 1827 { 1828 if (m_allow_unrecognised) 1829 { 1830 // keep unrecognised options in argument list, skip to next argument 1831 argv[nextKeep] = argv[current]; 1832 ++nextKeep; 1833 ++current; 1834 continue; 1835 } 1836 else 1837 { 1838 //error 1839 throw option_not_exists_exception(name); 1840 } 1841 } 1842 1843 auto opt = iter->second; 1844 1845 //equals provided for long option? 1846 if (result[2].length() != 0) 1847 { 1848 //parse the option given 1849 1850 parse_option(opt, name, result[3]); 1851 } 1852 else 1853 { 1854 //parse the next argument 1855 checked_parse_arg(argc, argv, current, opt, name); 1856 } 1857 } 1858 1859 } 1860 1861 ++current; 1862 } 1863 1864 for (auto& opt : *m_options) 1865 { 1866 auto& detail = opt.second; 1867 auto& value = detail->value(); 1868 1869 auto& store = m_results[detail]; 1870 1871 if(value.has_default() && !store.count() && !store.has_default()){ 1872 parse_default(detail); 1873 } 1874 } 1875 1876 if (consume_remaining) 1877 { 1878 while (current < argc) 1879 { 1880 if (!consume_positional(argv[current])) { 1881 break; 1882 } 1883 ++current; 1884 } 1885 1886 //adjust argv for any that couldn't be swallowed 1887 while (current != argc) { 1888 argv[nextKeep] = argv[current]; 1889 ++nextKeep; 1890 ++current; 1891 } 1892 } 1893 1894 argc = nextKeep; 1895 1896} 1897 1898inline 1899void 1900Options::add_option 1901( 1902 const std::string& group, 1903 const std::string& s, 1904 const std::string& l, 1905 std::string desc, 1906 std::shared_ptr<const Value> value, 1907 std::string arg_help 1908) 1909{ 1910 auto stringDesc = toLocalString(std::move(desc)); 1911 auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value); 1912 1913 if (s.size() > 0) 1914 { 1915 add_one_option(s, option); 1916 } 1917 1918 if (l.size() > 0) 1919 { 1920 add_one_option(l, option); 1921 } 1922 1923 //add the help details 1924 auto& options = m_help[group]; 1925 1926 options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, 1927 value->has_default(), value->get_default_value(), 1928 value->has_implicit(), value->get_implicit_value(), 1929 std::move(arg_help), 1930 value->is_container(), 1931 value->is_boolean()}); 1932} 1933 1934inline 1935void 1936Options::add_one_option 1937( 1938 const std::string& option, 1939 std::shared_ptr<OptionDetails> details 1940) 1941{ 1942 auto in = m_options->emplace(option, details); 1943 1944 if (!in.second) 1945 { 1946 throw option_exists_error(option); 1947 } 1948} 1949 1950inline 1951String 1952Options::help_one_group(const std::string& g) const 1953{ 1954 typedef std::vector<std::pair<String, String>> OptionHelp; 1955 1956 auto group = m_help.find(g); 1957 if (group == m_help.end()) 1958 { 1959 return ""; 1960 } 1961 1962 OptionHelp format; 1963 1964 size_t longest = 0; 1965 1966 String result; 1967 1968 if (!g.empty()) 1969 { 1970 result += toLocalString(" " + g + " options:\n"); 1971 } 1972 1973 for (const auto& o : group->second.options) 1974 { 1975 if (m_positional_set.find(o.l) != m_positional_set.end() && 1976 !m_show_positional) 1977 { 1978 continue; 1979 } 1980 1981 auto s = format_option(o); 1982 longest = (std::max)(longest, stringLength(s)); 1983 format.push_back(std::make_pair(s, String())); 1984 } 1985 1986 longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST)); 1987 1988 //widest allowed description 1989 auto allowed = size_t{76} - longest - OPTION_DESC_GAP; 1990 1991 auto fiter = format.begin(); 1992 for (const auto& o : group->second.options) 1993 { 1994 if (m_positional_set.find(o.l) != m_positional_set.end() && 1995 !m_show_positional) 1996 { 1997 continue; 1998 } 1999 2000 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); 2001 2002 result += fiter->first; 2003 if (stringLength(fiter->first) > longest) 2004 { 2005 result += '\n'; 2006 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); 2007 } 2008 else 2009 { 2010 result += toLocalString(std::string(longest + OPTION_DESC_GAP - 2011 stringLength(fiter->first), 2012 ' ')); 2013 } 2014 result += d; 2015 result += '\n'; 2016 2017 ++fiter; 2018 } 2019 2020 return result; 2021} 2022 2023inline 2024void 2025Options::generate_group_help 2026( 2027 String& result, 2028 const std::vector<std::string>& print_groups 2029) const 2030{ 2031 for (size_t i = 0; i != print_groups.size(); ++i) 2032 { 2033 const String& group_help_text = help_one_group(print_groups[i]); 2034 if (empty(group_help_text)) 2035 { 2036 continue; 2037 } 2038 result += group_help_text; 2039 if (i < print_groups.size() - 1) 2040 { 2041 result += '\n'; 2042 } 2043 } 2044} 2045 2046inline 2047void 2048Options::generate_all_groups_help(String& result) const 2049{ 2050 std::vector<std::string> all_groups; 2051 all_groups.reserve(m_help.size()); 2052 2053 for (auto& group : m_help) 2054 { 2055 all_groups.push_back(group.first); 2056 } 2057 2058 generate_group_help(result, all_groups); 2059} 2060 2061inline 2062std::string 2063Options::help(const std::vector<std::string>& help_groups) const 2064{ 2065 String result = m_help_string + "\nUsage:\n " + 2066 toLocalString(m_program) + " " + toLocalString(m_custom_help); 2067 2068 if (m_positional.size() > 0 && m_positional_help.size() > 0) { 2069 result += " " + toLocalString(m_positional_help); 2070 } 2071 2072 result += "\n\n"; 2073 2074 if (help_groups.size() == 0) 2075 { 2076 generate_all_groups_help(result); 2077 } 2078 else 2079 { 2080 generate_group_help(result, help_groups); 2081 } 2082 2083 return toUTF8String(result); 2084} 2085 2086inline 2087const std::vector<std::string> 2088Options::groups() const 2089{ 2090 std::vector<std::string> g; 2091 2092 std::transform( 2093 m_help.begin(), 2094 m_help.end(), 2095 std::back_inserter(g), 2096 [] (const std::map<std::string, HelpGroupDetails>::value_type& pair) 2097 { 2098 return pair.first; 2099 } 2100 ); 2101 2102 return g; 2103} 2104 2105inline 2106const HelpGroupDetails& 2107Options::group_help(const std::string& group) const 2108{ 2109 return m_help.at(group); 2110} 2111 2112} 2113 2114#endif //CXXOPTS_HPP_INCLUDED 2115