@@ -24,373 +24,25 @@ namespace CLI {
2424// [CLI11:config_hpp:verbatim]
2525namespace detail {
2626
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 = ' \' ' );
6928
7029// / 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 = ' \' ' );
9636
97- inline std::vector<std::string> generate_parents (const std::string §ion, 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 §ion, std::string &name, char parentSeparator);
12038
12139// / 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 ¤tSection, 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 ¤tSection, char parentSeparator);
17041} // namespace detail
17142
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-
39543// [CLI11:config_hpp:end]
39644} // namespace CLI
45+
46+ #ifndef CLI11_COMPILE
47+ #include " impl/Config_inl.hpp"
48+ #endif
0 commit comments