Commit 003e82579ea7fb460035e1343cfcafe60895b210

Authored by Daniel Herrera Castro
Committed by Henry Schreiner
1 parent 3e5173d4

[precompile] Split Config.hpp

CLI11.hpp.in
... ... @@ -68,6 +68,8 @@ namespace {namespace} {{
68 68  
69 69 {config_hpp}
70 70  
  71 +{config_inl_hpp}
  72 +
71 73 {formatter_hpp}
72 74  
73 75 }} // namespace {namespace}
... ...
include/CLI/Config.hpp
... ... @@ -24,373 +24,25 @@ namespace CLI {
24 24 // [CLI11:config_hpp:verbatim]
25 25 namespace detail {
26 26  
27   -inline std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'') {
28   - if(arg.empty()) {
29   - return std::string(2, stringQuote);
30   - }
31   - // some specifically supported strings
32   - if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
33   - return arg;
34   - }
35   - // floating point conversion can convert some hex codes, but don't try that here
36   - if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
37   - double val = 0.0;
38   - if(detail::lexical_cast(arg, val)) {
39   - return arg;
40   - }
41   - }
42   - // just quote a single non numeric character
43   - if(arg.size() == 1) {
44   - return std::string(1, characterQuote) + arg + characterQuote;
45   - }
46   - // handle hex, binary or octal arguments
47   - if(arg.front() == '0') {
48   - if(arg[1] == 'x') {
49   - if(std::all_of(arg.begin() + 2, arg.end(), [](char x) {
50   - return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f');
51   - })) {
52   - return arg;
53   - }
54   - } else if(arg[1] == 'o') {
55   - if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) {
56   - return arg;
57   - }
58   - } else if(arg[1] == 'b') {
59   - if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) {
60   - return arg;
61   - }
62   - }
63   - }
64   - if(arg.find_first_of(stringQuote) == std::string::npos) {
65   - return std::string(1, stringQuote) + arg + stringQuote;
66   - }
67   - return characterQuote + arg + characterQuote;
68   -}
  27 +std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'');
69 28  
70 29 /// Comma separated join, adds quotes if needed
71   -inline std::string ini_join(const std::vector<std::string> &args,
72   - char sepChar = ',',
73   - char arrayStart = '[',
74   - char arrayEnd = ']',
75   - char stringQuote = '"',
76   - char characterQuote = '\'') {
77   - std::string joined;
78   - if(args.size() > 1 && arrayStart != '\0') {
79   - joined.push_back(arrayStart);
80   - }
81   - std::size_t start = 0;
82   - for(const auto &arg : args) {
83   - if(start++ > 0) {
84   - joined.push_back(sepChar);
85   - if(!std::isspace<char>(sepChar, std::locale())) {
86   - joined.push_back(' ');
87   - }
88   - }
89   - joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));
90   - }
91   - if(args.size() > 1 && arrayEnd != '\0') {
92   - joined.push_back(arrayEnd);
93   - }
94   - return joined;
95   -}
  30 +std::string ini_join(const std::vector<std::string> &args,
  31 + char sepChar = ',',
  32 + char arrayStart = '[',
  33 + char arrayEnd = ']',
  34 + char stringQuote = '"',
  35 + char characterQuote = '\'');
96 36  
97   -inline std::vector<std::string> generate_parents(const std::string &section, std::string &name, char parentSeparator) {
98   - std::vector<std::string> parents;
99   - if(detail::to_lower(section) != "default") {
100   - if(section.find(parentSeparator) != std::string::npos) {
101   - parents = detail::split(section, parentSeparator);
102   - } else {
103   - parents = {section};
104   - }
105   - }
106   - if(name.find(parentSeparator) != std::string::npos) {
107   - std::vector<std::string> plist = detail::split(name, parentSeparator);
108   - name = plist.back();
109   - detail::remove_quotes(name);
110   - plist.pop_back();
111   - parents.insert(parents.end(), plist.begin(), plist.end());
112   - }
113   -
114   - // clean up quotes on the parents
115   - for(auto &parent : parents) {
116   - detail::remove_quotes(parent);
117   - }
118   - return parents;
119   -}
  37 +std::vector<std::string> generate_parents(const std::string &section, std::string &name, char parentSeparator);
120 38  
121 39 /// assuming non default segments do a check on the close and open of the segments in a configItem structure
122   -inline void
123   -checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator) {
124   -
125   - std::string estring;
126   - auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
127   - if(!output.empty() && output.back().name == "--") {
128   - std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
129   - while(output.back().parents.size() >= msize) {
130   - output.push_back(output.back());
131   - output.back().parents.pop_back();
132   - }
133   -
134   - if(parents.size() > 1) {
135   - std::size_t common = 0;
136   - std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
137   - for(std::size_t ii = 0; ii < mpair; ++ii) {
138   - if(output.back().parents[ii] != parents[ii]) {
139   - break;
140   - }
141   - ++common;
142   - }
143   - if(common == mpair) {
144   - output.pop_back();
145   - } else {
146   - while(output.back().parents.size() > common + 1) {
147   - output.push_back(output.back());
148   - output.back().parents.pop_back();
149   - }
150   - }
151   - for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
152   - output.emplace_back();
153   - output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
154   - output.back().name = "++";
155   - }
156   - }
157   - } else if(parents.size() > 1) {
158   - for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
159   - output.emplace_back();
160   - output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
161   - output.back().name = "++";
162   - }
163   - }
164   -
165   - // insert a section end which is just an empty items_buffer
166   - output.emplace_back();
167   - output.back().parents = std::move(parents);
168   - output.back().name = "++";
169   -}
  40 +void checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator);
170 41 } // namespace detail
171 42  
172   -inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
173   - std::string line;
174   - std::string currentSection = "default";
175   - std::string previousSection = "default";
176   - std::vector<ConfigItem> output;
177   - bool isDefaultArray = (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');
178   - bool isINIArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
179   - bool inSection{false};
180   - char aStart = (isINIArray) ? '[' : arrayStart;
181   - char aEnd = (isINIArray) ? ']' : arrayEnd;
182   - char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;
183   - int currentSectionIndex{0};
184   - while(getline(input, line)) {
185   - std::vector<std::string> items_buffer;
186   - std::string name;
187   -
188   - detail::trim(line);
189   - std::size_t len = line.length();
190   - // lines have to be at least 3 characters to have any meaning to CLI just skip the rest
191   - if(len < 3) {
192   - continue;
193   - }
194   - if(line.front() == '[' && line.back() == ']') {
195   - if(currentSection != "default") {
196   - // insert a section end which is just an empty items_buffer
197   - output.emplace_back();
198   - output.back().parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
199   - output.back().name = "--";
200   - }
201   - currentSection = line.substr(1, len - 2);
202   - // deal with double brackets for TOML
203   - if(currentSection.size() > 1 && currentSection.front() == '[' && currentSection.back() == ']') {
204   - currentSection = currentSection.substr(1, currentSection.size() - 2);
205   - }
206   - if(detail::to_lower(currentSection) == "default") {
207   - currentSection = "default";
208   - } else {
209   - detail::checkParentSegments(output, currentSection, parentSeparatorChar);
210   - }
211   - inSection = false;
212   - if(currentSection == previousSection) {
213   - ++currentSectionIndex;
214   - } else {
215   - currentSectionIndex = 0;
216   - previousSection = currentSection;
217   - }
218   - continue;
219   - }
220   -
221   - // comment lines
222   - if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
223   - continue;
224   - }
225   -
226   - // Find = in string, split and recombine
227   - auto pos = line.find(valueDelimiter);
228   - if(pos != std::string::npos) {
229   - name = detail::trim_copy(line.substr(0, pos));
230   - std::string item = detail::trim_copy(line.substr(pos + 1));
231   - auto cloc = item.find(commentChar);
232   - if(cloc != std::string::npos) {
233   - item.erase(cloc, std::string::npos); // NOLINT(readability-suspicious-call-argument)
234   - detail::trim(item);
235   - }
236   - if(item.size() > 1 && item.front() == aStart) {
237   - for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
238   - detail::trim(multiline);
239   - item += multiline;
240   - }
241   - items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
242   - } else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
243   - items_buffer = detail::split_up(item, aSep);
244   - } else if((isDefaultArray || isINIArray) && item.find_first_of(' ') != std::string::npos) {
245   - items_buffer = detail::split_up(item);
246   - } else {
247   - items_buffer = {item};
248   - }
249   - } else {
250   - name = detail::trim_copy(line);
251   - auto cloc = name.find(commentChar);
252   - if(cloc != std::string::npos) {
253   - name.erase(cloc, std::string::npos); // NOLINT(readability-suspicious-call-argument)
254   - detail::trim(name);
255   - }
256   -
257   - items_buffer = {"true"};
258   - }
259   - if(name.find(parentSeparatorChar) == std::string::npos) {
260   - detail::remove_quotes(name);
261   - }
262   - // clean up quotes on the items
263   - for(auto &it : items_buffer) {
264   - detail::remove_quotes(it);
265   - }
266   -
267   - std::vector<std::string> parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
268   - if(parents.size() > maximumLayers) {
269   - continue;
270   - }
271   - if(!configSection.empty() && !inSection) {
272   - if(parents.empty() || parents.front() != configSection) {
273   - continue;
274   - }
275   - if(configIndex >= 0 && currentSectionIndex != configIndex) {
276   - continue;
277   - }
278   - parents.erase(parents.begin());
279   - inSection = true;
280   - }
281   - if(!output.empty() && name == output.back().name && parents == output.back().parents) {
282   - output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end());
283   - } else {
284   - output.emplace_back();
285   - output.back().parents = std::move(parents);
286   - output.back().name = std::move(name);
287   - output.back().inputs = std::move(items_buffer);
288   - }
289   - }
290   - if(currentSection != "default") {
291   - // insert a section end which is just an empty items_buffer
292   - std::string ename;
293   - output.emplace_back();
294   - output.back().parents = detail::generate_parents(currentSection, ename, parentSeparatorChar);
295   - output.back().name = "--";
296   - while(output.back().parents.size() > 1) {
297   - output.push_back(output.back());
298   - output.back().parents.pop_back();
299   - }
300   - }
301   - return output;
302   -}
303   -
304   -inline std::string
305   -ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
306   - std::stringstream out;
307   - std::string commentLead;
308   - commentLead.push_back(commentChar);
309   - commentLead.push_back(' ');
310   -
311   - std::vector<std::string> groups = app->get_groups();
312   - bool defaultUsed = false;
313   - groups.insert(groups.begin(), std::string("Options"));
314   - if(write_description && (app->get_configurable() || app->get_parent() == nullptr || app->get_name().empty())) {
315   - out << commentLead << detail::fix_newlines(commentLead, app->get_description()) << '\n';
316   - }
317   - for(auto &group : groups) {
318   - if(group == "Options" || group.empty()) {
319   - if(defaultUsed) {
320   - continue;
321   - }
322   - defaultUsed = true;
323   - }
324   - if(write_description && group != "Options" && !group.empty()) {
325   - out << '\n' << commentLead << group << " Options\n";
326   - }
327   - for(const Option *opt : app->get_options({})) {
328   -
329   - // Only process options that are configurable
330   - if(opt->get_configurable()) {
331   - if(opt->get_group() != group) {
332   - if(!(group == "Options" && opt->get_group().empty())) {
333   - continue;
334   - }
335   - }
336   - std::string name = prefix + opt->get_single_name();
337   - std::string value = detail::ini_join(
338   - opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote);
339   -
340   - if(value.empty() && default_also) {
341   - if(!opt->get_default_str().empty()) {
342   - value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote);
343   - } else if(opt->get_expected_min() == 0) {
344   - value = "false";
345   - } else if(opt->get_run_callback_for_default()) {
346   - value = "\"\""; // empty string default value
347   - }
348   - }
349   -
350   - if(!value.empty()) {
351   - if(write_description && opt->has_description()) {
352   - out << '\n';
353   - out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
354   - }
355   - out << name << valueDelimiter << value << '\n';
356   - }
357   - }
358   - }
359   - }
360   - auto subcommands = app->get_subcommands({});
361   - for(const App *subcom : subcommands) {
362   - if(subcom->get_name().empty()) {
363   - if(write_description && !subcom->get_group().empty()) {
364   - out << '\n' << commentLead << subcom->get_group() << " Options\n";
365   - }
366   - out << to_config(subcom, default_also, write_description, prefix);
367   - }
368   - }
369   -
370   - for(const App *subcom : subcommands) {
371   - if(!subcom->get_name().empty()) {
372   - if(subcom->get_configurable() && app->got_subcommand(subcom)) {
373   - if(!prefix.empty() || app->get_parent() == nullptr) {
374   - out << '[' << prefix << subcom->get_name() << "]\n";
375   - } else {
376   - std::string subname = app->get_name() + parentSeparatorChar + subcom->get_name();
377   - const auto *p = app->get_parent();
378   - while(p->get_parent() != nullptr) {
379   - subname = p->get_name() + parentSeparatorChar + subname;
380   - p = p->get_parent();
381   - }
382   - out << '[' << subname << "]\n";
383   - }
384   - out << to_config(subcom, default_also, write_description, "");
385   - } else {
386   - out << to_config(
387   - subcom, default_also, write_description, prefix + subcom->get_name() + parentSeparatorChar);
388   - }
389   - }
390   - }
391   -
392   - return out.str();
393   -}
394   -
395 43 // [CLI11:config_hpp:end]
396 44 } // namespace CLI
  45 +
  46 +#ifndef CLI11_COMPILE
  47 +#include "impl/Config_inl.hpp"
  48 +#endif
... ...
include/CLI/impl/Config_inl.hpp 0 → 100644
  1 +// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
  2 +// under NSF AWARD 1414736 and by the respective contributors.
  3 +// All rights reserved.
  4 +//
  5 +// SPDX-License-Identifier: BSD-3-Clause
  6 +
  7 +#pragma once
  8 +
  9 +// This include is only needed for IDEs to discover symbols
  10 +#include <CLI/Config.hpp>
  11 +
  12 +// [CLI11:public_includes:set]
  13 +#include <algorithm>
  14 +#include <string>
  15 +#include <utility>
  16 +#include <vector>
  17 +// [CLI11:public_includes:end]
  18 +
  19 +namespace CLI {
  20 +// [CLI11:config_inl_hpp:verbatim]
  21 +
  22 +namespace detail {
  23 +
  24 +CLI11_INLINE std::string convert_arg_for_ini(const std::string &arg, char stringQuote, char characterQuote) {
  25 + if(arg.empty()) {
  26 + return std::string(2, stringQuote);
  27 + }
  28 + // some specifically supported strings
  29 + if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
  30 + return arg;
  31 + }
  32 + // floating point conversion can convert some hex codes, but don't try that here
  33 + if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
  34 + double val = 0.0;
  35 + if(detail::lexical_cast(arg, val)) {
  36 + return arg;
  37 + }
  38 + }
  39 + // just quote a single non numeric character
  40 + if(arg.size() == 1) {
  41 + return std::string(1, characterQuote) + arg + characterQuote;
  42 + }
  43 + // handle hex, binary or octal arguments
  44 + if(arg.front() == '0') {
  45 + if(arg[1] == 'x') {
  46 + if(std::all_of(arg.begin() + 2, arg.end(), [](char x) {
  47 + return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f');
  48 + })) {
  49 + return arg;
  50 + }
  51 + } else if(arg[1] == 'o') {
  52 + if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) {
  53 + return arg;
  54 + }
  55 + } else if(arg[1] == 'b') {
  56 + if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) {
  57 + return arg;
  58 + }
  59 + }
  60 + }
  61 + if(arg.find_first_of(stringQuote) == std::string::npos) {
  62 + return std::string(1, stringQuote) + arg + stringQuote;
  63 + }
  64 + return characterQuote + arg + characterQuote;
  65 +}
  66 +
  67 +CLI11_INLINE std::string ini_join(const std::vector<std::string> &args,
  68 + char sepChar,
  69 + char arrayStart,
  70 + char arrayEnd,
  71 + char stringQuote,
  72 + char characterQuote) {
  73 + std::string joined;
  74 + if(args.size() > 1 && arrayStart != '\0') {
  75 + joined.push_back(arrayStart);
  76 + }
  77 + std::size_t start = 0;
  78 + for(const auto &arg : args) {
  79 + if(start++ > 0) {
  80 + joined.push_back(sepChar);
  81 + if(!std::isspace<char>(sepChar, std::locale())) {
  82 + joined.push_back(' ');
  83 + }
  84 + }
  85 + joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));
  86 + }
  87 + if(args.size() > 1 && arrayEnd != '\0') {
  88 + joined.push_back(arrayEnd);
  89 + }
  90 + return joined;
  91 +}
  92 +
  93 +CLI11_INLINE std::vector<std::string>
  94 +generate_parents(const std::string &section, std::string &name, char parentSeparator) {
  95 + std::vector<std::string> parents;
  96 + if(detail::to_lower(section) != "default") {
  97 + if(section.find(parentSeparator) != std::string::npos) {
  98 + parents = detail::split(section, parentSeparator);
  99 + } else {
  100 + parents = {section};
  101 + }
  102 + }
  103 + if(name.find(parentSeparator) != std::string::npos) {
  104 + std::vector<std::string> plist = detail::split(name, parentSeparator);
  105 + name = plist.back();
  106 + detail::remove_quotes(name);
  107 + plist.pop_back();
  108 + parents.insert(parents.end(), plist.begin(), plist.end());
  109 + }
  110 +
  111 + // clean up quotes on the parents
  112 + for(auto &parent : parents) {
  113 + detail::remove_quotes(parent);
  114 + }
  115 + return parents;
  116 +}
  117 +
  118 +CLI11_INLINE void
  119 +checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator) {
  120 +
  121 + std::string estring;
  122 + auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
  123 + if(!output.empty() && output.back().name == "--") {
  124 + std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
  125 + while(output.back().parents.size() >= msize) {
  126 + output.push_back(output.back());
  127 + output.back().parents.pop_back();
  128 + }
  129 +
  130 + if(parents.size() > 1) {
  131 + std::size_t common = 0;
  132 + std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
  133 + for(std::size_t ii = 0; ii < mpair; ++ii) {
  134 + if(output.back().parents[ii] != parents[ii]) {
  135 + break;
  136 + }
  137 + ++common;
  138 + }
  139 + if(common == mpair) {
  140 + output.pop_back();
  141 + } else {
  142 + while(output.back().parents.size() > common + 1) {
  143 + output.push_back(output.back());
  144 + output.back().parents.pop_back();
  145 + }
  146 + }
  147 + for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
  148 + output.emplace_back();
  149 + output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
  150 + output.back().name = "++";
  151 + }
  152 + }
  153 + } else if(parents.size() > 1) {
  154 + for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
  155 + output.emplace_back();
  156 + output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
  157 + output.back().name = "++";
  158 + }
  159 + }
  160 +
  161 + // insert a section end which is just an empty items_buffer
  162 + output.emplace_back();
  163 + output.back().parents = std::move(parents);
  164 + output.back().name = "++";
  165 +}
  166 +} // namespace detail
  167 +
  168 +inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
  169 + std::string line;
  170 + std::string currentSection = "default";
  171 + std::string previousSection = "default";
  172 + std::vector<ConfigItem> output;
  173 + bool isDefaultArray = (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');
  174 + bool isINIArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
  175 + bool inSection{false};
  176 + char aStart = (isINIArray) ? '[' : arrayStart;
  177 + char aEnd = (isINIArray) ? ']' : arrayEnd;
  178 + char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;
  179 + int currentSectionIndex{0};
  180 + while(getline(input, line)) {
  181 + std::vector<std::string> items_buffer;
  182 + std::string name;
  183 +
  184 + detail::trim(line);
  185 + std::size_t len = line.length();
  186 + // lines have to be at least 3 characters to have any meaning to CLI just skip the rest
  187 + if(len < 3) {
  188 + continue;
  189 + }
  190 + if(line.front() == '[' && line.back() == ']') {
  191 + if(currentSection != "default") {
  192 + // insert a section end which is just an empty items_buffer
  193 + output.emplace_back();
  194 + output.back().parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
  195 + output.back().name = "--";
  196 + }
  197 + currentSection = line.substr(1, len - 2);
  198 + // deal with double brackets for TOML
  199 + if(currentSection.size() > 1 && currentSection.front() == '[' && currentSection.back() == ']') {
  200 + currentSection = currentSection.substr(1, currentSection.size() - 2);
  201 + }
  202 + if(detail::to_lower(currentSection) == "default") {
  203 + currentSection = "default";
  204 + } else {
  205 + detail::checkParentSegments(output, currentSection, parentSeparatorChar);
  206 + }
  207 + inSection = false;
  208 + if(currentSection == previousSection) {
  209 + ++currentSectionIndex;
  210 + } else {
  211 + currentSectionIndex = 0;
  212 + previousSection = currentSection;
  213 + }
  214 + continue;
  215 + }
  216 +
  217 + // comment lines
  218 + if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
  219 + continue;
  220 + }
  221 +
  222 + // Find = in string, split and recombine
  223 + auto pos = line.find(valueDelimiter);
  224 + if(pos != std::string::npos) {
  225 + name = detail::trim_copy(line.substr(0, pos));
  226 + std::string item = detail::trim_copy(line.substr(pos + 1));
  227 + auto cloc = item.find(commentChar);
  228 + if(cloc != std::string::npos) {
  229 + item.erase(cloc, std::string::npos); // NOLINT(readability-suspicious-call-argument)
  230 + detail::trim(item);
  231 + }
  232 + if(item.size() > 1 && item.front() == aStart) {
  233 + for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
  234 + detail::trim(multiline);
  235 + item += multiline;
  236 + }
  237 + items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
  238 + } else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
  239 + items_buffer = detail::split_up(item, aSep);
  240 + } else if((isDefaultArray || isINIArray) && item.find_first_of(' ') != std::string::npos) {
  241 + items_buffer = detail::split_up(item);
  242 + } else {
  243 + items_buffer = {item};
  244 + }
  245 + } else {
  246 + name = detail::trim_copy(line);
  247 + auto cloc = name.find(commentChar);
  248 + if(cloc != std::string::npos) {
  249 + name.erase(cloc, std::string::npos); // NOLINT(readability-suspicious-call-argument)
  250 + detail::trim(name);
  251 + }
  252 +
  253 + items_buffer = {"true"};
  254 + }
  255 + if(name.find(parentSeparatorChar) == std::string::npos) {
  256 + detail::remove_quotes(name);
  257 + }
  258 + // clean up quotes on the items
  259 + for(auto &it : items_buffer) {
  260 + detail::remove_quotes(it);
  261 + }
  262 +
  263 + std::vector<std::string> parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
  264 + if(parents.size() > maximumLayers) {
  265 + continue;
  266 + }
  267 + if(!configSection.empty() && !inSection) {
  268 + if(parents.empty() || parents.front() != configSection) {
  269 + continue;
  270 + }
  271 + if(configIndex >= 0 && currentSectionIndex != configIndex) {
  272 + continue;
  273 + }
  274 + parents.erase(parents.begin());
  275 + inSection = true;
  276 + }
  277 + if(!output.empty() && name == output.back().name && parents == output.back().parents) {
  278 + output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end());
  279 + } else {
  280 + output.emplace_back();
  281 + output.back().parents = std::move(parents);
  282 + output.back().name = std::move(name);
  283 + output.back().inputs = std::move(items_buffer);
  284 + }
  285 + }
  286 + if(currentSection != "default") {
  287 + // insert a section end which is just an empty items_buffer
  288 + std::string ename;
  289 + output.emplace_back();
  290 + output.back().parents = detail::generate_parents(currentSection, ename, parentSeparatorChar);
  291 + output.back().name = "--";
  292 + while(output.back().parents.size() > 1) {
  293 + output.push_back(output.back());
  294 + output.back().parents.pop_back();
  295 + }
  296 + }
  297 + return output;
  298 +}
  299 +
  300 +CLI11_INLINE std::string
  301 +ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
  302 + std::stringstream out;
  303 + std::string commentLead;
  304 + commentLead.push_back(commentChar);
  305 + commentLead.push_back(' ');
  306 +
  307 + std::vector<std::string> groups = app->get_groups();
  308 + bool defaultUsed = false;
  309 + groups.insert(groups.begin(), std::string("Options"));
  310 + if(write_description && (app->get_configurable() || app->get_parent() == nullptr || app->get_name().empty())) {
  311 + out << commentLead << detail::fix_newlines(commentLead, app->get_description()) << '\n';
  312 + }
  313 + for(auto &group : groups) {
  314 + if(group == "Options" || group.empty()) {
  315 + if(defaultUsed) {
  316 + continue;
  317 + }
  318 + defaultUsed = true;
  319 + }
  320 + if(write_description && group != "Options" && !group.empty()) {
  321 + out << '\n' << commentLead << group << " Options\n";
  322 + }
  323 + for(const Option *opt : app->get_options({})) {
  324 +
  325 + // Only process options that are configurable
  326 + if(opt->get_configurable()) {
  327 + if(opt->get_group() != group) {
  328 + if(!(group == "Options" && opt->get_group().empty())) {
  329 + continue;
  330 + }
  331 + }
  332 + std::string name = prefix + opt->get_single_name();
  333 + std::string value = detail::ini_join(
  334 + opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote);
  335 +
  336 + if(value.empty() && default_also) {
  337 + if(!opt->get_default_str().empty()) {
  338 + value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote);
  339 + } else if(opt->get_expected_min() == 0) {
  340 + value = "false";
  341 + } else if(opt->get_run_callback_for_default()) {
  342 + value = "\"\""; // empty string default value
  343 + }
  344 + }
  345 +
  346 + if(!value.empty()) {
  347 + if(write_description && opt->has_description()) {
  348 + out << '\n';
  349 + out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
  350 + }
  351 + out << name << valueDelimiter << value << '\n';
  352 + }
  353 + }
  354 + }
  355 + }
  356 + auto subcommands = app->get_subcommands({});
  357 + for(const App *subcom : subcommands) {
  358 + if(subcom->get_name().empty()) {
  359 + if(write_description && !subcom->get_group().empty()) {
  360 + out << '\n' << commentLead << subcom->get_group() << " Options\n";
  361 + }
  362 + out << to_config(subcom, default_also, write_description, prefix);
  363 + }
  364 + }
  365 +
  366 + for(const App *subcom : subcommands) {
  367 + if(!subcom->get_name().empty()) {
  368 + if(subcom->get_configurable() && app->got_subcommand(subcom)) {
  369 + if(!prefix.empty() || app->get_parent() == nullptr) {
  370 + out << '[' << prefix << subcom->get_name() << "]\n";
  371 + } else {
  372 + std::string subname = app->get_name() + parentSeparatorChar + subcom->get_name();
  373 + const auto *p = app->get_parent();
  374 + while(p->get_parent() != nullptr) {
  375 + subname = p->get_name() + parentSeparatorChar + subname;
  376 + p = p->get_parent();
  377 + }
  378 + out << '[' << subname << "]\n";
  379 + }
  380 + out << to_config(subcom, default_also, write_description, "");
  381 + } else {
  382 + out << to_config(
  383 + subcom, default_also, write_description, prefix + subcom->get_name() + parentSeparatorChar);
  384 + }
  385 + }
  386 + }
  387 +
  388 + return out.str();
  389 +}
  390 +// [CLI11:config_inl_hpp:end]
  391 +} // namespace CLI
... ...
src/Config.cpp 0 → 100644
  1 +// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
  2 +// under NSF AWARD 1414736 and by the respective contributors.
  3 +// All rights reserved.
  4 +//
  5 +// SPDX-License-Identifier: BSD-3-Clause
  6 +
  7 +#include <CLI/impl/Config_inl.hpp>
... ...