CLI11
C++11 Command Line Interface Parser
Loading...
Searching...
No Matches
Validators_inl.hpp
1// Copyright (c) 2017-2024, University of Cincinnati, developed by Henry Schreiner
2// under NSF AWARD 1414736 and by the respective contributors.
3// All rights reserved.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6
7#pragma once
8
9// IWYU pragma: private, include "CLI/CLI.hpp"
10#include "../Validators.hpp"
11
12#include "../Encoding.hpp"
13#include "../Macros.hpp"
14#include "../StringTools.hpp"
15#include "../TypeTools.hpp"
16
17// [CLI11:public_includes:set]
18#include <map>
19#include <string>
20#include <utility>
21// [CLI11:public_includes:end]
22
23namespace CLI {
24// [CLI11:validators_inl_hpp:verbatim]
25
26CLI11_INLINE std::string Validator::operator()(std::string &str) const {
27 std::string retstring;
28 if(active_) {
29 if(non_modifying_) {
30 std::string value = str;
31 retstring = func_(value);
32 } else {
33 retstring = func_(str);
34 }
35 }
36 return retstring;
37}
38
39CLI11_NODISCARD CLI11_INLINE Validator Validator::description(std::string validator_desc) const {
40 Validator newval(*this);
41 newval.desc_function_ = [validator_desc]() { return validator_desc; };
42 return newval;
43}
44
45CLI11_INLINE Validator Validator::operator&(const Validator &other) const {
46 Validator newval;
47
48 newval._merge_description(*this, other, " AND ");
49
50 // Give references (will make a copy in lambda function)
51 const std::function<std::string(std::string & filename)> &f1 = func_;
52 const std::function<std::string(std::string & filename)> &f2 = other.func_;
53
54 newval.func_ = [f1, f2](std::string &input) {
55 std::string s1 = f1(input);
56 std::string s2 = f2(input);
57 if(!s1.empty() && !s2.empty())
58 return std::string("(") + s1 + ") AND (" + s2 + ")";
59 return s1 + s2;
60 };
61
62 newval.active_ = active_ && other.active_;
63 newval.application_index_ = application_index_;
64 return newval;
65}
66
67CLI11_INLINE Validator Validator::operator|(const Validator &other) const {
68 Validator newval;
69
70 newval._merge_description(*this, other, " OR ");
71
72 // Give references (will make a copy in lambda function)
73 const std::function<std::string(std::string &)> &f1 = func_;
74 const std::function<std::string(std::string &)> &f2 = other.func_;
75
76 newval.func_ = [f1, f2](std::string &input) {
77 std::string s1 = f1(input);
78 std::string s2 = f2(input);
79 if(s1.empty() || s2.empty())
80 return std::string();
81
82 return std::string("(") + s1 + ") OR (" + s2 + ")";
83 };
84 newval.active_ = active_ && other.active_;
85 newval.application_index_ = application_index_;
86 return newval;
87}
88
89CLI11_INLINE Validator Validator::operator!() const {
90 Validator newval;
91 const std::function<std::string()> &dfunc1 = desc_function_;
92 newval.desc_function_ = [dfunc1]() {
93 auto str = dfunc1();
94 return (!str.empty()) ? std::string("NOT ") + str : std::string{};
95 };
96 // Give references (will make a copy in lambda function)
97 const std::function<std::string(std::string & res)> &f1 = func_;
98
99 newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
100 std::string s1 = f1(test);
101 if(s1.empty()) {
102 return std::string("check ") + dfunc1() + " succeeded improperly";
103 }
104 return std::string{};
105 };
106 newval.active_ = active_;
107 newval.application_index_ = application_index_;
108 return newval;
109}
110
111CLI11_INLINE void
112Validator::_merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
113
114 const std::function<std::string()> &dfunc1 = val1.desc_function_;
115 const std::function<std::string()> &dfunc2 = val2.desc_function_;
116
117 desc_function_ = [=]() {
118 std::string f1 = dfunc1();
119 std::string f2 = dfunc2();
120 if((f1.empty()) || (f2.empty())) {
121 return f1 + f2;
122 }
123 return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
124 };
125}
126
127namespace detail {
128
129#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
130CLI11_INLINE path_type check_path(const char *file) noexcept {
131 std::error_code ec;
132 auto stat = std::filesystem::status(to_path(file), ec);
133 if(ec) {
134 return path_type::nonexistent;
135 }
136 switch(stat.type()) {
137 case std::filesystem::file_type::none: // LCOV_EXCL_LINE
138 case std::filesystem::file_type::not_found:
139 return path_type::nonexistent; // LCOV_EXCL_LINE
140 case std::filesystem::file_type::directory:
141 return path_type::directory;
142 case std::filesystem::file_type::symlink:
143 case std::filesystem::file_type::block:
144 case std::filesystem::file_type::character:
145 case std::filesystem::file_type::fifo:
146 case std::filesystem::file_type::socket:
147 case std::filesystem::file_type::regular:
148 case std::filesystem::file_type::unknown:
149 default:
150 return path_type::file;
151 }
152}
153#else
154CLI11_INLINE path_type check_path(const char *file) noexcept {
155#if defined(_MSC_VER)
156 struct __stat64 buffer;
157 if(_stat64(file, &buffer) == 0) {
158 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
159 }
160#else
161 struct stat buffer;
162 if(stat(file, &buffer) == 0) {
163 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
164 }
165#endif
166 return path_type::nonexistent;
167}
168#endif
169
170CLI11_INLINE ExistingFileValidator::ExistingFileValidator() : Validator("FILE") {
171 func_ = [](std::string &filename) {
172 auto path_result = check_path(filename.c_str());
173 if(path_result == path_type::nonexistent) {
174 return "File does not exist: " + filename;
175 }
176 if(path_result == path_type::directory) {
177 return "File is actually a directory: " + filename;
178 }
179 return std::string();
180 };
181}
182
183CLI11_INLINE ExistingDirectoryValidator::ExistingDirectoryValidator() : Validator("DIR") {
184 func_ = [](std::string &filename) {
185 auto path_result = check_path(filename.c_str());
186 if(path_result == path_type::nonexistent) {
187 return "Directory does not exist: " + filename;
188 }
189 if(path_result == path_type::file) {
190 return "Directory is actually a file: " + filename;
191 }
192 return std::string();
193 };
194}
195
196CLI11_INLINE ExistingPathValidator::ExistingPathValidator() : Validator("PATH(existing)") {
197 func_ = [](std::string &filename) {
198 auto path_result = check_path(filename.c_str());
199 if(path_result == path_type::nonexistent) {
200 return "Path does not exist: " + filename;
201 }
202 return std::string();
203 };
204}
205
206CLI11_INLINE NonexistentPathValidator::NonexistentPathValidator() : Validator("PATH(non-existing)") {
207 func_ = [](std::string &filename) {
208 auto path_result = check_path(filename.c_str());
209 if(path_result != path_type::nonexistent) {
210 return "Path already exists: " + filename;
211 }
212 return std::string();
213 };
214}
215
216CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") {
217 func_ = [](std::string &ip_addr) {
218 auto result = CLI::detail::split(ip_addr, '.');
219 if(result.size() != 4) {
220 return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
221 }
222 int num = 0;
223 for(const auto &var : result) {
224 using CLI::detail::lexical_cast;
225 bool retval = lexical_cast(var, num);
226 if(!retval) {
227 return std::string("Failed parsing number (") + var + ')';
228 }
229 if(num < 0 || num > 255) {
230 return std::string("Each IP number must be between 0 and 255 ") + var;
231 }
232 }
233 return std::string{};
234 };
235}
236
237CLI11_INLINE EscapedStringTransformer::EscapedStringTransformer() {
238 func_ = [](std::string &str) {
239 try {
240 if(str.size() > 1 && (str.front() == '\"' || str.front() == '\'' || str.front() == '`') &&
241 str.front() == str.back()) {
242 process_quoted_string(str);
243 } else if(str.find_first_of('\\') != std::string::npos) {
244 if(detail::is_binary_escaped_string(str)) {
245 str = detail::extract_binary_string(str);
246 } else {
247 str = remove_escaped_characters(str);
248 }
249 }
250 return std::string{};
251 } catch(const std::invalid_argument &ia) {
252 return std::string(ia.what());
253 }
254 };
255}
256} // namespace detail
257
258CLI11_INLINE FileOnDefaultPath::FileOnDefaultPath(std::string default_path, bool enableErrorReturn)
259 : Validator("FILE") {
260 func_ = [default_path, enableErrorReturn](std::string &filename) {
261 auto path_result = detail::check_path(filename.c_str());
262 if(path_result == detail::path_type::nonexistent) {
263 std::string test_file_path = default_path;
264 if(default_path.back() != '/' && default_path.back() != '\\') {
265 // Add folder separator
266 test_file_path += '/';
267 }
268 test_file_path.append(filename);
269 path_result = detail::check_path(test_file_path.c_str());
270 if(path_result == detail::path_type::file) {
271 filename = test_file_path;
272 } else {
273 if(enableErrorReturn) {
274 return "File does not exist: " + filename;
275 }
276 }
277 }
278 return std::string{};
279 };
280}
281
282CLI11_INLINE AsSizeValue::AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
283 if(kb_is_1000) {
284 description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
285 } else {
286 description("SIZE [b, kb(=1024b), ...]");
287 }
288}
289
290CLI11_INLINE std::map<std::string, AsSizeValue::result_t> AsSizeValue::init_mapping(bool kb_is_1000) {
291 std::map<std::string, result_t> m;
292 result_t k_factor = kb_is_1000 ? 1000 : 1024;
293 result_t ki_factor = 1024;
294 result_t k = 1;
295 result_t ki = 1;
296 m["b"] = 1;
297 for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
298 k *= k_factor;
299 ki *= ki_factor;
300 m[p] = k;
301 m[p + "b"] = k;
302 m[p + "i"] = ki;
303 m[p + "ib"] = ki;
304 }
305 return m;
306}
307
308CLI11_INLINE std::map<std::string, AsSizeValue::result_t> AsSizeValue::get_mapping(bool kb_is_1000) {
309 if(kb_is_1000) {
310 static auto m = init_mapping(true);
311 return m;
312 }
313 static auto m = init_mapping(false);
314 return m;
315}
316
317namespace detail {
318
319CLI11_INLINE std::pair<std::string, std::string> split_program_name(std::string commandline) {
320 // try to determine the programName
321 std::pair<std::string, std::string> vals;
322 trim(commandline);
323 auto esp = commandline.find_first_of(' ', 1);
324 while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) {
325 esp = commandline.find_first_of(' ', esp + 1);
326 if(esp == std::string::npos) {
327 // if we have reached the end and haven't found a valid file just assume the first argument is the
328 // program name
329 if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') {
330 bool embeddedQuote = false;
331 auto keyChar = commandline[0];
332 auto end = commandline.find_first_of(keyChar, 1);
333 while((end != std::string::npos) && (commandline[end - 1] == '\\')) { // deal with escaped quotes
334 end = commandline.find_first_of(keyChar, end + 1);
335 embeddedQuote = true;
336 }
337 if(end != std::string::npos) {
338 vals.first = commandline.substr(1, end - 1);
339 esp = end + 1;
340 if(embeddedQuote) {
341 vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar));
342 }
343 } else {
344 esp = commandline.find_first_of(' ', 1);
345 }
346 } else {
347 esp = commandline.find_first_of(' ', 1);
348 }
349
350 break;
351 }
352 }
353 if(vals.first.empty()) {
354 vals.first = commandline.substr(0, esp);
355 rtrim(vals.first);
356 }
357
358 // strip the program name
359 vals.second = (esp < commandline.length() - 1) ? commandline.substr(esp + 1) : std::string{};
360 ltrim(vals.second);
361 return vals;
362}
363
364} // namespace detail
366
367// [CLI11:validators_inl_hpp:end]
368} // namespace CLI
Definition Validators.hpp:718
AsSizeValue(bool kb_is_1000)
Definition Validators_inl.hpp:282
Some validators that are provided.
Definition Validators.hpp:55
Validator operator&(const Validator &other) const
Definition Validators_inl.hpp:45
int application_index_
A Validator will only apply to an indexed value (-1 is all elements)
Definition Validators.hpp:66
Validator & description(std::string validator_desc)
Specify the type string.
Definition Validators.hpp:100
Validator operator|(const Validator &other) const
Definition Validators_inl.hpp:67
bool active_
Enable for Validator to allow it to be disabled if need be.
Definition Validators.hpp:68
bool non_modifying_
specify that a validator should not modify the input
Definition Validators.hpp:70
std::function< std::string()> desc_function_
This is the description function, if empty the description_ will be used.
Definition Validators.hpp:58
std::function< std::string(std::string &)> func_
Definition Validators.hpp:62
Validator operator!() const
Create a validator that fails when a given validator succeeds.
Definition Validators_inl.hpp:89
std::string operator()(std::string &str) const
Definition Validators_inl.hpp:26