1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 12:43:52 +00:00

Add environment variable processing to the configuration pointer. (#891)

Fixes #890 

Add parsing of environmental variables when supplied for the config file
option.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Philip Top 2023-06-27 11:40:22 -07:00 committed by GitHub
parent 84cad16974
commit a1135bb30c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 21 deletions

View File

@ -223,6 +223,8 @@ CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset);
/// Add quotes if the string contains spaces /// Add quotes if the string contains spaces
CLI11_INLINE std::string &add_quotes_if_needed(std::string &str); CLI11_INLINE std::string &add_quotes_if_needed(std::string &str);
/// get the value of an environmental variable or empty string if empty
CLI11_INLINE std::string get_environment_value(const std::string &env_name);
} // namespace detail } // namespace detail
// [CLI11:string_tools_hpp:end] // [CLI11:string_tools_hpp:end]

View File

@ -1333,6 +1333,9 @@ template <class AssignTo,
detail::enabler> = detail::dummy> detail::enabler> = detail::dummy>
bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) { bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
output.erase(output.begin(), output.end()); output.erase(output.begin(), output.end());
if(strings.empty()) {
return true;
}
if(strings.size() == 1 && strings[0] == "{}") { if(strings.size() == 1 && strings[0] == "{}") {
return true; return true;
} }

View File

@ -315,8 +315,11 @@ CLI11_INLINE Option *App::set_config(std::string option_name,
} }
if(!default_filename.empty()) { if(!default_filename.empty()) {
config_ptr_->default_str(std::move(default_filename)); config_ptr_->default_str(std::move(default_filename));
config_ptr_->force_callback_ = true;
} }
config_ptr_->configurable(false); config_ptr_->configurable(false);
// set the option to take the last value given by default
config_ptr_->take_last();
} }
return config_ptr_; return config_ptr_;
@ -1014,10 +1017,18 @@ CLI11_INLINE void App::_process_config_file() {
if(config_ptr_ != nullptr) { if(config_ptr_ != nullptr) {
bool config_required = config_ptr_->get_required(); bool config_required = config_ptr_->get_required();
auto file_given = config_ptr_->count() > 0; auto file_given = config_ptr_->count() > 0;
if(!(file_given || config_ptr_->envname_.empty())) {
std::string ename_string = detail::get_environment_value(config_ptr_->envname_);
if(!ename_string.empty()) {
config_ptr_->add_result(ename_string);
}
}
config_ptr_->run_callback();
auto config_files = config_ptr_->as<std::vector<std::string>>(); auto config_files = config_ptr_->as<std::vector<std::string>>();
if(config_files.empty() || config_files.front().empty()) { if(config_files.empty() || config_files.front().empty()) {
if(config_required) { if(config_required) {
throw FileError::Missing("no specified config file"); throw FileError("config file is required but none was given");
} }
return; return;
} }
@ -1028,9 +1039,6 @@ CLI11_INLINE void App::_process_config_file() {
try { try {
std::vector<ConfigItem> values = config_formatter_->from_file(config_file); std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
_parse_config(values); _parse_config(values);
if(!file_given) {
config_ptr_->add_result(config_file);
}
} catch(const FileError &) { } catch(const FileError &) {
if(config_required || file_given) if(config_required || file_given)
throw; throw;
@ -1045,23 +1053,7 @@ CLI11_INLINE void App::_process_config_file() {
CLI11_INLINE void App::_process_env() { CLI11_INLINE void App::_process_env() {
for(const Option_p &opt : options_) { for(const Option_p &opt : options_) {
if(opt->count() == 0 && !opt->envname_.empty()) { if(opt->count() == 0 && !opt->envname_.empty()) {
char *buffer = nullptr; std::string ename_string = detail::get_environment_value(opt->envname_);
std::string ename_string;
#ifdef _MSC_VER
// Windows version
std::size_t sz = 0;
if(_dupenv_s(&buffer, &sz, opt->envname_.c_str()) == 0 && buffer != nullptr) {
ename_string = std::string(buffer);
free(buffer);
}
#else
// This also works on Windows, but gives a warning
buffer = std::getenv(opt->envname_.c_str());
if(buffer != nullptr)
ename_string = std::string(buffer);
#endif
if(!ename_string.empty()) { if(!ename_string.empty()) {
opt->add_result(ename_string); opt->add_result(ename_string);
} }

View File

@ -255,6 +255,27 @@ CLI11_INLINE std::string &add_quotes_if_needed(std::string &str) {
return str; return str;
} }
std::string get_environment_value(const std::string &env_name) {
char *buffer = nullptr;
std::string ename_string;
#ifdef _MSC_VER
// Windows version
std::size_t sz = 0;
if(_dupenv_s(&buffer, &sz, env_name.c_str()) == 0 && buffer != nullptr) {
ename_string = std::string(buffer);
free(buffer);
}
#else
// This also works on Windows, but gives a warning
buffer = std::getenv(env_name.c_str());
if(buffer != nullptr) {
ename_string = std::string(buffer);
}
#endif
return ename_string;
}
} // namespace detail } // namespace detail
// [CLI11:string_tools_inl_hpp:end] // [CLI11:string_tools_inl_hpp:end]
} // namespace CLI } // namespace CLI

View File

@ -673,6 +673,36 @@ TEST_CASE_METHOD(TApp, "IniNotRequiredNotDefault", "[config]") {
CHECK(tmpini2.c_str() == app.get_config_ptr()->as<std::string>()); CHECK(tmpini2.c_str() == app.get_config_ptr()->as<std::string>());
} }
TEST_CASE_METHOD(TApp, "IniEnvironmentalFileName", "[config]") {
TempFile tmpini{"TestIniTmp.ini"};
app.set_config("--config", "")->envname("CONFIG")->required();
{
std::ofstream out{tmpini};
out << "[default]" << std::endl;
out << "two=99" << std::endl;
out << "three=3" << std::endl;
}
int one{0}, two{0}, three{0};
app.add_option("--one", one);
app.add_option("--two", two);
app.add_option("--three", three);
put_env("CONFIG", tmpini);
CHECK_NOTHROW(run());
CHECK(two == 99);
CHECK(three == 3);
unset_env("CONFIG");
CHECK_THROWS_AS(run(), CLI::FileError);
}
TEST_CASE_METHOD(TApp, "MultiConfig", "[config]") { TEST_CASE_METHOD(TApp, "MultiConfig", "[config]") {
TempFile tmpini{"TestIniTmp.ini"}; TempFile tmpini{"TestIniTmp.ini"};

View File

@ -1354,3 +1354,14 @@ TEST_CASE("FixNewLines: EdgesCheck", "[helpers]") {
std::string result = CLI::detail::fix_newlines("; ", input); std::string result = CLI::detail::fix_newlines("; ", input);
CHECK(output == result); CHECK(output == result);
} }
TEST_CASE("String: environment", "[helpers]") {
put_env("TEST1", "TESTS");
auto value = CLI::detail::get_environment_value("TEST1");
CHECK(value == "TESTS");
unset_env("TEST1");
value = CLI::detail::get_environment_value("TEST2");
CHECK(value.empty());
}