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);
265CLI11_INLINE std::size_t find_ml_close(
const std::string &str,
char quote,
char comment) {
266 const std::string closer(3, quote);
268 while((pos = str.find(closer, pos)) != std::string::npos) {
270 std::size_t slashes = 0;
271 while(slashes < pos && str[pos - slashes - 1] ==
'\\') {
274 if(slashes % 2 == 1) {
280 auto end = str.find_first_not_of(quote, pos);
281 if(end == std::string::npos) {
284 auto rest = str.find_first_not_of(
" \t\r", end);
285 if(rest == std::string::npos || str[rest] == comment) {
290 return std::string::npos;
294CLI11_INLINE
auto find_matching_config(std::vector<ConfigItem> &items,
295 const std::vector<std::string> &parents,
296 const std::string &name,
297 bool fullSearch) ->
decltype(items.begin()) {
301 auto search = items.end() - 1;
303 if(search->parents == parents && search->name == name) {
306 if(search == items.begin()) {
314CLI11_INLINE
void clean_name_string(std::string &name,
const std::string &keyChars) {
315 if(name.find_first_of(keyChars) != std::string::npos || (name.front() ==
'[' && name.back() ==
']') ||
316 (name.find_first_of(
"'`\"\\") != std::string::npos)) {
317 if(name.find_first_of(
'\'') == std::string::npos) {
318 name.insert(0, 1,
'\'');
319 name.push_back(
'\'');
321 if(detail::has_escapable_character(name)) {
322 name = detail::add_escaped_characters(name);
324 name.insert(0, 1,
'\"');
325 name.push_back(
'\"');
334 std::string currentSection =
"default";
335 std::string previousSection =
"default";
336 std::vector<ConfigItem> output;
339 bool inSection{
false};
340 bool inMLineComment{
false};
341 bool inMLineValue{
false};
343 char aStart = (isINIArray) ?
'[' :
arrayStart;
344 char aEnd = (isINIArray) ?
']' :
arrayEnd;
346 int currentSectionIndex{0};
349 while(getline(input, buffer)) {
350 std::vector<std::string> items_buffer;
352 line = detail::trim_copy(buffer);
353 std::size_t len = line.length();
358 if(line.compare(0, 3, multiline_string_quote) == 0 || line.compare(0, 3, multiline_literal_quote) == 0) {
361 if(len >= 6 && detail::hasMLString(line, line.front())) {
364 inMLineComment =
true;
365 auto cchar = line.front();
366 while(inMLineComment) {
367 if(getline(input, line)) {
372 if(detail::hasMLString(line, cchar)) {
373 inMLineComment =
false;
380 if(line.front() ==
'[' && line.back() !=
']' && line.find_first_of(
commentChar) != std::string::npos) {
381 std::size_t comment_search = 0;
382 while(comment_search < line.size()) {
383 auto test_char = line[comment_search];
384 if(test_char ==
'\"' || test_char ==
'\'' || test_char ==
'`') {
385 comment_search = detail::close_sequence(line, comment_search, line[comment_search]);
393 if(comment_search < line.size() && line[comment_search] ==
commentChar) {
394 line = detail::trim_copy(line.substr(0, comment_search));
401 if(line.front() ==
'[' && line.back() ==
']') {
402 if(currentSection !=
"default") {
404 output.emplace_back();
405 output.back().parents = detail::generate_parents(currentSection, name,
parentSeparatorChar);
406 output.back().name =
"--";
408 currentSection = line.substr(1, len - 2);
410 if(currentSection.size() > 1 && currentSection.front() ==
'[' && currentSection.back() ==
']') {
411 currentSection = currentSection.substr(1, currentSection.size() - 2);
413 if(detail::to_lower(currentSection) ==
"default") {
414 currentSection =
"default";
419 if(currentSection == previousSection) {
420 ++currentSectionIndex;
422 currentSectionIndex = 0;
423 previousSection = currentSection;
429 if(line.front() ==
';' || line.front() ==
'#' || line.front() ==
commentChar) {
432 std::size_t search_start = 0;
433 if(line.find_first_of(
"\"'`") != std::string::npos) {
434 while(search_start < line.size()) {
435 auto test_char = line[search_start];
436 if(test_char ==
'\"' || test_char ==
'\'' || test_char ==
'`') {
437 search_start = detail::close_sequence(line, search_start, line[search_start]);
445 search_start = line.find_first_of(line_sep_chars, search_start);
450 auto delimiter_pos = line.find_first_of(
valueDelimiter, search_start + 1);
451 auto comment_pos = line.find_first_of(
commentChar, search_start);
452 if(comment_pos < delimiter_pos) {
453 delimiter_pos = std::string::npos;
456 if(delimiter_pos != std::string::npos) {
458 name = detail::trim_copy(line.substr(0, delimiter_pos));
459 std::string item = detail::trim_copy(line.substr(delimiter_pos + 1, std::string::npos));
461 (item.compare(0, 3, multiline_literal_quote) == 0 || item.compare(0, 3, multiline_string_quote) == 0);
462 if(!mlquote && comment_pos != std::string::npos) {
464 item = detail::trim_copy(citems.front());
468 auto keyChar = item.front();
469 auto offset = buffer.find_first_not_of(
" \t");
470 item = buffer.substr((offset == std::string::npos ? 0 : offset) + delimiter_pos + 1, std::string::npos);
474 bool lineExtension{
false};
475 bool firstLine =
true;
476 auto close = detail::find_ml_close(item, keyChar,
commentChar);
477 if(close != std::string::npos) {
480 if(keyChar ==
'\"') {
482 item = detail::remove_escaped_characters(item);
483 }
catch(
const std::invalid_argument &iarg) {
487 inMLineValue =
false;
488 }
else if(!item.empty() && item.back() ==
'\\' && keyChar ==
'\"') {
490 lineExtension =
true;
492 while(inMLineValue) {
494 if(!std::getline(input, l2)) {
497 close = detail::find_ml_close(l2, keyChar,
commentChar);
498 if(close != std::string::npos) {
499 line = l2.substr(0, close);
502 }
else if(!(firstLine && item.empty())) {
503 item.push_back(
'\n');
507 inMLineValue =
false;
508 if(!item.empty() && item.back() ==
'\n') {
511 if(keyChar ==
'\"') {
513 item = detail::remove_escaped_characters(item);
514 }
catch(
const std::invalid_argument &iarg) {
521 }
else if(!(firstLine && item.empty())) {
522 item.push_back(
'\n');
524 lineExtension =
false;
526 if(!l2.empty() && l2.back() ==
'\\' && keyChar ==
'\"') {
527 lineExtension =
true;
533 if(detail::is_binary_escaped_string(item)) {
534 item = detail::extract_binary_string(item);
536 items_buffer = {item};
537 }
else if(!item.empty() && item.front() == aStart) {
538 for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
539 detail::trim(multiline);
542 if(item.back() == aEnd) {
543 items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
545 items_buffer = detail::split_up(item.substr(1, std::string::npos), aSep);
547 }
else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
548 items_buffer = detail::split_up(item, aSep);
549 }
else if((isDefaultArray || isINIArray) && item.find_first_of(
' ') != std::string::npos) {
550 items_buffer = detail::split_up(item,
'\0');
552 items_buffer = {item};
555 name = detail::trim_copy(line.substr(0, comment_pos));
556 items_buffer = {
"true"};
558 std::vector<std::string> parents;
561 detail::process_quoted_string(name,
'"',
'\'',
true);
564 for(
auto &it : items_buffer) {
568 }
catch(
const std::invalid_argument &ia) {
582 parents.erase(parents.begin());
586 if(match != output.end()) {
589 if(!(match->inputs.back().empty() || items_buffer.front().empty() || match->inputs.back() ==
"%%" ||
590 items_buffer.front() ==
"%%")) {
591 match->inputs.emplace_back(
"%%");
592 match->multiline =
true;
595 match->inputs.insert(match->inputs.end(), items_buffer.begin(), items_buffer.end());
597 output.emplace_back();
598 output.back().parents = std::move(parents);
599 output.back().name = std::move(name);
600 output.back().inputs = std::move(items_buffer);
603 if(currentSection !=
"default") {
606 output.emplace_back();
607 output.back().parents = detail::generate_parents(currentSection, ename,
parentSeparatorChar);
608 output.back().name =
"--";
609 while(output.back().parents.size() > 1) {
610 output.push_back(output.back());
611 output.back().parents.pop_back();
617CLI11_INLINE std::string
620 default_also ? ConfigOutputMode::AllDefaults : ConfigOutputMode::Active,
625CLI11_INLINE std::string
627 std::stringstream out;
628 const bool include_default_values = (mode != ConfigOutputMode::Active);
629 std::string commentLead;
631 commentLead.push_back(
' ');
633 std::string commentTest =
"#;";
637 std::string keyChars = commentTest;
645 std::vector<std::string> groups = app->
get_groups();
646 bool defaultUsed =
false;
647 groups.insert(groups.begin(), std::string(
"OPTIONS"));
651 const std::vector<const Option *> options =
653 for(
auto &group : groups) {
654 if(group ==
"OPTIONS" || group.empty()) {
660 if(write_description && group !=
"OPTIONS" && !group.empty()) {
661 out <<
'\n' <<
commentChar << commentLead << group <<
" Options\n";
663 for(
const Option *opt : options) {
667 if(!(group ==
"OPTIONS" && opt->
get_group().empty())) {
672 if(single_name.empty()) {
678 std::reverse(results.begin(), results.end());
681 results.size() == 1) {
684 auto pos = opt->_validate(results[0], 0);
697 auto delim_count = std::count(results[0].begin(), results[0].end(), delim);
698 if(results[0].back() == delim ||
699 static_cast<decltype(delim_count)
>(opt->
count()) <= delim_count ||
700 results[0].find(std::string(2, delim)) != std::string::npos) {
707 if(opt->
count() == 1 && results.size() == 2 && results.front() ==
"{}" && results.back() ==
"%%") {
717 bool isDefault =
false;
718 if(value.empty() && include_default_values) {
728 value =
"\"<REQUIRED>\"";
739 for(
const auto &test_name : opt->
get_fnames()) {
742 single_name = test_name;
749 value = detail::ini_join(
755 if(out.tellp() != std::streampos(0)) {
758 out << commentLead << detail::fix_newlines(commentLead, opt->
get_description()) <<
'\n';
760 detail::clean_name_string(single_name, keyChars);
762 std::string name = prefix + single_name;
773 for(
const App *subcom : subcommands) {
774 if(subcom->get_name().empty()) {
775 if(!include_default_values && (subcom->count_all() == 0)) {
779 if(write_description && !subcom->get_group().empty() && subcom->get_group().front() !=
'+') {
780 out <<
'\n' <<
commentChar << commentLead << subcom->get_group() <<
" Options\n";
794 out <<
to_config(subcom, mode, write_description, prefix);
798 for(
const App *subcom : subcommands) {
799 if(!subcom->get_name().empty()) {
800 if((!include_default_values && (subcom->count_all() == 0)) ||
801 (mode == ConfigOutputMode::ActiveSubcommandDefaults && !app->
got_subcommand(subcom))) {
804 std::string subname = subcom->get_name();
805 detail::clean_name_string(subname, keyChars);
807 if(subcom->get_configurable() && (app->
got_subcommand(subcom) || (mode == ConfigOutputMode::AllDefaults))) {
808 if(!prefix.empty() || app->
get_parent() ==
nullptr) {
810 out <<
'[' << prefix << subname <<
"]\n";
812 std::string appname = app->
get_name();
813 detail::clean_name_string(appname, keyChars);
816 while(p->get_parent() !=
nullptr) {
817 std::string pname = p->get_name();
818 detail::clean_name_string(pname, keyChars);
822 out <<
'[' << subname <<
"]\n";
824 out <<
to_config(subcom, mode, write_description,
"");
831 if(write_description && !out.str().empty()) {
832 std::string outString =
834 return outString + out.str();
839CLI11_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:1177
CLI11_NODISCARD std::vector< std::string > get_groups() const
Get the groups available directly from this option (in order).
Definition App_inl.hpp:1182
bool got_subcommand(const App *subcom) const
Check to see if given subcommand was selected.
Definition App_inl.hpp:852
CLI11_NODISCARD std::vector< App * > get_subcommands() const
Definition App.hpp:935
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:984
CLI11_NODISCARD std::string get_description() const
Get the app or subcommand description.
Definition App.hpp:1035
CLI11_NODISCARD const std::string & get_name() const
Get the name of the current app.
Definition App.hpp:1183
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:626
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:331
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.
CLI11_NODISCARD char get_delimiter() const
Get the current delimiter char.
Definition Option.hpp:154
CLI11_NODISCARD bool get_required() const
True if this is a required option.
Definition Option.hpp:139
CLI11_NODISCARD MultiOptionPolicy get_multi_option_policy() const
The status of the multi option policy.
Definition Option.hpp:160
CLI11_NODISCARD bool get_configurable() const
The status of configurable.
Definition Option.hpp:148
CLI11_NODISCARD const std::string & get_group() const
Get the group of this option.
Definition Option.hpp:136
Definition Option.hpp:261
CLI11_NODISCARD results_t reduced_results() const
Get a copy of the results.
Definition Option_inl.hpp:513
CLI11_NODISCARD bool get_run_callback_for_default() const
Get the current value of run_callback_for_default.
Definition Option.hpp:440
CLI11_NODISCARD std::string get_default_str() const
The default value (for help printing).
Definition Option.hpp:576
CLI11_NODISCARD const results_t & results() const
Get the current complete results set.
Definition Option.hpp:697
CLI11_NODISCARD std::size_t count() const
Count the total number of times an option was passed.
Definition Option.hpp:388
CLI11_NODISCARD const std::string & get_single_name() const
Get a single name for the option, first of lname, sname, pname, envname.
Definition Option_inl.hpp:574
App * parent_
link back up to the parent App for fallthrough
Definition Option.hpp:338
CLI11_NODISCARD std::string get_flag_value(const std::string &name, std::string input_value) const
Definition Option_inl.hpp:447
CLI11_NODISCARD const std::string & get_description() const
Get the description.
Definition Option.hpp:620
CLI11_NODISCARD int get_expected_min() const
The number of times the option expects to be included.
Definition Option.hpp:595
CLI11_NODISCARD const std::vector< std::string > & get_fnames() const
Get the flag names with specified default values.
Definition Option.hpp:588
CLI11_NODISCARD bool has_description() const
True if option has description.
Definition Option.hpp:617
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