12#include "../Config.hpp"
14#include "../Encoding.hpp"
31 std::vector<std::string> tmp =
parents;
32 tmp.emplace_back(
name);
33 return detail::join(tmp,
".");
37CLI11_INLINE std::string
38Config::to_config(
const App *app, ConfigOutputMode mode,
bool write_description, std::string prefix)
const {
39 return to_config(app, mode != ConfigOutputMode::Active, write_description, std::move(prefix));
43 if(item.
inputs.size() == 1) {
49 throw ConversionError::TooManyInputsFlag(item.
fullname());
53#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
54 std::ifstream input{to_path(name)};
56 std::ifstream input{name};
60 throw FileError::Missing(name);
65static constexpr auto multiline_literal_quote = R
"(''')";
66static constexpr auto multiline_string_quote = R
"(""")";
70CLI11_INLINE
bool is_printable(
const std::string &test_string) {
71 return std::all_of(test_string.begin(), test_string.end(), [](
char x) {
72 return (isprint(static_cast<unsigned char>(x)) != 0 || x ==
'\n' || x ==
'\t');
76CLI11_INLINE std::string
77convert_arg_for_ini(
const std::string &arg,
char stringQuote,
char literalQuote,
bool disable_multi_line) {
79 return std::string(2, stringQuote);
82 if(arg ==
"true" || arg ==
"false" || arg ==
"nan" || arg ==
"inf") {
86 if(arg.compare(0, 2,
"0x") != 0 && arg.compare(0, 2,
"0X") != 0) {
87 using CLI::detail::lexical_cast;
89 if(lexical_cast(arg, val)) {
90 if(arg.find_first_not_of(
"0123456789.-+eE") == std::string::npos) {
97 if(isprint(
static_cast<unsigned char>(arg.front())) == 0) {
98 return binary_escape_string(arg);
101 return std::string(1, stringQuote) +
"'" + stringQuote;
103 return std::string(1, literalQuote) + arg + literalQuote;
106 if(arg.front() ==
'0') {
108 if(std::all_of(arg.begin() + 2, arg.end(), [](
char x) {
109 return (x >=
'0' && x <=
'9') || (x >=
'A' && x <=
'F') || (x >=
'a' && x <=
'f');
113 }
else if(arg[1] ==
'o') {
114 if(std::all_of(arg.begin() + 2, arg.end(), [](
char x) { return (x >=
'0' && x <=
'7'); })) {
117 }
else if(arg[1] ==
'b') {
118 if(std::all_of(arg.begin() + 2, arg.end(), [](
char x) { return (x ==
'0' || x ==
'1'); })) {
123 if(!is_printable(arg)) {
124 return binary_escape_string(arg);
126 if(detail::has_escapable_character(arg)) {
127 if(arg.size() > 100 && !disable_multi_line) {
128 if(arg.find(multiline_literal_quote) != std::string::npos) {
129 return binary_escape_string(arg,
true);
131 std::string return_string{multiline_literal_quote};
132 return_string.reserve(7 + arg.size());
133 if(arg.front() ==
'\n') {
134 return_string.push_back(
'\n');
136 return_string.append(arg);
137 if(arg.back() ==
'\n') {
138 return_string.push_back(
'\n');
140 return_string.append(multiline_literal_quote, 3);
141 return return_string;
143 return std::string(1, stringQuote) + detail::add_escaped_characters(arg) + stringQuote;
145 return std::string(1, stringQuote) + arg + stringQuote;
148CLI11_INLINE std::string ini_join(
const std::vector<std::string> &args,
154 bool disable_multi_line{
false};
156 if(args.size() > 1 && arrayStart !=
'\0') {
157 joined.push_back(arrayStart);
158 disable_multi_line =
true;
160 const bool sep_is_space = std::isspace<char>(sepChar, std::locale());
161 std::size_t start = 0;
162 for(
const auto &arg : args) {
164 joined.push_back(sepChar);
166 joined.push_back(
' ');
169 joined.append(convert_arg_for_ini(arg, stringQuote, literalQuote, disable_multi_line));
171 if(args.size() > 1 && arrayEnd !=
'\0') {
172 joined.push_back(arrayEnd);
177CLI11_INLINE std::vector<std::string>
178generate_parents(
const std::string §ion, std::string &name,
char parentSeparator) {
179 std::vector<std::string> parents;
180 if(detail::to_lower(section) !=
"default") {
181 if(section.find(parentSeparator) != std::string::npos) {
182 parents = detail::split_up(section, parentSeparator);
187 if(name.find(parentSeparator) != std::string::npos) {
188 std::vector<std::string> plist = detail::split_up(name, parentSeparator);
191 parents.insert(parents.end(), plist.begin(), plist.end());
195 detail::remove_quotes(parents);
196 }
catch(
const std::invalid_argument &iarg) {
197 throw CLI::ParseError(iarg.what(), CLI::ExitCodes::InvalidError);
203checkParentSegments(std::vector<ConfigItem> &output,
const std::string ¤tSection,
char parentSeparator) {
206 auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
207 if(!output.empty() && output.back().name ==
"--") {
208 std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
209 while(output.back().parents.size() >= msize) {
210 output.push_back(output.back());
211 output.back().parents.pop_back();
214 if(parents.size() > 1) {
215 std::size_t common = 0;
216 std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
217 for(std::size_t ii = 0; ii < mpair; ++ii) {
218 if(output.back().parents[ii] != parents[ii]) {
223 if(common == mpair) {
226 while(output.back().parents.size() > common + 1) {
227 output.push_back(output.back());
228 output.back().parents.pop_back();
231 for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
232 output.emplace_back();
233 output.back().parents.assign(parents.begin(), parents.begin() +
static_cast<std::ptrdiff_t
>(ii) + 1);
234 output.back().name =
"++";
237 }
else if(parents.size() > 1) {
238 for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
239 output.emplace_back();
240 output.back().parents.assign(parents.begin(), parents.begin() +
static_cast<std::ptrdiff_t
>(ii) + 1);
241 output.back().name =
"++";
246 output.emplace_back();
247 output.back().parents = std::move(parents);
248 output.back().name =
"++";
252CLI11_INLINE
bool hasMLString(std::string
const &fullString,
char check) {
253 if(fullString.length() < 3) {
256 auto it = fullString.rbegin();
257 return (*it == check) && (*(it + 1) == check) && (*(it + 2) == check);
261CLI11_INLINE
auto find_matching_config(std::vector<ConfigItem> &items,
262 const std::vector<std::string> &parents,
263 const std::string &name,
264 bool fullSearch) ->
decltype(items.begin()) {
268 auto search = items.end() - 1;
270 if(search->parents == parents && search->name == name) {
273 if(search == items.begin()) {
281CLI11_INLINE
void clean_name_string(std::string &name,
const std::string &keyChars) {
282 if(name.find_first_of(keyChars) != std::string::npos || (name.front() ==
'[' && name.back() ==
']') ||
283 (name.find_first_of(
"'`\"\\") != std::string::npos)) {
284 if(name.find_first_of(
'\'') == std::string::npos) {
285 name.insert(0, 1,
'\'');
286 name.push_back(
'\'');
288 if(detail::has_escapable_character(name)) {
289 name = detail::add_escaped_characters(name);
291 name.insert(0, 1,
'\"');
292 name.push_back(
'\"');
301 std::string currentSection =
"default";
302 std::string previousSection =
"default";
303 std::vector<ConfigItem> output;
306 bool inSection{
false};
307 bool inMLineComment{
false};
308 bool inMLineValue{
false};
310 char aStart = (isINIArray) ?
'[' :
arrayStart;
311 char aEnd = (isINIArray) ?
']' :
arrayEnd;
313 int currentSectionIndex{0};
316 while(getline(input, buffer)) {
317 std::vector<std::string> items_buffer;
319 line = detail::trim_copy(buffer);
320 std::size_t len = line.length();
325 if(line.compare(0, 3, multiline_string_quote) == 0 || line.compare(0, 3, multiline_literal_quote) == 0) {
328 if(len >= 6 && detail::hasMLString(line, line.front())) {
331 inMLineComment =
true;
332 auto cchar = line.front();
333 while(inMLineComment) {
334 if(getline(input, line)) {
339 if(detail::hasMLString(line, cchar)) {
340 inMLineComment =
false;
347 if(line.front() ==
'[' && line.back() !=
']' && line.find_first_of(
commentChar) != std::string::npos) {
348 std::size_t comment_search = 0;
349 while(comment_search < line.size()) {
350 auto test_char = line[comment_search];
351 if(test_char ==
'\"' || test_char ==
'\'' || test_char ==
'`') {
352 comment_search = detail::close_sequence(line, comment_search, line[comment_search]);
360 if(comment_search < line.size() && line[comment_search] ==
commentChar) {
361 line = detail::trim_copy(line.substr(0, comment_search));
368 if(line.front() ==
'[' && line.back() ==
']') {
369 if(currentSection !=
"default") {
371 output.emplace_back();
372 output.back().parents = detail::generate_parents(currentSection, name,
parentSeparatorChar);
373 output.back().name =
"--";
375 currentSection = line.substr(1, len - 2);
377 if(currentSection.size() > 1 && currentSection.front() ==
'[' && currentSection.back() ==
']') {
378 currentSection = currentSection.substr(1, currentSection.size() - 2);
380 if(detail::to_lower(currentSection) ==
"default") {
381 currentSection =
"default";
386 if(currentSection == previousSection) {
387 ++currentSectionIndex;
389 currentSectionIndex = 0;
390 previousSection = currentSection;
396 if(line.front() ==
';' || line.front() ==
'#' || line.front() ==
commentChar) {
399 std::size_t search_start = 0;
400 if(line.find_first_of(
"\"'`") != std::string::npos) {
401 while(search_start < line.size()) {
402 auto test_char = line[search_start];
403 if(test_char ==
'\"' || test_char ==
'\'' || test_char ==
'`') {
404 search_start = detail::close_sequence(line, search_start, line[search_start]);
412 search_start = line.find_first_of(line_sep_chars, search_start);
417 auto delimiter_pos = line.find_first_of(
valueDelimiter, search_start + 1);
418 auto comment_pos = line.find_first_of(
commentChar, search_start);
419 if(comment_pos < delimiter_pos) {
420 delimiter_pos = std::string::npos;
422 if(delimiter_pos != std::string::npos) {
424 name = detail::trim_copy(line.substr(0, delimiter_pos));
425 std::string item = detail::trim_copy(line.substr(delimiter_pos + 1, std::string::npos));
427 (item.compare(0, 3, multiline_literal_quote) == 0 || item.compare(0, 3, multiline_string_quote) == 0);
428 if(!mlquote && comment_pos != std::string::npos) {
430 item = detail::trim_copy(citems.front());
434 auto keyChar = item.front();
435 auto offset = buffer.find_first_not_of(
" \t");
436 item = buffer.substr((offset == std::string::npos ? 0 : offset) + delimiter_pos + 1, std::string::npos);
440 bool lineExtension{
false};
441 bool firstLine =
true;
442 if(!item.empty() && item.back() ==
'\\' && keyChar ==
'\"') {
444 lineExtension =
true;
445 }
else if(detail::hasMLString(item, keyChar)) {
450 if(keyChar ==
'\"') {
452 item = detail::remove_escaped_characters(item);
453 }
catch(
const std::invalid_argument &iarg) {
457 inMLineValue =
false;
459 while(inMLineValue) {
461 if(!std::getline(input, l2)) {
466 if(detail::hasMLString(line, keyChar)) {
472 }
else if(!(firstLine && item.empty())) {
473 item.push_back(
'\n');
477 inMLineValue =
false;
478 if(!item.empty() && item.back() ==
'\n') {
481 if(keyChar ==
'\"') {
483 item = detail::remove_escaped_characters(item);
484 }
catch(
const std::invalid_argument &iarg) {
491 }
else if(!(firstLine && item.empty())) {
492 item.push_back(
'\n');
494 lineExtension =
false;
496 if(!l2.empty() && l2.back() ==
'\\' && keyChar ==
'\"') {
497 lineExtension =
true;
503 items_buffer = {item};
504 }
else if(!item.empty() && item.front() == aStart) {
505 for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
506 detail::trim(multiline);
509 if(item.back() == aEnd) {
510 items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
512 items_buffer = detail::split_up(item.substr(1, std::string::npos), aSep);
514 }
else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
515 items_buffer = detail::split_up(item, aSep);
516 }
else if((isDefaultArray || isINIArray) && item.find_first_of(
' ') != std::string::npos) {
517 items_buffer = detail::split_up(item,
'\0');
519 items_buffer = {item};
522 name = detail::trim_copy(line.substr(0, comment_pos));
523 items_buffer = {
"true"};
525 std::vector<std::string> parents;
528 detail::process_quoted_string(name,
'"',
'\'',
true);
530 for(
auto &it : items_buffer) {
533 }
catch(
const std::invalid_argument &ia) {
547 parents.erase(parents.begin());
551 if(match != output.end()) {
554 if(!(match->inputs.back().empty() || items_buffer.front().empty() || match->inputs.back() ==
"%%" ||
555 items_buffer.front() ==
"%%")) {
556 match->inputs.emplace_back(
"%%");
557 match->multiline =
true;
560 match->inputs.insert(match->inputs.end(), items_buffer.begin(), items_buffer.end());
562 output.emplace_back();
563 output.back().parents = std::move(parents);
564 output.back().name = std::move(name);
565 output.back().inputs = std::move(items_buffer);
568 if(currentSection !=
"default") {
571 output.emplace_back();
572 output.back().parents = detail::generate_parents(currentSection, ename,
parentSeparatorChar);
573 output.back().name =
"--";
574 while(output.back().parents.size() > 1) {
575 output.push_back(output.back());
576 output.back().parents.pop_back();
582CLI11_INLINE std::string
585 default_also ? ConfigOutputMode::AllDefaults : ConfigOutputMode::Active,
590CLI11_INLINE std::string
592 std::stringstream out;
593 const bool include_default_values = (mode != ConfigOutputMode::Active);
594 std::string commentLead;
596 commentLead.push_back(
' ');
598 std::string commentTest =
"#;";
602 std::string keyChars = commentTest;
610 std::vector<std::string> groups = app->
get_groups();
611 bool defaultUsed =
false;
612 groups.insert(groups.begin(), std::string(
"OPTIONS"));
614 const std::vector<const Option *> options = app->
get_options({});
615 for(
auto &group : groups) {
616 if(group ==
"OPTIONS" || group.empty()) {
622 if(write_description && group !=
"OPTIONS" && !group.empty()) {
623 out <<
'\n' <<
commentChar << commentLead << group <<
" Options\n";
627 if(opt->get_configurable()) {
628 if(opt->get_group() != group) {
629 if(!(group ==
"OPTIONS" && opt->get_group().empty())) {
633 std::string single_name = opt->get_single_name();
634 if(single_name.empty()) {
638 auto results = opt->reduced_results();
639 if(results.size() > 1 && opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Reverse) {
640 std::reverse(results.begin(), results.end());
642 if(opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Sum && opt->count() >= 1 &&
643 results.size() == 1) {
646 auto pos = opt->_validate(results[0], 0);
648 results = opt->results();
651 if(opt->get_multi_option_policy() == CLI::MultiOptionPolicy::Join && opt->count() > 1) {
652 char delim = opt->get_delimiter();
655 results = opt->results();
659 auto delim_count = std::count(results[0].begin(), results[0].end(), delim);
660 if(results[0].back() == delim ||
661 static_cast<decltype(delim_count)
>(opt->count()) <= delim_count ||
662 results[0].find(std::string(2, delim)) != std::string::npos) {
663 results = opt->results();
669 if(opt->count() == 1 && results.size() == 2 && results.front() ==
"{}" && results.back() ==
"%%") {
679 bool isDefault =
false;
680 if(value.empty() && include_default_values) {
681 if(!opt->get_default_str().empty()) {
685 }
else if(opt->get_expected_min() == 0) {
687 }
else if(opt->get_run_callback_for_default() || !opt->get_required()) {
690 value =
"\"<REQUIRED>\"";
696 if(!opt->get_fnames().empty()) {
698 value = opt->get_flag_value(single_name, value);
701 for(
const auto &test_name : opt->get_fnames()) {
703 value = opt->get_flag_value(test_name, value);
704 single_name = test_name;
711 value = detail::ini_join(
716 if(write_description && opt->has_description()) {
717 if(out.tellp() != std::streampos(0)) {
720 out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) <<
'\n';
722 detail::clean_name_string(single_name, keyChars);
724 std::string name = prefix + single_name;
735 for(
const App *subcom : subcommands) {
736 if(subcom->get_name().empty()) {
737 if(!include_default_values && (subcom->count_all() == 0)) {
740 if(write_description && !subcom->get_group().empty()) {
741 out <<
'\n' <<
commentChar << commentLead << subcom->get_group() <<
" Options\n";
755 out <<
to_config(subcom, mode, write_description, prefix);
759 for(
const App *subcom : subcommands) {
760 if(!subcom->get_name().empty()) {
761 if((!include_default_values && (subcom->count_all() == 0)) ||
762 (mode == ConfigOutputMode::ActiveSubcommandDefaults && !app->
got_subcommand(subcom))) {
765 std::string subname = subcom->get_name();
766 detail::clean_name_string(subname, keyChars);
768 if(subcom->get_configurable() && (app->
got_subcommand(subcom) || (mode == ConfigOutputMode::AllDefaults))) {
769 if(!prefix.empty() || app->
get_parent() ==
nullptr) {
771 out <<
'[' << prefix << subname <<
"]\n";
773 std::string appname = app->
get_name();
774 detail::clean_name_string(appname, keyChars);
777 while(p->get_parent() !=
nullptr) {
778 std::string pname = p->get_name();
779 detail::clean_name_string(pname, keyChars);
783 out <<
'[' << subname <<
"]\n";
785 out <<
to_config(subcom, mode, write_description,
"");
792 if(write_description && !out.str().empty()) {
793 std::string outString =
795 return outString + out.str();
800CLI11_INLINE ConfigINI::ConfigINI() {
Creates a command line program, with very few defaults.
Definition App.hpp:114
App * get_parent()
Get the parent of this subcommand (or nullptr if main app).
Definition App.hpp:1172
CLI11_NODISCARD std::vector< std::string > get_groups() const
Get the groups available directly from this option (in order).
Definition App_inl.hpp:1173
bool got_subcommand(const App *subcom) const
Check to see if given subcommand was selected.
Definition App_inl.hpp:833
CLI11_NODISCARD std::vector< App * > get_subcommands() const
Definition App.hpp:931
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:975
CLI11_NODISCARD std::string get_description() const
Get the app or subcommand description.
Definition App.hpp:1031
CLI11_NODISCARD const std::string & get_name() const
Get the name of the current app.
Definition App.hpp:1178
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:100
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:591
char arraySeparator
the character used to separate elements in an array
Definition ConfigFwd.hpp:82
std::vector< ConfigItem > from_config(std::istream &input) const override
Convert a configuration into an app.
Definition Config_inl.hpp:298
std::uint8_t maximumLayers
the maximum number of layers to allow
Definition ConfigFwd.hpp:90
char stringQuote
the character to use around strings
Definition ConfigFwd.hpp:86
char valueDelimiter
the character used separate the name from the value
Definition ConfigFwd.hpp:84
char arrayStart
the character used to start an array '\0' is a default to not use
Definition ConfigFwd.hpp:78
char parentSeparatorChar
the separator used to separator parent layers
Definition ConfigFwd.hpp:92
bool allowMultipleDuplicateFields
specify the config reader should collapse repeated field names to a single vector
Definition ConfigFwd.hpp:96
char literalQuote
the character to use around single characters and literal strings
Definition ConfigFwd.hpp:88
char arrayEnd
the character used to end an array '\0' is a default to not use
Definition ConfigFwd.hpp:80
bool commentDefaultsBool
comment default values
Definition ConfigFwd.hpp:94
int16_t configIndex
Specify the configuration index to use for arrayed sections.
Definition ConfigFwd.hpp:98
char commentChar
the character used for comments
Definition ConfigFwd.hpp:76
virtual CLI11_NODISCARD std::string to_flag(const ConfigItem &item) const
Get a flag value.
Definition Config_inl.hpp:42
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:52
virtual std::vector< ConfigItem > from_config(std::istream &) const =0
Convert a configuration into an app.
Definition Option.hpp:259
Anything that can error in Parse.
Definition Error.hpp:160
Holds values to load into Options.
Definition ConfigFwd.hpp:32
CLI11_NODISCARD std::string fullname() const
The list of parents and name joined by ".".
Definition Config_inl.hpp:30
std::vector< std::string > inputs
Listing of inputs.
Definition ConfigFwd.hpp:39
std::string name
This is the name.
Definition ConfigFwd.hpp:37
bool multiline
indicator if a multiline vector separator was inserted
Definition ConfigFwd.hpp:41
std::vector< std::string > parents
This is the list of parents.
Definition ConfigFwd.hpp:34