12#include "../Config.hpp"
14#include "../Encoding.hpp"
34 std::vector<std::string> tmp =
parents;
35 tmp.emplace_back(
name);
36 return detail::join(tmp,
".");
40CLI11_INLINE std::string
41Config::to_config(
const App *app, ConfigOutputMode mode,
bool write_description, std::string prefix)
const {
42 return to_config(app, mode != ConfigOutputMode::Active, write_description, std::move(prefix));
46 if(item.
inputs.size() == 1) {
52 throw ConversionError::TooManyInputsFlag(item.
fullname());
56#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
57 std::ifstream input{to_path(name)};
59 std::ifstream input{name};
63 throw FileError::Missing(name);
68static constexpr auto multiline_literal_quote = R
"(''')";
69static constexpr auto multiline_string_quote = R
"(""")";
73CLI11_INLINE
bool is_printable(
const std::string &test_string) {
74 return std::all_of(test_string.begin(), test_string.end(), [](
char x) {
75 return (isprint(static_cast<unsigned char>(x)) != 0 || x ==
'\n' || x ==
'\t');
79CLI11_INLINE std::string
80convert_arg_for_ini(
const std::string &arg,
char stringQuote,
char literalQuote,
bool disable_multi_line) {
82 return std::string(2, stringQuote);
85 if(arg ==
"true" || arg ==
"false" || arg ==
"nan" || arg ==
"inf") {
89 if(arg.compare(0, 2,
"0x") != 0 && arg.compare(0, 2,
"0X") != 0) {
90 using CLI::detail::lexical_cast;
92 if(lexical_cast(arg, val)) {
93 if(arg.find_first_not_of(
"0123456789.-+eE") == std::string::npos) {
100 if(isprint(
static_cast<unsigned char>(arg.front())) == 0) {
101 return binary_escape_string(arg);
104 return std::string(1, stringQuote) +
"'" + stringQuote;
106 return std::string(1, literalQuote) + arg + literalQuote;
109 if(arg.front() ==
'0') {
111 if(std::all_of(arg.begin() + 2, arg.end(), [](
char x) {
112 return (x >=
'0' && x <=
'9') || (x >=
'A' && x <=
'F') || (x >=
'a' && x <=
'f');
116 }
else if(arg[1] ==
'o') {
117 if(std::all_of(arg.begin() + 2, arg.end(), [](
char x) { return (x >=
'0' && x <=
'7'); })) {
120 }
else if(arg[1] ==
'b') {
121 if(std::all_of(arg.begin() + 2, arg.end(), [](
char x) { return (x ==
'0' || x ==
'1'); })) {
126 if(!is_printable(arg)) {
127 return binary_escape_string(arg);
129 if(detail::has_escapable_character(arg)) {
130 if(arg.size() > 100 && !disable_multi_line) {
131 if(arg.find(multiline_literal_quote) != std::string::npos) {
132 return binary_escape_string(arg,
true);
134 std::string return_string{multiline_literal_quote};
135 return_string.reserve(7 + arg.size());
136 if(arg.front() ==
'\n') {
137 return_string.push_back(
'\n');
139 return_string.append(arg);
140 if(arg.back() ==
'\n') {
141 return_string.push_back(
'\n');
143 return_string.append(multiline_literal_quote, 3);
144 return return_string;
146 return std::string(1, stringQuote) + detail::add_escaped_characters(arg) + stringQuote;
148 return std::string(1, stringQuote) + arg + stringQuote;
151CLI11_INLINE std::string ini_join(
const std::vector<std::string> &args,
157 bool disable_multi_line{
false};
159 if(args.size() > 1 && arrayStart !=
'\0') {
160 joined.push_back(arrayStart);
161 disable_multi_line =
true;
163 const bool sep_is_space = std::isspace<char>(sepChar, std::locale());
164 std::size_t start = 0;
165 for(
const auto &arg : args) {
167 joined.push_back(sepChar);
169 joined.push_back(
' ');
172 joined.append(convert_arg_for_ini(arg, stringQuote, literalQuote, disable_multi_line));
174 if(args.size() > 1 && arrayEnd !=
'\0') {
175 joined.push_back(arrayEnd);
180CLI11_INLINE std::vector<std::string>
181generate_parents(
const std::string §ion, std::string &name,
char parentSeparator) {
182 std::vector<std::string> parents;
183 if(detail::to_lower(section) !=
"default") {
184 if(section.find(parentSeparator) != std::string::npos) {
185 parents = detail::split_up(section, parentSeparator);
190 if(name.find(parentSeparator) != std::string::npos) {
191 std::vector<std::string> plist = detail::split_up(name, parentSeparator);
194 parents.insert(parents.end(), plist.begin(), plist.end());
198 detail::remove_quotes(parents);
199 }
catch(
const std::invalid_argument &iarg) {
200 throw CLI::ParseError(iarg.what(), CLI::ExitCodes::InvalidError);
206checkParentSegments(std::vector<ConfigItem> &output,
const std::string ¤tSection,
char parentSeparator) {
209 auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
210 if(!output.empty() && output.back().name ==
"--") {
211 std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
212 while(output.back().parents.size() >= msize) {
213 output.push_back(output.back());
214 output.back().parents.pop_back();
217 if(parents.size() > 1) {
218 std::size_t common = 0;
219 std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
220 for(std::size_t ii = 0; ii < mpair; ++ii) {
221 if(output.back().parents[ii] != parents[ii]) {
226 if(common == mpair) {
229 while(output.back().parents.size() > common + 1) {
230 output.push_back(output.back());
231 output.back().parents.pop_back();
234 for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
235 output.emplace_back();
236 output.back().parents.assign(parents.begin(), parents.begin() +
static_cast<std::ptrdiff_t
>(ii) + 1);
237 output.back().name =
"++";
240 }
else if(parents.size() > 1) {
241 for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
242 output.emplace_back();
243 output.back().parents.assign(parents.begin(), parents.begin() +
static_cast<std::ptrdiff_t
>(ii) + 1);
244 output.back().name =
"++";
249 output.emplace_back();
250 output.back().parents = std::move(parents);
251 output.back().name =
"++";
255CLI11_INLINE
bool hasMLString(std::string
const &fullString,
char check) {
256 if(fullString.length() < 3) {
259 auto it = fullString.rbegin();
260 return (*it == check) && (*(it + 1) == check) && (*(it + 2) == check);
264CLI11_INLINE
auto find_matching_config(std::vector<ConfigItem> &items,
265 const std::vector<std::string> &parents,
266 const std::string &name,
267 bool fullSearch) ->
decltype(items.begin()) {
271 auto search = items.end() - 1;
273 if(search->parents == parents && search->name == name) {
276 if(search == items.begin()) {
284CLI11_INLINE
void clean_name_string(std::string &name,
const std::string &keyChars) {
285 if(name.find_first_of(keyChars) != std::string::npos || (name.front() ==
'[' && name.back() ==
']') ||
286 (name.find_first_of(
"'`\"\\") != std::string::npos)) {
287 if(name.find_first_of(
'\'') == std::string::npos) {
288 name.insert(0, 1,
'\'');
289 name.push_back(
'\'');
291 if(detail::has_escapable_character(name)) {
292 name = detail::add_escaped_characters(name);
294 name.insert(0, 1,
'\"');
295 name.push_back(
'\"');
304 std::string currentSection =
"default";
305 std::string previousSection =
"default";
306 std::vector<ConfigItem> output;
309 bool inSection{
false};
310 bool inMLineComment{
false};
311 bool inMLineValue{
false};
313 char aStart = (isINIArray) ?
'[' :
arrayStart;
314 char aEnd = (isINIArray) ?
']' :
arrayEnd;
316 int currentSectionIndex{0};
319 while(getline(input, buffer)) {
320 std::vector<std::string> items_buffer;
322 line = detail::trim_copy(buffer);
323 std::size_t len = line.length();
328 if(line.compare(0, 3, multiline_string_quote) == 0 || line.compare(0, 3, multiline_literal_quote) == 0) {
331 if(len >= 6 && detail::hasMLString(line, line.front())) {
334 inMLineComment =
true;
335 auto cchar = line.front();
336 while(inMLineComment) {
337 if(getline(input, line)) {
342 if(detail::hasMLString(line, cchar)) {
343 inMLineComment =
false;
350 if(line.front() ==
'[' && line.back() !=
']' && line.find_first_of(
commentChar) != std::string::npos) {
351 std::size_t comment_search = 0;
352 while(comment_search < line.size()) {
353 auto test_char = line[comment_search];
354 if(test_char ==
'\"' || test_char ==
'\'' || test_char ==
'`') {
355 comment_search = detail::close_sequence(line, comment_search, line[comment_search]);
363 if(comment_search < line.size() && line[comment_search] ==
commentChar) {
364 line = detail::trim_copy(line.substr(0, comment_search));
371 if(line.front() ==
'[' && line.back() ==
']') {
372 if(currentSection !=
"default") {
374 output.emplace_back();
375 output.back().parents = detail::generate_parents(currentSection, name,
parentSeparatorChar);
376 output.back().name =
"--";
378 currentSection = line.substr(1, len - 2);
380 if(currentSection.size() > 1 && currentSection.front() ==
'[' && currentSection.back() ==
']') {
381 currentSection = currentSection.substr(1, currentSection.size() - 2);
383 if(detail::to_lower(currentSection) ==
"default") {
384 currentSection =
"default";
389 if(currentSection == previousSection) {
390 ++currentSectionIndex;
392 currentSectionIndex = 0;
393 previousSection = currentSection;
399 if(line.front() ==
';' || line.front() ==
'#' || line.front() ==
commentChar) {
402 std::size_t search_start = 0;
403 if(line.find_first_of(
"\"'`") != std::string::npos) {
404 while(search_start < line.size()) {
405 auto test_char = line[search_start];
406 if(test_char ==
'\"' || test_char ==
'\'' || test_char ==
'`') {
407 search_start = detail::close_sequence(line, search_start, line[search_start]);
415 search_start = line.find_first_of(line_sep_chars, search_start);
420 auto delimiter_pos = line.find_first_of(
valueDelimiter, search_start + 1);
421 auto comment_pos = line.find_first_of(
commentChar, search_start);
422 if(comment_pos < delimiter_pos) {
423 delimiter_pos = std::string::npos;
425 if(delimiter_pos != std::string::npos) {
427 name = detail::trim_copy(line.substr(0, delimiter_pos));
428 std::string item = detail::trim_copy(line.substr(delimiter_pos + 1, std::string::npos));
430 (item.compare(0, 3, multiline_literal_quote) == 0 || item.compare(0, 3, multiline_string_quote) == 0);
431 if(!mlquote && comment_pos != std::string::npos) {
433 item = detail::trim_copy(citems.front());
437 auto keyChar = item.front();
438 auto offset = buffer.find_first_not_of(
" \t");
439 item = buffer.substr((offset == std::string::npos ? 0 : offset) + delimiter_pos + 1, std::string::npos);
443 bool lineExtension{
false};
444 bool firstLine =
true;
445 if(!item.empty() && item.back() ==
'\\' && keyChar ==
'\"') {
447 lineExtension =
true;
448 }
else if(detail::hasMLString(item, keyChar)) {
453 if(keyChar ==
'\"') {
455 item = detail::remove_escaped_characters(item);
456 }
catch(
const std::invalid_argument &iarg) {
460 inMLineValue =
false;
462 while(inMLineValue) {
464 if(!std::getline(input, l2)) {
469 if(detail::hasMLString(line, keyChar)) {
475 }
else if(!(firstLine && item.empty())) {
476 item.push_back(
'\n');
480 inMLineValue =
false;
481 if(!item.empty() && item.back() ==
'\n') {
484 if(keyChar ==
'\"') {
486 item = detail::remove_escaped_characters(item);
487 }
catch(
const std::invalid_argument &iarg) {
494 }
else if(!(firstLine && item.empty())) {
495 item.push_back(
'\n');
497 lineExtension =
false;
499 if(!l2.empty() && l2.back() ==
'\\' && keyChar ==
'\"') {
500 lineExtension =
true;
506 items_buffer = {item};
507 }
else if(!item.empty() && item.front() == aStart) {
508 for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
509 detail::trim(multiline);
512 if(item.back() == aEnd) {
513 items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
515 items_buffer = detail::split_up(item.substr(1, std::string::npos), aSep);
517 }
else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
518 items_buffer = detail::split_up(item, aSep);
519 }
else if((isDefaultArray || isINIArray) && item.find_first_of(
' ') != std::string::npos) {
520 items_buffer = detail::split_up(item,
'\0');
522 items_buffer = {item};
525 name = detail::trim_copy(line.substr(0, comment_pos));
526 items_buffer = {
"true"};
528 std::vector<std::string> parents;
531 detail::process_quoted_string(name,
'"',
'\'',
true);
533 for(
auto &it : items_buffer) {
536 }
catch(
const std::invalid_argument &ia) {
550 parents.erase(parents.begin());
554 if(match != output.end()) {
557 if(!(match->inputs.back().empty() || items_buffer.front().empty() || match->inputs.back() ==
"%%" ||
558 items_buffer.front() ==
"%%")) {
559 match->inputs.emplace_back(
"%%");
560 match->multiline =
true;
563 match->inputs.insert(match->inputs.end(), items_buffer.begin(), items_buffer.end());
565 output.emplace_back();
566 output.back().parents = std::move(parents);
567 output.back().name = std::move(name);
568 output.back().inputs = std::move(items_buffer);
571 if(currentSection !=
"default") {
574 output.emplace_back();
575 output.back().parents = detail::generate_parents(currentSection, ename,
parentSeparatorChar);
576 output.back().name =
"--";
577 while(output.back().parents.size() > 1) {
578 output.push_back(output.back());
579 output.back().parents.pop_back();
585CLI11_INLINE std::string
588 default_also ? ConfigOutputMode::AllDefaults : ConfigOutputMode::Active,
593CLI11_INLINE std::string
595 std::stringstream out;
596 const bool include_default_values = (mode != ConfigOutputMode::Active);
597 std::string commentLead;
599 commentLead.push_back(
' ');
601 std::string commentTest =
"#;";
605 std::string keyChars = commentTest;
613 std::vector<std::string> groups = app->
get_groups();
614 bool defaultUsed =
false;
615 groups.insert(groups.begin(), std::string(
"OPTIONS"));
617 const std::vector<const Option *> options = app->
get_options({});
618 for(
auto &group : groups) {
619 if(group ==
"OPTIONS" || group.empty()) {
625 if(write_description && group !=
"OPTIONS" && !group.empty()) {
626 out <<
'\n' <<
commentChar << commentLead << group <<
" Options\n";
630 if(opt->get_configurable()) {
631 if(opt->get_group() != group) {
632 if(!(group ==
"OPTIONS" && opt->get_group().empty())) {
636 std::string single_name = opt->get_single_name();
637 if(single_name.empty()) {
641 auto results = opt->reduced_results();
642 if(results.size() > 1 && opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Reverse) {
643 std::reverse(results.begin(), results.end());
645 if(opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Sum && opt->count() >= 1 &&
646 results.size() == 1) {
649 auto pos = opt->_validate(results[0], 0);
651 results = opt->results();
654 if(opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Join && opt->count() > 1) {
655 char delim = opt->get_delimiter();
658 results = opt->results();
662 auto delim_count = std::count(results[0].begin(), results[0].end(), delim);
663 if(results[0].back() == delim ||
664 static_cast<decltype(delim_count)
>(opt->count()) <= delim_count ||
665 results[0].find(std::string(2, delim)) != std::string::npos) {
666 results = opt->results();
672 if(opt->count() == 1 && results.size() == 2 && results.front() ==
"{}" && results.back() ==
"%%") {
682 bool isDefault =
false;
683 if(value.empty() && include_default_values) {
684 if(!opt->get_default_str().empty()) {
688 }
else if(opt->get_expected_min() == 0) {
690 }
else if(opt->get_run_callback_for_default() || !opt->get_required()) {
693 value =
"\"<REQUIRED>\"";
699 if(!opt->get_fnames().empty()) {
701 value = opt->get_flag_value(single_name, value);
704 for(
const auto &test_name : opt->get_fnames()) {
706 value = opt->get_flag_value(test_name, value);
707 single_name = test_name;
714 value = detail::ini_join(
719 if(write_description && opt->has_description()) {
720 if(out.tellp() != std::streampos(0)) {
723 out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) <<
'\n';
725 detail::clean_name_string(single_name, keyChars);
727 std::string name = prefix + single_name;
738 for(
const App *subcom : subcommands) {
739 if(subcom->get_name().empty()) {
740 if(!include_default_values && (subcom->count_all() == 0)) {
743 if(write_description && !subcom->get_group().empty()) {
744 out <<
'\n' <<
commentChar << commentLead << subcom->get_group() <<
" Options\n";
758 out <<
to_config(subcom, mode, write_description, prefix);
762 for(
const App *subcom : subcommands) {
763 if(!subcom->get_name().empty()) {
764 if((!include_default_values && (subcom->count_all() == 0)) ||
765 (mode == ConfigOutputMode::ActiveSubcommandDefaults && !app->
got_subcommand(subcom))) {
768 std::string subname = subcom->get_name();
769 detail::clean_name_string(subname, keyChars);
771 if(subcom->get_configurable() && (app->
got_subcommand(subcom) || (mode == ConfigOutputMode::AllDefaults))) {
772 if(!prefix.empty() || app->
get_parent() ==
nullptr) {
774 out <<
'[' << prefix << subname <<
"]\n";
776 std::string appname = app->
get_name();
777 detail::clean_name_string(appname, keyChars);
780 while(p->get_parent() !=
nullptr) {
781 std::string pname = p->get_name();
782 detail::clean_name_string(pname, keyChars);
786 out <<
'[' << subname <<
"]\n";
788 out <<
to_config(subcom, mode, write_description,
"");
795 if(write_description && !out.str().empty()) {
796 std::string outString =
798 return outString + out.str();
803CLI11_INLINE ConfigINI::ConfigINI() {
Creates a command line program, with very few defaults.
Definition App.hpp:115
App * get_parent()
Get the parent of this subcommand (or nullptr if main app).
Definition App.hpp:1173
CLI11_NODISCARD std::vector< std::string > get_groups() const
Get the groups available directly from this option (in order).
Definition App_inl.hpp:1180
bool got_subcommand(const App *subcom) const
Check to see if given subcommand was selected.
Definition App_inl.hpp:840
CLI11_NODISCARD std::vector< App * > get_subcommands() const
Definition App.hpp:932
std::vector< const Option * > get_options(const std::function< bool(const Option *)> filter={}) const
Get the list of options (user facing function, so returns raw pointers), has optional filter function...
Definition App_inl.hpp:982
CLI11_NODISCARD std::string get_description() const
Get the app or subcommand description.
Definition App.hpp:1032
CLI11_NODISCARD const std::string & get_name() const
Get the name of the current app.
Definition App.hpp:1179
Thrown when the wrong number of arguments has been received.
Definition Error.hpp:264
std::string configSection
Specify the configuration section that should be used.
Definition ConfigFwd.hpp:97
std::string to_config(const App *, ConfigOutputMode mode, bool write_description, std::string prefix) const override
Convert an app into a configuration.
Definition Config_inl.hpp:594
char arraySeparator
the character used to separate elements in an array
Definition ConfigFwd.hpp:79
std::vector< ConfigItem > from_config(std::istream &input) const override
Convert a configuration into an app.
Definition Config_inl.hpp:301
std::uint8_t maximumLayers
the maximum number of layers to allow
Definition ConfigFwd.hpp:87
char stringQuote
the character to use around strings
Definition ConfigFwd.hpp:83
char valueDelimiter
the character used separate the name from the value
Definition ConfigFwd.hpp:81
char arrayStart
the character used to start an array '\0' is a default to not use
Definition ConfigFwd.hpp:75
char parentSeparatorChar
the separator used to separator parent layers
Definition ConfigFwd.hpp:89
bool allowMultipleDuplicateFields
specify the config reader should collapse repeated field names to a single vector
Definition ConfigFwd.hpp:93
char literalQuote
the character to use around single characters and literal strings
Definition ConfigFwd.hpp:85
char arrayEnd
the character used to end an array '\0' is a default to not use
Definition ConfigFwd.hpp:77
bool commentDefaultsBool
comment default values
Definition ConfigFwd.hpp:91
int16_t configIndex
Specify the configuration index to use for arrayed sections.
Definition ConfigFwd.hpp:95
char commentChar
the character used for comments
Definition ConfigFwd.hpp:73
virtual CLI11_NODISCARD std::string to_flag(const ConfigItem &item) const
Get a flag value.
Definition Config_inl.hpp:45
virtual std::string to_config(const App *, bool, bool, std::string) const =0
Convert an app into a configuration.
CLI11_NODISCARD std::vector< ConfigItem > from_file(const std::string &name) const
Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure.
Definition Config_inl.hpp:55
virtual std::vector< ConfigItem > from_config(std::istream &) const =0
Convert a configuration into an app.
Definition Option.hpp:261
Anything that can error in Parse.
Definition Error.hpp:160
Holds values to load into Options.
Definition ConfigFwd.hpp:29
CLI11_NODISCARD std::string fullname() const
The list of parents and name joined by ".".
Definition Config_inl.hpp:33
std::vector< std::string > inputs
Listing of inputs.
Definition ConfigFwd.hpp:36
std::string name
This is the name.
Definition ConfigFwd.hpp:34
bool multiline
indicator if a multiline vector separator was inserted
Definition ConfigFwd.hpp:38
std::vector< std::string > parents
This is the list of parents.
Definition ConfigFwd.hpp:31