Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-09-28 07:02:17

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