File indexing completed on 2025-01-18 09:54:42
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009
0010 #include <CLI/App.hpp>
0011
0012
0013 #include <algorithm>
0014 #include <memory>
0015 #include <string>
0016 #include <utility>
0017 #include <vector>
0018
0019
0020 namespace CLI {
0021
0022
0023 CLI11_INLINE App::App(std::string app_description, std::string app_name, App *parent)
0024 : name_(std::move(app_name)), description_(std::move(app_description)), parent_(parent) {
0025
0026 if(parent_ != nullptr) {
0027 if(parent_->help_ptr_ != nullptr)
0028 set_help_flag(parent_->help_ptr_->get_name(false, true), parent_->help_ptr_->get_description());
0029 if(parent_->help_all_ptr_ != nullptr)
0030 set_help_all_flag(parent_->help_all_ptr_->get_name(false, true), parent_->help_all_ptr_->get_description());
0031
0032
0033 option_defaults_ = parent_->option_defaults_;
0034
0035
0036 failure_message_ = parent_->failure_message_;
0037 allow_extras_ = parent_->allow_extras_;
0038 allow_config_extras_ = parent_->allow_config_extras_;
0039 prefix_command_ = parent_->prefix_command_;
0040 immediate_callback_ = parent_->immediate_callback_;
0041 ignore_case_ = parent_->ignore_case_;
0042 ignore_underscore_ = parent_->ignore_underscore_;
0043 fallthrough_ = parent_->fallthrough_;
0044 validate_positionals_ = parent_->validate_positionals_;
0045 validate_optional_arguments_ = parent_->validate_optional_arguments_;
0046 configurable_ = parent_->configurable_;
0047 allow_windows_style_options_ = parent_->allow_windows_style_options_;
0048 group_ = parent_->group_;
0049 footer_ = parent_->footer_;
0050 formatter_ = parent_->formatter_;
0051 config_formatter_ = parent_->config_formatter_;
0052 require_subcommand_max_ = parent_->require_subcommand_max_;
0053 }
0054 }
0055
0056 CLI11_INLINE App *App::name(std::string app_name) {
0057
0058 if(parent_ != nullptr) {
0059 auto oname = name_;
0060 name_ = app_name;
0061 const auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
0062 if(!res.empty()) {
0063 name_ = oname;
0064 throw(OptionAlreadyAdded(app_name + " conflicts with existing subcommand names"));
0065 }
0066 } else {
0067 name_ = app_name;
0068 }
0069 has_automatic_name_ = false;
0070 return this;
0071 }
0072
0073 CLI11_INLINE App *App::alias(std::string app_name) {
0074 if(app_name.empty() || !detail::valid_alias_name_string(app_name)) {
0075 throw IncorrectConstruction("Aliases may not be empty or contain newlines or null characters");
0076 }
0077 if(parent_ != nullptr) {
0078 aliases_.push_back(app_name);
0079 const auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
0080 if(!res.empty()) {
0081 aliases_.pop_back();
0082 throw(OptionAlreadyAdded("alias already matches an existing subcommand: " + app_name));
0083 }
0084 } else {
0085 aliases_.push_back(app_name);
0086 }
0087
0088 return this;
0089 }
0090
0091 CLI11_INLINE App *App::immediate_callback(bool immediate) {
0092 immediate_callback_ = immediate;
0093 if(immediate_callback_) {
0094 if(final_callback_ && !(parse_complete_callback_)) {
0095 std::swap(final_callback_, parse_complete_callback_);
0096 }
0097 } else if(!(final_callback_) && parse_complete_callback_) {
0098 std::swap(final_callback_, parse_complete_callback_);
0099 }
0100 return this;
0101 }
0102
0103 CLI11_INLINE App *App::ignore_case(bool value) {
0104 if(value && !ignore_case_) {
0105 ignore_case_ = true;
0106 auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
0107 const auto &match = _compare_subcommand_names(*this, *p);
0108 if(!match.empty()) {
0109 ignore_case_ = false;
0110 throw OptionAlreadyAdded("ignore case would cause subcommand name conflicts: " + match);
0111 }
0112 }
0113 ignore_case_ = value;
0114 return this;
0115 }
0116
0117 CLI11_INLINE App *App::ignore_underscore(bool value) {
0118 if(value && !ignore_underscore_) {
0119 ignore_underscore_ = true;
0120 auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
0121 const auto &match = _compare_subcommand_names(*this, *p);
0122 if(!match.empty()) {
0123 ignore_underscore_ = false;
0124 throw OptionAlreadyAdded("ignore underscore would cause subcommand name conflicts: " + match);
0125 }
0126 }
0127 ignore_underscore_ = value;
0128 return this;
0129 }
0130
0131 CLI11_INLINE Option *App::add_option(std::string option_name,
0132 callback_t option_callback,
0133 std::string option_description,
0134 bool defaulted,
0135 std::function<std::string()> func) {
0136 Option myopt{option_name, option_description, option_callback, this};
0137
0138 if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) { return *v == myopt; }) ==
0139 std::end(options_)) {
0140 options_.emplace_back();
0141 Option_p &option = options_.back();
0142 option.reset(new Option(option_name, option_description, option_callback, this));
0143
0144
0145 option->default_function(func);
0146
0147
0148 if(defaulted)
0149 option->capture_default_str();
0150
0151
0152 option_defaults_.copy_to(option.get());
0153
0154
0155 if(!defaulted && option->get_always_capture_default())
0156 option->capture_default_str();
0157
0158 return option.get();
0159 }
0160
0161 for(auto &opt : options_) {
0162 const auto &matchname = opt->matching_name(myopt);
0163 if(!matchname.empty()) {
0164 throw(OptionAlreadyAdded("added option matched existing option name: " + matchname));
0165 }
0166 }
0167
0168 throw(OptionAlreadyAdded("added option matched existing option name"));
0169 }
0170
0171 CLI11_INLINE Option *App::set_help_flag(std::string flag_name, const std::string &help_description) {
0172
0173 if(help_ptr_ != nullptr) {
0174 remove_option(help_ptr_);
0175 help_ptr_ = nullptr;
0176 }
0177
0178
0179 if(!flag_name.empty()) {
0180 help_ptr_ = add_flag(flag_name, help_description);
0181 help_ptr_->configurable(false);
0182 }
0183
0184 return help_ptr_;
0185 }
0186
0187 CLI11_INLINE Option *App::set_help_all_flag(std::string help_name, const std::string &help_description) {
0188
0189 if(help_all_ptr_ != nullptr) {
0190 remove_option(help_all_ptr_);
0191 help_all_ptr_ = nullptr;
0192 }
0193
0194
0195 if(!help_name.empty()) {
0196 help_all_ptr_ = add_flag(help_name, help_description);
0197 help_all_ptr_->configurable(false);
0198 }
0199
0200 return help_all_ptr_;
0201 }
0202
0203 CLI11_INLINE Option *
0204 App::set_version_flag(std::string flag_name, const std::string &versionString, const std::string &version_help) {
0205
0206 if(version_ptr_ != nullptr) {
0207 remove_option(version_ptr_);
0208 version_ptr_ = nullptr;
0209 }
0210
0211
0212 if(!flag_name.empty()) {
0213 version_ptr_ = add_flag_callback(
0214 flag_name, [versionString]() { throw(CLI::CallForVersion(versionString, 0)); }, version_help);
0215 version_ptr_->configurable(false);
0216 }
0217
0218 return version_ptr_;
0219 }
0220
0221 CLI11_INLINE Option *
0222 App::set_version_flag(std::string flag_name, std::function<std::string()> vfunc, const std::string &version_help) {
0223 if(version_ptr_ != nullptr) {
0224 remove_option(version_ptr_);
0225 version_ptr_ = nullptr;
0226 }
0227
0228
0229 if(!flag_name.empty()) {
0230 version_ptr_ = add_flag_callback(
0231 flag_name, [vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); }, version_help);
0232 version_ptr_->configurable(false);
0233 }
0234
0235 return version_ptr_;
0236 }
0237
0238 CLI11_INLINE Option *App::_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description) {
0239 Option *opt = nullptr;
0240 if(detail::has_default_flag_values(flag_name)) {
0241
0242 auto flag_defaults = detail::get_default_flag_values(flag_name);
0243 detail::remove_default_flag_values(flag_name);
0244 opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
0245 for(const auto &fname : flag_defaults)
0246 opt->fnames_.push_back(fname.first);
0247 opt->default_flag_values_ = std::move(flag_defaults);
0248 } else {
0249 opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
0250 }
0251
0252 if(opt->get_positional()) {
0253 auto pos_name = opt->get_name(true);
0254 remove_option(opt);
0255 throw IncorrectConstruction::PositionalFlag(pos_name);
0256 }
0257 opt->multi_option_policy(MultiOptionPolicy::TakeLast);
0258 opt->expected(0);
0259 opt->required(false);
0260 return opt;
0261 }
0262
0263 CLI11_INLINE Option *App::add_flag_callback(std::string flag_name,
0264 std::function<void(void)> function,
0265 std::string flag_description) {
0266
0267 CLI::callback_t fun = [function](const CLI::results_t &res) {
0268 using CLI::detail::lexical_cast;
0269 bool trigger{false};
0270 auto result = lexical_cast(res[0], trigger);
0271 if(result && trigger) {
0272 function();
0273 }
0274 return result;
0275 };
0276 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
0277 }
0278
0279 CLI11_INLINE Option *
0280 App::add_flag_function(std::string flag_name,
0281 std::function<void(std::int64_t)> function,
0282 std::string flag_description) {
0283
0284 CLI::callback_t fun = [function](const CLI::results_t &res) {
0285 using CLI::detail::lexical_cast;
0286 std::int64_t flag_count{0};
0287 lexical_cast(res[0], flag_count);
0288 function(flag_count);
0289 return true;
0290 };
0291 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
0292 ->multi_option_policy(MultiOptionPolicy::Sum);
0293 }
0294
0295 CLI11_INLINE Option *App::set_config(std::string option_name,
0296 std::string default_filename,
0297 const std::string &help_message,
0298 bool config_required) {
0299
0300
0301 if(config_ptr_ != nullptr) {
0302 remove_option(config_ptr_);
0303 config_ptr_ = nullptr;
0304 }
0305
0306
0307 if(!option_name.empty()) {
0308 config_ptr_ = add_option(option_name, help_message);
0309 if(config_required) {
0310 config_ptr_->required();
0311 }
0312 if(!default_filename.empty()) {
0313 config_ptr_->default_str(std::move(default_filename));
0314 }
0315 config_ptr_->configurable(false);
0316 }
0317
0318 return config_ptr_;
0319 }
0320
0321 CLI11_INLINE bool App::remove_option(Option *opt) {
0322
0323 for(Option_p &op : options_) {
0324 op->remove_needs(opt);
0325 op->remove_excludes(opt);
0326 }
0327
0328 if(help_ptr_ == opt)
0329 help_ptr_ = nullptr;
0330 if(help_all_ptr_ == opt)
0331 help_all_ptr_ = nullptr;
0332
0333 auto iterator =
0334 std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
0335 if(iterator != std::end(options_)) {
0336 options_.erase(iterator);
0337 return true;
0338 }
0339 return false;
0340 }
0341
0342 CLI11_INLINE App *App::add_subcommand(std::string subcommand_name, std::string subcommand_description) {
0343 if(!subcommand_name.empty() && !detail::valid_name_string(subcommand_name)) {
0344 if(!detail::valid_first_char(subcommand_name[0])) {
0345 throw IncorrectConstruction("Subcommand name starts with invalid character, '!' and '-' are not allowed");
0346 }
0347 for(auto c : subcommand_name) {
0348 if(!detail::valid_later_char(c)) {
0349 throw IncorrectConstruction(std::string("Subcommand name contains invalid character ('") + c +
0350 "'), all characters are allowed except"
0351 "'=',':','{','}', and ' '");
0352 }
0353 }
0354 }
0355 CLI::App_p subcom = std::shared_ptr<App>(new App(std::move(subcommand_description), subcommand_name, this));
0356 return add_subcommand(std::move(subcom));
0357 }
0358
0359 CLI11_INLINE App *App::add_subcommand(CLI::App_p subcom) {
0360 if(!subcom)
0361 throw IncorrectConstruction("passed App is not valid");
0362 auto *ckapp = (name_.empty() && parent_ != nullptr) ? _get_fallthrough_parent() : this;
0363 const auto &mstrg = _compare_subcommand_names(*subcom, *ckapp);
0364 if(!mstrg.empty()) {
0365 throw(OptionAlreadyAdded("subcommand name or alias matches existing subcommand: " + mstrg));
0366 }
0367 subcom->parent_ = this;
0368 subcommands_.push_back(std::move(subcom));
0369 return subcommands_.back().get();
0370 }
0371
0372 CLI11_INLINE bool App::remove_subcommand(App *subcom) {
0373
0374 for(App_p &sub : subcommands_) {
0375 sub->remove_excludes(subcom);
0376 sub->remove_needs(subcom);
0377 }
0378
0379 auto iterator = std::find_if(
0380 std::begin(subcommands_), std::end(subcommands_), [subcom](const App_p &v) { return v.get() == subcom; });
0381 if(iterator != std::end(subcommands_)) {
0382 subcommands_.erase(iterator);
0383 return true;
0384 }
0385 return false;
0386 }
0387
0388 CLI11_INLINE App *App::get_subcommand(const App *subcom) const {
0389 if(subcom == nullptr)
0390 throw OptionNotFound("nullptr passed");
0391 for(const App_p &subcomptr : subcommands_)
0392 if(subcomptr.get() == subcom)
0393 return subcomptr.get();
0394 throw OptionNotFound(subcom->get_name());
0395 }
0396
0397 CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(std::string subcom) const {
0398 auto *subc = _find_subcommand(subcom, false, false);
0399 if(subc == nullptr)
0400 throw OptionNotFound(subcom);
0401 return subc;
0402 }
0403
0404 CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(int index) const {
0405 if(index >= 0) {
0406 auto uindex = static_cast<unsigned>(index);
0407 if(uindex < subcommands_.size())
0408 return subcommands_[uindex].get();
0409 }
0410 throw OptionNotFound(std::to_string(index));
0411 }
0412
0413 CLI11_INLINE CLI::App_p App::get_subcommand_ptr(App *subcom) const {
0414 if(subcom == nullptr)
0415 throw OptionNotFound("nullptr passed");
0416 for(const App_p &subcomptr : subcommands_)
0417 if(subcomptr.get() == subcom)
0418 return subcomptr;
0419 throw OptionNotFound(subcom->get_name());
0420 }
0421
0422 CLI11_NODISCARD CLI11_INLINE CLI::App_p App::get_subcommand_ptr(std::string subcom) const {
0423 for(const App_p &subcomptr : subcommands_)
0424 if(subcomptr->check_name(subcom))
0425 return subcomptr;
0426 throw OptionNotFound(subcom);
0427 }
0428
0429 CLI11_NODISCARD CLI11_INLINE CLI::App_p App::get_subcommand_ptr(int index) const {
0430 if(index >= 0) {
0431 auto uindex = static_cast<unsigned>(index);
0432 if(uindex < subcommands_.size())
0433 return subcommands_[uindex];
0434 }
0435 throw OptionNotFound(std::to_string(index));
0436 }
0437
0438 CLI11_NODISCARD CLI11_INLINE CLI::App *App::get_option_group(std::string group_name) const {
0439 for(const App_p &app : subcommands_) {
0440 if(app->name_.empty() && app->group_ == group_name) {
0441 return app.get();
0442 }
0443 }
0444 throw OptionNotFound(group_name);
0445 }
0446
0447 CLI11_NODISCARD CLI11_INLINE std::size_t App::count_all() const {
0448 std::size_t cnt{0};
0449 for(const auto &opt : options_) {
0450 cnt += opt->count();
0451 }
0452 for(const auto &sub : subcommands_) {
0453 cnt += sub->count_all();
0454 }
0455 if(!get_name().empty()) {
0456 cnt += parsed_;
0457 }
0458 return cnt;
0459 }
0460
0461 CLI11_INLINE void App::clear() {
0462
0463 parsed_ = 0;
0464 pre_parse_called_ = false;
0465
0466 missing_.clear();
0467 parsed_subcommands_.clear();
0468 for(const Option_p &opt : options_) {
0469 opt->clear();
0470 }
0471 for(const App_p &subc : subcommands_) {
0472 subc->clear();
0473 }
0474 }
0475
0476 CLI11_INLINE void App::parse(int argc, const char *const *argv) {
0477
0478 if(name_.empty() || has_automatic_name_) {
0479 has_automatic_name_ = true;
0480 name_ = argv[0];
0481 }
0482
0483 std::vector<std::string> args;
0484 args.reserve(static_cast<std::size_t>(argc) - 1U);
0485 for(auto i = static_cast<std::size_t>(argc) - 1U; i > 0U; --i)
0486 args.emplace_back(argv[i]);
0487 parse(std::move(args));
0488 }
0489
0490 CLI11_INLINE void App::parse(std::string commandline, bool program_name_included) {
0491
0492 if(program_name_included) {
0493 auto nstr = detail::split_program_name(commandline);
0494 if((name_.empty()) || (has_automatic_name_)) {
0495 has_automatic_name_ = true;
0496 name_ = nstr.first;
0497 }
0498 commandline = std::move(nstr.second);
0499 } else {
0500 detail::trim(commandline);
0501 }
0502
0503 if(!commandline.empty()) {
0504 commandline = detail::find_and_modify(commandline, "=", detail::escape_detect);
0505 if(allow_windows_style_options_)
0506 commandline = detail::find_and_modify(commandline, ":", detail::escape_detect);
0507 }
0508
0509 auto args = detail::split_up(std::move(commandline));
0510
0511 args.erase(std::remove(args.begin(), args.end(), std::string{}), args.end());
0512 std::reverse(args.begin(), args.end());
0513
0514 parse(std::move(args));
0515 }
0516
0517 CLI11_INLINE void App::parse(std::vector<std::string> &args) {
0518
0519 if(parsed_ > 0)
0520 clear();
0521
0522
0523
0524
0525 parsed_ = 1;
0526 _validate();
0527 _configure();
0528
0529 parent_ = nullptr;
0530 parsed_ = 0;
0531
0532 _parse(args);
0533 run_callback();
0534 }
0535
0536 CLI11_INLINE void App::parse(std::vector<std::string> &&args) {
0537
0538 if(parsed_ > 0)
0539 clear();
0540
0541
0542
0543
0544 parsed_ = 1;
0545 _validate();
0546 _configure();
0547
0548 parent_ = nullptr;
0549 parsed_ = 0;
0550
0551 _parse(std::move(args));
0552 run_callback();
0553 }
0554
0555 CLI11_INLINE void App::parse_from_stream(std::istream &input) {
0556 if(parsed_ == 0) {
0557 _validate();
0558 _configure();
0559
0560 }
0561
0562 _parse_stream(input);
0563 run_callback();
0564 }
0565
0566 CLI11_INLINE int App::exit(const Error &e, std::ostream &out, std::ostream &err) const {
0567
0568
0569 if(e.get_name() == "RuntimeError")
0570 return e.get_exit_code();
0571
0572 if(e.get_name() == "CallForHelp") {
0573 out << help();
0574 return e.get_exit_code();
0575 }
0576
0577 if(e.get_name() == "CallForAllHelp") {
0578 out << help("", AppFormatMode::All);
0579 return e.get_exit_code();
0580 }
0581
0582 if(e.get_name() == "CallForVersion") {
0583 out << e.what() << std::endl;
0584 return e.get_exit_code();
0585 }
0586
0587 if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {
0588 if(failure_message_)
0589 err << failure_message_(this, e) << std::flush;
0590 }
0591
0592 return e.get_exit_code();
0593 }
0594
0595 CLI11_INLINE std::vector<const App *> App::get_subcommands(const std::function<bool(const App *)> &filter) const {
0596 std::vector<const App *> subcomms(subcommands_.size());
0597 std::transform(
0598 std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) { return v.get(); });
0599
0600 if(filter) {
0601 subcomms.erase(std::remove_if(std::begin(subcomms),
0602 std::end(subcomms),
0603 [&filter](const App *app) { return !filter(app); }),
0604 std::end(subcomms));
0605 }
0606
0607 return subcomms;
0608 }
0609
0610 CLI11_INLINE std::vector<App *> App::get_subcommands(const std::function<bool(App *)> &filter) {
0611 std::vector<App *> subcomms(subcommands_.size());
0612 std::transform(
0613 std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) { return v.get(); });
0614
0615 if(filter) {
0616 subcomms.erase(
0617 std::remove_if(std::begin(subcomms), std::end(subcomms), [&filter](App *app) { return !filter(app); }),
0618 std::end(subcomms));
0619 }
0620
0621 return subcomms;
0622 }
0623
0624 CLI11_INLINE bool App::remove_excludes(Option *opt) {
0625 auto iterator = std::find(std::begin(exclude_options_), std::end(exclude_options_), opt);
0626 if(iterator == std::end(exclude_options_)) {
0627 return false;
0628 }
0629 exclude_options_.erase(iterator);
0630 return true;
0631 }
0632
0633 CLI11_INLINE bool App::remove_excludes(App *app) {
0634 auto iterator = std::find(std::begin(exclude_subcommands_), std::end(exclude_subcommands_), app);
0635 if(iterator == std::end(exclude_subcommands_)) {
0636 return false;
0637 }
0638 auto *other_app = *iterator;
0639 exclude_subcommands_.erase(iterator);
0640 other_app->remove_excludes(this);
0641 return true;
0642 }
0643
0644 CLI11_INLINE bool App::remove_needs(Option *opt) {
0645 auto iterator = std::find(std::begin(need_options_), std::end(need_options_), opt);
0646 if(iterator == std::end(need_options_)) {
0647 return false;
0648 }
0649 need_options_.erase(iterator);
0650 return true;
0651 }
0652
0653 CLI11_INLINE bool App::remove_needs(App *app) {
0654 auto iterator = std::find(std::begin(need_subcommands_), std::end(need_subcommands_), app);
0655 if(iterator == std::end(need_subcommands_)) {
0656 return false;
0657 }
0658 need_subcommands_.erase(iterator);
0659 return true;
0660 }
0661
0662 CLI11_NODISCARD CLI11_INLINE std::string App::help(std::string prev, AppFormatMode mode) const {
0663 if(prev.empty())
0664 prev = get_name();
0665 else
0666 prev += " " + get_name();
0667
0668
0669 auto selected_subcommands = get_subcommands();
0670 if(!selected_subcommands.empty()) {
0671 return selected_subcommands.back()->help(prev, mode);
0672 }
0673 return formatter_->make_help(this, prev, mode);
0674 }
0675
0676 CLI11_NODISCARD CLI11_INLINE std::string App::version() const {
0677 std::string val;
0678 if(version_ptr_ != nullptr) {
0679 auto rv = version_ptr_->results();
0680 version_ptr_->clear();
0681 version_ptr_->add_result("true");
0682 try {
0683 version_ptr_->run_callback();
0684 } catch(const CLI::CallForVersion &cfv) {
0685 val = cfv.what();
0686 }
0687 version_ptr_->clear();
0688 version_ptr_->add_result(rv);
0689 }
0690 return val;
0691 }
0692
0693 CLI11_INLINE std::vector<const Option *> App::get_options(const std::function<bool(const Option *)> filter) const {
0694 std::vector<const Option *> options(options_.size());
0695 std::transform(
0696 std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) { return val.get(); });
0697
0698 if(filter) {
0699 options.erase(std::remove_if(std::begin(options),
0700 std::end(options),
0701 [&filter](const Option *opt) { return !filter(opt); }),
0702 std::end(options));
0703 }
0704
0705 return options;
0706 }
0707
0708 CLI11_INLINE std::vector<Option *> App::get_options(const std::function<bool(Option *)> filter) {
0709 std::vector<Option *> options(options_.size());
0710 std::transform(
0711 std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) { return val.get(); });
0712
0713 if(filter) {
0714 options.erase(
0715 std::remove_if(std::begin(options), std::end(options), [&filter](Option *opt) { return !filter(opt); }),
0716 std::end(options));
0717 }
0718
0719 return options;
0720 }
0721
0722 CLI11_INLINE Option *App::get_option_no_throw(std::string option_name) noexcept {
0723 for(Option_p &opt : options_) {
0724 if(opt->check_name(option_name)) {
0725 return opt.get();
0726 }
0727 }
0728 for(auto &subc : subcommands_) {
0729
0730 if(subc->get_name().empty()) {
0731 auto *opt = subc->get_option_no_throw(option_name);
0732 if(opt != nullptr) {
0733 return opt;
0734 }
0735 }
0736 }
0737 return nullptr;
0738 }
0739
0740 CLI11_NODISCARD CLI11_INLINE const Option *App::get_option_no_throw(std::string option_name) const noexcept {
0741 for(const Option_p &opt : options_) {
0742 if(opt->check_name(option_name)) {
0743 return opt.get();
0744 }
0745 }
0746 for(const auto &subc : subcommands_) {
0747
0748 if(subc->get_name().empty()) {
0749 auto *opt = subc->get_option_no_throw(option_name);
0750 if(opt != nullptr) {
0751 return opt;
0752 }
0753 }
0754 }
0755 return nullptr;
0756 }
0757
0758 CLI11_NODISCARD CLI11_INLINE std::string App::get_display_name(bool with_aliases) const {
0759 if(name_.empty()) {
0760 return std::string("[Option Group: ") + get_group() + "]";
0761 }
0762 if(aliases_.empty() || !with_aliases) {
0763 return name_;
0764 }
0765 std::string dispname = name_;
0766 for(const auto &lalias : aliases_) {
0767 dispname.push_back(',');
0768 dispname.push_back(' ');
0769 dispname.append(lalias);
0770 }
0771 return dispname;
0772 }
0773
0774 CLI11_NODISCARD CLI11_INLINE bool App::check_name(std::string name_to_check) const {
0775 std::string local_name = name_;
0776 if(ignore_underscore_) {
0777 local_name = detail::remove_underscore(name_);
0778 name_to_check = detail::remove_underscore(name_to_check);
0779 }
0780 if(ignore_case_) {
0781 local_name = detail::to_lower(name_);
0782 name_to_check = detail::to_lower(name_to_check);
0783 }
0784
0785 if(local_name == name_to_check) {
0786 return true;
0787 }
0788 for(auto les : aliases_) {
0789 if(ignore_underscore_) {
0790 les = detail::remove_underscore(les);
0791 }
0792 if(ignore_case_) {
0793 les = detail::to_lower(les);
0794 }
0795 if(les == name_to_check) {
0796 return true;
0797 }
0798 }
0799 return false;
0800 }
0801
0802 CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::get_groups() const {
0803 std::vector<std::string> groups;
0804
0805 for(const Option_p &opt : options_) {
0806
0807 if(std::find(groups.begin(), groups.end(), opt->get_group()) == groups.end()) {
0808 groups.push_back(opt->get_group());
0809 }
0810 }
0811
0812 return groups;
0813 }
0814
0815 CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::remaining(bool recurse) const {
0816 std::vector<std::string> miss_list;
0817 for(const std::pair<detail::Classifier, std::string> &miss : missing_) {
0818 miss_list.push_back(std::get<1>(miss));
0819 }
0820
0821 if(recurse) {
0822 if(!allow_extras_) {
0823 for(const auto &sub : subcommands_) {
0824 if(sub->name_.empty() && !sub->missing_.empty()) {
0825 for(const std::pair<detail::Classifier, std::string> &miss : sub->missing_) {
0826 miss_list.push_back(std::get<1>(miss));
0827 }
0828 }
0829 }
0830 }
0831
0832
0833 for(const App *sub : parsed_subcommands_) {
0834 std::vector<std::string> output = sub->remaining(recurse);
0835 std::copy(std::begin(output), std::end(output), std::back_inserter(miss_list));
0836 }
0837 }
0838 return miss_list;
0839 }
0840
0841 CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::remaining_for_passthrough(bool recurse) const {
0842 std::vector<std::string> miss_list = remaining(recurse);
0843 std::reverse(std::begin(miss_list), std::end(miss_list));
0844 return miss_list;
0845 }
0846
0847 CLI11_NODISCARD CLI11_INLINE std::size_t App::remaining_size(bool recurse) const {
0848 auto remaining_options = static_cast<std::size_t>(std::count_if(
0849 std::begin(missing_), std::end(missing_), [](const std::pair<detail::Classifier, std::string> &val) {
0850 return val.first != detail::Classifier::POSITIONAL_MARK;
0851 }));
0852
0853 if(recurse) {
0854 for(const App_p &sub : subcommands_) {
0855 remaining_options += sub->remaining_size(recurse);
0856 }
0857 }
0858 return remaining_options;
0859 }
0860
0861 CLI11_INLINE void App::_validate() const {
0862
0863 auto pcount = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
0864 return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional();
0865 });
0866 if(pcount > 1) {
0867 auto pcount_req = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
0868 return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional() &&
0869 opt->get_required();
0870 });
0871 if(pcount - pcount_req > 1) {
0872 throw InvalidError(name_);
0873 }
0874 }
0875
0876 std::size_t nameless_subs{0};
0877 for(const App_p &app : subcommands_) {
0878 app->_validate();
0879 if(app->get_name().empty())
0880 ++nameless_subs;
0881 }
0882
0883 if(require_option_min_ > 0) {
0884 if(require_option_max_ > 0) {
0885 if(require_option_max_ < require_option_min_) {
0886 throw(InvalidError("Required min options greater than required max options", ExitCodes::InvalidError));
0887 }
0888 }
0889 if(require_option_min_ > (options_.size() + nameless_subs)) {
0890 throw(
0891 InvalidError("Required min options greater than number of available options", ExitCodes::InvalidError));
0892 }
0893 }
0894 }
0895
0896 CLI11_INLINE void App::_configure() {
0897 if(default_startup == startup_mode::enabled) {
0898 disabled_ = false;
0899 } else if(default_startup == startup_mode::disabled) {
0900 disabled_ = true;
0901 }
0902 for(const App_p &app : subcommands_) {
0903 if(app->has_automatic_name_) {
0904 app->name_.clear();
0905 }
0906 if(app->name_.empty()) {
0907 app->fallthrough_ = false;
0908 app->prefix_command_ = false;
0909 }
0910
0911 app->parent_ = this;
0912 app->_configure();
0913 }
0914 }
0915
0916 CLI11_INLINE void App::run_callback(bool final_mode, bool suppress_final_callback) {
0917 pre_callback();
0918
0919 if(!final_mode && parse_complete_callback_) {
0920 parse_complete_callback_();
0921 }
0922
0923 for(App *subc : get_subcommands()) {
0924 if(subc->parent_ == this) {
0925 subc->run_callback(true, suppress_final_callback);
0926 }
0927 }
0928
0929 for(auto &subc : subcommands_) {
0930 if(subc->name_.empty() && subc->count_all() > 0) {
0931 subc->run_callback(true, suppress_final_callback);
0932 }
0933 }
0934
0935
0936 if(final_callback_ && (parsed_ > 0) && (!suppress_final_callback)) {
0937 if(!name_.empty() || count_all() > 0 || parent_ == nullptr) {
0938 final_callback_();
0939 }
0940 }
0941 }
0942
0943 CLI11_NODISCARD CLI11_INLINE bool App::_valid_subcommand(const std::string ¤t, bool ignore_used) const {
0944
0945 if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {
0946 return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
0947 }
0948 auto *com = _find_subcommand(current, true, ignore_used);
0949 if(com != nullptr) {
0950 return true;
0951 }
0952
0953 return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
0954 }
0955
0956 CLI11_NODISCARD CLI11_INLINE detail::Classifier App::_recognize(const std::string ¤t,
0957 bool ignore_used_subcommands) const {
0958 std::string dummy1, dummy2;
0959
0960 if(current == "--")
0961 return detail::Classifier::POSITIONAL_MARK;
0962 if(_valid_subcommand(current, ignore_used_subcommands))
0963 return detail::Classifier::SUBCOMMAND;
0964 if(detail::split_long(current, dummy1, dummy2))
0965 return detail::Classifier::LONG;
0966 if(detail::split_short(current, dummy1, dummy2)) {
0967 if(dummy1[0] >= '0' && dummy1[0] <= '9') {
0968 if(get_option_no_throw(std::string{'-', dummy1[0]}) == nullptr) {
0969 return detail::Classifier::NONE;
0970 }
0971 }
0972 return detail::Classifier::SHORT;
0973 }
0974 if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
0975 return detail::Classifier::WINDOWS_STYLE;
0976 if((current == "++") && !name_.empty() && parent_ != nullptr)
0977 return detail::Classifier::SUBCOMMAND_TERMINATOR;
0978 return detail::Classifier::NONE;
0979 }
0980
0981 CLI11_INLINE void App::_process_config_file() {
0982 if(config_ptr_ != nullptr) {
0983 bool config_required = config_ptr_->get_required();
0984 auto file_given = config_ptr_->count() > 0;
0985 auto config_files = config_ptr_->as<std::vector<std::string>>();
0986 if(config_files.empty() || config_files.front().empty()) {
0987 if(config_required) {
0988 throw FileError::Missing("no specified config file");
0989 }
0990 return;
0991 }
0992 for(auto rit = config_files.rbegin(); rit != config_files.rend(); ++rit) {
0993 const auto &config_file = *rit;
0994 auto path_result = detail::check_path(config_file.c_str());
0995 if(path_result == detail::path_type::file) {
0996 try {
0997 std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
0998 _parse_config(values);
0999 if(!file_given) {
1000 config_ptr_->add_result(config_file);
1001 }
1002 } catch(const FileError &) {
1003 if(config_required || file_given)
1004 throw;
1005 }
1006 } else if(config_required || file_given) {
1007 throw FileError::Missing(config_file);
1008 }
1009 }
1010 }
1011 }
1012
1013 CLI11_INLINE void App::_process_env() {
1014 for(const Option_p &opt : options_) {
1015 if(opt->count() == 0 && !opt->envname_.empty()) {
1016 char *buffer = nullptr;
1017 std::string ename_string;
1018
1019 #ifdef _MSC_VER
1020
1021 std::size_t sz = 0;
1022 if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) {
1023 ename_string = std::string(buffer);
1024 free(buffer);
1025 }
1026 #else
1027
1028 buffer = std::getenv(opt->envname_.c_str());
1029 if(buffer != nullptr)
1030 ename_string = std::string(buffer);
1031 #endif
1032
1033 if(!ename_string.empty()) {
1034 opt->add_result(ename_string);
1035 }
1036 }
1037 }
1038
1039 for(App_p &sub : subcommands_) {
1040 if(sub->get_name().empty() || !sub->parse_complete_callback_)
1041 sub->_process_env();
1042 }
1043 }
1044
1045 CLI11_INLINE void App::_process_callbacks() {
1046
1047 for(App_p &sub : subcommands_) {
1048
1049 if(sub->get_name().empty() && sub->parse_complete_callback_) {
1050 if(sub->count_all() > 0) {
1051 sub->_process_callbacks();
1052 sub->run_callback();
1053 }
1054 }
1055 }
1056
1057 for(const Option_p &opt : options_) {
1058 if((*opt) && !opt->get_callback_run()) {
1059 opt->run_callback();
1060 }
1061 }
1062 for(App_p &sub : subcommands_) {
1063 if(!sub->parse_complete_callback_) {
1064 sub->_process_callbacks();
1065 }
1066 }
1067 }
1068
1069 CLI11_INLINE void App::_process_help_flags(bool trigger_help, bool trigger_all_help) const {
1070 const Option *help_ptr = get_help_ptr();
1071 const Option *help_all_ptr = get_help_all_ptr();
1072
1073 if(help_ptr != nullptr && help_ptr->count() > 0)
1074 trigger_help = true;
1075 if(help_all_ptr != nullptr && help_all_ptr->count() > 0)
1076 trigger_all_help = true;
1077
1078
1079 if(!parsed_subcommands_.empty()) {
1080 for(const App *sub : parsed_subcommands_)
1081 sub->_process_help_flags(trigger_help, trigger_all_help);
1082
1083
1084 } else if(trigger_all_help) {
1085 throw CallForAllHelp();
1086 } else if(trigger_help) {
1087 throw CallForHelp();
1088 }
1089 }
1090
1091 CLI11_INLINE void App::_process_requirements() {
1092
1093 bool excluded{false};
1094 std::string excluder;
1095 for(const auto &opt : exclude_options_) {
1096 if(opt->count() > 0) {
1097 excluded = true;
1098 excluder = opt->get_name();
1099 }
1100 }
1101 for(const auto &subc : exclude_subcommands_) {
1102 if(subc->count_all() > 0) {
1103 excluded = true;
1104 excluder = subc->get_display_name();
1105 }
1106 }
1107 if(excluded) {
1108 if(count_all() > 0) {
1109 throw ExcludesError(get_display_name(), excluder);
1110 }
1111
1112 return;
1113 }
1114
1115
1116 bool missing_needed{false};
1117 std::string missing_need;
1118 for(const auto &opt : need_options_) {
1119 if(opt->count() == 0) {
1120 missing_needed = true;
1121 missing_need = opt->get_name();
1122 }
1123 }
1124 for(const auto &subc : need_subcommands_) {
1125 if(subc->count_all() == 0) {
1126 missing_needed = true;
1127 missing_need = subc->get_display_name();
1128 }
1129 }
1130 if(missing_needed) {
1131 if(count_all() > 0) {
1132 throw RequiresError(get_display_name(), missing_need);
1133 }
1134
1135 return;
1136 }
1137
1138 std::size_t used_options = 0;
1139 for(const Option_p &opt : options_) {
1140
1141 if(opt->count() != 0) {
1142 ++used_options;
1143 }
1144
1145 if(opt->get_required() && opt->count() == 0) {
1146 throw RequiredError(opt->get_name());
1147 }
1148
1149 for(const Option *opt_req : opt->needs_)
1150 if(opt->count() > 0 && opt_req->count() == 0)
1151 throw RequiresError(opt->get_name(), opt_req->get_name());
1152
1153 for(const Option *opt_ex : opt->excludes_)
1154 if(opt->count() > 0 && opt_ex->count() != 0)
1155 throw ExcludesError(opt->get_name(), opt_ex->get_name());
1156 }
1157
1158 if(require_subcommand_min_ > 0) {
1159 auto selected_subcommands = get_subcommands();
1160 if(require_subcommand_min_ > selected_subcommands.size())
1161 throw RequiredError::Subcommand(require_subcommand_min_);
1162 }
1163
1164
1165
1166
1167
1168 for(App_p &sub : subcommands_) {
1169 if(sub->disabled_)
1170 continue;
1171 if(sub->name_.empty() && sub->count_all() > 0) {
1172 ++used_options;
1173 }
1174 }
1175
1176 if(require_option_min_ > used_options || (require_option_max_ > 0 && require_option_max_ < used_options)) {
1177 auto option_list = detail::join(options_, [this](const Option_p &ptr) {
1178 if(ptr.get() == help_ptr_ || ptr.get() == help_all_ptr_) {
1179 return std::string{};
1180 }
1181 return ptr->get_name(false, true);
1182 });
1183
1184 auto subc_list = get_subcommands([](App *app) { return ((app->get_name().empty()) && (!app->disabled_)); });
1185 if(!subc_list.empty()) {
1186 option_list += "," + detail::join(subc_list, [](const App *app) { return app->get_display_name(); });
1187 }
1188 throw RequiredError::Option(require_option_min_, require_option_max_, used_options, option_list);
1189 }
1190
1191
1192 for(App_p &sub : subcommands_) {
1193 if(sub->disabled_)
1194 continue;
1195 if(sub->name_.empty() && sub->required_ == false) {
1196 if(sub->count_all() == 0) {
1197 if(require_option_min_ > 0 && require_option_min_ <= used_options) {
1198 continue;
1199
1200
1201 }
1202 if(require_option_max_ > 0 && used_options >= require_option_min_) {
1203 continue;
1204
1205
1206 }
1207 }
1208 }
1209 if(sub->count() > 0 || sub->name_.empty()) {
1210 sub->_process_requirements();
1211 }
1212
1213 if(sub->required_ && sub->count_all() == 0) {
1214 throw(CLI::RequiredError(sub->get_display_name()));
1215 }
1216 }
1217 }
1218
1219 CLI11_INLINE void App::_process() {
1220 try {
1221
1222
1223 _process_config_file();
1224
1225
1226 _process_env();
1227 } catch(const CLI::FileError &) {
1228
1229
1230 _process_callbacks();
1231 _process_help_flags();
1232 throw;
1233 }
1234
1235 _process_callbacks();
1236 _process_help_flags();
1237
1238 _process_requirements();
1239 }
1240
1241 CLI11_INLINE void App::_process_extras() {
1242 if(!(allow_extras_ || prefix_command_)) {
1243 std::size_t num_left_over = remaining_size();
1244 if(num_left_over > 0) {
1245 throw ExtrasError(name_, remaining(false));
1246 }
1247 }
1248
1249 for(App_p &sub : subcommands_) {
1250 if(sub->count() > 0)
1251 sub->_process_extras();
1252 }
1253 }
1254
1255 CLI11_INLINE void App::_process_extras(std::vector<std::string> &args) {
1256 if(!(allow_extras_ || prefix_command_)) {
1257 std::size_t num_left_over = remaining_size();
1258 if(num_left_over > 0) {
1259 args = remaining(false);
1260 throw ExtrasError(name_, args);
1261 }
1262 }
1263
1264 for(App_p &sub : subcommands_) {
1265 if(sub->count() > 0)
1266 sub->_process_extras(args);
1267 }
1268 }
1269
1270 CLI11_INLINE void App::increment_parsed() {
1271 ++parsed_;
1272 for(App_p &sub : subcommands_) {
1273 if(sub->get_name().empty())
1274 sub->increment_parsed();
1275 }
1276 }
1277
1278 CLI11_INLINE void App::_parse(std::vector<std::string> &args) {
1279 increment_parsed();
1280 _trigger_pre_parse(args.size());
1281 bool positional_only = false;
1282
1283 while(!args.empty()) {
1284 if(!_parse_single(args, positional_only)) {
1285 break;
1286 }
1287 }
1288
1289 if(parent_ == nullptr) {
1290 _process();
1291
1292
1293 _process_extras(args);
1294
1295
1296 args = remaining_for_passthrough(false);
1297 } else if(parse_complete_callback_) {
1298 _process_env();
1299 _process_callbacks();
1300 _process_help_flags();
1301 _process_requirements();
1302 run_callback(false, true);
1303 }
1304 }
1305
1306 CLI11_INLINE void App::_parse(std::vector<std::string> &&args) {
1307
1308
1309 increment_parsed();
1310 _trigger_pre_parse(args.size());
1311 bool positional_only = false;
1312
1313 while(!args.empty()) {
1314 _parse_single(args, positional_only);
1315 }
1316 _process();
1317
1318
1319 _process_extras();
1320 }
1321
1322 CLI11_INLINE void App::_parse_stream(std::istream &input) {
1323 auto values = config_formatter_->from_config(input);
1324 _parse_config(values);
1325 increment_parsed();
1326 _trigger_pre_parse(values.size());
1327 _process();
1328
1329
1330 _process_extras();
1331 }
1332
1333 CLI11_INLINE void App::_parse_config(const std::vector<ConfigItem> &args) {
1334 for(const ConfigItem &item : args) {
1335 if(!_parse_single_config(item) && allow_config_extras_ == config_extras_mode::error)
1336 throw ConfigError::Extras(item.fullname());
1337 }
1338 }
1339
1340 CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t level) {
1341 if(level < item.parents.size()) {
1342 try {
1343 auto *subcom = get_subcommand(item.parents.at(level));
1344 auto result = subcom->_parse_single_config(item, level + 1);
1345
1346 return result;
1347 } catch(const OptionNotFound &) {
1348 return false;
1349 }
1350 }
1351
1352 if(item.name == "++") {
1353 if(configurable_) {
1354 increment_parsed();
1355 _trigger_pre_parse(2);
1356 if(parent_ != nullptr) {
1357 parent_->parsed_subcommands_.push_back(this);
1358 }
1359 }
1360 return true;
1361 }
1362
1363 if(item.name == "--") {
1364 if(configurable_ && parse_complete_callback_) {
1365 _process_callbacks();
1366 _process_requirements();
1367 run_callback();
1368 }
1369 return true;
1370 }
1371 Option *op = get_option_no_throw("--" + item.name);
1372 if(op == nullptr) {
1373 if(item.name.size() == 1) {
1374 op = get_option_no_throw("-" + item.name);
1375 }
1376 }
1377 if(op == nullptr) {
1378 op = get_option_no_throw(item.name);
1379 }
1380 if(op == nullptr) {
1381
1382 if(get_allow_config_extras() == config_extras_mode::capture)
1383
1384 missing_.emplace_back(detail::Classifier::NONE, item.fullname());
1385 return false;
1386 }
1387
1388 if(!op->get_configurable()) {
1389 if(get_allow_config_extras() == config_extras_mode::ignore_all) {
1390 return false;
1391 }
1392 throw ConfigError::NotConfigurable(item.fullname());
1393 }
1394
1395 if(op->empty()) {
1396
1397 if(op->get_expected_min() == 0) {
1398 if(item.inputs.size() <= 1) {
1399
1400 auto res = config_formatter_->to_flag(item);
1401 bool converted{false};
1402 if(op->get_disable_flag_override()) {
1403
1404 try {
1405 auto val = detail::to_flag_value(res);
1406 if(val == 1) {
1407 res = op->get_flag_value(item.name, "{}");
1408 converted = true;
1409 }
1410 } catch(...) {
1411 }
1412 }
1413
1414 if(!converted) {
1415 res = op->get_flag_value(item.name, res);
1416 }
1417
1418 op->add_result(res);
1419 return true;
1420 }
1421 if(static_cast<int>(item.inputs.size()) > op->get_items_expected_max()) {
1422 if(op->get_items_expected_max() > 1) {
1423 throw ArgumentMismatch::AtMost(item.fullname(), op->get_items_expected_max(), item.inputs.size());
1424 }
1425 throw ConversionError::TooManyInputsFlag(item.fullname());
1426 }
1427 }
1428 op->add_result(item.inputs);
1429 op->run_callback();
1430 }
1431
1432 return true;
1433 }
1434
1435 CLI11_INLINE bool App::_parse_single(std::vector<std::string> &args, bool &positional_only) {
1436 bool retval = true;
1437 detail::Classifier classifier = positional_only ? detail::Classifier::NONE : _recognize(args.back());
1438 switch(classifier) {
1439 case detail::Classifier::POSITIONAL_MARK:
1440 args.pop_back();
1441 positional_only = true;
1442 if((!_has_remaining_positionals()) && (parent_ != nullptr)) {
1443 retval = false;
1444 } else {
1445 _move_to_missing(classifier, "--");
1446 }
1447 break;
1448 case detail::Classifier::SUBCOMMAND_TERMINATOR:
1449
1450 args.pop_back();
1451 retval = false;
1452 break;
1453 case detail::Classifier::SUBCOMMAND:
1454 retval = _parse_subcommand(args);
1455 break;
1456 case detail::Classifier::LONG:
1457 case detail::Classifier::SHORT:
1458 case detail::Classifier::WINDOWS_STYLE:
1459
1460 _parse_arg(args, classifier);
1461 break;
1462 case detail::Classifier::NONE:
1463
1464 retval = _parse_positional(args, false);
1465 if(retval && positionals_at_end_) {
1466 positional_only = true;
1467 }
1468 break;
1469
1470 default:
1471 throw HorribleError("unrecognized classifier (you should not see this!)");
1472
1473 }
1474 return retval;
1475 }
1476
1477 CLI11_NODISCARD CLI11_INLINE std::size_t App::_count_remaining_positionals(bool required_only) const {
1478 std::size_t retval = 0;
1479 for(const Option_p &opt : options_) {
1480 if(opt->get_positional() && (!required_only || opt->get_required())) {
1481 if(opt->get_items_expected_min() > 0 && static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
1482 retval += static_cast<std::size_t>(opt->get_items_expected_min()) - opt->count();
1483 }
1484 }
1485 }
1486 return retval;
1487 }
1488
1489 CLI11_NODISCARD CLI11_INLINE bool App::_has_remaining_positionals() const {
1490 for(const Option_p &opt : options_) {
1491 if(opt->get_positional() && ((static_cast<int>(opt->count()) < opt->get_items_expected_min()))) {
1492 return true;
1493 }
1494 }
1495
1496 return false;
1497 }
1498
1499 CLI11_INLINE bool App::_parse_positional(std::vector<std::string> &args, bool haltOnSubcommand) {
1500
1501 const std::string &positional = args.back();
1502
1503 if(positionals_at_end_) {
1504
1505 auto arg_rem = args.size();
1506 auto remreq = _count_remaining_positionals(true);
1507 if(arg_rem <= remreq) {
1508 for(const Option_p &opt : options_) {
1509 if(opt->get_positional() && opt->required_) {
1510 if(static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
1511 if(validate_positionals_) {
1512 std::string pos = positional;
1513 pos = opt->_validate(pos, 0);
1514 if(!pos.empty()) {
1515 continue;
1516 }
1517 }
1518
1519 parse_order_.push_back(opt.get());
1520
1521 if(opt->get_inject_separator()) {
1522 if(!opt->results().empty() && !opt->results().back().empty()) {
1523 opt->add_result(std::string{});
1524 }
1525 }
1526 if(opt->get_trigger_on_parse() &&
1527 opt->current_option_state_ == Option::option_state::callback_run) {
1528 opt->clear();
1529 }
1530 opt->add_result(positional);
1531 if(opt->get_trigger_on_parse()) {
1532 opt->run_callback();
1533 }
1534 args.pop_back();
1535 return true;
1536 }
1537 }
1538 }
1539 }
1540 }
1541 for(const Option_p &opt : options_) {
1542
1543 if(opt->get_positional() &&
1544 (static_cast<int>(opt->count()) < opt->get_items_expected_min() || opt->get_allow_extra_args())) {
1545 if(validate_positionals_) {
1546 std::string pos = positional;
1547 pos = opt->_validate(pos, 0);
1548 if(!pos.empty()) {
1549 continue;
1550 }
1551 }
1552 if(opt->get_inject_separator()) {
1553 if(!opt->results().empty() && !opt->results().back().empty()) {
1554 opt->add_result(std::string{});
1555 }
1556 }
1557 if(opt->get_trigger_on_parse() && opt->current_option_state_ == Option::option_state::callback_run) {
1558 opt->clear();
1559 }
1560 opt->add_result(positional);
1561 if(opt->get_trigger_on_parse()) {
1562 opt->run_callback();
1563 }
1564 parse_order_.push_back(opt.get());
1565 args.pop_back();
1566 return true;
1567 }
1568 }
1569
1570 for(auto &subc : subcommands_) {
1571 if((subc->name_.empty()) && (!subc->disabled_)) {
1572 if(subc->_parse_positional(args, false)) {
1573 if(!subc->pre_parse_called_) {
1574 subc->_trigger_pre_parse(args.size());
1575 }
1576 return true;
1577 }
1578 }
1579 }
1580
1581 if(parent_ != nullptr && fallthrough_)
1582 return _get_fallthrough_parent()->_parse_positional(args, static_cast<bool>(parse_complete_callback_));
1583
1584
1585 auto *com = _find_subcommand(args.back(), true, false);
1586 if(com != nullptr && (require_subcommand_max_ == 0 || require_subcommand_max_ > parsed_subcommands_.size())) {
1587 if(haltOnSubcommand) {
1588 return false;
1589 }
1590 args.pop_back();
1591 com->_parse(args);
1592 return true;
1593 }
1594
1595
1596 auto *parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
1597 com = parent_app->_find_subcommand(args.back(), true, false);
1598 if(com != nullptr && (com->parent_->require_subcommand_max_ == 0 ||
1599 com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size())) {
1600 return false;
1601 }
1602
1603 if(positionals_at_end_) {
1604 throw CLI::ExtrasError(name_, args);
1605 }
1606
1607 if(parent_ != nullptr && name_.empty()) {
1608 return false;
1609 }
1610
1611 _move_to_missing(detail::Classifier::NONE, positional);
1612 args.pop_back();
1613 if(prefix_command_) {
1614 while(!args.empty()) {
1615 _move_to_missing(detail::Classifier::NONE, args.back());
1616 args.pop_back();
1617 }
1618 }
1619
1620 return true;
1621 }
1622
1623 CLI11_NODISCARD CLI11_INLINE App *
1624 App::_find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept {
1625 for(const App_p &com : subcommands_) {
1626 if(com->disabled_ && ignore_disabled)
1627 continue;
1628 if(com->get_name().empty()) {
1629 auto *subc = com->_find_subcommand(subc_name, ignore_disabled, ignore_used);
1630 if(subc != nullptr) {
1631 return subc;
1632 }
1633 }
1634 if(com->check_name(subc_name)) {
1635 if((!*com) || !ignore_used)
1636 return com.get();
1637 }
1638 }
1639 return nullptr;
1640 }
1641
1642 CLI11_INLINE bool App::_parse_subcommand(std::vector<std::string> &args) {
1643 if(_count_remaining_positionals( true) > 0) {
1644 _parse_positional(args, false);
1645 return true;
1646 }
1647 auto *com = _find_subcommand(args.back(), true, true);
1648 if(com != nullptr) {
1649 args.pop_back();
1650 if(!com->silent_) {
1651 parsed_subcommands_.push_back(com);
1652 }
1653 com->_parse(args);
1654 auto *parent_app = com->parent_;
1655 while(parent_app != this) {
1656 parent_app->_trigger_pre_parse(args.size());
1657 if(!com->silent_) {
1658 parent_app->parsed_subcommands_.push_back(com);
1659 }
1660 parent_app = parent_app->parent_;
1661 }
1662 return true;
1663 }
1664
1665 if(parent_ == nullptr)
1666 throw HorribleError("Subcommand " + args.back() + " missing");
1667 return false;
1668 }
1669
1670 CLI11_INLINE bool App::_parse_arg(std::vector<std::string> &args, detail::Classifier current_type) {
1671
1672 std::string current = args.back();
1673
1674 std::string arg_name;
1675 std::string value;
1676 std::string rest;
1677
1678 switch(current_type) {
1679 case detail::Classifier::LONG:
1680 if(!detail::split_long(current, arg_name, value))
1681 throw HorribleError("Long parsed but missing (you should not see this):" + args.back());
1682 break;
1683 case detail::Classifier::SHORT:
1684 if(!detail::split_short(current, arg_name, rest))
1685 throw HorribleError("Short parsed but missing! You should not see this");
1686 break;
1687 case detail::Classifier::WINDOWS_STYLE:
1688 if(!detail::split_windows_style(current, arg_name, value))
1689 throw HorribleError("windows option parsed but missing! You should not see this");
1690 break;
1691 case detail::Classifier::SUBCOMMAND:
1692 case detail::Classifier::SUBCOMMAND_TERMINATOR:
1693 case detail::Classifier::POSITIONAL_MARK:
1694 case detail::Classifier::NONE:
1695 default:
1696 throw HorribleError("parsing got called with invalid option! You should not see this");
1697 }
1698
1699 auto op_ptr = std::find_if(std::begin(options_), std::end(options_), [arg_name, current_type](const Option_p &opt) {
1700 if(current_type == detail::Classifier::LONG)
1701 return opt->check_lname(arg_name);
1702 if(current_type == detail::Classifier::SHORT)
1703 return opt->check_sname(arg_name);
1704
1705 return opt->check_lname(arg_name) || opt->check_sname(arg_name);
1706 });
1707
1708
1709 if(op_ptr == std::end(options_)) {
1710 for(auto &subc : subcommands_) {
1711 if(subc->name_.empty() && !subc->disabled_) {
1712 if(subc->_parse_arg(args, current_type)) {
1713 if(!subc->pre_parse_called_) {
1714 subc->_trigger_pre_parse(args.size());
1715 }
1716 return true;
1717 }
1718 }
1719 }
1720
1721
1722 if(parent_ != nullptr && name_.empty()) {
1723 return false;
1724 }
1725
1726
1727 if(parent_ != nullptr && fallthrough_)
1728 return _get_fallthrough_parent()->_parse_arg(args, current_type);
1729
1730
1731 args.pop_back();
1732 _move_to_missing(current_type, current);
1733 return true;
1734 }
1735
1736 args.pop_back();
1737
1738
1739 Option_p &op = *op_ptr;
1740
1741 if(op->get_inject_separator()) {
1742 if(!op->results().empty() && !op->results().back().empty()) {
1743 op->add_result(std::string{});
1744 }
1745 }
1746 if(op->get_trigger_on_parse() && op->current_option_state_ == Option::option_state::callback_run) {
1747 op->clear();
1748 }
1749 int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min());
1750 int max_num = op->get_items_expected_max();
1751
1752
1753 if(max_num >= detail::expected_max_vector_size / 16 && !op->get_allow_extra_args()) {
1754 auto tmax = op->get_type_size_max();
1755 max_num = detail::checked_multiply(tmax, op->get_expected_min()) ? tmax : detail::expected_max_vector_size;
1756 }
1757
1758 int collected = 0;
1759 int result_count = 0;
1760
1761 if(max_num == 0) {
1762 auto res = op->get_flag_value(arg_name, value);
1763 op->add_result(res);
1764 parse_order_.push_back(op.get());
1765 } else if(!value.empty()) {
1766 op->add_result(value, result_count);
1767 parse_order_.push_back(op.get());
1768 collected += result_count;
1769
1770 } else if(!rest.empty()) {
1771 op->add_result(rest, result_count);
1772 parse_order_.push_back(op.get());
1773 rest = "";
1774 collected += result_count;
1775 }
1776
1777
1778 while(min_num > collected && !args.empty()) {
1779 std::string current_ = args.back();
1780 args.pop_back();
1781 op->add_result(current_, result_count);
1782 parse_order_.push_back(op.get());
1783 collected += result_count;
1784 }
1785
1786 if(min_num > collected) {
1787 throw ArgumentMismatch::TypedAtLeast(op->get_name(), min_num, op->get_type_name());
1788 }
1789
1790
1791 if(max_num > collected || op->get_allow_extra_args()) {
1792 auto remreqpos = _count_remaining_positionals(true);
1793
1794 while((collected < max_num || op->get_allow_extra_args()) && !args.empty() &&
1795 _recognize(args.back(), false) == detail::Classifier::NONE) {
1796
1797 if(remreqpos >= args.size()) {
1798 break;
1799 }
1800 if(validate_optional_arguments_) {
1801 std::string arg = args.back();
1802 arg = op->_validate(arg, 0);
1803 if(!arg.empty()) {
1804 break;
1805 }
1806 }
1807 op->add_result(args.back(), result_count);
1808 parse_order_.push_back(op.get());
1809 args.pop_back();
1810 collected += result_count;
1811 }
1812
1813
1814 if(!args.empty() && _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK)
1815 args.pop_back();
1816
1817 if(min_num == 0 && max_num > 0 && collected == 0) {
1818 auto res = op->get_flag_value(arg_name, std::string{});
1819 op->add_result(res);
1820 parse_order_.push_back(op.get());
1821 }
1822 }
1823
1824 if(min_num > 0 && (collected % op->get_type_size_max()) != 0) {
1825 if(op->get_type_size_max() != op->get_type_size_min()) {
1826 op->add_result(std::string{});
1827 } else {
1828 throw ArgumentMismatch::PartialType(op->get_name(), op->get_type_size_min(), op->get_type_name());
1829 }
1830 }
1831 if(op->get_trigger_on_parse()) {
1832 op->run_callback();
1833 }
1834 if(!rest.empty()) {
1835 rest = "-" + rest;
1836 args.push_back(rest);
1837 }
1838 return true;
1839 }
1840
1841 CLI11_INLINE void App::_trigger_pre_parse(std::size_t remaining_args) {
1842 if(!pre_parse_called_) {
1843 pre_parse_called_ = true;
1844 if(pre_parse_callback_) {
1845 pre_parse_callback_(remaining_args);
1846 }
1847 } else if(immediate_callback_) {
1848 if(!name_.empty()) {
1849 auto pcnt = parsed_;
1850 auto extras = std::move(missing_);
1851 clear();
1852 parsed_ = pcnt;
1853 pre_parse_called_ = true;
1854 missing_ = std::move(extras);
1855 }
1856 }
1857 }
1858
1859 CLI11_INLINE App *App::_get_fallthrough_parent() {
1860 if(parent_ == nullptr) {
1861 throw(HorribleError("No Valid parent"));
1862 }
1863 auto *fallthrough_parent = parent_;
1864 while((fallthrough_parent->parent_ != nullptr) && (fallthrough_parent->get_name().empty())) {
1865 fallthrough_parent = fallthrough_parent->parent_;
1866 }
1867 return fallthrough_parent;
1868 }
1869
1870 CLI11_NODISCARD CLI11_INLINE const std::string &App::_compare_subcommand_names(const App &subcom,
1871 const App &base) const {
1872 static const std::string estring;
1873 if(subcom.disabled_) {
1874 return estring;
1875 }
1876 for(const auto &subc : base.subcommands_) {
1877 if(subc.get() != &subcom) {
1878 if(subc->disabled_) {
1879 continue;
1880 }
1881 if(!subcom.get_name().empty()) {
1882 if(subc->check_name(subcom.get_name())) {
1883 return subcom.get_name();
1884 }
1885 }
1886 if(!subc->get_name().empty()) {
1887 if(subcom.check_name(subc->get_name())) {
1888 return subc->get_name();
1889 }
1890 }
1891 for(const auto &les : subcom.aliases_) {
1892 if(subc->check_name(les)) {
1893 return les;
1894 }
1895 }
1896
1897 for(const auto &les : subc->aliases_) {
1898 if(subcom.check_name(les)) {
1899 return les;
1900 }
1901 }
1902
1903 if(subc->get_name().empty()) {
1904 const auto &cmpres = _compare_subcommand_names(subcom, *subc);
1905 if(!cmpres.empty()) {
1906 return cmpres;
1907 }
1908 }
1909
1910 if(subcom.get_name().empty()) {
1911 const auto &cmpres = _compare_subcommand_names(*subc, subcom);
1912 if(!cmpres.empty()) {
1913 return cmpres;
1914 }
1915 }
1916 }
1917 }
1918 return estring;
1919 }
1920
1921 CLI11_INLINE void App::_move_to_missing(detail::Classifier val_type, const std::string &val) {
1922 if(allow_extras_ || subcommands_.empty()) {
1923 missing_.emplace_back(val_type, val);
1924 return;
1925 }
1926
1927 for(auto &subc : subcommands_) {
1928 if(subc->name_.empty() && subc->allow_extras_) {
1929 subc->missing_.emplace_back(val_type, val);
1930 return;
1931 }
1932 }
1933
1934 missing_.emplace_back(val_type, val);
1935 }
1936
1937 CLI11_INLINE void App::_move_option(Option *opt, App *app) {
1938 if(opt == nullptr) {
1939 throw OptionNotFound("the option is NULL");
1940 }
1941
1942 bool found = false;
1943 for(auto &subc : subcommands_) {
1944 if(app == subc.get()) {
1945 found = true;
1946 }
1947 }
1948 if(!found) {
1949 throw OptionNotFound("The Given app is not a subcommand");
1950 }
1951
1952 if((help_ptr_ == opt) || (help_all_ptr_ == opt))
1953 throw OptionAlreadyAdded("cannot move help options");
1954
1955 if(config_ptr_ == opt)
1956 throw OptionAlreadyAdded("cannot move config file options");
1957
1958 auto iterator =
1959 std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
1960 if(iterator != std::end(options_)) {
1961 const auto &opt_p = *iterator;
1962 if(std::find_if(std::begin(app->options_), std::end(app->options_), [&opt_p](const Option_p &v) {
1963 return (*v == *opt_p);
1964 }) == std::end(app->options_)) {
1965
1966 app->options_.push_back(std::move(*iterator));
1967 options_.erase(iterator);
1968 } else {
1969 throw OptionAlreadyAdded("option was not located: " + opt->get_name());
1970 }
1971 } else {
1972 throw OptionNotFound("could not locate the given Option");
1973 }
1974 }
1975
1976 CLI11_INLINE void TriggerOn(App *trigger_app, App *app_to_enable) {
1977 app_to_enable->enabled_by_default(false);
1978 app_to_enable->disabled_by_default();
1979 trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(false); });
1980 }
1981
1982 CLI11_INLINE void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable) {
1983 for(auto &app : apps_to_enable) {
1984 app->enabled_by_default(false);
1985 app->disabled_by_default();
1986 }
1987
1988 trigger_app->preparse_callback([apps_to_enable](std::size_t) {
1989 for(const auto &app : apps_to_enable) {
1990 app->disabled(false);
1991 }
1992 });
1993 }
1994
1995 CLI11_INLINE void TriggerOff(App *trigger_app, App *app_to_enable) {
1996 app_to_enable->disabled_by_default(false);
1997 app_to_enable->enabled_by_default();
1998 trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(); });
1999 }
2000
2001 CLI11_INLINE void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable) {
2002 for(auto &app : apps_to_enable) {
2003 app->disabled_by_default(false);
2004 app->enabled_by_default();
2005 }
2006
2007 trigger_app->preparse_callback([apps_to_enable](std::size_t) {
2008 for(const auto &app : apps_to_enable) {
2009 app->disabled();
2010 }
2011 });
2012 }
2013
2014 CLI11_INLINE void deprecate_option(Option *opt, const std::string &replacement) {
2015 Validator deprecate_warning{[opt, replacement](std::string &) {
2016 std::cout << opt->get_name() << " is deprecated please use '" << replacement
2017 << "' instead\n";
2018 return std::string();
2019 },
2020 "DEPRECATED"};
2021 deprecate_warning.application_index(0);
2022 opt->check(deprecate_warning);
2023 if(!replacement.empty()) {
2024 opt->description(opt->get_description() + " DEPRECATED: please use '" + replacement + "' instead");
2025 }
2026 }
2027
2028 CLI11_INLINE void retire_option(App *app, Option *opt) {
2029 App temp;
2030 auto *option_copy = temp.add_option(opt->get_name(false, true))
2031 ->type_size(opt->get_type_size_min(), opt->get_type_size_max())
2032 ->expected(opt->get_expected_min(), opt->get_expected_max())
2033 ->allow_extra_args(opt->get_allow_extra_args());
2034
2035 app->remove_option(opt);
2036 auto *opt2 = app->add_option(option_copy->get_name(false, true), "option has been retired and has no effect")
2037 ->type_name("RETIRED")
2038 ->default_str("RETIRED")
2039 ->type_size(option_copy->get_type_size_min(), option_copy->get_type_size_max())
2040 ->expected(option_copy->get_expected_min(), option_copy->get_expected_max())
2041 ->allow_extra_args(option_copy->get_allow_extra_args());
2042
2043 Validator retired_warning{[opt2](std::string &) {
2044 std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
2045 return std::string();
2046 },
2047 ""};
2048 retired_warning.application_index(0);
2049 opt2->check(retired_warning);
2050 }
2051
2052 CLI11_INLINE void retire_option(App &app, Option *opt) { retire_option(&app, opt); }
2053
2054 CLI11_INLINE void retire_option(App *app, const std::string &option_name) {
2055
2056 auto *opt = app->get_option_no_throw(option_name);
2057 if(opt != nullptr) {
2058 retire_option(app, opt);
2059 return;
2060 }
2061 auto *opt2 = app->add_option(option_name, "option has been retired and has no effect")
2062 ->type_name("RETIRED")
2063 ->expected(0, 1)
2064 ->default_str("RETIRED");
2065 Validator retired_warning{[opt2](std::string &) {
2066 std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
2067 return std::string();
2068 },
2069 ""};
2070 retired_warning.application_index(0);
2071 opt2->check(retired_warning);
2072 }
2073
2074 CLI11_INLINE void retire_option(App &app, const std::string &option_name) { retire_option(&app, option_name); }
2075
2076 namespace FailureMessage {
2077
2078 CLI11_INLINE std::string simple(const App *app, const Error &e) {
2079 std::string header = std::string(e.what()) + "\n";
2080 std::vector<std::string> names;
2081
2082
2083 if(app->get_help_ptr() != nullptr)
2084 names.push_back(app->get_help_ptr()->get_name());
2085
2086 if(app->get_help_all_ptr() != nullptr)
2087 names.push_back(app->get_help_all_ptr()->get_name());
2088
2089
2090 if(!names.empty())
2091 header += "Run with " + detail::join(names, " or ") + " for more information.\n";
2092
2093 return header;
2094 }
2095
2096 CLI11_INLINE std::string help(const App *app, const Error &e) {
2097 std::string header = std::string("ERROR: ") + e.get_name() + ": " + e.what() + "\n";
2098 header += app->help();
2099 return header;
2100 }
2101
2102 }
2103
2104
2105 }