Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-14 09:04:07

0001 // Copyright (c) 2017-2025, 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 // IWYU pragma: private, include "CLI/CLI.hpp"
0010 
0011 // This include is only needed for IDEs to discover symbols
0012 #include "../Config.hpp"
0013 
0014 // [CLI11:public_includes:set]
0015 #include <algorithm>
0016 #include <string>
0017 #include <utility>
0018 #include <vector>
0019 // [CLI11:public_includes:end]
0020 
0021 namespace CLI {
0022 // [CLI11:config_inl_hpp:verbatim]
0023 
0024 static constexpr auto multiline_literal_quote = R"(''')";
0025 static constexpr auto multiline_string_quote = R"(""")";
0026 
0027 namespace detail {
0028 
0029 CLI11_INLINE bool is_printable(const std::string &test_string) {
0030     return std::all_of(test_string.begin(), test_string.end(), [](char x) {
0031         return (isprint(static_cast<unsigned char>(x)) != 0 || x == '\n' || x == '\t');
0032     });
0033 }
0034 
0035 CLI11_INLINE std::string
0036 convert_arg_for_ini(const std::string &arg, char stringQuote, char literalQuote, bool disable_multi_line) {
0037     if(arg.empty()) {
0038         return std::string(2, stringQuote);
0039     }
0040     // some specifically supported strings
0041     if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
0042         return arg;
0043     }
0044     // floating point conversion can convert some hex codes, but don't try that here
0045     if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
0046         using CLI::detail::lexical_cast;
0047         double val = 0.0;
0048         if(lexical_cast(arg, val)) {
0049             if(arg.find_first_not_of("0123456789.-+eE") == std::string::npos) {
0050                 return arg;
0051             }
0052         }
0053     }
0054     // just quote a single non numeric character
0055     if(arg.size() == 1) {
0056         if(isprint(static_cast<unsigned char>(arg.front())) == 0) {
0057             return binary_escape_string(arg);
0058         }
0059         if(arg == "'") {
0060             return std::string(1, stringQuote) + "'" + stringQuote;
0061         }
0062         return std::string(1, literalQuote) + arg + literalQuote;
0063     }
0064     // handle hex, binary or octal arguments
0065     if(arg.front() == '0') {
0066         if(arg[1] == 'x') {
0067             if(std::all_of(arg.begin() + 2, arg.end(), [](char x) {
0068                    return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f');
0069                })) {
0070                 return arg;
0071             }
0072         } else if(arg[1] == 'o') {
0073             if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) {
0074                 return arg;
0075             }
0076         } else if(arg[1] == 'b') {
0077             if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) {
0078                 return arg;
0079             }
0080         }
0081     }
0082     if(!is_printable(arg)) {
0083         return binary_escape_string(arg);
0084     }
0085     if(detail::has_escapable_character(arg)) {
0086         if(arg.size() > 100 && !disable_multi_line) {
0087             if(arg.find(multiline_literal_quote) != std::string::npos) {
0088                 return binary_escape_string(arg, true);
0089             }
0090             std::string return_string{multiline_literal_quote};
0091             return_string.reserve(7 + arg.size());
0092             if(arg.front() == '\n' || arg.front() == '\r') {
0093                 return_string.push_back('\n');
0094             }
0095             return_string.append(arg);
0096             if(arg.back() == '\n' || arg.back() == '\r') {
0097                 return_string.push_back('\n');
0098             }
0099             return_string.append(multiline_literal_quote, 3);
0100             return return_string;
0101         }
0102         return std::string(1, stringQuote) + detail::add_escaped_characters(arg) + stringQuote;
0103     }
0104     return std::string(1, stringQuote) + arg + stringQuote;
0105 }
0106 
0107 CLI11_INLINE std::string ini_join(const std::vector<std::string> &args,
0108                                   char sepChar,
0109                                   char arrayStart,
0110                                   char arrayEnd,
0111                                   char stringQuote,
0112                                   char literalQuote) {
0113     bool disable_multi_line{false};
0114     std::string joined;
0115     if(args.size() > 1 && arrayStart != '\0') {
0116         joined.push_back(arrayStart);
0117         disable_multi_line = true;
0118     }
0119     std::size_t start = 0;
0120     for(const auto &arg : args) {
0121         if(start++ > 0) {
0122             joined.push_back(sepChar);
0123             if(!std::isspace<char>(sepChar, std::locale())) {
0124                 joined.push_back(' ');
0125             }
0126         }
0127         joined.append(convert_arg_for_ini(arg, stringQuote, literalQuote, disable_multi_line));
0128     }
0129     if(args.size() > 1 && arrayEnd != '\0') {
0130         joined.push_back(arrayEnd);
0131     }
0132     return joined;
0133 }
0134 
0135 CLI11_INLINE std::vector<std::string>
0136 generate_parents(const std::string &section, std::string &name, char parentSeparator) {
0137     std::vector<std::string> parents;
0138     if(detail::to_lower(section) != "default") {
0139         if(section.find(parentSeparator) != std::string::npos) {
0140             parents = detail::split_up(section, parentSeparator);
0141         } else {
0142             parents = {section};
0143         }
0144     }
0145     if(name.find(parentSeparator) != std::string::npos) {
0146         std::vector<std::string> plist = detail::split_up(name, parentSeparator);
0147         name = plist.back();
0148         plist.pop_back();
0149         parents.insert(parents.end(), plist.begin(), plist.end());
0150     }
0151     // clean up quotes on the parents
0152     try {
0153         detail::remove_quotes(parents);
0154     } catch(const std::invalid_argument &iarg) {
0155         throw CLI::ParseError(iarg.what(), CLI::ExitCodes::InvalidError);
0156     }
0157     return parents;
0158 }
0159 
0160 CLI11_INLINE void
0161 checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator) {
0162 
0163     std::string estring;
0164     auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
0165     if(!output.empty() && output.back().name == "--") {
0166         std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
0167         while(output.back().parents.size() >= msize) {
0168             output.push_back(output.back());
0169             output.back().parents.pop_back();
0170         }
0171 
0172         if(parents.size() > 1) {
0173             std::size_t common = 0;
0174             std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
0175             for(std::size_t ii = 0; ii < mpair; ++ii) {
0176                 if(output.back().parents[ii] != parents[ii]) {
0177                     break;
0178                 }
0179                 ++common;
0180             }
0181             if(common == mpair) {
0182                 output.pop_back();
0183             } else {
0184                 while(output.back().parents.size() > common + 1) {
0185                     output.push_back(output.back());
0186                     output.back().parents.pop_back();
0187                 }
0188             }
0189             for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
0190                 output.emplace_back();
0191                 output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
0192                 output.back().name = "++";
0193             }
0194         }
0195     } else if(parents.size() > 1) {
0196         for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
0197             output.emplace_back();
0198             output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
0199             output.back().name = "++";
0200         }
0201     }
0202 
0203     // insert a section end which is just an empty items_buffer
0204     output.emplace_back();
0205     output.back().parents = std::move(parents);
0206     output.back().name = "++";
0207 }
0208 
0209 /// @brief  checks if a string represents a multiline comment
0210 CLI11_INLINE bool hasMLString(std::string const &fullString, char check) {
0211     if(fullString.length() < 3) {
0212         return false;
0213     }
0214     auto it = fullString.rbegin();
0215     return (*it == check) && (*(it + 1) == check) && (*(it + 2) == check);
0216 }
0217 
0218 /// @brief  find a matching configItem in a list
0219 inline auto find_matching_config(std::vector<ConfigItem> &items,
0220                                  const std::vector<std::string> &parents,
0221                                  const std::string &name,
0222                                  bool fullSearch) -> decltype(items.begin()) {
0223     if(items.empty()) {
0224         return items.end();
0225     }
0226     auto search = items.end() - 1;
0227     do {
0228         if(search->parents == parents && search->name == name) {
0229             return search;
0230         }
0231         if(search == items.begin()) {
0232             break;
0233         }
0234         --search;
0235     } while(fullSearch);
0236     return items.end();
0237 }
0238 }  // namespace detail
0239 
0240 inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
0241     std::string line;
0242     std::string buffer;
0243     std::string currentSection = "default";
0244     std::string previousSection = "default";
0245     std::vector<ConfigItem> output;
0246     bool isDefaultArray = (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');
0247     bool isINIArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
0248     bool inSection{false};
0249     bool inMLineComment{false};
0250     bool inMLineValue{false};
0251 
0252     char aStart = (isINIArray) ? '[' : arrayStart;
0253     char aEnd = (isINIArray) ? ']' : arrayEnd;
0254     char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;
0255     int currentSectionIndex{0};
0256 
0257     std::string line_sep_chars{parentSeparatorChar, commentChar, valueDelimiter};
0258     while(getline(input, buffer)) {
0259         std::vector<std::string> items_buffer;
0260         std::string name;
0261         line = detail::trim_copy(buffer);
0262         std::size_t len = line.length();
0263         // lines have to be at least 3 characters to have any meaning to CLI just skip the rest
0264         if(len < 3) {
0265             continue;
0266         }
0267         if(line.compare(0, 3, multiline_string_quote) == 0 || line.compare(0, 3, multiline_literal_quote) == 0) {
0268             inMLineComment = true;
0269             auto cchar = line.front();
0270             while(inMLineComment) {
0271                 if(getline(input, line)) {
0272                     detail::trim(line);
0273                 } else {
0274                     break;
0275                 }
0276                 if(detail::hasMLString(line, cchar)) {
0277                     inMLineComment = false;
0278                 }
0279             }
0280             continue;
0281         }
0282         if(line.front() == '[' && line.back() == ']') {
0283             if(currentSection != "default") {
0284                 // insert a section end which is just an empty items_buffer
0285                 output.emplace_back();
0286                 output.back().parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
0287                 output.back().name = "--";
0288             }
0289             currentSection = line.substr(1, len - 2);
0290             // deal with double brackets for TOML
0291             if(currentSection.size() > 1 && currentSection.front() == '[' && currentSection.back() == ']') {
0292                 currentSection = currentSection.substr(1, currentSection.size() - 2);
0293             }
0294             if(detail::to_lower(currentSection) == "default") {
0295                 currentSection = "default";
0296             } else {
0297                 detail::checkParentSegments(output, currentSection, parentSeparatorChar);
0298             }
0299             inSection = false;
0300             if(currentSection == previousSection) {
0301                 ++currentSectionIndex;
0302             } else {
0303                 currentSectionIndex = 0;
0304                 previousSection = currentSection;
0305             }
0306             continue;
0307         }
0308 
0309         // comment lines
0310         if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
0311             continue;
0312         }
0313         std::size_t search_start = 0;
0314         if(line.find_first_of("\"'`") != std::string::npos) {
0315             while(search_start < line.size()) {
0316                 auto test_char = line[search_start];
0317                 if(test_char == '\"' || test_char == '\'' || test_char == '`') {
0318                     search_start = detail::close_sequence(line, search_start, line[search_start]);
0319                     ++search_start;
0320                 } else if(test_char == valueDelimiter || test_char == commentChar) {
0321                     --search_start;
0322                     break;
0323                 } else if(test_char == ' ' || test_char == '\t' || test_char == parentSeparatorChar) {
0324                     ++search_start;
0325                 } else {
0326                     search_start = line.find_first_of(line_sep_chars, search_start);
0327                 }
0328             }
0329         }
0330         // Find = in string, split and recombine
0331         auto delimiter_pos = line.find_first_of(valueDelimiter, search_start + 1);
0332         auto comment_pos = line.find_first_of(commentChar, search_start);
0333         if(comment_pos < delimiter_pos) {
0334             delimiter_pos = std::string::npos;
0335         }
0336         if(delimiter_pos != std::string::npos) {
0337 
0338             name = detail::trim_copy(line.substr(0, delimiter_pos));
0339             std::string item = detail::trim_copy(line.substr(delimiter_pos + 1, std::string::npos));
0340             bool mlquote =
0341                 (item.compare(0, 3, multiline_literal_quote) == 0 || item.compare(0, 3, multiline_string_quote) == 0);
0342             if(!mlquote && comment_pos != std::string::npos) {
0343                 auto citems = detail::split_up(item, commentChar);
0344                 item = detail::trim_copy(citems.front());
0345             }
0346             if(mlquote) {
0347                 // multiline string
0348                 auto keyChar = item.front();
0349                 item = buffer.substr(delimiter_pos + 1, std::string::npos);
0350                 detail::ltrim(item);
0351                 item.erase(0, 3);
0352                 inMLineValue = true;
0353                 bool lineExtension{false};
0354                 bool firstLine = true;
0355                 if(!item.empty() && item.back() == '\\' && keyChar == '\"') {
0356                     item.pop_back();
0357                     lineExtension = true;
0358                 } else if(detail::hasMLString(item, keyChar)) {
0359                     // deal with the first line closing the multiline literal
0360                     item.pop_back();
0361                     item.pop_back();
0362                     item.pop_back();
0363                     if(keyChar == '\"') {
0364                         try {
0365                             item = detail::remove_escaped_characters(item);
0366                         } catch(const std::invalid_argument &iarg) {
0367                             throw CLI::ParseError(iarg.what(), CLI::ExitCodes::InvalidError);
0368                         }
0369                     }
0370                     inMLineValue = false;
0371                 }
0372                 while(inMLineValue) {
0373                     std::string l2;
0374                     if(!std::getline(input, l2)) {
0375                         break;
0376                     }
0377                     line = l2;
0378                     detail::rtrim(line);
0379                     if(detail::hasMLString(line, keyChar)) {
0380                         line.pop_back();
0381                         line.pop_back();
0382                         line.pop_back();
0383                         if(lineExtension) {
0384                             detail::ltrim(line);
0385                         } else if(!(firstLine && item.empty())) {
0386                             item.push_back('\n');
0387                         }
0388                         firstLine = false;
0389                         item += line;
0390                         inMLineValue = false;
0391                         if(!item.empty() && item.back() == '\n') {
0392                             item.pop_back();
0393                         }
0394                         if(keyChar == '\"') {
0395                             try {
0396                                 item = detail::remove_escaped_characters(item);
0397                             } catch(const std::invalid_argument &iarg) {
0398                                 throw CLI::ParseError(iarg.what(), CLI::ExitCodes::InvalidError);
0399                             }
0400                         }
0401                     } else {
0402                         if(lineExtension) {
0403                             detail::trim(l2);
0404                         } else if(!(firstLine && item.empty())) {
0405                             item.push_back('\n');
0406                         }
0407                         lineExtension = false;
0408                         firstLine = false;
0409                         if(!l2.empty() && l2.back() == '\\' && keyChar == '\"') {
0410                             lineExtension = true;
0411                             l2.pop_back();
0412                         }
0413                         item += l2;
0414                     }
0415                 }
0416                 items_buffer = {item};
0417             } else if(!item.empty() && item.front() == aStart) {
0418                 for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
0419                     detail::trim(multiline);
0420                     item += multiline;
0421                 }
0422                 if(item.back() == aEnd) {
0423                     items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
0424                 } else {
0425                     items_buffer = detail::split_up(item.substr(1, std::string::npos), aSep);
0426                 }
0427             } else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
0428                 items_buffer = detail::split_up(item, aSep);
0429             } else if((isDefaultArray || isINIArray) && item.find_first_of(' ') != std::string::npos) {
0430                 items_buffer = detail::split_up(item, '\0');
0431             } else {
0432                 items_buffer = {item};
0433             }
0434         } else {
0435             name = detail::trim_copy(line.substr(0, comment_pos));
0436             items_buffer = {"true"};
0437         }
0438         std::vector<std::string> parents;
0439         try {
0440             parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
0441             detail::process_quoted_string(name, '"', '\'', true);
0442             // clean up quotes on the items and check for escaped strings
0443             for(auto &it : items_buffer) {
0444                 detail::process_quoted_string(it, stringQuote, literalQuote);
0445             }
0446         } catch(const std::invalid_argument &ia) {
0447             throw CLI::ParseError(ia.what(), CLI::ExitCodes::InvalidError);
0448         }
0449 
0450         if(parents.size() > maximumLayers) {
0451             continue;
0452         }
0453         if(!configSection.empty() && !inSection) {
0454             if(parents.empty() || parents.front() != configSection) {
0455                 continue;
0456             }
0457             if(configIndex >= 0 && currentSectionIndex != configIndex) {
0458                 continue;
0459             }
0460             parents.erase(parents.begin());
0461             inSection = true;
0462         }
0463         auto match = detail::find_matching_config(output, parents, name, allowMultipleDuplicateFields);
0464         if(match != output.end()) {
0465             if((match->inputs.size() > 1 && items_buffer.size() > 1) || allowMultipleDuplicateFields) {
0466                 // insert a separator if one is not already present
0467                 if(!(match->inputs.back().empty() || items_buffer.front().empty() || match->inputs.back() == "%%" ||
0468                      items_buffer.front() == "%%")) {
0469                     match->inputs.emplace_back("%%");
0470                     match->multiline = true;
0471                 }
0472             }
0473             match->inputs.insert(match->inputs.end(), items_buffer.begin(), items_buffer.end());
0474         } else {
0475             output.emplace_back();
0476             output.back().parents = std::move(parents);
0477             output.back().name = std::move(name);
0478             output.back().inputs = std::move(items_buffer);
0479         }
0480     }
0481     if(currentSection != "default") {
0482         // insert a section end which is just an empty items_buffer
0483         std::string ename;
0484         output.emplace_back();
0485         output.back().parents = detail::generate_parents(currentSection, ename, parentSeparatorChar);
0486         output.back().name = "--";
0487         while(output.back().parents.size() > 1) {
0488             output.push_back(output.back());
0489             output.back().parents.pop_back();
0490         }
0491     }
0492     return output;
0493 }
0494 
0495 CLI11_INLINE std::string &clean_name_string(std::string &name, const std::string &keyChars) {
0496     if(name.find_first_of(keyChars) != std::string::npos || (name.front() == '[' && name.back() == ']') ||
0497        (name.find_first_of("'`\"\\") != std::string::npos)) {
0498         if(name.find_first_of('\'') == std::string::npos) {
0499             name.insert(0, 1, '\'');
0500             name.push_back('\'');
0501         } else {
0502             if(detail::has_escapable_character(name)) {
0503                 name = detail::add_escaped_characters(name);
0504             }
0505             name.insert(0, 1, '\"');
0506             name.push_back('\"');
0507         }
0508     }
0509     return name;
0510 }
0511 
0512 CLI11_INLINE std::string
0513 ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
0514     std::stringstream out;
0515     std::string commentLead;
0516     commentLead.push_back(commentChar);
0517     commentLead.push_back(' ');
0518 
0519     std::string commentTest = "#;";
0520     commentTest.push_back(commentChar);
0521     commentTest.push_back(parentSeparatorChar);
0522 
0523     std::string keyChars = commentTest;
0524     keyChars.push_back(literalQuote);
0525     keyChars.push_back(stringQuote);
0526     keyChars.push_back(arrayStart);
0527     keyChars.push_back(arrayEnd);
0528     keyChars.push_back(valueDelimiter);
0529     keyChars.push_back(arraySeparator);
0530 
0531     std::vector<std::string> groups = app->get_groups();
0532     bool defaultUsed = false;
0533     groups.insert(groups.begin(), std::string("OPTIONS"));
0534 
0535     for(auto &group : groups) {
0536         if(group == "OPTIONS" || group.empty()) {
0537             if(defaultUsed) {
0538                 continue;
0539             }
0540             defaultUsed = true;
0541         }
0542         if(write_description && group != "OPTIONS" && !group.empty()) {
0543             out << '\n' << commentChar << commentLead << group << " Options\n";
0544         }
0545         for(const Option *opt : app->get_options({})) {
0546             // Only process options that are configurable
0547             if(opt->get_configurable()) {
0548                 if(opt->get_group() != group) {
0549                     if(!(group == "OPTIONS" && opt->get_group().empty())) {
0550                         continue;
0551                     }
0552                 }
0553                 std::string single_name = opt->get_single_name();
0554                 if(single_name.empty()) {
0555                     continue;
0556                 }
0557 
0558                 auto results = opt->reduced_results();
0559                 if(results.size() > 1 && opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Reverse) {
0560                     std::reverse(results.begin(), results.end());
0561                 }
0562                 if(opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Sum && opt->count() >= 1 &&
0563                    results.size() == 1) {
0564                     // if the multi option policy is sum then there is a possibility of incorrect fields being produced
0565                     // best to just use the original data for config files
0566                     auto pos = opt->_validate(results[0], 0);
0567                     if(!pos.empty()) {
0568                         results = opt->results();
0569                     }
0570                 }
0571                 if(opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Join && opt->count() > 1) {
0572                     char delim = opt->get_delimiter();
0573                     if(delim == '\0') {
0574                         // this branch deals with a situation where the output would not be readable by a config file
0575                         results = opt->results();
0576                     } else {
0577                         // this branch deals with the case of the strings containing the delimiter itself or empty
0578                         // strings which would be interpreted incorrectly
0579                         auto delim_count = std::count(results[0].begin(), results[0].end(), delim);
0580                         if(results[0].back() == delim ||
0581                            static_cast<decltype(delim_count)>(opt->count()) < delim_count - 1 ||
0582                            results[0].find(std::string(2, delim)) != std::string::npos) {
0583                             results = opt->results();
0584                         }
0585                     }
0586                 }
0587                 std::string value;
0588 
0589                 if(opt->count() == 1 && results.size() == 2 && results.front() == "{}" && results.back() == "%%") {
0590                     // there is a catch to allow for {} to used as as string in the output
0591                     //  it will append a sequence terminator to the output so the lexical conversion handles it
0592                     //  correctly but that is meant for config files so when outputting for a config file we need to
0593                     //  makes sure to get the correct output
0594                     value = "\"{}\"";
0595                 } else {
0596                     value = detail::ini_join(results, arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
0597                 }
0598 
0599                 bool isDefault = false;
0600                 if(value.empty() && default_also) {
0601                     if(!opt->get_default_str().empty()) {
0602                         results_t res;
0603                         opt->results(res);
0604                         value = detail::ini_join(res, arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
0605                     } else if(opt->get_expected_min() == 0) {
0606                         value = "false";
0607                     } else if(opt->get_run_callback_for_default() || !opt->get_required()) {
0608                         value = "\"\"";  // empty string default value
0609                     } else {
0610                         value = "\"<REQUIRED>\"";
0611                     }
0612                     isDefault = true;
0613                 }
0614 
0615                 if(!value.empty()) {
0616                     if(!opt->get_fnames().empty()) {
0617                         try {
0618                             value = opt->get_flag_value(single_name, value);
0619                         } catch(const CLI::ArgumentMismatch &) {
0620                             bool valid{false};
0621                             for(const auto &test_name : opt->get_fnames()) {
0622                                 try {
0623                                     value = opt->get_flag_value(test_name, value);
0624                                     single_name = test_name;
0625                                     valid = true;
0626                                 } catch(const CLI::ArgumentMismatch &) {
0627                                     continue;
0628                                 }
0629                             }
0630                             if(!valid) {
0631                                 value = detail::ini_join(
0632                                     opt->results(), arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
0633                             }
0634                         }
0635                     }
0636                     if(write_description && opt->has_description()) {
0637                         if(out.tellp() != std::streampos(0)) {
0638                             out << '\n';
0639                         }
0640                         out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
0641                     }
0642                     clean_name_string(single_name, keyChars);
0643 
0644                     std::string name = prefix + single_name;
0645                     if(commentDefaultsBool && isDefault) {
0646                         name = commentChar + name;
0647                     }
0648                     out << name << valueDelimiter << value << '\n';
0649                 }
0650             }
0651         }
0652     }
0653 
0654     auto subcommands = app->get_subcommands({});
0655     for(const App *subcom : subcommands) {
0656         if(subcom->get_name().empty()) {
0657             if(!default_also && (subcom->count_all() == 0)) {
0658                 continue;
0659             }
0660             if(write_description && !subcom->get_group().empty()) {
0661                 out << '\n' << commentLead << subcom->get_group() << " Options\n";
0662             }
0663             /*if (!prefix.empty() || app->get_parent() == nullptr) {
0664                 out << '[' << prefix << "___"<< subcom->get_group() << "]\n";
0665             } else {
0666                 std::string subname = app->get_name() + parentSeparatorChar + "___"+subcom->get_group();
0667                 const auto *p = app->get_parent();
0668                 while(p->get_parent() != nullptr) {
0669                     subname = p->get_name() + parentSeparatorChar +subname;
0670                     p = p->get_parent();
0671                 }
0672                 out << '[' << subname << "]\n";
0673             }
0674             */
0675             out << to_config(subcom, default_also, write_description, prefix);
0676         }
0677     }
0678 
0679     for(const App *subcom : subcommands) {
0680         if(!subcom->get_name().empty()) {
0681             if(!default_also && (subcom->count_all() == 0)) {
0682                 continue;
0683             }
0684             std::string subname = subcom->get_name();
0685             clean_name_string(subname, keyChars);
0686 
0687             if(subcom->get_configurable() && (default_also || app->got_subcommand(subcom))) {
0688                 if(!prefix.empty() || app->get_parent() == nullptr) {
0689 
0690                     out << '[' << prefix << subname << "]\n";
0691                 } else {
0692                     std::string appname = app->get_name();
0693                     clean_name_string(appname, keyChars);
0694                     subname = appname + parentSeparatorChar + subname;
0695                     const auto *p = app->get_parent();
0696                     while(p->get_parent() != nullptr) {
0697                         std::string pname = p->get_name();
0698                         clean_name_string(pname, keyChars);
0699                         subname = pname + parentSeparatorChar + subname;
0700                         p = p->get_parent();
0701                     }
0702                     out << '[' << subname << "]\n";
0703                 }
0704                 out << to_config(subcom, default_also, write_description, "");
0705             } else {
0706                 out << to_config(subcom, default_also, write_description, prefix + subname + parentSeparatorChar);
0707             }
0708         }
0709     }
0710 
0711     if(write_description && !out.str().empty()) {
0712         std::string outString =
0713             commentChar + commentLead + detail::fix_newlines(commentChar + commentLead, app->get_description()) + '\n';
0714         return outString + out.str();
0715     }
0716     return out.str();
0717 }
0718 // [CLI11:config_inl_hpp:end]
0719 }  // namespace CLI