Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:42

0001 // Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
0002 // under NSF AWARD 1414736 and by the respective contributors.
0003 // All rights reserved.
0004 //
0005 // SPDX-License-Identifier: BSD-3-Clause
0006 
0007 #pragma once
0008 
0009 // This include is only needed for IDEs to discover symbols
0010 #include <CLI/App.hpp>
0011 
0012 // [CLI11:public_includes:set]
0013 #include <algorithm>
0014 #include <memory>
0015 #include <string>
0016 #include <utility>
0017 #include <vector>
0018 // [CLI11:public_includes:end]
0019 
0020 namespace CLI {
0021 // [CLI11:app_inl_hpp:verbatim]
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     // Inherit if not from a nullptr
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         /// OptionDefaults
0033         option_defaults_ = parent_->option_defaults_;
0034 
0035         // INHERITABLE
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;  // we are throwing so need to be exception invariant
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         // Set the default string capture function
0145         option->default_function(func);
0146 
0147         // For compatibility with CLI11 1.7 and before, capture the default string here
0148         if(defaulted)
0149             option->capture_default_str();
0150 
0151         // Transfer defaults to the new option
0152         option_defaults_.copy_to(option.get());
0153 
0154         // Don't bother to capture if we already did
0155         if(!defaulted && option->get_always_capture_default())
0156             option->capture_default_str();
0157 
0158         return option.get();
0159     }
0160     // we know something matches now find what it is so we can produce more error information
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     // this line should not be reached the above loop should trigger the throw
0168     throw(OptionAlreadyAdded("added option matched existing option name"));  // LCOV_EXCL_LINE
0169 }
0170 
0171 CLI11_INLINE Option *App::set_help_flag(std::string flag_name, const std::string &help_description) {
0172     // take flag_description by const reference otherwise add_flag tries to assign to help_description
0173     if(help_ptr_ != nullptr) {
0174         remove_option(help_ptr_);
0175         help_ptr_ = nullptr;
0176     }
0177 
0178     // Empty name will simply remove the help flag
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     // take flag_description by const reference otherwise add_flag tries to assign to flag_description
0189     if(help_all_ptr_ != nullptr) {
0190         remove_option(help_all_ptr_);
0191         help_all_ptr_ = nullptr;
0192     }
0193 
0194     // Empty name will simply remove the help all flag
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     // take flag_description by const reference otherwise add_flag tries to assign to version_description
0206     if(version_ptr_ != nullptr) {
0207         remove_option(version_ptr_);
0208         version_ptr_ = nullptr;
0209     }
0210 
0211     // Empty name will simply remove the version flag
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     // Empty name will simply remove the version flag
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         // check for default values and if it has them
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     // flags cannot have positional values
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,  ///< A function to call, void(void)
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,  ///< A function to call, void(int)
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     // Remove existing config if present
0301     if(config_ptr_ != nullptr) {
0302         remove_option(config_ptr_);
0303         config_ptr_ = nullptr;  // need to remove the config_ptr completely
0304     }
0305 
0306     // Only add config if option passed
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     // Make sure no links exist
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     // Make sure no links exist
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()) {  // for named subcommands add the number of times the subcommand was called
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     // If the name is not set, read from command line
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     // the next section of code is to deal with quoted arguments after an '=' or ':' for windows like operations
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     // remove all empty strings
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     // Clear if parsed
0519     if(parsed_ > 0)
0520         clear();
0521 
0522     // parsed_ is incremented in commands/subcommands,
0523     // but placed here to make sure this is cleared when
0524     // running parse after an error is thrown, even by _validate or _configure.
0525     parsed_ = 1;
0526     _validate();
0527     _configure();
0528     // set the parent as nullptr as this object should be the top now
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     // Clear if parsed
0538     if(parsed_ > 0)
0539         clear();
0540 
0541     // parsed_ is incremented in commands/subcommands,
0542     // but placed here to make sure this is cleared when
0543     // running parse after an error is thrown, even by _validate or _configure.
0544     parsed_ = 1;
0545     _validate();
0546     _configure();
0547     // set the parent as nullptr as this object should be the top now
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         // set the parent as nullptr as this object should be the top now
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     /// Avoid printing anything if this is a CLI::RuntimeError
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     // Delegate to subcommand if needed
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         // also check down into nameless subcommands
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         // also check down into nameless subcommands
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_) {  // NOLINT(performance-for-range-copy)
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         // Add group if it is not already in there
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     // Get from a subcommand that may allow extras
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         // Recurse into subcommands
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     // count the number of positional only args
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;  // make sure fallthrough_ is false to prevent infinite loop
0908             app->prefix_command_ = false;
0909         }
0910         // make sure the parent is set to be this object in preparation for parse
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     // in the main app if immediate_callback_ is set it runs the main callback before the used subcommands
0919     if(!final_mode && parse_complete_callback_) {
0920         parse_complete_callback_();
0921     }
0922     // run the callbacks for the received subcommands
0923     for(App *subc : get_subcommands()) {
0924         if(subc->parent_ == this) {
0925             subc->run_callback(true, suppress_final_callback);
0926         }
0927     }
0928     // now run callbacks for option_groups
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     // finally run the main callback
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 &current, bool ignore_used) const {
0944     // Don't match if max has been reached - but still check parents
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     // Check parent if exists, else return false
0953     return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
0954 }
0955 
0956 CLI11_NODISCARD CLI11_INLINE detail::Classifier App::_recognize(const std::string &current,
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             // Windows version
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             // This also works on Windows, but gives a warning
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         // process the priority option_groups first
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     // If there were parsed subcommands, call those. First subcommand wins if there are multiple ones.
1079     if(!parsed_subcommands_.empty()) {
1080         for(const App *sub : parsed_subcommands_)
1081             sub->_process_help_flags(trigger_help, trigger_all_help);
1082 
1083         // Only the final subcommand should call for help. All help wins over help.
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     // check excludes
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         // if we are excluded but didn't receive anything, just return
1112         return;
1113     }
1114 
1115     // check excludes
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         // if we missing something but didn't have any options, just return
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         // Required but empty
1145         if(opt->get_required() && opt->count() == 0) {
1146             throw RequiredError(opt->get_name());
1147         }
1148         // Requires
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         // Excludes
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     // check for the required number of subcommands
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     // Max error cannot occur, the extra subcommand will parse as an ExtrasError or a remaining item.
1165 
1166     // run this loop to check how many unnamed subcommands were actually used since they are considered options
1167     // from the perspective of an App
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     // now process the requirements for subcommands if needed
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                     // if we have met the requirement and there is nothing in this option group skip checking
1200                     // requirements
1201                 }
1202                 if(require_option_max_ > 0 && used_options >= require_option_min_) {
1203                     continue;
1204                     // if we have met the requirement and there is nothing in this option group skip checking
1205                     // requirements
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         // the config file might generate a FileError but that should not be processed until later in the process
1222         // to allow for help, version and other errors to generate first.
1223         _process_config_file();
1224 
1225         // process env shouldn't throw but no reason to process it if config generated an error
1226         _process_env();
1227     } catch(const CLI::FileError &) {
1228         // callbacks and help_flags can generate exceptions which should take priority
1229         // over the config file error if one exists.
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         // Throw error if any items are left over (depending on settings)
1293         _process_extras(args);
1294 
1295         // Convert missing (pairs) to extras (string only) ready for processing in another app
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     // this can only be called by the top level in which case parent == nullptr by definition
1308     // operation is simplified
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     // Throw error if any items are left over (depending on settings)
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     // Throw error if any items are left over (depending on settings)
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     // check for section open
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     // check for section close
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         // If the option was not present
1382         if(get_allow_config_extras() == config_extras_mode::capture)
1383             // Should we worry about classifying the extras properly?
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                 // Flag parsing
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         // treat this like a positional mark if in the parent app
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         // If already parsed a subcommand, don't accept options_
1460         _parse_arg(args, classifier);
1461         break;
1462     case detail::Classifier::NONE:
1463         // Probably a positional or something for a parent (sub)command
1464         retval = _parse_positional(args, false);
1465         if(retval && positionals_at_end_) {
1466             positional_only = true;
1467         }
1468         break;
1469         // LCOV_EXCL_START
1470     default:
1471         throw HorribleError("unrecognized classifier (you should not see this!)");
1472         // LCOV_EXCL_STOP
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         // deal with the case of required arguments at the end which should take precedence over other arguments
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                         /// if we require a separator add it here
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         // Eat options, one by one, until done
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     // let the parent deal with it if possible
1581     if(parent_ != nullptr && fallthrough_)
1582         return _get_fallthrough_parent()->_parse_positional(args, static_cast<bool>(parse_complete_callback_));
1583 
1584     /// Try to find a local subcommand that is repeated
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     /// now try one last gasp at subcommands that have been executed before, go to root app and try to find a
1595     /// subcommand in a broader way, if one exists let the parent deal with it
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     /// If this is an option group don't deal with it
1607     if(parent_ != nullptr && name_.empty()) {
1608         return false;
1609     }
1610     /// We are out of other options this goes to missing
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(/* required */ 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         // this will only get called for detail::Classifier::WINDOWS_STYLE
1705         return opt->check_lname(arg_name) || opt->check_sname(arg_name);
1706     });
1707 
1708     // Option not found
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         // don't capture missing if this is a nameless subcommand and nameless subcommands can't fallthrough
1722         if(parent_ != nullptr && name_.empty()) {
1723             return false;
1724         }
1725 
1726         // If a subcommand, try the main command
1727         if(parent_ != nullptr && fallthrough_)
1728             return _get_fallthrough_parent()->_parse_arg(args, current_type);
1729 
1730         // Otherwise, add to missing
1731         args.pop_back();
1732         _move_to_missing(current_type, current);
1733         return true;
1734     }
1735 
1736     args.pop_back();
1737 
1738     // Get a reference to the pointer to make syntax bearable
1739     Option_p &op = *op_ptr;
1740     /// if we require a separator add it here
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     // check container like options to limit the argument size to a single type if the allow_extra_flags argument is
1752     // set. 16 is somewhat arbitrary (needs to be at least 4)
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     // Make sure we always eat the minimum for unlimited vectors
1758     int collected = 0;     // total number of arguments collected
1759     int result_count = 0;  // local variable for number of results in a single arg string
1760     // deal with purely flag like things
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()) {  // --this=value
1766         op->add_result(value, result_count);
1767         parse_order_.push_back(op.get());
1768         collected += result_count;
1769         // -Trest
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     // gather the minimum number of arguments
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) {  // if we have run out of arguments and the minimum was not met
1787         throw ArgumentMismatch::TypedAtLeast(op->get_name(), min_num, op->get_type_name());
1788     }
1789 
1790     // now check for optional arguments
1791     if(max_num > collected || op->get_allow_extra_args()) {  // we allow optional arguments
1792         auto remreqpos = _count_remaining_positionals(true);
1793         // we have met the minimum now optionally check up to the maximum
1794         while((collected < max_num || op->get_allow_extra_args()) && !args.empty() &&
1795               _recognize(args.back(), false) == detail::Classifier::NONE) {
1796             // If any required positionals remain, don't keep eating
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         // Allow -- to end an unlimited list and "eat" it
1814         if(!args.empty() && _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK)
1815             args.pop_back();
1816         // optional flag that didn't receive anything now get the default value
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     // if we only partially completed a type then add an empty string if allowed for later processing
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             // this loop is needed in case of ignore_underscore or ignore_case on one but not the other
1897             for(const auto &les : subc->aliases_) {
1898                 if(subcom.check_name(les)) {
1899                     return les;
1900                 }
1901             }
1902             // if the subcommand is an option group we need to check deeper
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             // if the test subcommand is an option group we need to check deeper
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     // allow extra arguments to be places in an option group if it is allowed there
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     // if we haven't found any place to put them yet put them in missing
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     // verify that the give app is actually a subcommand
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             // only erase after the insertion was successful
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     // Collect names
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     // If any names found, suggest those
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 }  // namespace FailureMessage
2103 
2104 // [CLI11:app_inl_hpp:end]
2105 }  // namespace CLI