mirror of
https://github.com/CLIUtils/CLI11.git
synced 2026-01-19 04:52:08 +00:00
Rework some of the validator locations, add documentation, and fix some lingering issues with validators. The extra will will enable additions of some new validators and reduce compile times for those that are not needed. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
313 lines
10 KiB
C++
313 lines
10 KiB
C++
// Copyright (c) 2017-2025, 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
|
|
|
|
// IWYU pragma: private, include "CLI/CLI.hpp"
|
|
#include "../Validators.hpp"
|
|
|
|
#include "../Encoding.hpp"
|
|
#include "../Macros.hpp"
|
|
#include "../StringTools.hpp"
|
|
#include "../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 EscapedStringTransformer::EscapedStringTransformer() {
|
|
func_ = [](std::string &str) {
|
|
try {
|
|
if(str.size() > 1 && (str.front() == '\"' || str.front() == '\'' || str.front() == '`') &&
|
|
str.front() == str.back()) {
|
|
process_quoted_string(str);
|
|
} else if(str.find_first_of('\\') != std::string::npos) {
|
|
if(detail::is_binary_escaped_string(str)) {
|
|
str = detail::extract_binary_string(str);
|
|
} else {
|
|
str = remove_escaped_characters(str);
|
|
}
|
|
}
|
|
return std::string{};
|
|
} catch(const std::invalid_argument &ia) {
|
|
return std::string(ia.what());
|
|
}
|
|
};
|
|
}
|
|
} // 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{};
|
|
};
|
|
}
|
|
|
|
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
|