@@ -165,94 +165,103 @@ CLI11_INLINE Option *App::add_option(std::string option_name,
165165 std::function<std::string()> func) {
166166 Option myopt{option_name, option_description, option_callback, this , allow_non_standard_options_};
167167
168- if (std::find_if (std::begin (options_), std::end (options_), [&myopt](const Option_p &v) { return *v == myopt; }) ==
169- std::end (options_)) {
170- if (myopt.lnames_ .empty () && myopt.snames_ .empty ()) {
171- // if the option is positional only there is additional potential for ambiguities in config files and needs
172- // to be checked
173- std::string test_name = " --" + myopt.get_single_name ();
174- if (test_name.size () == 3 ) {
175- test_name.erase (0 , 1 );
168+ // do a quick search in current subcommand for options
169+ auto res =
170+ std::find_if (std::begin (options_), std::end (options_), [&myopt](const Option_p &v) { return *v == myopt; });
171+ if (res != options_.end ()) {
172+ const auto &matchname = (*res)->matching_name (myopt);
173+ throw (OptionAlreadyAdded (" added option matched existing option name: " + matchname));
174+ }
175+ /* * get a top level parent*/
176+ const App *top_level_parent = this ;
177+ while (top_level_parent->name_ .empty () && top_level_parent->parent_ != nullptr ) {
178+ top_level_parent = top_level_parent->parent_ ;
179+ }
180+
181+ if (myopt.lnames_ .empty () && myopt.snames_ .empty ()) {
182+ // if the option is positional only there is additional potential for ambiguities in config files and needs
183+ // to be checked
184+ std::string test_name = " --" + myopt.get_single_name ();
185+ if (test_name.size () == 3 ) {
186+ test_name.erase (0 , 1 );
187+ }
188+ // if we are in option group
189+ const auto *op = top_level_parent->get_option_no_throw (test_name);
190+ if (op != nullptr && op->get_configurable ()) {
191+ throw (OptionAlreadyAdded (" added option positional name matches existing option: " + test_name));
192+ }
193+ // need to check if there is another positional with the same name that also doesn't have any long or
194+ // short names
195+ op = top_level_parent->get_option_no_throw (myopt.get_single_name ());
196+ if (op != nullptr && op->lnames_ .empty () && op->snames_ .empty ()) {
197+ throw (OptionAlreadyAdded (" unable to disambiguate with existing option: " + test_name));
198+ }
199+ } else if (top_level_parent != this ) {
200+ for (auto &ln : myopt.lnames_ ) {
201+ const auto *op = top_level_parent->get_option_no_throw (ln);
202+ if (op != nullptr && op->get_configurable ()) {
203+ throw (OptionAlreadyAdded (" added option matches existing positional option: " + ln));
176204 }
177-
178- auto *op = get_option_no_throw (test_name);
205+ op = top_level_parent->get_option_no_throw (" --" + ln);
179206 if (op != nullptr && op->get_configurable ()) {
180- throw (OptionAlreadyAdded (" added option positional name matches existing option: " + test_name ));
207+ throw (OptionAlreadyAdded (" added option matches existing option: -- " + ln ));
181208 }
182- // need to check if there is another positional with the same name that also doesn't have any long or short
183- // names
184- op = get_option_no_throw (myopt. get_single_name () );
185- if (op != nullptr && op->lnames_ . empty () && op-> snames_ . empty ()) {
186- throw (OptionAlreadyAdded (" unable to disambiguate with existing option: " + test_name ));
209+ }
210+ for ( auto &sn : myopt. snames_ ) {
211+ const auto * op = top_level_parent-> get_option_no_throw (sn );
212+ if (op != nullptr && op->get_configurable ()) {
213+ throw (OptionAlreadyAdded (" added option matches existing positional option: " + sn ));
187214 }
188- } else if (parent_ != nullptr ) {
189- for (auto &ln : myopt.lnames_ ) {
190- auto *op = parent_->get_option_no_throw (ln);
191- if (op != nullptr && op->get_configurable ()) {
192- throw (OptionAlreadyAdded (" added option matches existing positional option: " + ln));
193- }
215+ op = top_level_parent->get_option_no_throw (" -" + sn);
216+ if (op != nullptr && op->get_configurable ()) {
217+ throw (OptionAlreadyAdded (" added option matches existing option: -" + sn));
194218 }
195- for (auto &sn : myopt.snames_ ) {
196- auto *op = parent_->get_option_no_throw (sn);
197- if (op != nullptr && op->get_configurable ()) {
198- throw (OptionAlreadyAdded (" added option matches existing positional option: " + sn));
219+ }
220+ }
221+ if (allow_non_standard_options_ && !myopt.snames_ .empty ()) {
222+
223+ for (auto &sname : myopt.snames_ ) {
224+ if (sname.length () > 1 ) {
225+ std::string test_name;
226+ test_name.push_back (' -' );
227+ test_name.push_back (sname.front ());
228+ const auto *op = top_level_parent->get_option_no_throw (test_name);
229+ if (op != nullptr ) {
230+ throw (OptionAlreadyAdded (" added option interferes with existing short option: " + sname));
199231 }
200232 }
201233 }
202- if (allow_non_standard_options_ && !myopt. snames_ . empty ()) {
203- for (auto &sname : myopt. snames_ ) {
204- if (sname. length () > 1 ) {
234+ for ( auto &opt : top_level_parent-> get_options ()) {
235+ for (const auto &osn : opt-> snames_ ) {
236+ if (osn. size () > 1 ) {
205237 std::string test_name;
206- test_name.push_back (' -' );
207- test_name.push_back (sname.front ());
208- auto *op = get_option_no_throw (test_name);
209- if (op != nullptr ) {
210- throw (OptionAlreadyAdded (" added option interferes with existing short option: " + sname));
211- }
212- }
213- }
214- for (auto &opt : options_) {
215- for (const auto &osn : opt->snames_ ) {
216- if (osn.size () > 1 ) {
217- std::string test_name;
218- test_name.push_back (osn.front ());
219- if (myopt.check_sname (test_name)) {
220- throw (OptionAlreadyAdded (" added option interferes with existing non standard option: " +
221- osn));
222- }
238+ test_name.push_back (osn.front ());
239+ if (myopt.check_sname (test_name)) {
240+ throw (OptionAlreadyAdded (" added option interferes with existing non standard option: " + osn));
223241 }
224242 }
225243 }
226244 }
227- options_.emplace_back ();
228- Option_p &option = options_.back ();
229- option.reset (new Option (option_name, option_description, option_callback, this , allow_non_standard_options_));
245+ }
246+ options_.emplace_back ();
247+ Option_p &option = options_.back ();
248+ option.reset (new Option (option_name, option_description, option_callback, this , allow_non_standard_options_));
230249
231- // Set the default string capture function
232- option->default_function (func);
250+ // Set the default string capture function
251+ option->default_function (func);
233252
234- // For compatibility with CLI11 1.7 and before, capture the default string here
235- if (defaulted)
236- option->capture_default_str ();
253+ // For compatibility with CLI11 1.7 and before, capture the default string here
254+ if (defaulted)
255+ option->capture_default_str ();
237256
238- // Transfer defaults to the new option
239- option_defaults_.copy_to (option.get ());
257+ // Transfer defaults to the new option
258+ option_defaults_.copy_to (option.get ());
240259
241- // Don't bother to capture if we already did
242- if (!defaulted && option->get_always_capture_default ())
243- option->capture_default_str ();
260+ // Don't bother to capture if we already did
261+ if (!defaulted && option->get_always_capture_default ())
262+ option->capture_default_str ();
244263
245- return option.get ();
246- }
247- // we know something matches now find what it is so we can produce more error information
248- for (auto &opt : options_) {
249- const auto &matchname = opt->matching_name (myopt);
250- if (!matchname.empty ()) {
251- throw (OptionAlreadyAdded (" added option matched existing option name: " + matchname));
252- }
253- }
254- // this line should not be reached the above loop should trigger the throw
255- throw (OptionAlreadyAdded (" added option matched existing option name" )); // LCOV_EXCL_LINE
264+ return option.get ();
256265}
257266
258267CLI11_INLINE Option *App::set_help_flag (std::string flag_name, const std::string &help_description) {
@@ -841,8 +850,8 @@ CLI11_INLINE std::vector<Option *> App::get_options(const std::function<bool(Opt
841850 std::end (options));
842851 }
843852 for (auto &subc : subcommands_) {
844- // also check down into nameless subcommands
845- if (subc->get_name ().empty () && !subc->get_group ().empty () && subc->get_group ().front () == ' +' ) {
853+ // also check down into nameless subcommands and specific groups
854+ if (subc->get_name ().empty () || ( !subc->get_group ().empty () && subc->get_group ().front () == ' +' ) ) {
846855 auto subcopts = subc->get_options (filter);
847856 options.insert (options.end (), subcopts.begin (), subcopts.end ());
848857 }
@@ -1539,7 +1548,8 @@ App::_add_flag_like_result(Option *op, const ConfigItem &item, const std::vector
15391548 return true ;
15401549 }
15411550 if (static_cast <int >(inputs.size ()) > op->get_items_expected_max () &&
1542- op->get_multi_option_policy () != MultiOptionPolicy::TakeAll) {
1551+ op->get_multi_option_policy () != MultiOptionPolicy::TakeAll &&
1552+ op->get_multi_option_policy () != MultiOptionPolicy::Join) {
15431553 if (op->get_items_expected_max () > 1 ) {
15441554 throw ArgumentMismatch::AtMost (item.fullname (), op->get_items_expected_max (), inputs.size ());
15451555 }
@@ -1608,11 +1618,6 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
16081618 }
16091619 if (op == nullptr ) {
16101620 op = get_option_no_throw (item.name );
1611- } else if (!op->get_configurable ()) {
1612- auto *testop = get_option_no_throw (item.name );
1613- if (testop != nullptr && testop->get_configurable ()) {
1614- op = testop;
1615- }
16161621 }
16171622 } else if (!op->get_configurable ()) {
16181623 if (item.name .size () == 1 ) {
@@ -1621,14 +1626,17 @@ CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t
16211626 op = testop;
16221627 }
16231628 }
1624- if (!op->get_configurable ()) {
1625- auto *testop = get_option_no_throw (item.name );
1626- if (testop != nullptr && testop->get_configurable ()) {
1627- op = testop;
1628- }
1629+ }
1630+ if (op == nullptr || !op->get_configurable ()) {
1631+ std::string iname = item.name ;
1632+ auto options = get_options ([iname](const CLI::Option *opt) {
1633+ return (opt->get_configurable () &&
1634+ (opt->check_name (iname) || opt->check_lname (iname) || opt->check_sname (iname)));
1635+ });
1636+ if (!options.empty ()) {
1637+ op = options[0 ];
16291638 }
16301639 }
1631-
16321640 if (op == nullptr ) {
16331641 // If the option was not present
16341642 if (get_allow_config_extras () == config_extras_mode::capture) {
@@ -1785,7 +1793,9 @@ CLI11_INLINE bool App::_parse_positional(std::vector<std::string> &args, bool ha
17851793 posOpt->add_result (std::string{});
17861794 }
17871795 }
1796+ results_t prev;
17881797 if (posOpt->get_trigger_on_parse () && posOpt->current_option_state_ == Option::option_state::callback_run) {
1798+ prev = posOpt->results ();
17891799 posOpt->clear ();
17901800 }
17911801 if (posOpt->get_expected_min () == 0 ) {
@@ -1801,6 +1811,10 @@ CLI11_INLINE bool App::_parse_positional(std::vector<std::string> &args, bool ha
18011811 if (posOpt->get_trigger_on_parse ()) {
18021812 if (!posOpt->empty ()) {
18031813 posOpt->run_callback ();
1814+ } else {
1815+ if (!prev.empty ()) {
1816+ posOpt->add_result (prev);
1817+ }
18041818 }
18051819 }
18061820
0 commit comments