Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ If it is desired that multiple configuration be allowed. Use
app.set_config("--config")->expected(1, X);
```

Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments.
Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments. Value strings with quote characters in it will be printed with a single quote.🚧 All other arguments will use double quote. Empty strings will use a double quoted argument.🚧 Numerical or boolean values are not quoted. 🚧

### Inheriting defaults

Expand Down
16 changes: 16 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@ add_test(NAME subcom_partitioned_help COMMAND subcom_partitioned --help)
set_property(TEST subcom_partitioned_help PROPERTY PASS_REGULAR_EXPRESSION
"-f,--file TEXT REQUIRED" "-d,--double FLOAT")

####################################################
add_cli_exe(config_app config_app.cpp)
add_test(NAME config_app1 COMMAND config_app -p)
set_property(TEST config_app1 PROPERTY PASS_REGULAR_EXPRESSION "file=")

add_test(NAME config_app2 COMMAND config_app -p -f /)
set_property(TEST config_app2 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"")

add_test(NAME config_app3 COMMAND config_app -f "" -p)
set_property(TEST config_app3 PROPERTY PASS_REGULAR_EXPRESSION "file=\"\"")

add_test(NAME config_app4 COMMAND config_app -f "/" -p)
set_property(TEST config_app4 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"")

####################################################

add_cli_exe(option_groups option_groups.cpp)
add_test(NAME option_groups_missing COMMAND option_groups)
set_property(TEST option_groups_missing PROPERTY PASS_REGULAR_EXPRESSION "Exactly 1 option from"
Expand Down
50 changes: 50 additions & 0 deletions examples/config_app.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause

#include <CLI/CLI.hpp>
#include <iostream>
#include <string>

int main(int argc, char **argv) {

CLI::App app("configuration print example");

app.add_flag("-p,--print", "Print configuration and exit")->configurable(false); // NEW: print flag

std::string file;
CLI::Option *opt = app.add_option("-f,--file,file", file, "File name")
->capture_default_str()
->run_callback_for_default(); // NEW: capture_default_str()

int count{0};
CLI::Option *copt =
app.add_option("-c,--count", count, "Counter")->capture_default_str(); // NEW: capture_default_str()

int v{0};
CLI::Option *flag = app.add_flag("--flag", v, "Some flag that can be passed multiple times")
->capture_default_str(); // NEW: capture_default_str()

double value{0.0}; // = 3.14;
app.add_option("-d,--double", value, "Some Value")->capture_default_str(); // NEW: capture_default_str()

app.get_config_formatter_base()->quoteCharacter('"', '"');

CLI11_PARSE(app, argc, argv);

if(app.get_option("--print")->as<bool>()) { // NEW: print configuration and exit
std::cout << app.config_to_str(true, false);
return 0;
}

std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
<< ", opt count: " << opt->count() << std::endl;
std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
<< ", opt count: " << copt->count() << std::endl;
std::cout << "Received flag: " << v << " (" << flag->count() << ") times\n";
std::cout << "Some value: " << value << std::endl;

return 0;
}
29 changes: 18 additions & 11 deletions include/CLI/Config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ namespace CLI {
// [CLI11:config_hpp:verbatim]
namespace detail {

inline std::string convert_arg_for_ini(const std::string &arg) {
inline std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'') {
if(arg.empty()) {
return std::string(2, '"');
return std::string(2, stringQuote);
}
// some specifically supported strings
if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
Expand All @@ -40,7 +40,7 @@ inline std::string convert_arg_for_ini(const std::string &arg) {
}
// just quote a single non numeric character
if(arg.size() == 1) {
return std::string("'") + arg + '\'';
return std::string(1, characterQuote) + arg + characterQuote;
}
// handle hex, binary or octal arguments
if(arg.front() == '0') {
Expand All @@ -60,16 +60,20 @@ inline std::string convert_arg_for_ini(const std::string &arg) {
}
}
}
if(arg.find_first_of('"') == std::string::npos) {
return std::string("\"") + arg + '"';
if(arg.find_first_of(stringQuote) == std::string::npos) {
return std::string(1, stringQuote) + arg + stringQuote;
} else {
return std::string("'") + arg + '\'';
return characterQuote + arg + characterQuote;
}
}

/// Comma separated join, adds quotes if needed
inline std::string
ini_join(const std::vector<std::string> &args, char sepChar = ',', char arrayStart = '[', char arrayEnd = ']') {
inline std::string ini_join(const std::vector<std::string> &args,
char sepChar = ',',
char arrayStart = '[',
char arrayEnd = ']',
char stringQuote = '"',
char characterQuote = '\'') {
std::string joined;
if(args.size() > 1 && arrayStart != '\0') {
joined.push_back(arrayStart);
Expand All @@ -82,7 +86,7 @@ ini_join(const std::vector<std::string> &args, char sepChar = ',', char arraySta
joined.push_back(' ');
}
}
joined.append(convert_arg_for_ini(arg));
joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));
}
if(args.size() > 1 && arrayEnd != '\0') {
joined.push_back(arrayEnd);
Expand Down Expand Up @@ -296,13 +300,16 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
}
}
std::string name = prefix + opt->get_single_name();
std::string value = detail::ini_join(opt->reduced_results(), arraySeparator, arrayStart, arrayEnd);
std::string value = detail::ini_join(
opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote);

if(value.empty() && default_also) {
if(!opt->get_default_str().empty()) {
value = detail::convert_arg_for_ini(opt->get_default_str());
value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote);
} else if(opt->get_expected_min() == 0) {
value = "false";
} else if(opt->get_run_callback_for_default()) {
value = "\"\""; // empty string default value
}
}

Expand Down
10 changes: 10 additions & 0 deletions include/CLI/ConfigFwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class ConfigBase : public Config {
char arraySeparator = ',';
/// the character used separate the name from the value
char valueDelimiter = '=';
/// the character to use around strings
char stringQuote = '"';
/// the character to use around single characters
char characterQuote = '\'';

public:
std::string
Expand Down Expand Up @@ -114,6 +118,12 @@ class ConfigBase : public Config {
valueDelimiter = vSep;
return this;
}
/// Specify the quote characters used around strings and characters
ConfigBase *quoteCharacter(char qString, char qChar) {
stringQuote = qString;
characterQuote = qChar;
return this;
}
};

/// the default Config is the TOML file format
Expand Down