File indexing completed on 2024-11-19 23:20:31
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025 #ifndef CXXOPTS_HPP_INCLUDED
0026 #define CXXOPTS_HPP_INCLUDED
0027
0028 #include <cstring>
0029 #include <cctype>
0030 #include <exception>
0031 #include <iostream>
0032 #include <map>
0033 #include <memory>
0034 #include <regex>
0035 #include <sstream>
0036 #include <string>
0037 #include <unordered_map>
0038 #include <unordered_set>
0039 #include <vector>
0040 #include <limits>
0041
0042 #ifdef __cpp_lib_optional
0043 #include <optional>
0044 #define CXXOPTS_HAS_OPTIONAL
0045 #endif
0046
0047 #define CXXOPTS__VERSION_MAJOR 2
0048 #define CXXOPTS__VERSION_MINOR 2
0049 #define CXXOPTS__VERSION_PATCH 0
0050
0051 namespace cxxopts {
0052 static constexpr struct {
0053 uint8_t major, minor, patch;
0054 } version = {CXXOPTS__VERSION_MAJOR, CXXOPTS__VERSION_MINOR, CXXOPTS__VERSION_PATCH};
0055 }
0056
0057
0058
0059
0060
0061
0062
0063 #ifdef CXXOPTS_USE_UNICODE
0064 #include <unicode/unistr.h>
0065
0066 namespace cxxopts {
0067 typedef icu::UnicodeString String;
0068
0069 inline String toLocalString(std::string s) { return icu::UnicodeString::fromUTF8(std::move(s)); }
0070
0071 class UnicodeStringIterator : public std::iterator<std::forward_iterator_tag, int32_t> {
0072 public:
0073 UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) : s(string), i(pos) {}
0074
0075 value_type operator*() const { return s->char32At(i); }
0076
0077 bool operator==(const UnicodeStringIterator& rhs) const { return s == rhs.s && i == rhs.i; }
0078
0079 bool operator!=(const UnicodeStringIterator& rhs) const { return !(*this == rhs); }
0080
0081 UnicodeStringIterator& operator++() {
0082 ++i;
0083 return *this;
0084 }
0085
0086 UnicodeStringIterator operator+(int32_t v) { return UnicodeStringIterator(s, i + v); }
0087
0088 private:
0089 const icu::UnicodeString* s;
0090 int32_t i;
0091 };
0092
0093 inline String& stringAppend(String& s, String a) { return s.append(std::move(a)); }
0094
0095 inline String& stringAppend(String& s, int n, UChar32 c) {
0096 for (int i = 0; i != n; ++i) {
0097 s.append(c);
0098 }
0099
0100 return s;
0101 }
0102
0103 template <typename Iterator>
0104 String& stringAppend(String& s, Iterator begin, Iterator end) {
0105 while (begin != end) {
0106 s.append(*begin);
0107 ++begin;
0108 }
0109
0110 return s;
0111 }
0112
0113 inline size_t stringLength(const String& s) { return s.length(); }
0114
0115 inline std::string toUTF8String(const String& s) {
0116 std::string result;
0117 s.toUTF8String(result);
0118
0119 return result;
0120 }
0121
0122 inline bool empty(const String& s) { return s.isEmpty(); }
0123 }
0124
0125 namespace std {
0126 inline cxxopts::UnicodeStringIterator begin(const icu::UnicodeString& s) {
0127 return cxxopts::UnicodeStringIterator(&s, 0);
0128 }
0129
0130 inline cxxopts::UnicodeStringIterator end(const icu::UnicodeString& s) {
0131 return cxxopts::UnicodeStringIterator(&s, s.length());
0132 }
0133 }
0134
0135
0136 #else
0137
0138 namespace cxxopts {
0139 typedef std::string String;
0140
0141 template <typename T>
0142 T toLocalString(T&& t) {
0143 return std::forward<T>(t);
0144 }
0145
0146 inline size_t stringLength(const String& s) { return s.length(); }
0147
0148 inline String& stringAppend(String& s, String a) { return s.append(std::move(a)); }
0149
0150 inline String& stringAppend(String& s, size_t n, char c) { return s.append(n, c); }
0151
0152 template <typename Iterator>
0153 String& stringAppend(String& s, Iterator begin, Iterator end) {
0154 return s.append(begin, end);
0155 }
0156
0157 template <typename T>
0158 std::string toUTF8String(T&& t) {
0159 return std::forward<T>(t);
0160 }
0161
0162 inline bool empty(const std::string& s) { return s.empty(); }
0163 }
0164
0165
0166 #endif
0167
0168 namespace cxxopts {
0169 namespace {
0170 #ifdef _WIN32
0171 const std::string LQUOTE("\'");
0172 const std::string RQUOTE("\'");
0173 #else
0174 const std::string LQUOTE("‘");
0175 const std::string RQUOTE("’");
0176 #endif
0177 }
0178
0179 class Value : public std::enable_shared_from_this<Value> {
0180 public:
0181 virtual ~Value() = default;
0182
0183 virtual std::shared_ptr<Value> clone() const = 0;
0184
0185 virtual void parse(const std::string& text) const = 0;
0186
0187 virtual void parse() const = 0;
0188
0189 virtual bool has_default() const = 0;
0190
0191 virtual bool is_container() const = 0;
0192
0193 virtual bool has_implicit() const = 0;
0194
0195 virtual std::string get_default_value() const = 0;
0196
0197 virtual std::string get_implicit_value() const = 0;
0198
0199 virtual std::shared_ptr<Value> default_value(const std::string& value) = 0;
0200
0201 virtual std::shared_ptr<Value> implicit_value(const std::string& value) = 0;
0202
0203 virtual bool is_boolean() const = 0;
0204 };
0205
0206 class OptionException : public std::exception {
0207 public:
0208 OptionException(const std::string& message) : m_message(message) {}
0209
0210 virtual const char* what() const noexcept { return m_message.c_str(); }
0211
0212 private:
0213 std::string m_message;
0214 };
0215
0216 class OptionSpecException : public OptionException {
0217 public:
0218 OptionSpecException(const std::string& message) : OptionException(message) {}
0219 };
0220
0221 class OptionParseException : public OptionException {
0222 public:
0223 OptionParseException(const std::string& message) : OptionException(message) {}
0224 };
0225
0226 class Option_exists_error : public OptionSpecException {
0227 public:
0228 Option_exists_error(const std::string& option)
0229 : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") {}
0230 };
0231
0232 class Invalid_option_format_error : public OptionSpecException {
0233 public:
0234 Invalid_option_format_error(const std::string& format)
0235 : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) {}
0236 };
0237
0238 class Option_syntax_exception : public OptionParseException {
0239 public:
0240 Option_syntax_exception(const std::string& text)
0241 : OptionParseException("Argument " + LQUOTE + text + RQUOTE + " starts with a - but has incorrect syntax") {}
0242 };
0243
0244 class Option_not_exists_exception : public OptionParseException {
0245 public:
0246 Option_not_exists_exception(const std::string& option)
0247 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") {}
0248 };
0249
0250 class Missing_argument_exception : public OptionParseException {
0251 public:
0252 Missing_argument_exception(const std::string& option)
0253 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " is missing an argument") {}
0254 };
0255
0256 class Option_requires_argument_exception : public OptionParseException {
0257 public:
0258 Option_requires_argument_exception(const std::string& option)
0259 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " requires an argument") {}
0260 };
0261
0262 class Option_not_has_argument_exception : public OptionParseException {
0263 public:
0264 Option_not_has_argument_exception(const std::string& option, const std::string& arg)
0265 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not take an argument, but argument " +
0266 LQUOTE + arg + RQUOTE + " given") {}
0267 };
0268
0269 class Option_not_present_exception : public OptionParseException {
0270 public:
0271 Option_not_present_exception(const std::string& option)
0272 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") {}
0273 };
0274
0275 class Argument_incorrect_type : public OptionParseException {
0276 public:
0277 Argument_incorrect_type(const std::string& arg)
0278 : OptionParseException("Argument " + LQUOTE + arg + RQUOTE + " failed to parse") {}
0279 };
0280
0281 class Option_required_exception : public OptionParseException {
0282 public:
0283 Option_required_exception(const std::string& option)
0284 : OptionParseException("Option " + LQUOTE + option + RQUOTE + " is required but not present") {}
0285 };
0286
0287 namespace values {
0288 namespace {
0289 std::basic_regex<char> integer_pattern("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
0290 std::basic_regex<char> truthy_pattern("(t|T)(rue)?");
0291 std::basic_regex<char> falsy_pattern("((f|F)(alse)?)?");
0292 }
0293
0294 namespace detail {
0295 template <typename T, bool B>
0296 struct SignedCheck;
0297
0298 template <typename T>
0299 struct SignedCheck<T, true> {
0300 template <typename U>
0301 void operator()(bool negative, U u, const std::string& text) {
0302 if (negative) {
0303 if (u > static_cast<U>(-(std::numeric_limits<T>::min)())) {
0304 throw Argument_incorrect_type(text);
0305 }
0306 } else {
0307 if (u > static_cast<U>((std::numeric_limits<T>::max)())) {
0308 throw Argument_incorrect_type(text);
0309 }
0310 }
0311 }
0312 };
0313
0314 template <typename T>
0315 struct SignedCheck<T, false> {
0316 template <typename U>
0317 void operator()(bool, U, const std::string&) {}
0318 };
0319
0320 template <typename T, typename U>
0321 void check_signed_range(bool negative, U value, const std::string& text) {
0322 SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
0323 }
0324 }
0325
0326 template <typename R, typename T>
0327 R checked_negate(T&& t, const std::string&, std::true_type) {
0328
0329
0330
0331 return -static_cast<R>(t);
0332 }
0333
0334 template <typename R, typename T>
0335 T checked_negate(T&&, const std::string& text, std::false_type) {
0336 throw Argument_incorrect_type(text);
0337 }
0338
0339 template <typename T>
0340 void integer_parser(const std::string& text, T& value) {
0341 std::smatch match;
0342 std::regex_match(text, match, integer_pattern);
0343
0344 if (match.length() == 0) {
0345 throw Argument_incorrect_type(text);
0346 }
0347
0348 if (match.length(4) > 0) {
0349 value = 0;
0350 return;
0351 }
0352
0353 using US = typename std::make_unsigned<T>::type;
0354
0355 constexpr auto umax = (std::numeric_limits<US>::max)();
0356 constexpr bool is_signed = std::numeric_limits<T>::is_signed;
0357 const bool negative = match.length(1) > 0;
0358 const uint8_t base = match.length(2) > 0 ? 16 : 10;
0359
0360 auto value_match = match[3];
0361
0362 US result = 0;
0363
0364 for (auto iter = value_match.first; iter != value_match.second; ++iter) {
0365 US digit = 0;
0366
0367 if (*iter >= '0' && *iter <= '9') {
0368 digit = *iter - '0';
0369 } else if (base == 16 && *iter >= 'a' && *iter <= 'f') {
0370 digit = *iter - 'a' + 10;
0371 } else if (base == 16 && *iter >= 'A' && *iter <= 'F') {
0372 digit = *iter - 'A' + 10;
0373 } else {
0374 throw Argument_incorrect_type(text);
0375 }
0376
0377 if (umax - digit < result * base) {
0378 throw Argument_incorrect_type(text);
0379 }
0380
0381 result = result * base + digit;
0382 }
0383
0384 detail::check_signed_range<T>(negative, result, text);
0385
0386 if (negative) {
0387 value = checked_negate<T>(result, text, std::integral_constant<bool, is_signed>());
0388 } else {
0389 value = result;
0390 }
0391 }
0392
0393 template <typename T>
0394 void stringstream_parser(const std::string& text, T& value) {
0395 std::stringstream in(text);
0396 in >> value;
0397 if (!in) {
0398 throw Argument_incorrect_type(text);
0399 }
0400 }
0401
0402 inline void parse_value(const std::string& text, uint8_t& value) { integer_parser(text, value); }
0403
0404 inline void parse_value(const std::string& text, int8_t& value) { integer_parser(text, value); }
0405
0406 inline void parse_value(const std::string& text, uint16_t& value) { integer_parser(text, value); }
0407
0408 inline void parse_value(const std::string& text, int16_t& value) { integer_parser(text, value); }
0409
0410 inline void parse_value(const std::string& text, uint32_t& value) { integer_parser(text, value); }
0411
0412 inline void parse_value(const std::string& text, int32_t& value) { integer_parser(text, value); }
0413
0414 inline void parse_value(const std::string& text, uint64_t& value) { integer_parser(text, value); }
0415
0416 inline void parse_value(const std::string& text, int64_t& value) { integer_parser(text, value); }
0417
0418 inline void parse_value(const std::string& text, bool& value) {
0419 std::smatch result;
0420 std::regex_match(text, result, truthy_pattern);
0421
0422 if (!result.empty()) {
0423 value = true;
0424 return;
0425 }
0426
0427 std::regex_match(text, result, falsy_pattern);
0428 if (!result.empty()) {
0429 value = false;
0430 return;
0431 }
0432
0433 throw Argument_incorrect_type(text);
0434 }
0435
0436 inline void parse_value(const std::string& text, std::string& value) { value = text; }
0437
0438
0439
0440
0441 template <typename T>
0442 void parse_value(const std::string& text, T& value) {
0443 stringstream_parser(text, value);
0444 }
0445
0446 template <typename T>
0447 void parse_value(const std::string& text, std::vector<T>& value) {
0448 T v;
0449 parse_value(text, v);
0450 value.push_back(v);
0451 }
0452
0453 #ifdef CXXOPTS_HAS_OPTIONAL
0454 template <typename T>
0455 void parse_value(const std::string& text, std::optional<T>& value) {
0456 T result;
0457 parse_value(text, result);
0458 value = std::move(result);
0459 }
0460 #endif
0461
0462 template <typename T>
0463 struct Type_is_container {
0464 static constexpr bool value = false;
0465 };
0466
0467 template <typename T>
0468 struct Type_is_container<std::vector<T>> {
0469 static constexpr bool value = true;
0470 };
0471
0472 template <typename T>
0473 class Abstract_value : public Value {
0474 using Self = Abstract_value<T>;
0475
0476 public:
0477 Abstract_value() : m_result(std::make_shared<T>()), m_store(m_result.get()) {}
0478
0479 Abstract_value(T* t) : m_store(t) {}
0480
0481 virtual ~Abstract_value() = default;
0482
0483 Abstract_value(const Abstract_value& rhs) {
0484 if (rhs.m_result) {
0485 m_result = std::make_shared<T>();
0486 m_store = m_result.get();
0487 } else {
0488 m_store = rhs.m_store;
0489 }
0490
0491 m_default = rhs.m_default;
0492 m_implicit = rhs.m_implicit;
0493 m_default_value = rhs.m_default_value;
0494 m_implicit_value = rhs.m_implicit_value;
0495 }
0496
0497 void parse(const std::string& text) const { parse_value(text, *m_store); }
0498
0499 bool is_container() const { return Type_is_container<T>::value; }
0500
0501 void parse() const { parse_value(m_default_value, *m_store); }
0502
0503 bool has_default() const { return m_default; }
0504
0505 bool has_implicit() const { return m_implicit; }
0506
0507 std::shared_ptr<Value> default_value(const std::string& value) {
0508 m_default = true;
0509 m_default_value = value;
0510 return shared_from_this();
0511 }
0512
0513 std::shared_ptr<Value> implicit_value(const std::string& value) {
0514 m_implicit = true;
0515 m_implicit_value = value;
0516 return shared_from_this();
0517 }
0518
0519 std::string get_default_value() const { return m_default_value; }
0520
0521 std::string get_implicit_value() const { return m_implicit_value; }
0522
0523 bool is_boolean() const { return std::is_same<T, bool>::value; }
0524
0525 const T& get() const {
0526 if (m_store == nullptr) {
0527 return *m_result;
0528 } else {
0529 return *m_store;
0530 }
0531 }
0532
0533 protected:
0534 std::shared_ptr<T> m_result;
0535 T* m_store;
0536
0537 bool m_default = false;
0538 bool m_implicit = false;
0539
0540 std::string m_default_value;
0541 std::string m_implicit_value;
0542 };
0543
0544 template <typename T>
0545 class Standard_value : public Abstract_value<T> {
0546 public:
0547 using Abstract_value<T>::Abstract_value;
0548
0549 std::shared_ptr<Value> clone() const { return std::make_shared<Standard_value<T>>(*this); }
0550 };
0551
0552 template <>
0553 class Standard_value<bool> : public Abstract_value<bool> {
0554 public:
0555 ~Standard_value() = default;
0556
0557 Standard_value() { set_default_and_implicit(); }
0558
0559 Standard_value(bool* b) : Abstract_value(b) { set_default_and_implicit(); }
0560
0561 std::shared_ptr<Value> clone() const { return std::make_shared<Standard_value<bool>>(*this); }
0562
0563 private:
0564 void set_default_and_implicit() {
0565 m_default = true;
0566 m_default_value = "false";
0567 m_implicit = true;
0568 m_implicit_value = "true";
0569 }
0570 };
0571 }
0572
0573 template <typename T>
0574 std::shared_ptr<Value> value() {
0575 return std::make_shared<values::Standard_value<T>>();
0576 }
0577
0578 template <typename T>
0579 std::shared_ptr<Value> value(T& t) {
0580 return std::make_shared<values::Standard_value<T>>(&t);
0581 }
0582
0583 class OptionAdder;
0584
0585 class OptionDetails {
0586 public:
0587 OptionDetails(const std::string& short_,
0588 const std::string& long_,
0589 const String& desc,
0590 std::shared_ptr<const Value> val)
0591 : m_short(short_), m_long(long_), m_desc(desc), m_value(val), m_count(0) {}
0592
0593 OptionDetails(const OptionDetails& rhs) : m_desc(rhs.m_desc), m_count(rhs.m_count) {
0594 m_value = rhs.m_value->clone();
0595 }
0596
0597 OptionDetails(OptionDetails&& rhs) = default;
0598
0599 const String& description() const { return m_desc; }
0600
0601 const Value& value() const { return *m_value; }
0602
0603 std::shared_ptr<Value> make_storage() const { return m_value->clone(); }
0604
0605 const std::string& short_name() const { return m_short; }
0606
0607 const std::string& long_name() const { return m_long; }
0608
0609 private:
0610 std::string m_short;
0611 std::string m_long;
0612 String m_desc;
0613 std::shared_ptr<const Value> m_value;
0614 int m_count;
0615 };
0616
0617 struct HelpOptionDetails {
0618 std::string s;
0619 std::string l;
0620 String desc;
0621 bool has_default;
0622 std::string default_value;
0623 bool has_implicit;
0624 std::string implicit_value;
0625 std::string arg_help;
0626 bool is_container;
0627 bool is_boolean;
0628 };
0629
0630 struct HelpGroupDetails {
0631 std::string name;
0632 std::string description;
0633 std::vector<HelpOptionDetails> options;
0634 };
0635
0636 class OptionValue {
0637 public:
0638 void parse(std::shared_ptr<const OptionDetails> details, const std::string& text) {
0639 ensure_value(details);
0640 ++m_count;
0641 m_value->parse(text);
0642 }
0643
0644 void parse_default(std::shared_ptr<const OptionDetails> details) {
0645 ensure_value(details);
0646 m_value->parse();
0647 }
0648
0649 size_t count() const { return m_count; }
0650
0651 template <typename T>
0652 const T& as() const {
0653 if (m_value == nullptr) {
0654 throw std::domain_error("No value");
0655 }
0656
0657 #ifdef CXXOPTS_NO_RTTI
0658 return static_cast<const values::Standard_value<T>&>(*m_value).get();
0659 #else
0660 return dynamic_cast<const values::Standard_value<T>&>(*m_value).get();
0661 #endif
0662 }
0663
0664 private:
0665 void ensure_value(std::shared_ptr<const OptionDetails> details) {
0666 if (m_value == nullptr) {
0667 m_value = details->make_storage();
0668 }
0669 }
0670
0671 std::shared_ptr<Value> m_value;
0672 size_t m_count = 0;
0673 };
0674
0675 class KeyValue {
0676 public:
0677 KeyValue(std::string key_, std::string value_) : m_key(std::move(key_)), m_value(std::move(value_)) {}
0678
0679 const std::string& key() const { return m_key; }
0680
0681 const std::string& value() const { return m_value; }
0682
0683 template <typename T>
0684 T as() const {
0685 T result;
0686 values::parse_value(m_value, result);
0687 return result;
0688 }
0689
0690 private:
0691 std::string m_key;
0692 std::string m_value;
0693 };
0694
0695 class ParseResult {
0696 public:
0697 ParseResult(const std::shared_ptr<std::unordered_map<std::string, std::shared_ptr<OptionDetails>>>,
0698 std::vector<std::string>,
0699 bool allow_unrecognised,
0700 int&,
0701 char**&);
0702
0703 size_t count(const std::string& o) const {
0704 auto iter = m_options->find(o);
0705 if (iter == m_options->end()) {
0706 return 0;
0707 }
0708
0709 auto riter = m_results.find(iter->second);
0710
0711 return riter->second.count();
0712 }
0713
0714 const OptionValue& operator[](const std::string& option) const {
0715 auto iter = m_options->find(option);
0716
0717 if (iter == m_options->end()) {
0718 throw Option_not_present_exception(option);
0719 }
0720
0721 auto riter = m_results.find(iter->second);
0722
0723 return riter->second;
0724 }
0725
0726 const std::vector<KeyValue>& arguments() const { return m_sequential; }
0727
0728 private:
0729 void parse(int& argc, char**& argv);
0730
0731 void add_to_option(const std::string& option, const std::string& arg);
0732
0733 bool consume_positional(std::string a);
0734
0735 void parse_option(std::shared_ptr<OptionDetails> value, const std::string& name, const std::string& arg = "");
0736
0737 void parse_default(std::shared_ptr<OptionDetails> details);
0738
0739 void checked_parse_arg(
0740 int argc, char* argv[], int& current, std::shared_ptr<OptionDetails> value, const std::string& name);
0741
0742 const std::shared_ptr<std::unordered_map<std::string, std::shared_ptr<OptionDetails>>> m_options;
0743 std::vector<std::string> m_positional;
0744 std::vector<std::string>::iterator m_next_positional;
0745 std::unordered_set<std::string> m_positional_set;
0746 std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results;
0747
0748 bool m_allow_unrecognised;
0749
0750 std::vector<KeyValue> m_sequential;
0751 };
0752
0753 class Options {
0754 typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>> OptionMap;
0755
0756 public:
0757 Options(std::string program, std::string help_string = "")
0758 : m_program(std::move(program)),
0759 m_help_string(toLocalString(std::move(help_string))),
0760 m_custom_help("[OPTION...]"),
0761 m_positional_help("positional parameters"),
0762 m_show_positional(false),
0763 m_allow_unrecognised(false),
0764 m_options(std::make_shared<OptionMap>()),
0765 m_next_positional(m_positional.end()) {}
0766
0767 Options& positional_help(std::string help_text) {
0768 m_positional_help = std::move(help_text);
0769 return *this;
0770 }
0771
0772 Options& custom_help(std::string help_text) {
0773 m_custom_help = std::move(help_text);
0774 return *this;
0775 }
0776
0777 Options& show_positional_help() {
0778 m_show_positional = true;
0779 return *this;
0780 }
0781
0782 Options& allow_unrecognised_options() {
0783 m_allow_unrecognised = true;
0784 return *this;
0785 }
0786
0787 ParseResult parse(int& argc, char**& argv);
0788
0789 OptionAdder add_options(std::string group = "");
0790
0791 void add_option(const std::string& group,
0792 const std::string& s,
0793 const std::string& l,
0794 std::string desc,
0795 std::shared_ptr<const Value> value,
0796 std::string arg_help);
0797
0798
0799 void parse_positional(std::string option);
0800
0801 void parse_positional(std::vector<std::string> options);
0802
0803 void parse_positional(std::initializer_list<std::string> options);
0804
0805 template <typename Iterator>
0806 void parse_positional(Iterator begin, Iterator end) {
0807 parse_positional(std::vector<std::string>{begin, end});
0808 }
0809
0810 std::string help(const std::vector<std::string>& groups = {}) const;
0811
0812 const std::vector<std::string> groups() const;
0813
0814 const HelpGroupDetails& group_help(const std::string& group) const;
0815
0816 private:
0817 void add_one_option(const std::string& option, std::shared_ptr<OptionDetails> details);
0818
0819 String help_one_group(const std::string& group) const;
0820
0821 void generate_group_help(String& result, const std::vector<std::string>& groups) const;
0822
0823 void generate_all_groups_help(String& result) const;
0824
0825 std::string m_program;
0826 String m_help_string;
0827 std::string m_custom_help;
0828 std::string m_positional_help;
0829 bool m_show_positional;
0830 bool m_allow_unrecognised;
0831
0832 std::shared_ptr<OptionMap> m_options;
0833 std::vector<std::string> m_positional;
0834 std::vector<std::string>::iterator m_next_positional;
0835 std::unordered_set<std::string> m_positional_set;
0836
0837
0838 std::map<std::string, HelpGroupDetails> m_help;
0839 };
0840
0841 class OptionAdder {
0842 public:
0843 OptionAdder(Options& options, std::string group) : m_options(options), m_group(std::move(group)) {}
0844
0845 OptionAdder& operator()(const std::string& opts,
0846 const std::string& desc,
0847 std::shared_ptr<const Value> value = ::cxxopts::value<bool>(),
0848 std::string arg_help = "");
0849
0850 private:
0851 Options& m_options;
0852 std::string m_group;
0853 };
0854
0855 namespace {
0856 constexpr int OPTION_LONGEST = 30;
0857 constexpr int OPTION_DESC_GAP = 2;
0858
0859 std::basic_regex<char> option_matcher("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
0860
0861 std::basic_regex<char> option_specifier("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
0862
0863 String format_option(const HelpOptionDetails& o) {
0864 auto& s = o.s;
0865 auto& l = o.l;
0866
0867 String result = " ";
0868
0869 if (s.size() > 0) {
0870 result += "-" + toLocalString(s) + ",";
0871 } else {
0872 result += " ";
0873 }
0874
0875 if (l.size() > 0) {
0876 result += " --" + toLocalString(l);
0877 }
0878
0879 auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
0880
0881 if (!o.is_boolean) {
0882 if (o.has_implicit) {
0883 result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
0884 } else {
0885 result += " " + arg;
0886 }
0887 }
0888
0889 return result;
0890 }
0891
0892 String format_description(const HelpOptionDetails& o, size_t start, size_t width) {
0893 auto desc = o.desc;
0894
0895 if (o.has_default && (!o.is_boolean || o.default_value != "false")) {
0896 desc += toLocalString(" (default: " + o.default_value + ")");
0897 }
0898
0899 String result;
0900
0901 auto current = std::begin(desc);
0902 auto startLine = current;
0903 auto lastSpace = current;
0904
0905 auto size = size_t{};
0906
0907 while (current != std::end(desc)) {
0908 if (*current == ' ') {
0909 lastSpace = current;
0910 }
0911
0912 if (*current == '\n') {
0913 startLine = current + 1;
0914 lastSpace = startLine;
0915 } else if (size > width) {
0916 if (lastSpace == startLine) {
0917 stringAppend(result, startLine, current + 1);
0918 stringAppend(result, "\n");
0919 stringAppend(result, start, ' ');
0920 startLine = current + 1;
0921 lastSpace = startLine;
0922 } else {
0923 stringAppend(result, startLine, lastSpace);
0924 stringAppend(result, "\n");
0925 stringAppend(result, start, ' ');
0926 startLine = lastSpace + 1;
0927 }
0928 size = 0;
0929 } else {
0930 ++size;
0931 }
0932
0933 ++current;
0934 }
0935
0936
0937 stringAppend(result, startLine, current);
0938
0939 return result;
0940 }
0941 }
0942
0943 inline ParseResult::ParseResult(
0944 const std::shared_ptr<std::unordered_map<std::string, std::shared_ptr<OptionDetails>>> options,
0945 std::vector<std::string> positional,
0946 bool allow_unrecognised,
0947 int& argc,
0948 char**& argv)
0949 : m_options(options),
0950 m_positional(std::move(positional)),
0951 m_next_positional(m_positional.begin()),
0952 m_allow_unrecognised(allow_unrecognised) {
0953 parse(argc, argv);
0954 }
0955
0956 inline OptionAdder Options::add_options(std::string group) { return OptionAdder(*this, std::move(group)); }
0957
0958 inline OptionAdder& OptionAdder::operator()(const std::string& opts,
0959 const std::string& desc,
0960 std::shared_ptr<const Value> value,
0961 std::string arg_help) {
0962 std::match_results<const char*> result;
0963 std::regex_match(opts.c_str(), result, option_specifier);
0964
0965 if (result.empty()) {
0966 throw Invalid_option_format_error(opts);
0967 }
0968
0969 const auto& short_match = result[2];
0970 const auto& long_match = result[3];
0971
0972 if (!short_match.length() && !long_match.length()) {
0973 throw Invalid_option_format_error(opts);
0974 } else if (long_match.length() == 1 && short_match.length()) {
0975 throw Invalid_option_format_error(opts);
0976 }
0977
0978 auto option_names = [](const std::sub_match<const char*>& short_, const std::sub_match<const char*>& long_) {
0979 if (long_.length() == 1) {
0980 return std::make_tuple(long_.str(), short_.str());
0981 } else {
0982 return std::make_tuple(short_.str(), long_.str());
0983 }
0984 }(short_match, long_match);
0985
0986 m_options.add_option(
0987 m_group, std::get<0>(option_names), std::get<1>(option_names), desc, value, std::move(arg_help));
0988
0989 return *this;
0990 }
0991
0992 inline void ParseResult::parse_default(std::shared_ptr<OptionDetails> details) {
0993 m_results[details].parse_default(details);
0994 }
0995
0996 inline void ParseResult::parse_option(std::shared_ptr<OptionDetails> value,
0997 const std::string& ,
0998 const std::string& arg) {
0999 auto& result = m_results[value];
1000 result.parse(value, arg);
1001
1002 m_sequential.emplace_back(value->long_name(), arg);
1003 }
1004
1005 inline void ParseResult::checked_parse_arg(
1006 int argc, char* argv[], int& current, std::shared_ptr<OptionDetails> value, const std::string& name) {
1007 if (current + 1 >= argc) {
1008 if (value->value().has_implicit()) {
1009 parse_option(value, name, value->value().get_implicit_value());
1010 } else {
1011 throw Missing_argument_exception(name);
1012 }
1013 } else {
1014 if (value->value().has_implicit()) {
1015 parse_option(value, name, value->value().get_implicit_value());
1016 } else {
1017 parse_option(value, name, argv[current + 1]);
1018 ++current;
1019 }
1020 }
1021 }
1022
1023 inline void ParseResult::add_to_option(const std::string& option, const std::string& arg) {
1024 auto iter = m_options->find(option);
1025
1026 if (iter == m_options->end()) {
1027 throw Option_not_exists_exception(option);
1028 }
1029
1030 parse_option(iter->second, option, arg);
1031 }
1032
1033 inline bool ParseResult::consume_positional(std::string a) {
1034 while (m_next_positional != m_positional.end()) {
1035 auto iter = m_options->find(*m_next_positional);
1036 if (iter != m_options->end()) {
1037 auto& result = m_results[iter->second];
1038 if (!iter->second->value().is_container()) {
1039 if (result.count() == 0) {
1040 add_to_option(*m_next_positional, a);
1041 ++m_next_positional;
1042 return true;
1043 } else {
1044 ++m_next_positional;
1045 continue;
1046 }
1047 } else {
1048 add_to_option(*m_next_positional, a);
1049 return true;
1050 }
1051 }
1052 ++m_next_positional;
1053 }
1054
1055 return false;
1056 }
1057
1058 inline void Options::parse_positional(std::string option) {
1059 parse_positional(std::vector<std::string>{std::move(option)});
1060 }
1061
1062 inline void Options::parse_positional(std::vector<std::string> options) {
1063 m_positional = std::move(options);
1064 m_next_positional = m_positional.begin();
1065
1066 m_positional_set.insert(m_positional.begin(), m_positional.end());
1067 }
1068
1069 inline void Options::parse_positional(std::initializer_list<std::string> options) {
1070 parse_positional(std::vector<std::string>(std::move(options)));
1071 }
1072
1073 inline ParseResult Options::parse(int& argc, char**& argv) {
1074 ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);
1075 return result;
1076 }
1077
1078 inline void ParseResult::parse(int& argc, char**& argv) {
1079 int current = 1;
1080
1081 int nextKeep = 1;
1082
1083 bool consume_remaining = false;
1084
1085 while (current != argc) {
1086 if (strcmp(argv[current], "--") == 0) {
1087 consume_remaining = true;
1088 ++current;
1089 break;
1090 }
1091
1092 std::match_results<const char*> result;
1093 std::regex_match(argv[current], result, option_matcher);
1094
1095 if (result.empty()) {
1096
1097
1098
1099 if (argv[current][0] == '-' && argv[current][1] != '\0') {
1100 throw Option_syntax_exception(argv[current]);
1101 }
1102
1103
1104
1105 if (consume_positional(argv[current])) {
1106 } else {
1107 argv[nextKeep] = argv[current];
1108 ++nextKeep;
1109 }
1110
1111 } else {
1112
1113 if (result[4].length() != 0) {
1114 const std::string& s = result[4];
1115
1116 for (std::size_t i = 0; i != s.size(); ++i) {
1117 std::string name(1, s[i]);
1118 auto iter = m_options->find(name);
1119
1120 if (iter == m_options->end()) {
1121 if (m_allow_unrecognised) {
1122 continue;
1123 } else {
1124
1125 throw Option_not_exists_exception(name);
1126 }
1127 }
1128
1129 auto value = iter->second;
1130
1131 if (i + 1 == s.size()) {
1132
1133 checked_parse_arg(argc, argv, current, value, name);
1134 } else if (value->value().has_implicit()) {
1135 parse_option(value, name, value->value().get_implicit_value());
1136 } else {
1137
1138 throw Option_requires_argument_exception(name);
1139 }
1140 }
1141 } else if (result[1].length() != 0) {
1142 const std::string& name = result[1];
1143
1144 auto iter = m_options->find(name);
1145
1146 if (iter == m_options->end()) {
1147 if (m_allow_unrecognised) {
1148
1149 argv[nextKeep] = argv[current];
1150 ++nextKeep;
1151 ++current;
1152 continue;
1153 } else {
1154
1155 throw Option_not_exists_exception(name);
1156 }
1157 }
1158
1159 auto opt = iter->second;
1160
1161
1162 if (result[2].length() != 0) {
1163
1164
1165 parse_option(opt, name, result[3]);
1166 } else {
1167
1168 checked_parse_arg(argc, argv, current, opt, name);
1169 }
1170 }
1171 }
1172
1173 ++current;
1174 }
1175
1176 for (auto& opt : *m_options) {
1177 auto& detail = opt.second;
1178 auto& value = detail->value();
1179
1180 auto& store = m_results[detail];
1181
1182 if (!store.count() && value.has_default()) {
1183 parse_default(detail);
1184 }
1185 }
1186
1187 if (consume_remaining) {
1188 while (current < argc) {
1189 if (!consume_positional(argv[current])) {
1190 break;
1191 }
1192 ++current;
1193 }
1194
1195
1196 while (current != argc) {
1197 argv[nextKeep] = argv[current];
1198 ++nextKeep;
1199 ++current;
1200 }
1201 }
1202
1203 argc = nextKeep;
1204 }
1205
1206 inline void Options::add_option(const std::string& group,
1207 const std::string& s,
1208 const std::string& l,
1209 std::string desc,
1210 std::shared_ptr<const Value> value,
1211 std::string arg_help) {
1212 auto stringDesc = toLocalString(std::move(desc));
1213 auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
1214
1215 if (s.size() > 0) {
1216 add_one_option(s, option);
1217 }
1218
1219 if (l.size() > 0) {
1220 add_one_option(l, option);
1221 }
1222
1223
1224 auto& options = m_help[group];
1225
1226 options.options.emplace_back(HelpOptionDetails{s,
1227 l,
1228 stringDesc,
1229 value->has_default(),
1230 value->get_default_value(),
1231 value->has_implicit(),
1232 value->get_implicit_value(),
1233 std::move(arg_help),
1234 value->is_container(),
1235 value->is_boolean()});
1236 }
1237
1238 inline void Options::add_one_option(const std::string& option, std::shared_ptr<OptionDetails> details) {
1239 auto in = m_options->emplace(option, details);
1240
1241 if (!in.second) {
1242 throw Option_exists_error(option);
1243 }
1244 }
1245
1246 inline String Options::help_one_group(const std::string& g) const {
1247 typedef std::vector<std::pair<String, String>> OptionHelp;
1248
1249 auto group = m_help.find(g);
1250 if (group == m_help.end()) {
1251 return "";
1252 }
1253
1254 OptionHelp format;
1255
1256 size_t longest = 0;
1257
1258 String result;
1259
1260 if (!g.empty()) {
1261 result += toLocalString(" " + g + " options:\n");
1262 }
1263
1264 for (const auto& o : group->second.options) {
1265 if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end() && !m_show_positional) {
1266 continue;
1267 }
1268
1269 auto s = format_option(o);
1270 longest = (std::max)(longest, stringLength(s));
1271 format.push_back(std::make_pair(s, String()));
1272 }
1273
1274 longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST));
1275
1276
1277 auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
1278
1279 auto fiter = format.begin();
1280 for (const auto& o : group->second.options) {
1281 if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end() && !m_show_positional) {
1282 continue;
1283 }
1284
1285 auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
1286
1287 result += fiter->first;
1288 if (stringLength(fiter->first) > longest) {
1289 result += '\n';
1290 result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
1291 } else {
1292 result += toLocalString(std::string(longest + OPTION_DESC_GAP - stringLength(fiter->first), ' '));
1293 }
1294 result += d;
1295 result += '\n';
1296
1297 ++fiter;
1298 }
1299
1300 return result;
1301 }
1302
1303 inline void Options::generate_group_help(String& result, const std::vector<std::string>& print_groups) const {
1304 for (size_t i = 0; i != print_groups.size(); ++i) {
1305 const String& group_help_text = help_one_group(print_groups[i]);
1306 if (empty(group_help_text)) {
1307 continue;
1308 }
1309 result += group_help_text;
1310 if (i < print_groups.size() - 1) {
1311 result += '\n';
1312 }
1313 }
1314 }
1315
1316 inline void Options::generate_all_groups_help(String& result) const {
1317 std::vector<std::string> all_groups;
1318 all_groups.reserve(m_help.size());
1319
1320 for (auto& group : m_help) {
1321 all_groups.push_back(group.first);
1322 }
1323
1324 generate_group_help(result, all_groups);
1325 }
1326
1327 inline std::string Options::help(const std::vector<std::string>& help_groups) const {
1328 String result = m_help_string + "\nUsage:\n " + toLocalString(m_program) + " " + toLocalString(m_custom_help);
1329
1330 if (m_positional.size() > 0 && m_positional_help.size() > 0) {
1331 result += " " + toLocalString(m_positional_help);
1332 }
1333
1334 result += "\n\n";
1335
1336 if (help_groups.size() == 0) {
1337 generate_all_groups_help(result);
1338 } else {
1339 generate_group_help(result, help_groups);
1340 }
1341
1342 return toUTF8String(result);
1343 }
1344
1345 inline const std::vector<std::string> Options::groups() const {
1346 std::vector<std::string> g;
1347
1348 std::transform(m_help.begin(),
1349 m_help.end(),
1350 std::back_inserter(g),
1351 [](const std::map<std::string, HelpGroupDetails>::value_type& pair) { return pair.first; });
1352
1353 return g;
1354 }
1355
1356 inline const HelpGroupDetails& Options::group_help(const std::string& group) const { return m_help.at(group); }
1357
1358 }
1359
1360 #endif