mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
remove old code since all arguments are quoted now and the code was not being used, add coverage exclusion on some code that should never be executed and add an additional test --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
349 lines
11 KiB
C++
349 lines
11 KiB
C++
// Copyright (c) 2017-2023, 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
|
|
|
|
#pragma once
|
|
|
|
#include <CLI/Validators.hpp>
|
|
|
|
#include <CLI/Encoding.hpp>
|
|
#include <CLI/Macros.hpp>
|
|
#include <CLI/StringTools.hpp>
|
|
#include <CLI/TypeTools.hpp>
|
|
|
|
// [CLI11:public_includes:set]
|
|
#include <map>
|
|
#include <string>
|
|
#include <utility>
|
|
// [CLI11:public_includes:end]
|
|
|
|
namespace CLI {
|
|
// [CLI11:validators_inl_hpp:verbatim]
|
|
|
|
CLI11_INLINE std::string Validator::operator()(std::string &str) const {
|
|
std::string retstring;
|
|
if(active_) {
|
|
if(non_modifying_) {
|
|
std::string value = str;
|
|
retstring = func_(value);
|
|
} else {
|
|
retstring = func_(str);
|
|
}
|
|
}
|
|
return retstring;
|
|
}
|
|
|
|
CLI11_NODISCARD CLI11_INLINE Validator Validator::description(std::string validator_desc) const {
|
|
Validator newval(*this);
|
|
newval.desc_function_ = [validator_desc]() { return validator_desc; };
|
|
return newval;
|
|
}
|
|
|
|
CLI11_INLINE Validator Validator::operator&(const Validator &other) const {
|
|
Validator newval;
|
|
|
|
newval._merge_description(*this, other, " AND ");
|
|
|
|
// Give references (will make a copy in lambda function)
|
|
const std::function<std::string(std::string & filename)> &f1 = func_;
|
|
const std::function<std::string(std::string & filename)> &f2 = other.func_;
|
|
|
|
newval.func_ = [f1, f2](std::string &input) {
|
|
std::string s1 = f1(input);
|
|
std::string s2 = f2(input);
|
|
if(!s1.empty() && !s2.empty())
|
|
return std::string("(") + s1 + ") AND (" + s2 + ")";
|
|
return s1 + s2;
|
|
};
|
|
|
|
newval.active_ = active_ && other.active_;
|
|
newval.application_index_ = application_index_;
|
|
return newval;
|
|
}
|
|
|
|
CLI11_INLINE Validator Validator::operator|(const Validator &other) const {
|
|
Validator newval;
|
|
|
|
newval._merge_description(*this, other, " OR ");
|
|
|
|
// Give references (will make a copy in lambda function)
|
|
const std::function<std::string(std::string &)> &f1 = func_;
|
|
const std::function<std::string(std::string &)> &f2 = other.func_;
|
|
|
|
newval.func_ = [f1, f2](std::string &input) {
|
|
std::string s1 = f1(input);
|
|
std::string s2 = f2(input);
|
|
if(s1.empty() || s2.empty())
|
|
return std::string();
|
|
|
|
return std::string("(") + s1 + ") OR (" + s2 + ")";
|
|
};
|
|
newval.active_ = active_ && other.active_;
|
|
newval.application_index_ = application_index_;
|
|
return newval;
|
|
}
|
|
|
|
CLI11_INLINE Validator Validator::operator!() const {
|
|
Validator newval;
|
|
const std::function<std::string()> &dfunc1 = desc_function_;
|
|
newval.desc_function_ = [dfunc1]() {
|
|
auto str = dfunc1();
|
|
return (!str.empty()) ? std::string("NOT ") + str : std::string{};
|
|
};
|
|
// Give references (will make a copy in lambda function)
|
|
const std::function<std::string(std::string & res)> &f1 = func_;
|
|
|
|
newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
|
|
std::string s1 = f1(test);
|
|
if(s1.empty()) {
|
|
return std::string("check ") + dfunc1() + " succeeded improperly";
|
|
}
|
|
return std::string{};
|
|
};
|
|
newval.active_ = active_;
|
|
newval.application_index_ = application_index_;
|
|
return newval;
|
|
}
|
|
|
|
CLI11_INLINE void
|
|
Validator::_merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
|
|
|
|
const std::function<std::string()> &dfunc1 = val1.desc_function_;
|
|
const std::function<std::string()> &dfunc2 = val2.desc_function_;
|
|
|
|
desc_function_ = [=]() {
|
|
std::string f1 = dfunc1();
|
|
std::string f2 = dfunc2();
|
|
if((f1.empty()) || (f2.empty())) {
|
|
return f1 + f2;
|
|
}
|
|
return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
|
|
};
|
|
}
|
|
|
|
namespace detail {
|
|
|
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
|
CLI11_INLINE path_type check_path(const char *file) noexcept {
|
|
std::error_code ec;
|
|
auto stat = std::filesystem::status(to_path(file), ec);
|
|
if(ec) {
|
|
return path_type::nonexistent;
|
|
}
|
|
switch(stat.type()) {
|
|
case std::filesystem::file_type::none: // LCOV_EXCL_LINE
|
|
case std::filesystem::file_type::not_found:
|
|
return path_type::nonexistent; // LCOV_EXCL_LINE
|
|
case std::filesystem::file_type::directory:
|
|
return path_type::directory;
|
|
case std::filesystem::file_type::symlink:
|
|
case std::filesystem::file_type::block:
|
|
case std::filesystem::file_type::character:
|
|
case std::filesystem::file_type::fifo:
|
|
case std::filesystem::file_type::socket:
|
|
case std::filesystem::file_type::regular:
|
|
case std::filesystem::file_type::unknown:
|
|
default:
|
|
return path_type::file;
|
|
}
|
|
}
|
|
#else
|
|
CLI11_INLINE path_type check_path(const char *file) noexcept {
|
|
#if defined(_MSC_VER)
|
|
struct __stat64 buffer;
|
|
if(_stat64(file, &buffer) == 0) {
|
|
return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
|
|
}
|
|
#else
|
|
struct stat buffer;
|
|
if(stat(file, &buffer) == 0) {
|
|
return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
|
|
}
|
|
#endif
|
|
return path_type::nonexistent;
|
|
}
|
|
#endif
|
|
|
|
CLI11_INLINE ExistingFileValidator::ExistingFileValidator() : Validator("FILE") {
|
|
func_ = [](std::string &filename) {
|
|
auto path_result = check_path(filename.c_str());
|
|
if(path_result == path_type::nonexistent) {
|
|
return "File does not exist: " + filename;
|
|
}
|
|
if(path_result == path_type::directory) {
|
|
return "File is actually a directory: " + filename;
|
|
}
|
|
return std::string();
|
|
};
|
|
}
|
|
|
|
CLI11_INLINE ExistingDirectoryValidator::ExistingDirectoryValidator() : Validator("DIR") {
|
|
func_ = [](std::string &filename) {
|
|
auto path_result = check_path(filename.c_str());
|
|
if(path_result == path_type::nonexistent) {
|
|
return "Directory does not exist: " + filename;
|
|
}
|
|
if(path_result == path_type::file) {
|
|
return "Directory is actually a file: " + filename;
|
|
}
|
|
return std::string();
|
|
};
|
|
}
|
|
|
|
CLI11_INLINE ExistingPathValidator::ExistingPathValidator() : Validator("PATH(existing)") {
|
|
func_ = [](std::string &filename) {
|
|
auto path_result = check_path(filename.c_str());
|
|
if(path_result == path_type::nonexistent) {
|
|
return "Path does not exist: " + filename;
|
|
}
|
|
return std::string();
|
|
};
|
|
}
|
|
|
|
CLI11_INLINE NonexistentPathValidator::NonexistentPathValidator() : Validator("PATH(non-existing)") {
|
|
func_ = [](std::string &filename) {
|
|
auto path_result = check_path(filename.c_str());
|
|
if(path_result != path_type::nonexistent) {
|
|
return "Path already exists: " + filename;
|
|
}
|
|
return std::string();
|
|
};
|
|
}
|
|
|
|
CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") {
|
|
func_ = [](std::string &ip_addr) {
|
|
auto result = CLI::detail::split(ip_addr, '.');
|
|
if(result.size() != 4) {
|
|
return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
|
|
}
|
|
int num = 0;
|
|
for(const auto &var : result) {
|
|
using CLI::detail::lexical_cast;
|
|
bool retval = lexical_cast(var, num);
|
|
if(!retval) {
|
|
return std::string("Failed parsing number (") + var + ')';
|
|
}
|
|
if(num < 0 || num > 255) {
|
|
return std::string("Each IP number must be between 0 and 255 ") + var;
|
|
}
|
|
}
|
|
return std::string();
|
|
};
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
CLI11_INLINE FileOnDefaultPath::FileOnDefaultPath(std::string default_path, bool enableErrorReturn)
|
|
: Validator("FILE") {
|
|
func_ = [default_path, enableErrorReturn](std::string &filename) {
|
|
auto path_result = detail::check_path(filename.c_str());
|
|
if(path_result == detail::path_type::nonexistent) {
|
|
std::string test_file_path = default_path;
|
|
if(default_path.back() != '/' && default_path.back() != '\\') {
|
|
// Add folder separator
|
|
test_file_path += '/';
|
|
}
|
|
test_file_path.append(filename);
|
|
path_result = detail::check_path(test_file_path.c_str());
|
|
if(path_result == detail::path_type::file) {
|
|
filename = test_file_path;
|
|
} else {
|
|
if(enableErrorReturn) {
|
|
return "File does not exist: " + filename;
|
|
}
|
|
}
|
|
}
|
|
return std::string{};
|
|
};
|
|
}
|
|
|
|
CLI11_INLINE AsSizeValue::AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
|
|
if(kb_is_1000) {
|
|
description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
|
|
} else {
|
|
description("SIZE [b, kb(=1024b), ...]");
|
|
}
|
|
}
|
|
|
|
CLI11_INLINE std::map<std::string, AsSizeValue::result_t> AsSizeValue::init_mapping(bool kb_is_1000) {
|
|
std::map<std::string, result_t> m;
|
|
result_t k_factor = kb_is_1000 ? 1000 : 1024;
|
|
result_t ki_factor = 1024;
|
|
result_t k = 1;
|
|
result_t ki = 1;
|
|
m["b"] = 1;
|
|
for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
|
|
k *= k_factor;
|
|
ki *= ki_factor;
|
|
m[p] = k;
|
|
m[p + "b"] = k;
|
|
m[p + "i"] = ki;
|
|
m[p + "ib"] = ki;
|
|
}
|
|
return m;
|
|
}
|
|
|
|
CLI11_INLINE std::map<std::string, AsSizeValue::result_t> AsSizeValue::get_mapping(bool kb_is_1000) {
|
|
if(kb_is_1000) {
|
|
static auto m = init_mapping(true);
|
|
return m;
|
|
}
|
|
static auto m = init_mapping(false);
|
|
return m;
|
|
}
|
|
|
|
namespace detail {
|
|
|
|
CLI11_INLINE std::pair<std::string, std::string> split_program_name(std::string commandline) {
|
|
// try to determine the programName
|
|
std::pair<std::string, std::string> vals;
|
|
trim(commandline);
|
|
auto esp = commandline.find_first_of(' ', 1);
|
|
while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) {
|
|
esp = commandline.find_first_of(' ', esp + 1);
|
|
if(esp == std::string::npos) {
|
|
// if we have reached the end and haven't found a valid file just assume the first argument is the
|
|
// program name
|
|
if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') {
|
|
bool embeddedQuote = false;
|
|
auto keyChar = commandline[0];
|
|
auto end = commandline.find_first_of(keyChar, 1);
|
|
while((end != std::string::npos) && (commandline[end - 1] == '\\')) { // deal with escaped quotes
|
|
end = commandline.find_first_of(keyChar, end + 1);
|
|
embeddedQuote = true;
|
|
}
|
|
if(end != std::string::npos) {
|
|
vals.first = commandline.substr(1, end - 1);
|
|
esp = end + 1;
|
|
if(embeddedQuote) {
|
|
vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar));
|
|
}
|
|
} else {
|
|
esp = commandline.find_first_of(' ', 1);
|
|
}
|
|
} else {
|
|
esp = commandline.find_first_of(' ', 1);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
if(vals.first.empty()) {
|
|
vals.first = commandline.substr(0, esp);
|
|
rtrim(vals.first);
|
|
}
|
|
|
|
// strip the program name
|
|
vals.second = (esp < commandline.length() - 1) ? commandline.substr(esp + 1) : std::string{};
|
|
ltrim(vals.second);
|
|
return vals;
|
|
}
|
|
|
|
} // namespace detail
|
|
/// @}
|
|
|
|
// [CLI11:validators_inl_hpp:end]
|
|
} // namespace CLI
|