@@ -122,23 +122,19 @@ generate_parents(const std::string §ion, std::string &name, char parentSepar
122122 std::vector<std::string> parents;
123123 if (detail::to_lower (section) != " default" ) {
124124 if (section.find (parentSeparator) != std::string::npos) {
125- parents = detail::split (section, parentSeparator);
125+ parents = detail::split_up (section, parentSeparator);
126126 } else {
127127 parents = {section};
128128 }
129129 }
130130 if (name.find (parentSeparator) != std::string::npos) {
131- std::vector<std::string> plist = detail::split (name, parentSeparator);
131+ std::vector<std::string> plist = detail::split_up (name, parentSeparator);
132132 name = plist.back ();
133- detail::remove_quotes (name);
134133 plist.pop_back ();
135134 parents.insert (parents.end (), plist.begin (), plist.end ());
136135 }
137-
138136 // clean up quotes on the parents
139- for (auto &parent : parents) {
140- detail::remove_quotes (parent);
141- }
137+ detail::remove_quotes (parents);
142138 return parents;
143139}
144140
@@ -218,10 +214,10 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
218214 char aSep = (isINIArray && arraySeparator == ' ' ) ? ' ,' : arraySeparator;
219215 int currentSectionIndex{0 };
220216
217+ std::string line_sep_chars{parentSeparatorChar, commentChar, valueDelimiter};
221218 while (getline (input, buffer)) {
222219 std::vector<std::string> items_buffer;
223220 std::string name;
224- bool literalName{false };
225221 line = detail::trim_copy (buffer);
226222 std::size_t len = line.length ();
227223 // lines have to be at least 3 characters to have any meaning to CLI just skip the rest
@@ -275,8 +271,21 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
275271 continue ;
276272 }
277273 std::size_t search_start = 0 ;
278- if (line.front () == stringQuote || line.front () == literalQuote || line.front () == ' `' ) {
279- search_start = detail::close_sequence (line, 0 , line.front ());
274+ if (line.find_first_of (" \" '`" ) != std::string::npos) {
275+ while (search_start < line.size ()) {
276+ auto test_char = line[search_start];
277+ if (test_char == ' \" ' || test_char == ' \' ' || test_char == ' `' ) {
278+ search_start = detail::close_sequence (line, search_start, line[search_start]);
279+ ++search_start;
280+ } else if (test_char == valueDelimiter || test_char == commentChar) {
281+ --search_start;
282+ break ;
283+ } else if (test_char == ' ' || test_char == ' \t ' || test_char == parentSeparatorChar) {
284+ ++search_start;
285+ } else {
286+ search_start = line.find_first_of (line_sep_chars, search_start);
287+ }
288+ }
280289 }
281290 // Find = in string, split and recombine
282291 auto delimiter_pos = line.find_first_of (valueDelimiter, search_start + 1 );
@@ -290,7 +299,7 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
290299 std::string item = detail::trim_copy (line.substr (delimiter_pos + 1 , std::string::npos));
291300 bool mlquote =
292301 (item.compare (0 , 3 , multiline_literal_quote) == 0 || item.compare (0 , 3 , multiline_string_quote) == 0 );
293- if (!mlquote && comment_pos != std::string::npos && !literalName ) {
302+ if (!mlquote && comment_pos != std::string::npos) {
294303 auto citems = detail::split_up (item, commentChar);
295304 item = detail::trim_copy (citems.front ());
296305 }
@@ -365,23 +374,18 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
365374 name = detail::trim_copy (line.substr (0 , comment_pos));
366375 items_buffer = {" true" };
367376 }
377+ std::vector<std::string> parents;
368378 try {
369- literalName = detail::process_quoted_string (name, stringQuote, literalQuote );
370-
379+ parents = detail::generate_parents (currentSection, name, parentSeparatorChar );
380+ detail::process_quoted_string (name);
371381 // clean up quotes on the items and check for escaped strings
372382 for (auto &it : items_buffer) {
373383 detail::process_quoted_string (it, stringQuote, literalQuote);
374384 }
375385 } catch (const std::invalid_argument &ia) {
376386 throw CLI::ParseError (ia.what (), CLI::ExitCodes::InvalidError);
377387 }
378- std::vector<std::string> parents;
379- if (literalName) {
380- std::string noname{};
381- parents = detail::generate_parents (currentSection, noname, parentSeparatorChar);
382- } else {
383- parents = detail::generate_parents (currentSection, name, parentSeparatorChar);
384- }
388+
385389 if (parents.size () > maximumLayers) {
386390 continue ;
387391 }
@@ -418,6 +422,23 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
418422 return output;
419423}
420424
425+ CLI11_INLINE std::string &clean_name_string (std::string &name, const std::string &keyChars) {
426+ if (name.find_first_of (keyChars) != std::string::npos || (name.front () == ' [' && name.back () == ' ]' ) ||
427+ (name.find_first_of (" '`\"\\ " ) != std::string::npos)) {
428+ if (name.find_first_of (' \' ' ) == std::string::npos) {
429+ name.insert (0 , 1 , ' \' ' );
430+ name.push_back (' \' ' );
431+ } else {
432+ if (detail::has_escapable_character (name)) {
433+ name = detail::add_escaped_characters (name);
434+ }
435+ name.insert (0 , 1 , ' \" ' );
436+ name.push_back (' \" ' );
437+ }
438+ }
439+ return name;
440+ }
441+
421442CLI11_INLINE std::string
422443ConfigBase::to_config (const App *app, bool default_also, bool write_description, std::string prefix) const {
423444 std::stringstream out;
@@ -429,6 +450,14 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
429450 commentTest.push_back (commentChar);
430451 commentTest.push_back (parentSeparatorChar);
431452
453+ std::string keyChars = commentTest;
454+ keyChars.push_back (literalQuote);
455+ keyChars.push_back (stringQuote);
456+ keyChars.push_back (arrayStart);
457+ keyChars.push_back (arrayEnd);
458+ keyChars.push_back (valueDelimiter);
459+ keyChars.push_back (arraySeparator);
460+
432461 std::vector<std::string> groups = app->get_groups ();
433462 bool defaultUsed = false ;
434463 groups.insert (groups.begin (), std::string (" Options" ));
@@ -498,24 +527,7 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
498527 out << ' \n ' ;
499528 out << commentLead << detail::fix_newlines (commentLead, opt->get_description ()) << ' \n ' ;
500529 }
501- if (single_name.find_first_of (commentTest) != std::string::npos ||
502- single_name.compare (0 , 3 , multiline_string_quote) == 0 ||
503- single_name.compare (0 , 3 , multiline_literal_quote) == 0 ||
504- (single_name.front () == ' [' && single_name.back () == ' ]' ) ||
505- (single_name.find_first_of (stringQuote) != std::string::npos) ||
506- (single_name.find_first_of (literalQuote) != std::string::npos) ||
507- (single_name.find_first_of (' `' ) != std::string::npos)) {
508- if (single_name.find_first_of (literalQuote) == std::string::npos) {
509- single_name.insert (0 , 1 , literalQuote);
510- single_name.push_back (literalQuote);
511- } else {
512- if (detail::has_escapable_character (single_name)) {
513- single_name = detail::add_escaped_characters (single_name);
514- }
515- single_name.insert (0 , 1 , stringQuote);
516- single_name.push_back (stringQuote);
517- }
518- }
530+ clean_name_string (single_name, keyChars);
519531
520532 std::string name = prefix + single_name;
521533
@@ -554,22 +566,29 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
554566 if (!default_also && (subcom->count_all () == 0 )) {
555567 continue ;
556568 }
569+ std::string subname = subcom->get_name ();
570+ clean_name_string (subname, keyChars);
571+
557572 if (subcom->get_configurable () && app->got_subcommand (subcom)) {
558573 if (!prefix.empty () || app->get_parent () == nullptr ) {
559- out << ' [' << prefix << subcom->get_name () << " ]\n " ;
574+
575+ out << ' [' << prefix << subname << " ]\n " ;
560576 } else {
561- std::string subname = app->get_name () + parentSeparatorChar + subcom->get_name ();
577+ std::string appname = app->get_name ();
578+ clean_name_string (appname, keyChars);
579+ subname = appname + parentSeparatorChar + subname;
562580 const auto *p = app->get_parent ();
563581 while (p->get_parent () != nullptr ) {
564- subname = p->get_name () + parentSeparatorChar + subname;
582+ std::string pname = p->get_name ();
583+ clean_name_string (pname, keyChars);
584+ subname = pname + parentSeparatorChar + subname;
565585 p = p->get_parent ();
566586 }
567587 out << ' [' << subname << " ]\n " ;
568588 }
569589 out << to_config (subcom, default_also, write_description, " " );
570590 } else {
571- out << to_config (
572- subcom, default_also, write_description, prefix + subcom->get_name () + parentSeparatorChar);
591+ out << to_config (subcom, default_also, write_description, prefix + subname + parentSeparatorChar);
573592 }
574593 }
575594 }
0 commit comments