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,6 +68,8 @@ namespace {namespace} {{
68 68
69 {config_hpp} 69 {config_hpp}
70 70
  71 +{config_inl_hpp}
  72 +
71 {formatter_hpp} 73 {formatter_hpp}
72 74
73 }} // namespace {namespace} 75 }} // namespace {namespace}
include/CLI/Config.hpp
@@ -24,373 +24,25 @@ namespace CLI { @@ -24,373 +24,25 @@ namespace CLI {
24 // [CLI11:config_hpp:verbatim] 24 // [CLI11:config_hpp:verbatim]
25 namespace detail { 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 /// Comma separated join, adds quotes if needed 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 /// assuming non default segments do a check on the close and open of the segments in a configItem structure 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 } // namespace detail 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 // [CLI11:config_hpp:end] 43 // [CLI11:config_hpp:end]
396 } // namespace CLI 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>