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/Config.hpp>
0011 
0012 // [CLI11:public_includes:set]
0013 #include <algorithm>
0014 #include <string>
0015 #include <utility>
0016 #include <vector>
0017 // [CLI11:public_includes:end]
0018 
0019 namespace CLI {
0020 // [CLI11:config_inl_hpp:verbatim]
0021 
0022 namespace detail {
0023 
0024 CLI11_INLINE std::string convert_arg_for_ini(const std::string &arg, char stringQuote, char characterQuote) {
0025     if(arg.empty()) {
0026         return std::string(2, stringQuote);
0027     }
0028     // some specifically supported strings
0029     if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
0030         return arg;
0031     }
0032     // floating point conversion can convert some hex codes, but don't try that here
0033     if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
0034         using CLI::detail::lexical_cast;
0035         double val = 0.0;
0036         if(lexical_cast(arg, val)) {
0037             return arg;
0038         }
0039     }
0040     // just quote a single non numeric character
0041     if(arg.size() == 1) {
0042         return std::string(1, characterQuote) + arg + characterQuote;
0043     }
0044     // handle hex, binary or octal arguments
0045     if(arg.front() == '0') {
0046         if(arg[1] == 'x') {
0047             if(std::all_of(arg.begin() + 2, arg.end(), [](char x) {
0048                    return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f');
0049                })) {
0050                 return arg;
0051             }
0052         } else if(arg[1] == 'o') {
0053             if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) {
0054                 return arg;
0055             }
0056         } else if(arg[1] == 'b') {
0057             if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) {
0058                 return arg;
0059             }
0060         }
0061     }
0062     if(arg.find_first_of(stringQuote) == std::string::npos) {
0063         return std::string(1, stringQuote) + arg + stringQuote;
0064     }
0065     return characterQuote + arg + characterQuote;
0066 }
0067 
0068 CLI11_INLINE std::string ini_join(const std::vector<std::string> &args,
0069                                   char sepChar,
0070                                   char arrayStart,
0071                                   char arrayEnd,
0072                                   char stringQuote,
0073                                   char characterQuote) {
0074     std::string joined;
0075     if(args.size() > 1 && arrayStart != '\0') {
0076         joined.push_back(arrayStart);
0077     }
0078     std::size_t start = 0;
0079     for(const auto &arg : args) {
0080         if(start++ > 0) {
0081             joined.push_back(sepChar);
0082             if(!std::isspace<char>(sepChar, std::locale())) {
0083                 joined.push_back(' ');
0084             }
0085         }
0086         joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));
0087     }
0088     if(args.size() > 1 && arrayEnd != '\0') {
0089         joined.push_back(arrayEnd);
0090     }
0091     return joined;
0092 }
0093 
0094 CLI11_INLINE std::vector<std::string>
0095 generate_parents(const std::string &section, std::string &name, char parentSeparator) {
0096     std::vector<std::string> parents;
0097     if(detail::to_lower(section) != "default") {
0098         if(section.find(parentSeparator) != std::string::npos) {
0099             parents = detail::split(section, parentSeparator);
0100         } else {
0101             parents = {section};
0102         }
0103     }
0104     if(name.find(parentSeparator) != std::string::npos) {
0105         std::vector<std::string> plist = detail::split(name, parentSeparator);
0106         name = plist.back();
0107         detail::remove_quotes(name);
0108         plist.pop_back();
0109         parents.insert(parents.end(), plist.begin(), plist.end());
0110     }
0111 
0112     // clean up quotes on the parents
0113     for(auto &parent : parents) {
0114         detail::remove_quotes(parent);
0115     }
0116     return parents;
0117 }
0118 
0119 CLI11_INLINE void
0120 checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator) {
0121 
0122     std::string estring;
0123     auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
0124     if(!output.empty() && output.back().name == "--") {
0125         std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
0126         while(output.back().parents.size() >= msize) {
0127             output.push_back(output.back());
0128             output.back().parents.pop_back();
0129         }
0130 
0131         if(parents.size() > 1) {
0132             std::size_t common = 0;
0133             std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
0134             for(std::size_t ii = 0; ii < mpair; ++ii) {
0135                 if(output.back().parents[ii] != parents[ii]) {
0136                     break;
0137                 }
0138                 ++common;
0139             }
0140             if(common == mpair) {
0141                 output.pop_back();
0142             } else {
0143                 while(output.back().parents.size() > common + 1) {
0144                     output.push_back(output.back());
0145                     output.back().parents.pop_back();
0146                 }
0147             }
0148             for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
0149                 output.emplace_back();
0150                 output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
0151                 output.back().name = "++";
0152             }
0153         }
0154     } else if(parents.size() > 1) {
0155         for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
0156             output.emplace_back();
0157             output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
0158             output.back().name = "++";
0159         }
0160     }
0161 
0162     // insert a section end which is just an empty items_buffer
0163     output.emplace_back();
0164     output.back().parents = std::move(parents);
0165     output.back().name = "++";
0166 }
0167 }  // namespace detail
0168 
0169 inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
0170     std::string line;
0171     std::string currentSection = "default";
0172     std::string previousSection = "default";
0173     std::vector<ConfigItem> output;
0174     bool isDefaultArray = (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');
0175     bool isINIArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
0176     bool inSection{false};
0177     char aStart = (isINIArray) ? '[' : arrayStart;
0178     char aEnd = (isINIArray) ? ']' : arrayEnd;
0179     char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;
0180     int currentSectionIndex{0};
0181     while(getline(input, line)) {
0182         std::vector<std::string> items_buffer;
0183         std::string name;
0184 
0185         detail::trim(line);
0186         std::size_t len = line.length();
0187         // lines have to be at least 3 characters to have any meaning to CLI just skip the rest
0188         if(len < 3) {
0189             continue;
0190         }
0191         if(line.front() == '[' && line.back() == ']') {
0192             if(currentSection != "default") {
0193                 // insert a section end which is just an empty items_buffer
0194                 output.emplace_back();
0195                 output.back().parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
0196                 output.back().name = "--";
0197             }
0198             currentSection = line.substr(1, len - 2);
0199             // deal with double brackets for TOML
0200             if(currentSection.size() > 1 && currentSection.front() == '[' && currentSection.back() == ']') {
0201                 currentSection = currentSection.substr(1, currentSection.size() - 2);
0202             }
0203             if(detail::to_lower(currentSection) == "default") {
0204                 currentSection = "default";
0205             } else {
0206                 detail::checkParentSegments(output, currentSection, parentSeparatorChar);
0207             }
0208             inSection = false;
0209             if(currentSection == previousSection) {
0210                 ++currentSectionIndex;
0211             } else {
0212                 currentSectionIndex = 0;
0213                 previousSection = currentSection;
0214             }
0215             continue;
0216         }
0217 
0218         // comment lines
0219         if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
0220             continue;
0221         }
0222 
0223         // Find = in string, split and recombine
0224         auto pos = line.find(valueDelimiter);
0225         if(pos != std::string::npos) {
0226             name = detail::trim_copy(line.substr(0, pos));
0227             std::string item = detail::trim_copy(line.substr(pos + 1));
0228             auto cloc = item.find(commentChar);
0229             if(cloc != std::string::npos) {
0230                 item.erase(cloc, std::string::npos);  // NOLINT(readability-suspicious-call-argument)
0231                 detail::trim(item);
0232             }
0233             if(item.size() > 1 && item.front() == aStart) {
0234                 for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
0235                     detail::trim(multiline);
0236                     item += multiline;
0237                 }
0238                 items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
0239             } else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
0240                 items_buffer = detail::split_up(item, aSep);
0241             } else if((isDefaultArray || isINIArray) && item.find_first_of(' ') != std::string::npos) {
0242                 items_buffer = detail::split_up(item);
0243             } else {
0244                 items_buffer = {item};
0245             }
0246         } else {
0247             name = detail::trim_copy(line);
0248             auto cloc = name.find(commentChar);
0249             if(cloc != std::string::npos) {
0250                 name.erase(cloc, std::string::npos);  // NOLINT(readability-suspicious-call-argument)
0251                 detail::trim(name);
0252             }
0253 
0254             items_buffer = {"true"};
0255         }
0256         if(name.find(parentSeparatorChar) == std::string::npos) {
0257             detail::remove_quotes(name);
0258         }
0259         // clean up quotes on the items
0260         for(auto &it : items_buffer) {
0261             detail::remove_quotes(it);
0262         }
0263 
0264         std::vector<std::string> parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
0265         if(parents.size() > maximumLayers) {
0266             continue;
0267         }
0268         if(!configSection.empty() && !inSection) {
0269             if(parents.empty() || parents.front() != configSection) {
0270                 continue;
0271             }
0272             if(configIndex >= 0 && currentSectionIndex != configIndex) {
0273                 continue;
0274             }
0275             parents.erase(parents.begin());
0276             inSection = true;
0277         }
0278         if(!output.empty() && name == output.back().name && parents == output.back().parents) {
0279             output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end());
0280         } else {
0281             output.emplace_back();
0282             output.back().parents = std::move(parents);
0283             output.back().name = std::move(name);
0284             output.back().inputs = std::move(items_buffer);
0285         }
0286     }
0287     if(currentSection != "default") {
0288         // insert a section end which is just an empty items_buffer
0289         std::string ename;
0290         output.emplace_back();
0291         output.back().parents = detail::generate_parents(currentSection, ename, parentSeparatorChar);
0292         output.back().name = "--";
0293         while(output.back().parents.size() > 1) {
0294             output.push_back(output.back());
0295             output.back().parents.pop_back();
0296         }
0297     }
0298     return output;
0299 }
0300 
0301 CLI11_INLINE std::string
0302 ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
0303     std::stringstream out;
0304     std::string commentLead;
0305     commentLead.push_back(commentChar);
0306     commentLead.push_back(' ');
0307 
0308     std::vector<std::string> groups = app->get_groups();
0309     bool defaultUsed = false;
0310     groups.insert(groups.begin(), std::string("Options"));
0311     if(write_description && (app->get_configurable() || app->get_parent() == nullptr || app->get_name().empty())) {
0312         out << commentLead << detail::fix_newlines(commentLead, app->get_description()) << '\n';
0313     }
0314     for(auto &group : groups) {
0315         if(group == "Options" || group.empty()) {
0316             if(defaultUsed) {
0317                 continue;
0318             }
0319             defaultUsed = true;
0320         }
0321         if(write_description && group != "Options" && !group.empty()) {
0322             out << '\n' << commentLead << group << " Options\n";
0323         }
0324         for(const Option *opt : app->get_options({})) {
0325 
0326             // Only process options that are configurable
0327             if(opt->get_configurable()) {
0328                 if(opt->get_group() != group) {
0329                     if(!(group == "Options" && opt->get_group().empty())) {
0330                         continue;
0331                     }
0332                 }
0333                 std::string name = prefix + opt->get_single_name();
0334                 std::string value = detail::ini_join(
0335                     opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote);
0336 
0337                 if(value.empty() && default_also) {
0338                     if(!opt->get_default_str().empty()) {
0339                         value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote);
0340                     } else if(opt->get_expected_min() == 0) {
0341                         value = "false";
0342                     } else if(opt->get_run_callback_for_default()) {
0343                         value = "\"\"";  // empty string default value
0344                     }
0345                 }
0346 
0347                 if(!value.empty()) {
0348                     if(!opt->get_fnames().empty()) {
0349                         value = opt->get_flag_value(name, value);
0350                     }
0351                     if(write_description && opt->has_description()) {
0352                         out << '\n';
0353                         out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
0354                     }
0355                     out << name << valueDelimiter << value << '\n';
0356                 }
0357             }
0358         }
0359     }
0360     auto subcommands = app->get_subcommands({});
0361     for(const App *subcom : subcommands) {
0362         if(subcom->get_name().empty()) {
0363             if(write_description && !subcom->get_group().empty()) {
0364                 out << '\n' << commentLead << subcom->get_group() << " Options\n";
0365             }
0366             out << to_config(subcom, default_also, write_description, prefix);
0367         }
0368     }
0369 
0370     for(const App *subcom : subcommands) {
0371         if(!subcom->get_name().empty()) {
0372             if(subcom->get_configurable() && app->got_subcommand(subcom)) {
0373                 if(!prefix.empty() || app->get_parent() == nullptr) {
0374                     out << '[' << prefix << subcom->get_name() << "]\n";
0375                 } else {
0376                     std::string subname = app->get_name() + parentSeparatorChar + subcom->get_name();
0377                     const auto *p = app->get_parent();
0378                     while(p->get_parent() != nullptr) {
0379                         subname = p->get_name() + parentSeparatorChar + subname;
0380                         p = p->get_parent();
0381                     }
0382                     out << '[' << subname << "]\n";
0383                 }
0384                 out << to_config(subcom, default_also, write_description, "");
0385             } else {
0386                 out << to_config(
0387                     subcom, default_also, write_description, prefix + subcom->get_name() + parentSeparatorChar);
0388             }
0389         }
0390     }
0391 
0392     return out.str();
0393 }
0394 // [CLI11:config_inl_hpp:end]
0395 }  // namespace CLI