File indexing completed on 2024-09-28 07:02:17
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009
0010 #include <algorithm>
0011 #include <cstdint>
0012 #include <functional>
0013 #include <iostream>
0014 #include <iterator>
0015 #include <memory>
0016 #include <numeric>
0017 #include <set>
0018 #include <sstream>
0019 #include <string>
0020 #include <utility>
0021 #include <vector>
0022
0023
0024
0025 #include "ConfigFwd.hpp"
0026 #include "Error.hpp"
0027 #include "FormatterFwd.hpp"
0028 #include "Macros.hpp"
0029 #include "Option.hpp"
0030 #include "Split.hpp"
0031 #include "StringTools.hpp"
0032 #include "TypeTools.hpp"
0033
0034 namespace CLI {
0035
0036
0037 #ifndef CLI11_PARSE
0038 #define CLI11_PARSE(app, argc, argv) \
0039 try { \
0040 (app).parse((argc), (argv)); \
0041 } catch(const CLI::ParseError &e) { \
0042 return (app).exit(e); \
0043 }
0044 #endif
0045
0046 namespace detail {
0047 enum class Classifier { NONE, POSITIONAL_MARK, SHORT, LONG, WINDOWS_STYLE, SUBCOMMAND, SUBCOMMAND_TERMINATOR };
0048 struct AppFriend;
0049 }
0050
0051 namespace FailureMessage {
0052 std::string simple(const App *app, const Error &e);
0053 std::string help(const App *app, const Error &e);
0054 }
0055
0056
0057
0058 enum class config_extras_mode : char { error = 0, ignore, capture };
0059
0060 class App;
0061
0062 using App_p = std::shared_ptr<App>;
0063
0064 class Option_group;
0065
0066
0067
0068
0069 class App {
0070 friend Option;
0071 friend detail::AppFriend;
0072
0073 protected:
0074
0075
0076
0077
0078
0079
0080 std::string name_{};
0081
0082
0083 std::string description_{};
0084
0085
0086 bool allow_extras_{false};
0087
0088
0089
0090 config_extras_mode allow_config_extras_{config_extras_mode::ignore};
0091
0092
0093 bool prefix_command_{false};
0094
0095
0096 bool has_automatic_name_{false};
0097
0098
0099 bool required_{false};
0100
0101
0102 bool disabled_{false};
0103
0104
0105 bool pre_parse_called_{false};
0106
0107
0108
0109 bool immediate_callback_{false};
0110
0111
0112 std::function<void(std::size_t)> pre_parse_callback_{};
0113
0114
0115 std::function<void()> parse_complete_callback_{};
0116
0117
0118 std::function<void()> final_callback_{};
0119
0120
0121
0122
0123
0124
0125 OptionDefaults option_defaults_{};
0126
0127
0128 std::vector<Option_p> options_{};
0129
0130
0131
0132
0133
0134
0135 std::string footer_{};
0136
0137
0138 std::function<std::string()> footer_callback_{};
0139
0140
0141 Option *help_ptr_{nullptr};
0142
0143
0144 Option *help_all_ptr_{nullptr};
0145
0146
0147 Option *version_ptr_{nullptr};
0148
0149
0150 std::shared_ptr<FormatterBase> formatter_{new Formatter()};
0151
0152
0153 std::function<std::string(const App *, const Error &e)> failure_message_{FailureMessage::simple};
0154
0155
0156
0157
0158
0159 using missing_t = std::vector<std::pair<detail::Classifier, std::string>>;
0160
0161
0162
0163
0164 missing_t missing_{};
0165
0166
0167 std::vector<Option *> parse_order_{};
0168
0169
0170 std::vector<App *> parsed_subcommands_{};
0171
0172
0173 std::set<App *> exclude_subcommands_{};
0174
0175
0176
0177 std::set<Option *> exclude_options_{};
0178
0179
0180
0181 std::set<App *> need_subcommands_{};
0182
0183
0184
0185 std::set<Option *> need_options_{};
0186
0187
0188
0189
0190
0191
0192 std::vector<App_p> subcommands_{};
0193
0194
0195 bool ignore_case_{false};
0196
0197
0198 bool ignore_underscore_{false};
0199
0200
0201 bool fallthrough_{false};
0202
0203
0204 bool allow_windows_style_options_{
0205 #ifdef _WIN32
0206 true
0207 #else
0208 false
0209 #endif
0210 };
0211
0212 bool positionals_at_end_{false};
0213
0214 enum class startup_mode : char { stable, enabled, disabled };
0215
0216
0217 startup_mode default_startup{startup_mode::stable};
0218
0219
0220 bool configurable_{false};
0221
0222
0223 bool validate_positionals_{false};
0224
0225
0226
0227 bool silent_{false};
0228
0229
0230 std::uint32_t parsed_{0U};
0231
0232
0233 std::size_t require_subcommand_min_{0};
0234
0235
0236 std::size_t require_subcommand_max_{0};
0237
0238
0239 std::size_t require_option_min_{0};
0240
0241
0242 std::size_t require_option_max_{0};
0243
0244
0245 App *parent_{nullptr};
0246
0247
0248 std::string group_{"Subcommands"};
0249
0250
0251 std::vector<std::string> aliases_{};
0252
0253
0254
0255
0256
0257
0258 Option *config_ptr_{nullptr};
0259
0260
0261 std::shared_ptr<Config> config_formatter_{new ConfigTOML()};
0262
0263
0264
0265
0266 App(std::string app_description, std::string app_name, App *parent)
0267 : name_(std::move(app_name)), description_(std::move(app_description)), parent_(parent) {
0268
0269 if(parent_ != nullptr) {
0270 if(parent_->help_ptr_ != nullptr)
0271 set_help_flag(parent_->help_ptr_->get_name(false, true), parent_->help_ptr_->get_description());
0272 if(parent_->help_all_ptr_ != nullptr)
0273 set_help_all_flag(parent_->help_all_ptr_->get_name(false, true),
0274 parent_->help_all_ptr_->get_description());
0275
0276
0277 option_defaults_ = parent_->option_defaults_;
0278
0279
0280 failure_message_ = parent_->failure_message_;
0281 allow_extras_ = parent_->allow_extras_;
0282 allow_config_extras_ = parent_->allow_config_extras_;
0283 prefix_command_ = parent_->prefix_command_;
0284 immediate_callback_ = parent_->immediate_callback_;
0285 ignore_case_ = parent_->ignore_case_;
0286 ignore_underscore_ = parent_->ignore_underscore_;
0287 fallthrough_ = parent_->fallthrough_;
0288 validate_positionals_ = parent_->validate_positionals_;
0289 configurable_ = parent_->configurable_;
0290 allow_windows_style_options_ = parent_->allow_windows_style_options_;
0291 group_ = parent_->group_;
0292 footer_ = parent_->footer_;
0293 formatter_ = parent_->formatter_;
0294 config_formatter_ = parent_->config_formatter_;
0295 require_subcommand_max_ = parent_->require_subcommand_max_;
0296 }
0297 }
0298
0299 public:
0300
0301
0302
0303
0304 explicit App(std::string app_description = "", std::string app_name = "")
0305 : App(app_description, app_name, nullptr) {
0306 set_help_flag("-h,--help", "Print this help message and exit");
0307 }
0308
0309 App(const App &) = delete;
0310 App &operator=(const App &) = delete;
0311
0312
0313 virtual ~App() = default;
0314
0315
0316
0317
0318
0319
0320
0321 App *callback(std::function<void()> app_callback) {
0322 if(immediate_callback_) {
0323 parse_complete_callback_ = std::move(app_callback);
0324 } else {
0325 final_callback_ = std::move(app_callback);
0326 }
0327 return this;
0328 }
0329
0330
0331
0332 App *final_callback(std::function<void()> app_callback) {
0333 final_callback_ = std::move(app_callback);
0334 return this;
0335 }
0336
0337
0338
0339 App *parse_complete_callback(std::function<void()> pc_callback) {
0340 parse_complete_callback_ = std::move(pc_callback);
0341 return this;
0342 }
0343
0344
0345
0346 App *preparse_callback(std::function<void(std::size_t)> pp_callback) {
0347 pre_parse_callback_ = std::move(pp_callback);
0348 return this;
0349 }
0350
0351
0352 App *name(std::string app_name = "") {
0353
0354 if(parent_ != nullptr) {
0355 auto oname = name_;
0356 name_ = app_name;
0357 auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
0358 if(!res.empty()) {
0359 name_ = oname;
0360 throw(OptionAlreadyAdded(app_name + " conflicts with existing subcommand names"));
0361 }
0362 } else {
0363 name_ = app_name;
0364 }
0365 has_automatic_name_ = false;
0366 return this;
0367 }
0368
0369
0370 App *alias(std::string app_name) {
0371 if(!detail::valid_name_string(app_name)) {
0372 if(app_name.empty()) {
0373 throw IncorrectConstruction("Empty aliases are not allowed");
0374 }
0375 if(!detail::valid_first_char(app_name[0])) {
0376 throw IncorrectConstruction(
0377 "Alias starts with invalid character, allowed characters are [a-zA-z0-9]+'_','?','@' ");
0378 }
0379 for(auto c : app_name) {
0380 if(!detail::valid_later_char(c)) {
0381 throw IncorrectConstruction(std::string("Alias contains invalid character ('") + c +
0382 "'), allowed characters are "
0383 "[a-zA-z0-9]+'_','?','@','.','-' ");
0384 }
0385 }
0386 }
0387
0388 if(parent_ != nullptr) {
0389 aliases_.push_back(app_name);
0390 auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
0391 if(!res.empty()) {
0392 aliases_.pop_back();
0393 throw(OptionAlreadyAdded("alias already matches an existing subcommand: " + app_name));
0394 }
0395 } else {
0396 aliases_.push_back(app_name);
0397 }
0398
0399 return this;
0400 }
0401
0402
0403 App *allow_extras(bool allow = true) {
0404 allow_extras_ = allow;
0405 return this;
0406 }
0407
0408
0409 App *required(bool require = true) {
0410 required_ = require;
0411 return this;
0412 }
0413
0414
0415 App *disabled(bool disable = true) {
0416 disabled_ = disable;
0417 return this;
0418 }
0419
0420
0421 App *silent(bool silence = true) {
0422 silent_ = silence;
0423 return this;
0424 }
0425
0426
0427 App *disabled_by_default(bool disable = true) {
0428 if(disable) {
0429 default_startup = startup_mode::disabled;
0430 } else {
0431 default_startup = (default_startup == startup_mode::enabled) ? startup_mode::enabled : startup_mode::stable;
0432 }
0433 return this;
0434 }
0435
0436
0437
0438 App *enabled_by_default(bool enable = true) {
0439 if(enable) {
0440 default_startup = startup_mode::enabled;
0441 } else {
0442 default_startup =
0443 (default_startup == startup_mode::disabled) ? startup_mode::disabled : startup_mode::stable;
0444 }
0445 return this;
0446 }
0447
0448
0449 App *immediate_callback(bool immediate = true) {
0450 immediate_callback_ = immediate;
0451 if(immediate_callback_) {
0452 if(final_callback_ && !(parse_complete_callback_)) {
0453 std::swap(final_callback_, parse_complete_callback_);
0454 }
0455 } else if(!(final_callback_) && parse_complete_callback_) {
0456 std::swap(final_callback_, parse_complete_callback_);
0457 }
0458 return this;
0459 }
0460
0461
0462 App *validate_positionals(bool validate = true) {
0463 validate_positionals_ = validate;
0464 return this;
0465 }
0466
0467
0468 App *allow_config_extras(bool allow = true) {
0469 if(allow) {
0470 allow_config_extras_ = config_extras_mode::capture;
0471 allow_extras_ = true;
0472 } else {
0473 allow_config_extras_ = config_extras_mode::error;
0474 }
0475 return this;
0476 }
0477
0478
0479 App *allow_config_extras(config_extras_mode mode) {
0480 allow_config_extras_ = mode;
0481 return this;
0482 }
0483
0484
0485 App *prefix_command(bool allow = true) {
0486 prefix_command_ = allow;
0487 return this;
0488 }
0489
0490
0491 App *ignore_case(bool value = true) {
0492 if(value && !ignore_case_) {
0493 ignore_case_ = true;
0494 auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
0495 auto &match = _compare_subcommand_names(*this, *p);
0496 if(!match.empty()) {
0497 ignore_case_ = false;
0498 throw OptionAlreadyAdded("ignore case would cause subcommand name conflicts: " + match);
0499 }
0500 }
0501 ignore_case_ = value;
0502 return this;
0503 }
0504
0505
0506
0507 App *allow_windows_style_options(bool value = true) {
0508 allow_windows_style_options_ = value;
0509 return this;
0510 }
0511
0512
0513 App *positionals_at_end(bool value = true) {
0514 positionals_at_end_ = value;
0515 return this;
0516 }
0517
0518
0519 App *configurable(bool value = true) {
0520 configurable_ = value;
0521 return this;
0522 }
0523
0524
0525 App *ignore_underscore(bool value = true) {
0526 if(value && !ignore_underscore_) {
0527 ignore_underscore_ = true;
0528 auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
0529 auto &match = _compare_subcommand_names(*this, *p);
0530 if(!match.empty()) {
0531 ignore_underscore_ = false;
0532 throw OptionAlreadyAdded("ignore underscore would cause subcommand name conflicts: " + match);
0533 }
0534 }
0535 ignore_underscore_ = value;
0536 return this;
0537 }
0538
0539
0540 App *formatter(std::shared_ptr<FormatterBase> fmt) {
0541 formatter_ = fmt;
0542 return this;
0543 }
0544
0545
0546 App *formatter_fn(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) {
0547 formatter_ = std::make_shared<FormatterLambda>(fmt);
0548 return this;
0549 }
0550
0551
0552 App *config_formatter(std::shared_ptr<Config> fmt) {
0553 config_formatter_ = fmt;
0554 return this;
0555 }
0556
0557
0558 bool parsed() const { return parsed_ > 0; }
0559
0560
0561 OptionDefaults *option_defaults() { return &option_defaults_; }
0562
0563
0564
0565
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581 Option *add_option(std::string option_name,
0582 callback_t option_callback,
0583 std::string option_description = "",
0584 bool defaulted = false,
0585 std::function<std::string()> func = {}) {
0586 Option myopt{option_name, option_description, option_callback, this};
0587
0588 if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {
0589 return *v == myopt;
0590 }) == std::end(options_)) {
0591 options_.emplace_back();
0592 Option_p &option = options_.back();
0593 option.reset(new Option(option_name, option_description, option_callback, this));
0594
0595
0596 option->default_function(func);
0597
0598
0599 if(defaulted)
0600 option->capture_default_str();
0601
0602
0603 option_defaults_.copy_to(option.get());
0604
0605
0606 if(!defaulted && option->get_always_capture_default())
0607 option->capture_default_str();
0608
0609 return option.get();
0610 }
0611
0612 for(auto &opt : options_) {
0613 auto &matchname = opt->matching_name(myopt);
0614 if(!matchname.empty()) {
0615 throw(OptionAlreadyAdded("added option matched existing option name: " + matchname));
0616 }
0617 }
0618
0619 throw(OptionAlreadyAdded("added option matched existing option name"));
0620 }
0621
0622
0623 template <typename AssignTo,
0624 typename ConvertTo = AssignTo,
0625 enable_if_t<!std::is_const<ConvertTo>::value, detail::enabler> = detail::dummy>
0626 Option *add_option(std::string option_name,
0627 AssignTo &variable,
0628 std::string option_description = "") {
0629
0630 auto fun = [&variable](const CLI::results_t &res) {
0631 return detail::lexical_conversion<AssignTo, ConvertTo>(res, variable);
0632 };
0633
0634 Option *opt = add_option(option_name, fun, option_description, false, [&variable]() {
0635 return CLI::detail::checked_to_string<AssignTo, ConvertTo>(variable);
0636 });
0637 opt->type_name(detail::type_name<ConvertTo>());
0638
0639
0640 auto Tcount = detail::type_count<AssignTo>::value;
0641 auto XCcount = detail::type_count<ConvertTo>::value;
0642 opt->type_size(detail::type_count_min<ConvertTo>::value, (std::max)(Tcount, XCcount));
0643 opt->expected(detail::expected_count<ConvertTo>::value);
0644 opt->run_callback_for_default();
0645 return opt;
0646 }
0647
0648
0649 template <typename AssignTo, enable_if_t<!std::is_const<AssignTo>::value, detail::enabler> = detail::dummy>
0650 Option *add_option_no_stream(std::string option_name,
0651 AssignTo &variable,
0652 std::string option_description = "") {
0653
0654 auto fun = [&variable](const CLI::results_t &res) {
0655 return detail::lexical_conversion<AssignTo, AssignTo>(res, variable);
0656 };
0657
0658 Option *opt = add_option(option_name, fun, option_description, false, []() { return std::string{}; });
0659 opt->type_name(detail::type_name<AssignTo>());
0660 opt->type_size(detail::type_count_min<AssignTo>::value, detail::type_count<AssignTo>::value);
0661 opt->expected(detail::expected_count<AssignTo>::value);
0662 opt->run_callback_for_default();
0663 return opt;
0664 }
0665
0666
0667 template <typename ArgType>
0668 Option *add_option_function(std::string option_name,
0669 const std::function<void(const ArgType &)> &func,
0670 std::string option_description = "") {
0671
0672 auto fun = [func](const CLI::results_t &res) {
0673 ArgType variable;
0674 bool result = detail::lexical_conversion<ArgType, ArgType>(res, variable);
0675 if(result) {
0676 func(variable);
0677 }
0678 return result;
0679 };
0680
0681 Option *opt = add_option(option_name, std::move(fun), option_description, false);
0682 opt->type_name(detail::type_name<ArgType>());
0683 opt->type_size(detail::type_count_min<ArgType>::value, detail::type_count<ArgType>::value);
0684 opt->expected(detail::expected_count<ArgType>::value);
0685 return opt;
0686 }
0687
0688
0689 Option *add_option(std::string option_name) {
0690 return add_option(option_name, CLI::callback_t{}, std::string{}, false);
0691 }
0692
0693
0694 template <typename T,
0695 enable_if_t<std::is_const<T>::value && std::is_constructible<std::string, T>::value, detail::enabler> =
0696 detail::dummy>
0697 Option *add_option(std::string option_name, T &option_description) {
0698 return add_option(option_name, CLI::callback_t(), option_description, false);
0699 }
0700
0701
0702 Option *set_help_flag(std::string flag_name = "", const std::string &help_description = "") {
0703
0704 if(help_ptr_ != nullptr) {
0705 remove_option(help_ptr_);
0706 help_ptr_ = nullptr;
0707 }
0708
0709
0710 if(!flag_name.empty()) {
0711 help_ptr_ = add_flag(flag_name, help_description);
0712 help_ptr_->configurable(false);
0713 }
0714
0715 return help_ptr_;
0716 }
0717
0718
0719 Option *set_help_all_flag(std::string help_name = "", const std::string &help_description = "") {
0720
0721 if(help_all_ptr_ != nullptr) {
0722 remove_option(help_all_ptr_);
0723 help_all_ptr_ = nullptr;
0724 }
0725
0726
0727 if(!help_name.empty()) {
0728 help_all_ptr_ = add_flag(help_name, help_description);
0729 help_all_ptr_->configurable(false);
0730 }
0731
0732 return help_all_ptr_;
0733 }
0734
0735
0736 Option *set_version_flag(std::string flag_name = "",
0737 const std::string &versionString = "",
0738 const std::string &version_help = "Display program version information and exit") {
0739
0740 if(version_ptr_ != nullptr) {
0741 remove_option(version_ptr_);
0742 version_ptr_ = nullptr;
0743 }
0744
0745
0746 if(!flag_name.empty()) {
0747 version_ptr_ = add_flag_callback(
0748 flag_name, [versionString]() { throw(CLI::CallForVersion(versionString, 0)); }, version_help);
0749 version_ptr_->configurable(false);
0750 }
0751
0752 return version_ptr_;
0753 }
0754
0755 Option *set_version_flag(std::string flag_name,
0756 std::function<std::string()> vfunc,
0757 const std::string &version_help = "Display program version information and exit") {
0758 if(version_ptr_ != nullptr) {
0759 remove_option(version_ptr_);
0760 version_ptr_ = nullptr;
0761 }
0762
0763
0764 if(!flag_name.empty()) {
0765 version_ptr_ = add_flag_callback(
0766 flag_name, [vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); }, version_help);
0767 version_ptr_->configurable(false);
0768 }
0769
0770 return version_ptr_;
0771 }
0772
0773 private:
0774
0775 Option *_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description) {
0776 Option *opt;
0777 if(detail::has_default_flag_values(flag_name)) {
0778
0779 auto flag_defaults = detail::get_default_flag_values(flag_name);
0780 detail::remove_default_flag_values(flag_name);
0781 opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
0782 for(const auto &fname : flag_defaults)
0783 opt->fnames_.push_back(fname.first);
0784 opt->default_flag_values_ = std::move(flag_defaults);
0785 } else {
0786 opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
0787 }
0788
0789 if(opt->get_positional()) {
0790 auto pos_name = opt->get_name(true);
0791 remove_option(opt);
0792 throw IncorrectConstruction::PositionalFlag(pos_name);
0793 }
0794 opt->multi_option_policy(MultiOptionPolicy::TakeLast);
0795 opt->expected(0);
0796 opt->required(false);
0797 return opt;
0798 }
0799
0800 public:
0801
0802 Option *add_flag(std::string flag_name) { return _add_flag_internal(flag_name, CLI::callback_t(), std::string{}); }
0803
0804
0805
0806
0807 template <typename T,
0808 enable_if_t<std::is_const<T>::value && std::is_constructible<std::string, T>::value, detail::enabler> =
0809 detail::dummy>
0810 Option *add_flag(std::string flag_name, T &flag_description) {
0811 return _add_flag_internal(flag_name, CLI::callback_t(), flag_description);
0812 }
0813
0814
0815
0816 template <typename T,
0817 enable_if_t<std::is_constructible<T, std::int64_t>::value && !is_bool<T>::value, detail::enabler> =
0818 detail::dummy>
0819 Option *add_flag(std::string flag_name,
0820 T &flag_count,
0821 std::string flag_description = "") {
0822 flag_count = 0;
0823 CLI::callback_t fun = [&flag_count](const CLI::results_t &res) {
0824 try {
0825 detail::sum_flag_vector(res, flag_count);
0826 } catch(const std::invalid_argument &) {
0827 return false;
0828 }
0829 return true;
0830 };
0831 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
0832 ->multi_option_policy(MultiOptionPolicy::TakeAll);
0833 }
0834
0835
0836
0837 template <typename T,
0838 enable_if_t<!detail::is_mutable_container<T>::value && !std::is_const<T>::value &&
0839 (!std::is_constructible<T, std::int64_t>::value || is_bool<T>::value) &&
0840 !std::is_constructible<std::function<void(int)>, T>::value,
0841 detail::enabler> = detail::dummy>
0842 Option *add_flag(std::string flag_name,
0843 T &flag_result,
0844 std::string flag_description = "") {
0845
0846 CLI::callback_t fun = [&flag_result](const CLI::results_t &res) {
0847 return CLI::detail::lexical_cast(res[0], flag_result);
0848 };
0849 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))->run_callback_for_default();
0850 }
0851
0852
0853 template <typename T,
0854 enable_if_t<!std::is_assignable<std::function<void(std::int64_t)> &, T>::value, detail::enabler> =
0855 detail::dummy>
0856 Option *add_flag(std::string flag_name,
0857 std::vector<T> &flag_results,
0858 std::string flag_description = "") {
0859 CLI::callback_t fun = [&flag_results](const CLI::results_t &res) {
0860 bool retval = true;
0861 for(const auto &elem : res) {
0862 flag_results.emplace_back();
0863 retval &= detail::lexical_cast(elem, flag_results.back());
0864 }
0865 return retval;
0866 };
0867 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
0868 ->multi_option_policy(MultiOptionPolicy::TakeAll)
0869 ->run_callback_for_default();
0870 }
0871
0872
0873 Option *add_flag_callback(std::string flag_name,
0874 std::function<void(void)> function,
0875 std::string flag_description = "") {
0876
0877 CLI::callback_t fun = [function](const CLI::results_t &res) {
0878 bool trigger{false};
0879 auto result = CLI::detail::lexical_cast(res[0], trigger);
0880 if(result && trigger) {
0881 function();
0882 }
0883 return result;
0884 };
0885 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
0886 }
0887
0888
0889 Option *add_flag_function(std::string flag_name,
0890 std::function<void(std::int64_t)> function,
0891 std::string flag_description = "") {
0892
0893 CLI::callback_t fun = [function](const CLI::results_t &res) {
0894 std::int64_t flag_count = 0;
0895 detail::sum_flag_vector(res, flag_count);
0896 function(flag_count);
0897 return true;
0898 };
0899 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
0900 ->multi_option_policy(MultiOptionPolicy::TakeAll);
0901 }
0902
0903 #ifdef CLI11_CPP14
0904
0905 Option *add_flag(std::string flag_name,
0906 std::function<void(std::int64_t)> function,
0907 std::string flag_description = "") {
0908 return add_flag_function(std::move(flag_name), std::move(function), std::move(flag_description));
0909 }
0910 #endif
0911
0912
0913 Option *set_config(std::string option_name = "",
0914 std::string default_filename = "",
0915 const std::string &help_message = "Read an ini file",
0916 bool config_required = false) {
0917
0918
0919 if(config_ptr_ != nullptr) {
0920 remove_option(config_ptr_);
0921 config_ptr_ = nullptr;
0922 }
0923
0924
0925 if(!option_name.empty()) {
0926 config_ptr_ = add_option(option_name, help_message);
0927 if(config_required) {
0928 config_ptr_->required();
0929 }
0930 if(!default_filename.empty()) {
0931 config_ptr_->default_str(std::move(default_filename));
0932 }
0933 config_ptr_->configurable(false);
0934 }
0935
0936 return config_ptr_;
0937 }
0938
0939
0940 bool remove_option(Option *opt) {
0941
0942 for(Option_p &op : options_) {
0943 op->remove_needs(opt);
0944 op->remove_excludes(opt);
0945 }
0946
0947 if(help_ptr_ == opt)
0948 help_ptr_ = nullptr;
0949 if(help_all_ptr_ == opt)
0950 help_all_ptr_ = nullptr;
0951
0952 auto iterator =
0953 std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
0954 if(iterator != std::end(options_)) {
0955 options_.erase(iterator);
0956 return true;
0957 }
0958 return false;
0959 }
0960
0961
0962 template <typename T = Option_group>
0963 T *add_option_group(std::string group_name, std::string group_description = "") {
0964 auto option_group = std::make_shared<T>(std::move(group_description), group_name, this);
0965 auto ptr = option_group.get();
0966
0967 App_p app_ptr = std::dynamic_pointer_cast<App>(option_group);
0968 add_subcommand(std::move(app_ptr));
0969 return ptr;
0970 }
0971
0972
0973
0974
0975
0976
0977 App *add_subcommand(std::string subcommand_name = "", std::string subcommand_description = "") {
0978 if(!subcommand_name.empty() && !detail::valid_name_string(subcommand_name)) {
0979 if(!detail::valid_first_char(subcommand_name[0])) {
0980 throw IncorrectConstruction(
0981 "Subcommand name starts with invalid character, allowed characters are [a-zA-z0-9]+'_','?','@' ");
0982 }
0983 for(auto c : subcommand_name) {
0984 if(!detail::valid_later_char(c)) {
0985 throw IncorrectConstruction(std::string("Subcommand name contains invalid character ('") + c +
0986 "'), allowed characters are "
0987 "[a-zA-z0-9]+'_','?','@','.','-' ");
0988 }
0989 }
0990 }
0991 CLI::App_p subcom = std::shared_ptr<App>(new App(std::move(subcommand_description), subcommand_name, this));
0992 return add_subcommand(std::move(subcom));
0993 }
0994
0995
0996 App *add_subcommand(CLI::App_p subcom) {
0997 if(!subcom)
0998 throw IncorrectConstruction("passed App is not valid");
0999 auto ckapp = (name_.empty() && parent_ != nullptr) ? _get_fallthrough_parent() : this;
1000 auto &mstrg = _compare_subcommand_names(*subcom, *ckapp);
1001 if(!mstrg.empty()) {
1002 throw(OptionAlreadyAdded("subcommand name or alias matches existing subcommand: " + mstrg));
1003 }
1004 subcom->parent_ = this;
1005 subcommands_.push_back(std::move(subcom));
1006 return subcommands_.back().get();
1007 }
1008
1009
1010 bool remove_subcommand(App *subcom) {
1011
1012 for(App_p &sub : subcommands_) {
1013 sub->remove_excludes(subcom);
1014 sub->remove_needs(subcom);
1015 }
1016
1017 auto iterator = std::find_if(
1018 std::begin(subcommands_), std::end(subcommands_), [subcom](const App_p &v) { return v.get() == subcom; });
1019 if(iterator != std::end(subcommands_)) {
1020 subcommands_.erase(iterator);
1021 return true;
1022 }
1023 return false;
1024 }
1025
1026
1027 App *get_subcommand(const App *subcom) const {
1028 if(subcom == nullptr)
1029 throw OptionNotFound("nullptr passed");
1030 for(const App_p &subcomptr : subcommands_)
1031 if(subcomptr.get() == subcom)
1032 return subcomptr.get();
1033 throw OptionNotFound(subcom->get_name());
1034 }
1035
1036
1037 App *get_subcommand(std::string subcom) const {
1038 auto subc = _find_subcommand(subcom, false, false);
1039 if(subc == nullptr)
1040 throw OptionNotFound(subcom);
1041 return subc;
1042 }
1043
1044 App *get_subcommand(int index = 0) const {
1045 if(index >= 0) {
1046 auto uindex = static_cast<unsigned>(index);
1047 if(uindex < subcommands_.size())
1048 return subcommands_[uindex].get();
1049 }
1050 throw OptionNotFound(std::to_string(index));
1051 }
1052
1053
1054 CLI::App_p get_subcommand_ptr(App *subcom) const {
1055 if(subcom == nullptr)
1056 throw OptionNotFound("nullptr passed");
1057 for(const App_p &subcomptr : subcommands_)
1058 if(subcomptr.get() == subcom)
1059 return subcomptr;
1060 throw OptionNotFound(subcom->get_name());
1061 }
1062
1063
1064 CLI::App_p get_subcommand_ptr(std::string subcom) const {
1065 for(const App_p &subcomptr : subcommands_)
1066 if(subcomptr->check_name(subcom))
1067 return subcomptr;
1068 throw OptionNotFound(subcom);
1069 }
1070
1071
1072 CLI::App_p get_subcommand_ptr(int index = 0) const {
1073 if(index >= 0) {
1074 auto uindex = static_cast<unsigned>(index);
1075 if(uindex < subcommands_.size())
1076 return subcommands_[uindex];
1077 }
1078 throw OptionNotFound(std::to_string(index));
1079 }
1080
1081
1082 App *get_option_group(std::string group_name) const {
1083 for(const App_p &app : subcommands_) {
1084 if(app->name_.empty() && app->group_ == group_name) {
1085 return app.get();
1086 }
1087 }
1088 throw OptionNotFound(group_name);
1089 }
1090
1091
1092
1093
1094 std::size_t count() const { return parsed_; }
1095
1096
1097
1098 std::size_t count_all() const {
1099 std::size_t cnt{0};
1100 for(auto &opt : options_) {
1101 cnt += opt->count();
1102 }
1103 for(auto &sub : subcommands_) {
1104 cnt += sub->count_all();
1105 }
1106 if(!get_name().empty()) {
1107 cnt += parsed_;
1108 }
1109 return cnt;
1110 }
1111
1112
1113 App *group(std::string group_name) {
1114 group_ = group_name;
1115 return this;
1116 }
1117
1118
1119 App *require_subcommand() {
1120 require_subcommand_min_ = 1;
1121 require_subcommand_max_ = 0;
1122 return this;
1123 }
1124
1125
1126
1127
1128 App *require_subcommand(int value) {
1129 if(value < 0) {
1130 require_subcommand_min_ = 0;
1131 require_subcommand_max_ = static_cast<std::size_t>(-value);
1132 } else {
1133 require_subcommand_min_ = static_cast<std::size_t>(value);
1134 require_subcommand_max_ = static_cast<std::size_t>(value);
1135 }
1136 return this;
1137 }
1138
1139
1140
1141 App *require_subcommand(std::size_t min, std::size_t max) {
1142 require_subcommand_min_ = min;
1143 require_subcommand_max_ = max;
1144 return this;
1145 }
1146
1147
1148 App *require_option() {
1149 require_option_min_ = 1;
1150 require_option_max_ = 0;
1151 return this;
1152 }
1153
1154
1155
1156
1157 App *require_option(int value) {
1158 if(value < 0) {
1159 require_option_min_ = 0;
1160 require_option_max_ = static_cast<std::size_t>(-value);
1161 } else {
1162 require_option_min_ = static_cast<std::size_t>(value);
1163 require_option_max_ = static_cast<std::size_t>(value);
1164 }
1165 return this;
1166 }
1167
1168
1169
1170 App *require_option(std::size_t min, std::size_t max) {
1171 require_option_min_ = min;
1172 require_option_max_ = max;
1173 return this;
1174 }
1175
1176
1177
1178 App *fallthrough(bool value = true) {
1179 fallthrough_ = value;
1180 return this;
1181 }
1182
1183
1184
1185 explicit operator bool() const { return parsed_ > 0; }
1186
1187
1188
1189
1190
1191
1192
1193
1194 virtual void pre_callback() {}
1195
1196
1197
1198
1199
1200
1201 void clear() {
1202
1203 parsed_ = 0;
1204 pre_parse_called_ = false;
1205
1206 missing_.clear();
1207 parsed_subcommands_.clear();
1208 for(const Option_p &opt : options_) {
1209 opt->clear();
1210 }
1211 for(const App_p &subc : subcommands_) {
1212 subc->clear();
1213 }
1214 }
1215
1216
1217
1218 void parse(int argc, const char *const *argv) {
1219
1220 if(name_.empty() || has_automatic_name_) {
1221 has_automatic_name_ = true;
1222 name_ = argv[0];
1223 }
1224
1225 std::vector<std::string> args;
1226 args.reserve(static_cast<std::size_t>(argc) - 1);
1227 for(int i = argc - 1; i > 0; i--)
1228 args.emplace_back(argv[i]);
1229 parse(std::move(args));
1230 }
1231
1232
1233
1234
1235
1236 void parse(std::string commandline, bool program_name_included = false) {
1237
1238 if(program_name_included) {
1239 auto nstr = detail::split_program_name(commandline);
1240 if((name_.empty()) || (has_automatic_name_)) {
1241 has_automatic_name_ = true;
1242 name_ = nstr.first;
1243 }
1244 commandline = std::move(nstr.second);
1245 } else {
1246 detail::trim(commandline);
1247 }
1248
1249 if(!commandline.empty()) {
1250 commandline = detail::find_and_modify(commandline, "=", detail::escape_detect);
1251 if(allow_windows_style_options_)
1252 commandline = detail::find_and_modify(commandline, ":", detail::escape_detect);
1253 }
1254
1255 auto args = detail::split_up(std::move(commandline));
1256
1257 args.erase(std::remove(args.begin(), args.end(), std::string{}), args.end());
1258 std::reverse(args.begin(), args.end());
1259
1260 parse(std::move(args));
1261 }
1262
1263
1264
1265 void parse(std::vector<std::string> &args) {
1266
1267 if(parsed_ > 0)
1268 clear();
1269
1270
1271
1272
1273 parsed_ = 1;
1274 _validate();
1275 _configure();
1276
1277 parent_ = nullptr;
1278 parsed_ = 0;
1279
1280 _parse(args);
1281 run_callback();
1282 }
1283
1284
1285 void parse(std::vector<std::string> &&args) {
1286
1287 if(parsed_ > 0)
1288 clear();
1289
1290
1291
1292
1293 parsed_ = 1;
1294 _validate();
1295 _configure();
1296
1297 parent_ = nullptr;
1298 parsed_ = 0;
1299
1300 _parse(std::move(args));
1301 run_callback();
1302 }
1303
1304
1305 void failure_message(std::function<std::string(const App *, const Error &e)> function) {
1306 failure_message_ = function;
1307 }
1308
1309
1310 int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const {
1311
1312
1313 if(e.get_name() == "RuntimeError")
1314 return e.get_exit_code();
1315
1316 if(e.get_name() == "CallForHelp") {
1317 out << help();
1318 return e.get_exit_code();
1319 }
1320
1321 if(e.get_name() == "CallForAllHelp") {
1322 out << help("", AppFormatMode::All);
1323 return e.get_exit_code();
1324 }
1325
1326 if(e.get_name() == "CallForVersion") {
1327 out << e.what() << std::endl;
1328 return e.get_exit_code();
1329 }
1330
1331 if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {
1332 if(failure_message_)
1333 err << failure_message_(this, e) << std::flush;
1334 }
1335
1336 return e.get_exit_code();
1337 }
1338
1339
1340
1341
1342
1343
1344 std::size_t count(std::string option_name) const { return get_option(option_name)->count(); }
1345
1346
1347
1348 std::vector<App *> get_subcommands() const { return parsed_subcommands_; }
1349
1350
1351
1352 std::vector<const App *> get_subcommands(const std::function<bool(const App *)> &filter) const {
1353 std::vector<const App *> subcomms(subcommands_.size());
1354 std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) {
1355 return v.get();
1356 });
1357
1358 if(filter) {
1359 subcomms.erase(std::remove_if(std::begin(subcomms),
1360 std::end(subcomms),
1361 [&filter](const App *app) { return !filter(app); }),
1362 std::end(subcomms));
1363 }
1364
1365 return subcomms;
1366 }
1367
1368
1369
1370 std::vector<App *> get_subcommands(const std::function<bool(App *)> &filter) {
1371 std::vector<App *> subcomms(subcommands_.size());
1372 std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) {
1373 return v.get();
1374 });
1375
1376 if(filter) {
1377 subcomms.erase(
1378 std::remove_if(std::begin(subcomms), std::end(subcomms), [&filter](App *app) { return !filter(app); }),
1379 std::end(subcomms));
1380 }
1381
1382 return subcomms;
1383 }
1384
1385
1386 bool got_subcommand(const App *subcom) const {
1387
1388 return get_subcommand(subcom)->parsed_ > 0;
1389 }
1390
1391
1392 bool got_subcommand(std::string subcommand_name) const { return get_subcommand(subcommand_name)->parsed_ > 0; }
1393
1394
1395 App *excludes(Option *opt) {
1396 if(opt == nullptr) {
1397 throw OptionNotFound("nullptr passed");
1398 }
1399 exclude_options_.insert(opt);
1400 return this;
1401 }
1402
1403
1404 App *excludes(App *app) {
1405 if(app == nullptr) {
1406 throw OptionNotFound("nullptr passed");
1407 }
1408 if(app == this) {
1409 throw OptionNotFound("cannot self reference in needs");
1410 }
1411 auto res = exclude_subcommands_.insert(app);
1412
1413 if(res.second) {
1414 app->exclude_subcommands_.insert(this);
1415 }
1416 return this;
1417 }
1418
1419 App *needs(Option *opt) {
1420 if(opt == nullptr) {
1421 throw OptionNotFound("nullptr passed");
1422 }
1423 need_options_.insert(opt);
1424 return this;
1425 }
1426
1427 App *needs(App *app) {
1428 if(app == nullptr) {
1429 throw OptionNotFound("nullptr passed");
1430 }
1431 if(app == this) {
1432 throw OptionNotFound("cannot self reference in needs");
1433 }
1434 need_subcommands_.insert(app);
1435 return this;
1436 }
1437
1438
1439 bool remove_excludes(Option *opt) {
1440 auto iterator = std::find(std::begin(exclude_options_), std::end(exclude_options_), opt);
1441 if(iterator == std::end(exclude_options_)) {
1442 return false;
1443 }
1444 exclude_options_.erase(iterator);
1445 return true;
1446 }
1447
1448
1449 bool remove_excludes(App *app) {
1450 auto iterator = std::find(std::begin(exclude_subcommands_), std::end(exclude_subcommands_), app);
1451 if(iterator == std::end(exclude_subcommands_)) {
1452 return false;
1453 }
1454 auto other_app = *iterator;
1455 exclude_subcommands_.erase(iterator);
1456 other_app->remove_excludes(this);
1457 return true;
1458 }
1459
1460
1461 bool remove_needs(Option *opt) {
1462 auto iterator = std::find(std::begin(need_options_), std::end(need_options_), opt);
1463 if(iterator == std::end(need_options_)) {
1464 return false;
1465 }
1466 need_options_.erase(iterator);
1467 return true;
1468 }
1469
1470
1471 bool remove_needs(App *app) {
1472 auto iterator = std::find(std::begin(need_subcommands_), std::end(need_subcommands_), app);
1473 if(iterator == std::end(need_subcommands_)) {
1474 return false;
1475 }
1476 need_subcommands_.erase(iterator);
1477 return true;
1478 }
1479
1480
1481
1482
1483
1484
1485 App *footer(std::string footer_string) {
1486 footer_ = std::move(footer_string);
1487 return this;
1488 }
1489
1490 App *footer(std::function<std::string()> footer_function) {
1491 footer_callback_ = std::move(footer_function);
1492 return this;
1493 }
1494
1495
1496 std::string config_to_str(bool default_also = false, bool write_description = false) const {
1497 return config_formatter_->to_config(this, default_also, write_description, "");
1498 }
1499
1500
1501
1502 std::string help(std::string prev = "", AppFormatMode mode = AppFormatMode::Normal) const {
1503 if(prev.empty())
1504 prev = get_name();
1505 else
1506 prev += " " + get_name();
1507
1508
1509 auto selected_subcommands = get_subcommands();
1510 if(!selected_subcommands.empty()) {
1511 return selected_subcommands.at(0)->help(prev, mode);
1512 }
1513 return formatter_->make_help(this, prev, mode);
1514 }
1515
1516
1517 std::string version() const {
1518 std::string val;
1519 if(version_ptr_ != nullptr) {
1520 auto rv = version_ptr_->results();
1521 version_ptr_->clear();
1522 version_ptr_->add_result("true");
1523 try {
1524 version_ptr_->run_callback();
1525 } catch(const CLI::CallForVersion &cfv) {
1526 val = cfv.what();
1527 }
1528 version_ptr_->clear();
1529 version_ptr_->add_result(rv);
1530 }
1531 return val;
1532 }
1533
1534
1535
1536
1537
1538 std::shared_ptr<FormatterBase> get_formatter() const { return formatter_; }
1539
1540
1541 std::shared_ptr<Config> get_config_formatter() const { return config_formatter_; }
1542
1543
1544 std::shared_ptr<ConfigBase> get_config_formatter_base() const {
1545
1546 #if defined(__cpp_rtti) || (defined(__GXX_RTTI) && __GXX_RTTI) || (defined(_HAS_STATIC_RTTI) && (_HAS_STATIC_RTTI == 0))
1547 return std::dynamic_pointer_cast<ConfigBase>(config_formatter_);
1548 #else
1549 return std::static_pointer_cast<ConfigBase>(config_formatter_);
1550 #endif
1551 }
1552
1553
1554 std::string get_description() const { return description_; }
1555
1556
1557 App *description(std::string app_description) {
1558 description_ = std::move(app_description);
1559 return this;
1560 }
1561
1562
1563 std::vector<const Option *> get_options(const std::function<bool(const Option *)> filter = {}) const {
1564 std::vector<const Option *> options(options_.size());
1565 std::transform(std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) {
1566 return val.get();
1567 });
1568
1569 if(filter) {
1570 options.erase(std::remove_if(std::begin(options),
1571 std::end(options),
1572 [&filter](const Option *opt) { return !filter(opt); }),
1573 std::end(options));
1574 }
1575
1576 return options;
1577 }
1578
1579
1580 std::vector<Option *> get_options(const std::function<bool(Option *)> filter = {}) {
1581 std::vector<Option *> options(options_.size());
1582 std::transform(std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) {
1583 return val.get();
1584 });
1585
1586 if(filter) {
1587 options.erase(
1588 std::remove_if(std::begin(options), std::end(options), [&filter](Option *opt) { return !filter(opt); }),
1589 std::end(options));
1590 }
1591
1592 return options;
1593 }
1594
1595
1596 Option *get_option_no_throw(std::string option_name) noexcept {
1597 for(Option_p &opt : options_) {
1598 if(opt->check_name(option_name)) {
1599 return opt.get();
1600 }
1601 }
1602 for(auto &subc : subcommands_) {
1603
1604 if(subc->get_name().empty()) {
1605 auto opt = subc->get_option_no_throw(option_name);
1606 if(opt != nullptr) {
1607 return opt;
1608 }
1609 }
1610 }
1611 return nullptr;
1612 }
1613
1614
1615 const Option *get_option_no_throw(std::string option_name) const noexcept {
1616 for(const Option_p &opt : options_) {
1617 if(opt->check_name(option_name)) {
1618 return opt.get();
1619 }
1620 }
1621 for(const auto &subc : subcommands_) {
1622
1623 if(subc->get_name().empty()) {
1624 auto opt = subc->get_option_no_throw(option_name);
1625 if(opt != nullptr) {
1626 return opt;
1627 }
1628 }
1629 }
1630 return nullptr;
1631 }
1632
1633
1634 const Option *get_option(std::string option_name) const {
1635 auto opt = get_option_no_throw(option_name);
1636 if(opt == nullptr) {
1637 throw OptionNotFound(option_name);
1638 }
1639 return opt;
1640 }
1641
1642
1643 Option *get_option(std::string option_name) {
1644 auto opt = get_option_no_throw(option_name);
1645 if(opt == nullptr) {
1646 throw OptionNotFound(option_name);
1647 }
1648 return opt;
1649 }
1650
1651
1652 const Option *operator[](const std::string &option_name) const { return get_option(option_name); }
1653
1654
1655 const Option *operator[](const char *option_name) const { return get_option(option_name); }
1656
1657
1658 bool get_ignore_case() const { return ignore_case_; }
1659
1660
1661 bool get_ignore_underscore() const { return ignore_underscore_; }
1662
1663
1664 bool get_fallthrough() const { return fallthrough_; }
1665
1666
1667 bool get_allow_windows_style_options() const { return allow_windows_style_options_; }
1668
1669
1670 bool get_positionals_at_end() const { return positionals_at_end_; }
1671
1672
1673 bool get_configurable() const { return configurable_; }
1674
1675
1676 const std::string &get_group() const { return group_; }
1677
1678
1679 std::string get_footer() const { return (footer_callback_) ? footer_callback_() + '\n' + footer_ : footer_; }
1680
1681
1682 std::size_t get_require_subcommand_min() const { return require_subcommand_min_; }
1683
1684
1685 std::size_t get_require_subcommand_max() const { return require_subcommand_max_; }
1686
1687
1688 std::size_t get_require_option_min() const { return require_option_min_; }
1689
1690
1691 std::size_t get_require_option_max() const { return require_option_max_; }
1692
1693
1694 bool get_prefix_command() const { return prefix_command_; }
1695
1696
1697 bool get_allow_extras() const { return allow_extras_; }
1698
1699
1700 bool get_required() const { return required_; }
1701
1702
1703 bool get_disabled() const { return disabled_; }
1704
1705
1706 bool get_silent() const { return silent_; }
1707
1708
1709 bool get_immediate_callback() const { return immediate_callback_; }
1710
1711
1712 bool get_disabled_by_default() const { return (default_startup == startup_mode::disabled); }
1713
1714
1715 bool get_enabled_by_default() const { return (default_startup == startup_mode::enabled); }
1716
1717 bool get_validate_positionals() const { return validate_positionals_; }
1718
1719
1720 config_extras_mode get_allow_config_extras() const { return allow_config_extras_; }
1721
1722
1723 Option *get_help_ptr() { return help_ptr_; }
1724
1725
1726 const Option *get_help_ptr() const { return help_ptr_; }
1727
1728
1729 const Option *get_help_all_ptr() const { return help_all_ptr_; }
1730
1731
1732 Option *get_config_ptr() { return config_ptr_; }
1733
1734
1735 const Option *get_config_ptr() const { return config_ptr_; }
1736
1737
1738 Option *get_version_ptr() { return version_ptr_; }
1739
1740
1741 const Option *get_version_ptr() const { return version_ptr_; }
1742
1743
1744 App *get_parent() { return parent_; }
1745
1746
1747 const App *get_parent() const { return parent_; }
1748
1749
1750 const std::string &get_name() const { return name_; }
1751
1752
1753 const std::vector<std::string> &get_aliases() const { return aliases_; }
1754
1755
1756 App *clear_aliases() {
1757 aliases_.clear();
1758 return this;
1759 }
1760
1761
1762 std::string get_display_name(bool with_aliases = false) const {
1763 if(name_.empty()) {
1764 return std::string("[Option Group: ") + get_group() + "]";
1765 }
1766 if(aliases_.empty() || !with_aliases || aliases_.empty()) {
1767 return name_;
1768 }
1769 std::string dispname = name_;
1770 for(const auto &lalias : aliases_) {
1771 dispname.push_back(',');
1772 dispname.push_back(' ');
1773 dispname.append(lalias);
1774 }
1775 return dispname;
1776 }
1777
1778
1779 bool check_name(std::string name_to_check) const {
1780 std::string local_name = name_;
1781 if(ignore_underscore_) {
1782 local_name = detail::remove_underscore(name_);
1783 name_to_check = detail::remove_underscore(name_to_check);
1784 }
1785 if(ignore_case_) {
1786 local_name = detail::to_lower(name_);
1787 name_to_check = detail::to_lower(name_to_check);
1788 }
1789
1790 if(local_name == name_to_check) {
1791 return true;
1792 }
1793 for(auto les : aliases_) {
1794 if(ignore_underscore_) {
1795 les = detail::remove_underscore(les);
1796 }
1797 if(ignore_case_) {
1798 les = detail::to_lower(les);
1799 }
1800 if(les == name_to_check) {
1801 return true;
1802 }
1803 }
1804 return false;
1805 }
1806
1807
1808 std::vector<std::string> get_groups() const {
1809 std::vector<std::string> groups;
1810
1811 for(const Option_p &opt : options_) {
1812
1813 if(std::find(groups.begin(), groups.end(), opt->get_group()) == groups.end()) {
1814 groups.push_back(opt->get_group());
1815 }
1816 }
1817
1818 return groups;
1819 }
1820
1821
1822 const std::vector<Option *> &parse_order() const { return parse_order_; }
1823
1824
1825 std::vector<std::string> remaining(bool recurse = false) const {
1826 std::vector<std::string> miss_list;
1827 for(const std::pair<detail::Classifier, std::string> &miss : missing_) {
1828 miss_list.push_back(std::get<1>(miss));
1829 }
1830
1831 if(recurse) {
1832 if(!allow_extras_) {
1833 for(const auto &sub : subcommands_) {
1834 if(sub->name_.empty() && !sub->missing_.empty()) {
1835 for(const std::pair<detail::Classifier, std::string> &miss : sub->missing_) {
1836 miss_list.push_back(std::get<1>(miss));
1837 }
1838 }
1839 }
1840 }
1841
1842
1843 for(const App *sub : parsed_subcommands_) {
1844 std::vector<std::string> output = sub->remaining(recurse);
1845 std::copy(std::begin(output), std::end(output), std::back_inserter(miss_list));
1846 }
1847 }
1848 return miss_list;
1849 }
1850
1851
1852 std::vector<std::string> remaining_for_passthrough(bool recurse = false) const {
1853 std::vector<std::string> miss_list = remaining(recurse);
1854 std::reverse(std::begin(miss_list), std::end(miss_list));
1855 return miss_list;
1856 }
1857
1858
1859 std::size_t remaining_size(bool recurse = false) const {
1860 auto remaining_options = static_cast<std::size_t>(std::count_if(
1861 std::begin(missing_), std::end(missing_), [](const std::pair<detail::Classifier, std::string> &val) {
1862 return val.first != detail::Classifier::POSITIONAL_MARK;
1863 }));
1864
1865 if(recurse) {
1866 for(const App_p &sub : subcommands_) {
1867 remaining_options += sub->remaining_size(recurse);
1868 }
1869 }
1870 return remaining_options;
1871 }
1872
1873
1874
1875 protected:
1876
1877
1878
1879
1880 void _validate() const {
1881
1882 auto pcount = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
1883 return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional();
1884 });
1885 if(pcount > 1) {
1886 auto pcount_req = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
1887 return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional() &&
1888 opt->get_required();
1889 });
1890 if(pcount - pcount_req > 1) {
1891 throw InvalidError(name_);
1892 }
1893 }
1894
1895 std::size_t nameless_subs{0};
1896 for(const App_p &app : subcommands_) {
1897 app->_validate();
1898 if(app->get_name().empty())
1899 ++nameless_subs;
1900 }
1901
1902 if(require_option_min_ > 0) {
1903 if(require_option_max_ > 0) {
1904 if(require_option_max_ < require_option_min_) {
1905 throw(InvalidError("Required min options greater than required max options",
1906 ExitCodes::InvalidError));
1907 }
1908 }
1909 if(require_option_min_ > (options_.size() + nameless_subs)) {
1910 throw(InvalidError("Required min options greater than number of available options",
1911 ExitCodes::InvalidError));
1912 }
1913 }
1914 }
1915
1916
1917
1918
1919 void _configure() {
1920 if(default_startup == startup_mode::enabled) {
1921 disabled_ = false;
1922 } else if(default_startup == startup_mode::disabled) {
1923 disabled_ = true;
1924 }
1925 for(const App_p &app : subcommands_) {
1926 if(app->has_automatic_name_) {
1927 app->name_.clear();
1928 }
1929 if(app->name_.empty()) {
1930 app->fallthrough_ = false;
1931 app->prefix_command_ = false;
1932 }
1933
1934 app->parent_ = this;
1935 app->_configure();
1936 }
1937 }
1938
1939
1940 void run_callback(bool final_mode = false, bool suppress_final_callback = false) {
1941 pre_callback();
1942
1943 if(!final_mode && parse_complete_callback_) {
1944 parse_complete_callback_();
1945 }
1946
1947 for(App *subc : get_subcommands()) {
1948 subc->run_callback(true, suppress_final_callback);
1949 }
1950
1951 for(auto &subc : subcommands_) {
1952 if(subc->name_.empty() && subc->count_all() > 0) {
1953 subc->run_callback(true, suppress_final_callback);
1954 }
1955 }
1956
1957
1958 if(final_callback_ && (parsed_ > 0) && (!suppress_final_callback)) {
1959 if(!name_.empty() || count_all() > 0 || parent_ == nullptr) {
1960 final_callback_();
1961 }
1962 }
1963 }
1964
1965
1966 bool _valid_subcommand(const std::string ¤t, bool ignore_used = true) const {
1967
1968 if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {
1969 return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
1970 }
1971 auto com = _find_subcommand(current, true, ignore_used);
1972 if(com != nullptr) {
1973 return true;
1974 }
1975
1976 return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
1977 }
1978
1979
1980 detail::Classifier _recognize(const std::string ¤t, bool ignore_used_subcommands = true) const {
1981 std::string dummy1, dummy2;
1982
1983 if(current == "--")
1984 return detail::Classifier::POSITIONAL_MARK;
1985 if(_valid_subcommand(current, ignore_used_subcommands))
1986 return detail::Classifier::SUBCOMMAND;
1987 if(detail::split_long(current, dummy1, dummy2))
1988 return detail::Classifier::LONG;
1989 if(detail::split_short(current, dummy1, dummy2)) {
1990 if(dummy1[0] >= '0' && dummy1[0] <= '9') {
1991 if(get_option_no_throw(std::string{'-', dummy1[0]}) == nullptr) {
1992 return detail::Classifier::NONE;
1993 }
1994 }
1995 return detail::Classifier::SHORT;
1996 }
1997 if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
1998 return detail::Classifier::WINDOWS_STYLE;
1999 if((current == "++") && !name_.empty() && parent_ != nullptr)
2000 return detail::Classifier::SUBCOMMAND_TERMINATOR;
2001 return detail::Classifier::NONE;
2002 }
2003
2004
2005
2006
2007 void _process_config_file() {
2008 if(config_ptr_ != nullptr) {
2009 bool config_required = config_ptr_->get_required();
2010 auto file_given = config_ptr_->count() > 0;
2011 auto config_files = config_ptr_->as<std::vector<std::string>>();
2012 if(config_files.empty() || config_files.front().empty()) {
2013 if(config_required) {
2014 throw FileError::Missing("no specified config file");
2015 }
2016 return;
2017 }
2018 for(auto rit = config_files.rbegin(); rit != config_files.rend(); ++rit) {
2019 const auto &config_file = *rit;
2020 auto path_result = detail::check_path(config_file.c_str());
2021 if(path_result == detail::path_type::file) {
2022 try {
2023 std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
2024 _parse_config(values);
2025 if(!file_given) {
2026 config_ptr_->add_result(config_file);
2027 }
2028 } catch(const FileError &) {
2029 if(config_required || file_given)
2030 throw;
2031 }
2032 } else if(config_required || file_given) {
2033 throw FileError::Missing(config_file);
2034 }
2035 }
2036 }
2037 }
2038
2039
2040 void _process_env() {
2041 for(const Option_p &opt : options_) {
2042 if(opt->count() == 0 && !opt->envname_.empty()) {
2043 char *buffer = nullptr;
2044 std::string ename_string;
2045
2046 #ifdef _MSC_VER
2047
2048 std::size_t sz = 0;
2049 if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) {
2050 ename_string = std::string(buffer);
2051 free(buffer);
2052 }
2053 #else
2054
2055 buffer = std::getenv(opt->envname_.c_str());
2056 if(buffer != nullptr)
2057 ename_string = std::string(buffer);
2058 #endif
2059
2060 if(!ename_string.empty()) {
2061 opt->add_result(ename_string);
2062 }
2063 }
2064 }
2065
2066 for(App_p &sub : subcommands_) {
2067 if(sub->get_name().empty() || !sub->parse_complete_callback_)
2068 sub->_process_env();
2069 }
2070 }
2071
2072
2073 void _process_callbacks() {
2074
2075 for(App_p &sub : subcommands_) {
2076
2077 if(sub->get_name().empty() && sub->parse_complete_callback_) {
2078 if(sub->count_all() > 0) {
2079 sub->_process_callbacks();
2080 sub->run_callback();
2081 }
2082 }
2083 }
2084
2085 for(const Option_p &opt : options_) {
2086 if(opt->count() > 0 && !opt->get_callback_run()) {
2087 opt->run_callback();
2088 }
2089 }
2090 for(App_p &sub : subcommands_) {
2091 if(!sub->parse_complete_callback_) {
2092 sub->_process_callbacks();
2093 }
2094 }
2095 }
2096
2097
2098
2099
2100 void _process_help_flags(bool trigger_help = false, bool trigger_all_help = false) const {
2101 const Option *help_ptr = get_help_ptr();
2102 const Option *help_all_ptr = get_help_all_ptr();
2103
2104 if(help_ptr != nullptr && help_ptr->count() > 0)
2105 trigger_help = true;
2106 if(help_all_ptr != nullptr && help_all_ptr->count() > 0)
2107 trigger_all_help = true;
2108
2109
2110 if(!parsed_subcommands_.empty()) {
2111 for(const App *sub : parsed_subcommands_)
2112 sub->_process_help_flags(trigger_help, trigger_all_help);
2113
2114
2115 } else if(trigger_all_help) {
2116 throw CallForAllHelp();
2117 } else if(trigger_help) {
2118 throw CallForHelp();
2119 }
2120 }
2121
2122
2123 void _process_requirements() {
2124
2125 bool excluded{false};
2126 std::string excluder;
2127 for(auto &opt : exclude_options_) {
2128 if(opt->count() > 0) {
2129 excluded = true;
2130 excluder = opt->get_name();
2131 }
2132 }
2133 for(auto &subc : exclude_subcommands_) {
2134 if(subc->count_all() > 0) {
2135 excluded = true;
2136 excluder = subc->get_display_name();
2137 }
2138 }
2139 if(excluded) {
2140 if(count_all() > 0) {
2141 throw ExcludesError(get_display_name(), excluder);
2142 }
2143
2144 return;
2145 }
2146
2147
2148 bool missing_needed{false};
2149 std::string missing_need;
2150 for(auto &opt : need_options_) {
2151 if(opt->count() == 0) {
2152 missing_needed = true;
2153 missing_need = opt->get_name();
2154 }
2155 }
2156 for(auto &subc : need_subcommands_) {
2157 if(subc->count_all() == 0) {
2158 missing_needed = true;
2159 missing_need = subc->get_display_name();
2160 }
2161 }
2162 if(missing_needed) {
2163 if(count_all() > 0) {
2164 throw RequiresError(get_display_name(), missing_need);
2165 }
2166
2167 return;
2168 }
2169
2170 std::size_t used_options = 0;
2171 for(const Option_p &opt : options_) {
2172
2173 if(opt->count() != 0) {
2174 ++used_options;
2175 }
2176
2177 if(opt->get_required() && opt->count() == 0) {
2178 throw RequiredError(opt->get_name());
2179 }
2180
2181 for(const Option *opt_req : opt->needs_)
2182 if(opt->count() > 0 && opt_req->count() == 0)
2183 throw RequiresError(opt->get_name(), opt_req->get_name());
2184
2185 for(const Option *opt_ex : opt->excludes_)
2186 if(opt->count() > 0 && opt_ex->count() != 0)
2187 throw ExcludesError(opt->get_name(), opt_ex->get_name());
2188 }
2189
2190 if(require_subcommand_min_ > 0) {
2191 auto selected_subcommands = get_subcommands();
2192 if(require_subcommand_min_ > selected_subcommands.size())
2193 throw RequiredError::Subcommand(require_subcommand_min_);
2194 }
2195
2196
2197
2198
2199
2200 for(App_p &sub : subcommands_) {
2201 if(sub->disabled_)
2202 continue;
2203 if(sub->name_.empty() && sub->count_all() > 0) {
2204 ++used_options;
2205 }
2206 }
2207
2208 if(require_option_min_ > used_options || (require_option_max_ > 0 && require_option_max_ < used_options)) {
2209 auto option_list = detail::join(options_, [this](const Option_p &ptr) {
2210 if(ptr.get() == help_ptr_ || ptr.get() == help_all_ptr_) {
2211 return std::string{};
2212 }
2213 return ptr->get_name(false, true);
2214 });
2215
2216 auto subc_list = get_subcommands([](App *app) { return ((app->get_name().empty()) && (!app->disabled_)); });
2217 if(!subc_list.empty()) {
2218 option_list += "," + detail::join(subc_list, [](const App *app) { return app->get_display_name(); });
2219 }
2220 throw RequiredError::Option(require_option_min_, require_option_max_, used_options, option_list);
2221 }
2222
2223
2224 for(App_p &sub : subcommands_) {
2225 if(sub->disabled_)
2226 continue;
2227 if(sub->name_.empty() && sub->required_ == false) {
2228 if(sub->count_all() == 0) {
2229 if(require_option_min_ > 0 && require_option_min_ <= used_options) {
2230 continue;
2231
2232
2233 }
2234 if(require_option_max_ > 0 && used_options >= require_option_min_) {
2235 continue;
2236
2237
2238 }
2239 }
2240 }
2241 if(sub->count() > 0 || sub->name_.empty()) {
2242 sub->_process_requirements();
2243 }
2244
2245 if(sub->required_ && sub->count_all() == 0) {
2246 throw(CLI::RequiredError(sub->get_display_name()));
2247 }
2248 }
2249 }
2250
2251
2252 void _process() {
2253 CLI::FileError fe("ne");
2254 bool caught_error{false};
2255 try {
2256
2257
2258 _process_config_file();
2259
2260 _process_env();
2261 } catch(const CLI::FileError &fe2) {
2262 fe = fe2;
2263 caught_error = true;
2264 }
2265
2266
2267 _process_callbacks();
2268 _process_help_flags();
2269
2270 if(caught_error) {
2271 throw CLI::FileError(std::move(fe));
2272 }
2273
2274 _process_requirements();
2275 }
2276
2277
2278 void _process_extras() {
2279 if(!(allow_extras_ || prefix_command_)) {
2280 std::size_t num_left_over = remaining_size();
2281 if(num_left_over > 0) {
2282 throw ExtrasError(name_, remaining(false));
2283 }
2284 }
2285
2286 for(App_p &sub : subcommands_) {
2287 if(sub->count() > 0)
2288 sub->_process_extras();
2289 }
2290 }
2291
2292
2293
2294 void _process_extras(std::vector<std::string> &args) {
2295 if(!(allow_extras_ || prefix_command_)) {
2296 std::size_t num_left_over = remaining_size();
2297 if(num_left_over > 0) {
2298 args = remaining(false);
2299 throw ExtrasError(name_, args);
2300 }
2301 }
2302
2303 for(App_p &sub : subcommands_) {
2304 if(sub->count() > 0)
2305 sub->_process_extras(args);
2306 }
2307 }
2308
2309
2310 void increment_parsed() {
2311 ++parsed_;
2312 for(App_p &sub : subcommands_) {
2313 if(sub->get_name().empty())
2314 sub->increment_parsed();
2315 }
2316 }
2317
2318 void _parse(std::vector<std::string> &args) {
2319 increment_parsed();
2320 _trigger_pre_parse(args.size());
2321 bool positional_only = false;
2322
2323 while(!args.empty()) {
2324 if(!_parse_single(args, positional_only)) {
2325 break;
2326 }
2327 }
2328
2329 if(parent_ == nullptr) {
2330 _process();
2331
2332
2333 _process_extras(args);
2334
2335
2336 args = remaining_for_passthrough(false);
2337 } else if(parse_complete_callback_) {
2338 _process_env();
2339 _process_callbacks();
2340 _process_help_flags();
2341 _process_requirements();
2342 run_callback(false, true);
2343 }
2344 }
2345
2346
2347 void _parse(std::vector<std::string> &&args) {
2348
2349
2350 increment_parsed();
2351 _trigger_pre_parse(args.size());
2352 bool positional_only = false;
2353
2354 while(!args.empty()) {
2355 _parse_single(args, positional_only);
2356 }
2357 _process();
2358
2359
2360 _process_extras();
2361 }
2362
2363
2364
2365
2366
2367 void _parse_config(const std::vector<ConfigItem> &args) {
2368 for(const ConfigItem &item : args) {
2369 if(!_parse_single_config(item) && allow_config_extras_ == config_extras_mode::error)
2370 throw ConfigError::Extras(item.fullname());
2371 }
2372 }
2373
2374
2375 bool _parse_single_config(const ConfigItem &item, std::size_t level = 0) {
2376 if(level < item.parents.size()) {
2377 try {
2378 auto subcom = get_subcommand(item.parents.at(level));
2379 auto result = subcom->_parse_single_config(item, level + 1);
2380
2381 return result;
2382 } catch(const OptionNotFound &) {
2383 return false;
2384 }
2385 }
2386
2387 if(item.name == "++") {
2388 if(configurable_) {
2389 increment_parsed();
2390 _trigger_pre_parse(2);
2391 if(parent_ != nullptr) {
2392 parent_->parsed_subcommands_.push_back(this);
2393 }
2394 }
2395 return true;
2396 }
2397
2398 if(item.name == "--") {
2399 if(configurable_) {
2400 _process_callbacks();
2401 _process_requirements();
2402 run_callback();
2403 }
2404 return true;
2405 }
2406 Option *op = get_option_no_throw("--" + item.name);
2407 if(op == nullptr) {
2408 if(item.name.size() == 1) {
2409 op = get_option_no_throw("-" + item.name);
2410 }
2411 }
2412 if(op == nullptr) {
2413 op = get_option_no_throw(item.name);
2414 }
2415 if(op == nullptr) {
2416
2417 if(get_allow_config_extras() == config_extras_mode::capture)
2418
2419 missing_.emplace_back(detail::Classifier::NONE, item.fullname());
2420 return false;
2421 }
2422
2423 if(!op->get_configurable())
2424 throw ConfigError::NotConfigurable(item.fullname());
2425
2426 if(op->empty()) {
2427
2428 if(op->get_expected_min() == 0) {
2429 auto res = config_formatter_->to_flag(item);
2430 res = op->get_flag_value(item.name, res);
2431
2432 op->add_result(res);
2433
2434 } else {
2435 op->add_result(item.inputs);
2436 op->run_callback();
2437 }
2438 }
2439
2440 return true;
2441 }
2442
2443
2444
2445 bool _parse_single(std::vector<std::string> &args, bool &positional_only) {
2446 bool retval = true;
2447 detail::Classifier classifier = positional_only ? detail::Classifier::NONE : _recognize(args.back());
2448 switch(classifier) {
2449 case detail::Classifier::POSITIONAL_MARK:
2450 args.pop_back();
2451 positional_only = true;
2452 if((!_has_remaining_positionals()) && (parent_ != nullptr)) {
2453 retval = false;
2454 } else {
2455 _move_to_missing(classifier, "--");
2456 }
2457 break;
2458 case detail::Classifier::SUBCOMMAND_TERMINATOR:
2459
2460 args.pop_back();
2461 retval = false;
2462 break;
2463 case detail::Classifier::SUBCOMMAND:
2464 retval = _parse_subcommand(args);
2465 break;
2466 case detail::Classifier::LONG:
2467 case detail::Classifier::SHORT:
2468 case detail::Classifier::WINDOWS_STYLE:
2469
2470 _parse_arg(args, classifier);
2471 break;
2472 case detail::Classifier::NONE:
2473
2474 retval = _parse_positional(args, false);
2475 if(retval && positionals_at_end_) {
2476 positional_only = true;
2477 }
2478 break;
2479
2480 default:
2481 throw HorribleError("unrecognized classifier (you should not see this!)");
2482
2483 }
2484 return retval;
2485 }
2486
2487
2488 std::size_t _count_remaining_positionals(bool required_only = false) const {
2489 std::size_t retval = 0;
2490 for(const Option_p &opt : options_) {
2491 if(opt->get_positional() && (!required_only || opt->get_required())) {
2492 if(opt->get_items_expected_min() > 0 &&
2493 static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
2494 retval += static_cast<std::size_t>(opt->get_items_expected_min()) - opt->count();
2495 }
2496 }
2497 }
2498 return retval;
2499 }
2500
2501
2502 bool _has_remaining_positionals() const {
2503 for(const Option_p &opt : options_) {
2504 if(opt->get_positional() && ((static_cast<int>(opt->count()) < opt->get_items_expected_min()))) {
2505 return true;
2506 }
2507 }
2508
2509 return false;
2510 }
2511
2512
2513
2514
2515 bool _parse_positional(std::vector<std::string> &args, bool haltOnSubcommand) {
2516
2517 const std::string &positional = args.back();
2518
2519 if(positionals_at_end_) {
2520
2521 auto arg_rem = args.size();
2522 auto remreq = _count_remaining_positionals(true);
2523 if(arg_rem <= remreq) {
2524 for(const Option_p &opt : options_) {
2525 if(opt->get_positional() && opt->required_) {
2526 if(static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
2527 if(validate_positionals_) {
2528 std::string pos = positional;
2529 pos = opt->_validate(pos, 0);
2530 if(!pos.empty()) {
2531 continue;
2532 }
2533 }
2534 opt->add_result(positional);
2535 parse_order_.push_back(opt.get());
2536 args.pop_back();
2537 return true;
2538 }
2539 }
2540 }
2541 }
2542 }
2543 for(const Option_p &opt : options_) {
2544
2545 if(opt->get_positional() &&
2546 (static_cast<int>(opt->count()) < opt->get_items_expected_min() || opt->get_allow_extra_args())) {
2547 if(validate_positionals_) {
2548 std::string pos = positional;
2549 pos = opt->_validate(pos, 0);
2550 if(!pos.empty()) {
2551 continue;
2552 }
2553 }
2554 opt->add_result(positional);
2555 parse_order_.push_back(opt.get());
2556 args.pop_back();
2557 return true;
2558 }
2559 }
2560
2561 for(auto &subc : subcommands_) {
2562 if((subc->name_.empty()) && (!subc->disabled_)) {
2563 if(subc->_parse_positional(args, false)) {
2564 if(!subc->pre_parse_called_) {
2565 subc->_trigger_pre_parse(args.size());
2566 }
2567 return true;
2568 }
2569 }
2570 }
2571
2572 if(parent_ != nullptr && fallthrough_)
2573 return _get_fallthrough_parent()->_parse_positional(args, static_cast<bool>(parse_complete_callback_));
2574
2575
2576 auto com = _find_subcommand(args.back(), true, false);
2577 if(com != nullptr && (require_subcommand_max_ == 0 || require_subcommand_max_ > parsed_subcommands_.size())) {
2578 if(haltOnSubcommand) {
2579 return false;
2580 }
2581 args.pop_back();
2582 com->_parse(args);
2583 return true;
2584 }
2585
2586
2587 auto parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
2588 com = parent_app->_find_subcommand(args.back(), true, false);
2589 if(com != nullptr && (com->parent_->require_subcommand_max_ == 0 ||
2590 com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size())) {
2591 return false;
2592 }
2593
2594 if(positionals_at_end_) {
2595 throw CLI::ExtrasError(name_, args);
2596 }
2597
2598 if(parent_ != nullptr && name_.empty()) {
2599 return false;
2600 }
2601
2602 _move_to_missing(detail::Classifier::NONE, positional);
2603 args.pop_back();
2604 if(prefix_command_) {
2605 while(!args.empty()) {
2606 _move_to_missing(detail::Classifier::NONE, args.back());
2607 args.pop_back();
2608 }
2609 }
2610
2611 return true;
2612 }
2613
2614
2615
2616 App *_find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept {
2617 for(const App_p &com : subcommands_) {
2618 if(com->disabled_ && ignore_disabled)
2619 continue;
2620 if(com->get_name().empty()) {
2621 auto subc = com->_find_subcommand(subc_name, ignore_disabled, ignore_used);
2622 if(subc != nullptr) {
2623 return subc;
2624 }
2625 }
2626 if(com->check_name(subc_name)) {
2627 if((!*com) || !ignore_used)
2628 return com.get();
2629 }
2630 }
2631 return nullptr;
2632 }
2633
2634
2635
2636
2637
2638 bool _parse_subcommand(std::vector<std::string> &args) {
2639 if(_count_remaining_positionals( true) > 0) {
2640 _parse_positional(args, false);
2641 return true;
2642 }
2643 auto com = _find_subcommand(args.back(), true, true);
2644 if(com != nullptr) {
2645 args.pop_back();
2646 if(!com->silent_) {
2647 parsed_subcommands_.push_back(com);
2648 }
2649 com->_parse(args);
2650 auto parent_app = com->parent_;
2651 while(parent_app != this) {
2652 parent_app->_trigger_pre_parse(args.size());
2653 if(!com->silent_) {
2654 parent_app->parsed_subcommands_.push_back(com);
2655 }
2656 parent_app = parent_app->parent_;
2657 }
2658 return true;
2659 }
2660
2661 if(parent_ == nullptr)
2662 throw HorribleError("Subcommand " + args.back() + " missing");
2663 return false;
2664 }
2665
2666
2667
2668 bool _parse_arg(std::vector<std::string> &args, detail::Classifier current_type) {
2669
2670 std::string current = args.back();
2671
2672 std::string arg_name;
2673 std::string value;
2674 std::string rest;
2675
2676 switch(current_type) {
2677 case detail::Classifier::LONG:
2678 if(!detail::split_long(current, arg_name, value))
2679 throw HorribleError("Long parsed but missing (you should not see this):" + args.back());
2680 break;
2681 case detail::Classifier::SHORT:
2682 if(!detail::split_short(current, arg_name, rest))
2683 throw HorribleError("Short parsed but missing! You should not see this");
2684 break;
2685 case detail::Classifier::WINDOWS_STYLE:
2686 if(!detail::split_windows_style(current, arg_name, value))
2687 throw HorribleError("windows option parsed but missing! You should not see this");
2688 break;
2689 case detail::Classifier::SUBCOMMAND:
2690 case detail::Classifier::SUBCOMMAND_TERMINATOR:
2691 case detail::Classifier::POSITIONAL_MARK:
2692 case detail::Classifier::NONE:
2693 default:
2694 throw HorribleError("parsing got called with invalid option! You should not see this");
2695 }
2696
2697 auto op_ptr =
2698 std::find_if(std::begin(options_), std::end(options_), [arg_name, current_type](const Option_p &opt) {
2699 if(current_type == detail::Classifier::LONG)
2700 return opt->check_lname(arg_name);
2701 if(current_type == detail::Classifier::SHORT)
2702 return opt->check_sname(arg_name);
2703
2704 return opt->check_lname(arg_name) || opt->check_sname(arg_name);
2705 });
2706
2707
2708 if(op_ptr == std::end(options_)) {
2709 for(auto &subc : subcommands_) {
2710 if(subc->name_.empty() && !subc->disabled_) {
2711 if(subc->_parse_arg(args, current_type)) {
2712 if(!subc->pre_parse_called_) {
2713 subc->_trigger_pre_parse(args.size());
2714 }
2715 return true;
2716 }
2717 }
2718 }
2719
2720 if(parent_ != nullptr && fallthrough_)
2721 return _get_fallthrough_parent()->_parse_arg(args, current_type);
2722
2723 if(parent_ != nullptr && name_.empty()) {
2724 return false;
2725 }
2726
2727 args.pop_back();
2728 _move_to_missing(current_type, current);
2729 return true;
2730 }
2731
2732 args.pop_back();
2733
2734
2735 Option_p &op = *op_ptr;
2736
2737 if(op->get_inject_separator()) {
2738 if(!op->results().empty() && !op->results().back().empty()) {
2739 op->add_result(std::string{});
2740 }
2741 }
2742 int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min());
2743 int max_num = op->get_items_expected_max();
2744
2745
2746 if(max_num >= detail::expected_max_vector_size / 16 && !op->get_allow_extra_args()) {
2747 auto tmax = op->get_type_size_max();
2748 max_num = detail::checked_multiply(tmax, op->get_expected_min()) ? tmax : detail::expected_max_vector_size;
2749 }
2750
2751 int collected = 0;
2752 int result_count = 0;
2753
2754 if(max_num == 0) {
2755 auto res = op->get_flag_value(arg_name, value);
2756 op->add_result(res);
2757 parse_order_.push_back(op.get());
2758 } else if(!value.empty()) {
2759 op->add_result(value, result_count);
2760 parse_order_.push_back(op.get());
2761 collected += result_count;
2762
2763 } else if(!rest.empty()) {
2764 op->add_result(rest, result_count);
2765 parse_order_.push_back(op.get());
2766 rest = "";
2767 collected += result_count;
2768 }
2769
2770
2771 while(min_num > collected && !args.empty()) {
2772 std::string current_ = args.back();
2773 args.pop_back();
2774 op->add_result(current_, result_count);
2775 parse_order_.push_back(op.get());
2776 collected += result_count;
2777 }
2778
2779 if(min_num > collected) {
2780 throw ArgumentMismatch::TypedAtLeast(op->get_name(), min_num, op->get_type_name());
2781 }
2782
2783 if(max_num > collected || op->get_allow_extra_args()) {
2784 auto remreqpos = _count_remaining_positionals(true);
2785
2786 while((collected < max_num || op->get_allow_extra_args()) && !args.empty() &&
2787 _recognize(args.back(), false) == detail::Classifier::NONE) {
2788
2789 if(remreqpos >= args.size()) {
2790 break;
2791 }
2792
2793 op->add_result(args.back(), result_count);
2794 parse_order_.push_back(op.get());
2795 args.pop_back();
2796 collected += result_count;
2797 }
2798
2799
2800 if(!args.empty() && _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK)
2801 args.pop_back();
2802
2803 if(min_num == 0 && max_num > 0 && collected == 0) {
2804 auto res = op->get_flag_value(arg_name, std::string{});
2805 op->add_result(res);
2806 parse_order_.push_back(op.get());
2807 }
2808 }
2809
2810
2811 if(min_num > 0 && op->get_type_size_max() != min_num && (collected % op->get_type_size_max()) != 0) {
2812 op->add_result(std::string{});
2813 }
2814
2815 if(!rest.empty()) {
2816 rest = "-" + rest;
2817 args.push_back(rest);
2818 }
2819 return true;
2820 }
2821
2822
2823 void _trigger_pre_parse(std::size_t remaining_args) {
2824 if(!pre_parse_called_) {
2825 pre_parse_called_ = true;
2826 if(pre_parse_callback_) {
2827 pre_parse_callback_(remaining_args);
2828 }
2829 } else if(immediate_callback_) {
2830 if(!name_.empty()) {
2831 auto pcnt = parsed_;
2832 auto extras = std::move(missing_);
2833 clear();
2834 parsed_ = pcnt;
2835 pre_parse_called_ = true;
2836 missing_ = std::move(extras);
2837 }
2838 }
2839 }
2840
2841
2842 App *_get_fallthrough_parent() {
2843 if(parent_ == nullptr) {
2844 throw(HorribleError("No Valid parent"));
2845 }
2846 auto fallthrough_parent = parent_;
2847 while((fallthrough_parent->parent_ != nullptr) && (fallthrough_parent->get_name().empty())) {
2848 fallthrough_parent = fallthrough_parent->parent_;
2849 }
2850 return fallthrough_parent;
2851 }
2852
2853
2854 const std::string &_compare_subcommand_names(const App &subcom, const App &base) const {
2855 static const std::string estring;
2856 if(subcom.disabled_) {
2857 return estring;
2858 }
2859 for(auto &subc : base.subcommands_) {
2860 if(subc.get() != &subcom) {
2861 if(subc->disabled_) {
2862 continue;
2863 }
2864 if(!subcom.get_name().empty()) {
2865 if(subc->check_name(subcom.get_name())) {
2866 return subcom.get_name();
2867 }
2868 }
2869 if(!subc->get_name().empty()) {
2870 if(subcom.check_name(subc->get_name())) {
2871 return subc->get_name();
2872 }
2873 }
2874 for(const auto &les : subcom.aliases_) {
2875 if(subc->check_name(les)) {
2876 return les;
2877 }
2878 }
2879
2880 for(const auto &les : subc->aliases_) {
2881 if(subcom.check_name(les)) {
2882 return les;
2883 }
2884 }
2885
2886 if(subc->get_name().empty()) {
2887 auto &cmpres = _compare_subcommand_names(subcom, *subc);
2888 if(!cmpres.empty()) {
2889 return cmpres;
2890 }
2891 }
2892
2893 if(subcom.get_name().empty()) {
2894 auto &cmpres = _compare_subcommand_names(*subc, subcom);
2895 if(!cmpres.empty()) {
2896 return cmpres;
2897 }
2898 }
2899 }
2900 }
2901 return estring;
2902 }
2903
2904 void _move_to_missing(detail::Classifier val_type, const std::string &val) {
2905 if(allow_extras_ || subcommands_.empty()) {
2906 missing_.emplace_back(val_type, val);
2907 return;
2908 }
2909
2910 for(auto &subc : subcommands_) {
2911 if(subc->name_.empty() && subc->allow_extras_) {
2912 subc->missing_.emplace_back(val_type, val);
2913 return;
2914 }
2915 }
2916
2917 missing_.emplace_back(val_type, val);
2918 }
2919
2920 public:
2921
2922 void _move_option(Option *opt, App *app) {
2923 if(opt == nullptr) {
2924 throw OptionNotFound("the option is NULL");
2925 }
2926
2927 bool found = false;
2928 for(auto &subc : subcommands_) {
2929 if(app == subc.get()) {
2930 found = true;
2931 }
2932 }
2933 if(!found) {
2934 throw OptionNotFound("The Given app is not a subcommand");
2935 }
2936
2937 if((help_ptr_ == opt) || (help_all_ptr_ == opt))
2938 throw OptionAlreadyAdded("cannot move help options");
2939
2940 if(config_ptr_ == opt)
2941 throw OptionAlreadyAdded("cannot move config file options");
2942
2943 auto iterator =
2944 std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
2945 if(iterator != std::end(options_)) {
2946 const auto &opt_p = *iterator;
2947 if(std::find_if(std::begin(app->options_), std::end(app->options_), [&opt_p](const Option_p &v) {
2948 return (*v == *opt_p);
2949 }) == std::end(app->options_)) {
2950
2951 app->options_.push_back(std::move(*iterator));
2952 options_.erase(iterator);
2953 } else {
2954 throw OptionAlreadyAdded("option was not located: " + opt->get_name());
2955 }
2956 } else {
2957 throw OptionNotFound("could not locate the given Option");
2958 }
2959 }
2960 };
2961
2962
2963 class Option_group : public App {
2964 public:
2965 Option_group(std::string group_description, std::string group_name, App *parent)
2966 : App(std::move(group_description), "", parent) {
2967 group(group_name);
2968
2969 }
2970 using App::add_option;
2971
2972 Option *add_option(Option *opt) {
2973 if(get_parent() == nullptr) {
2974 throw OptionNotFound("Unable to locate the specified option");
2975 }
2976 get_parent()->_move_option(opt, this);
2977 return opt;
2978 }
2979
2980 void add_options(Option *opt) { add_option(opt); }
2981
2982 template <typename... Args> void add_options(Option *opt, Args... args) {
2983 add_option(opt);
2984 add_options(args...);
2985 }
2986 using App::add_subcommand;
2987
2988 App *add_subcommand(App *subcom) {
2989 App_p subc = subcom->get_parent()->get_subcommand_ptr(subcom);
2990 subc->get_parent()->remove_subcommand(subcom);
2991 add_subcommand(std::move(subc));
2992 return subcom;
2993 }
2994 };
2995
2996 inline void TriggerOn(App *trigger_app, App *app_to_enable) {
2997 app_to_enable->enabled_by_default(false);
2998 app_to_enable->disabled_by_default();
2999 trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(false); });
3000 }
3001
3002
3003 inline void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable) {
3004 for(auto &app : apps_to_enable) {
3005 app->enabled_by_default(false);
3006 app->disabled_by_default();
3007 }
3008
3009 trigger_app->preparse_callback([apps_to_enable](std::size_t) {
3010 for(auto &app : apps_to_enable) {
3011 app->disabled(false);
3012 }
3013 });
3014 }
3015
3016
3017 inline void TriggerOff(App *trigger_app, App *app_to_enable) {
3018 app_to_enable->disabled_by_default(false);
3019 app_to_enable->enabled_by_default();
3020 trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(); });
3021 }
3022
3023
3024 inline void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable) {
3025 for(auto &app : apps_to_enable) {
3026 app->disabled_by_default(false);
3027 app->enabled_by_default();
3028 }
3029
3030 trigger_app->preparse_callback([apps_to_enable](std::size_t) {
3031 for(auto &app : apps_to_enable) {
3032 app->disabled();
3033 }
3034 });
3035 }
3036
3037
3038 inline void deprecate_option(Option *opt, const std::string &replacement = "") {
3039 Validator deprecate_warning{[opt, replacement](std::string &) {
3040 std::cout << opt->get_name() << " is deprecated please use '" << replacement
3041 << "' instead\n";
3042 return std::string();
3043 },
3044 "DEPRECATED"};
3045 deprecate_warning.application_index(0);
3046 opt->check(deprecate_warning);
3047 if(!replacement.empty()) {
3048 opt->description(opt->get_description() + " DEPRECATED: please use '" + replacement + "' instead");
3049 }
3050 }
3051
3052
3053 inline void deprecate_option(App *app, const std::string &option_name, const std::string &replacement = "") {
3054 auto opt = app->get_option(option_name);
3055 deprecate_option(opt, replacement);
3056 }
3057
3058
3059 inline void deprecate_option(App &app, const std::string &option_name, const std::string &replacement = "") {
3060 auto opt = app.get_option(option_name);
3061 deprecate_option(opt, replacement);
3062 }
3063
3064
3065 inline void retire_option(App *app, Option *opt) {
3066 App temp;
3067 auto option_copy = temp.add_option(opt->get_name(false, true))
3068 ->type_size(opt->get_type_size_min(), opt->get_type_size_max())
3069 ->expected(opt->get_expected_min(), opt->get_expected_max())
3070 ->allow_extra_args(opt->get_allow_extra_args());
3071
3072 app->remove_option(opt);
3073 auto opt2 = app->add_option(option_copy->get_name(false, true), "option has been retired and has no effect")
3074 ->type_name("RETIRED")
3075 ->default_str("RETIRED")
3076 ->type_size(option_copy->get_type_size_min(), option_copy->get_type_size_max())
3077 ->expected(option_copy->get_expected_min(), option_copy->get_expected_max())
3078 ->allow_extra_args(option_copy->get_allow_extra_args());
3079
3080 Validator retired_warning{[opt2](std::string &) {
3081 std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
3082 return std::string();
3083 },
3084 ""};
3085 retired_warning.application_index(0);
3086 opt2->check(retired_warning);
3087 }
3088
3089
3090 inline void retire_option(App &app, Option *opt) { retire_option(&app, opt); }
3091
3092
3093 inline void retire_option(App *app, const std::string &option_name) {
3094
3095 auto opt = app->get_option_no_throw(option_name);
3096 if(opt != nullptr) {
3097 retire_option(app, opt);
3098 return;
3099 }
3100 auto opt2 = app->add_option(option_name, "option has been retired and has no effect")
3101 ->type_name("RETIRED")
3102 ->expected(0, 1)
3103 ->default_str("RETIRED");
3104 Validator retired_warning{[opt2](std::string &) {
3105 std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
3106 return std::string();
3107 },
3108 ""};
3109 retired_warning.application_index(0);
3110 opt2->check(retired_warning);
3111 }
3112
3113
3114 inline void retire_option(App &app, const std::string &option_name) { retire_option(&app, option_name); }
3115
3116 namespace FailureMessage {
3117
3118
3119 inline std::string simple(const App *app, const Error &e) {
3120 std::string header = std::string(e.what()) + "\n";
3121 std::vector<std::string> names;
3122
3123
3124 if(app->get_help_ptr() != nullptr)
3125 names.push_back(app->get_help_ptr()->get_name());
3126
3127 if(app->get_help_all_ptr() != nullptr)
3128 names.push_back(app->get_help_all_ptr()->get_name());
3129
3130
3131 if(!names.empty())
3132 header += "Run with " + detail::join(names, " or ") + " for more information.\n";
3133
3134 return header;
3135 }
3136
3137
3138 inline std::string help(const App *app, const Error &e) {
3139 std::string header = std::string("ERROR: ") + e.get_name() + ": " + e.what() + "\n";
3140 header += app->help();
3141 return header;
3142 }
3143
3144 }
3145
3146 namespace detail {
3147
3148 struct AppFriend {
3149 #ifdef CLI11_CPP14
3150
3151
3152 template <typename... Args> static decltype(auto) parse_arg(App *app, Args &&... args) {
3153 return app->_parse_arg(std::forward<Args>(args)...);
3154 }
3155
3156
3157 template <typename... Args> static decltype(auto) parse_subcommand(App *app, Args &&... args) {
3158 return app->_parse_subcommand(std::forward<Args>(args)...);
3159 }
3160 #else
3161
3162 template <typename... Args>
3163 static auto parse_arg(App *app, Args &&... args) ->
3164 typename std::result_of<decltype (&App::_parse_arg)(App, Args...)>::type {
3165 return app->_parse_arg(std::forward<Args>(args)...);
3166 }
3167
3168
3169 template <typename... Args>
3170 static auto parse_subcommand(App *app, Args &&... args) ->
3171 typename std::result_of<decltype (&App::_parse_subcommand)(App, Args...)>::type {
3172 return app->_parse_subcommand(std::forward<Args>(args)...);
3173 }
3174 #endif
3175
3176 static App *get_fallthrough_parent(App *app) { return app->_get_fallthrough_parent(); }
3177 };
3178 }
3179
3180
3181 }