12#include "../StringTools.hpp"
25CLI11_INLINE std::vector<std::string> split(
const std::string &s,
char delim) {
26 std::vector<std::string> elems;
34 while(std::getline(ss, item, delim)) {
35 elems.push_back(item);
41CLI11_INLINE std::string <rim(std::string &str) {
42 auto it = std::find_if(str.begin(), str.end(), [](
char ch) { return !std::isspace<char>(ch, std::locale()); });
43 str.erase(str.begin(), it);
47CLI11_INLINE std::string <rim(std::string &str,
const std::string &filter) {
48 auto it = std::find_if(str.begin(), str.end(), [&filter](
char ch) { return filter.find(ch) == std::string::npos; });
49 str.erase(str.begin(), it);
53CLI11_INLINE std::string &rtrim(std::string &str) {
54 auto it = std::find_if(str.rbegin(), str.rend(), [](
char ch) { return !std::isspace<char>(ch, std::locale()); });
55 str.erase(it.base(), str.end());
59CLI11_INLINE std::string &rtrim(std::string &str,
const std::string &filter) {
61 std::find_if(str.rbegin(), str.rend(), [&filter](
char ch) { return filter.find(ch) == std::string::npos; });
62 str.erase(it.base(), str.end());
66CLI11_INLINE std::string &remove_quotes(std::string &str) {
67 if(str.length() > 1 && (str.front() ==
'"' || str.front() ==
'\'' || str.front() ==
'`')) {
68 if(str.front() == str.back()) {
70 str.erase(str.begin(), str.begin() + 1);
76CLI11_INLINE std::string &remove_outer(std::string &str,
char key) {
77 if(str.length() > 1 && (str.front() == key)) {
78 if(str.front() == str.back()) {
80 str.erase(str.begin(), str.begin() + 1);
86CLI11_INLINE std::string fix_newlines(
const std::string &leader, std::string input) {
87 std::string::size_type n = 0;
88 while(n != std::string::npos && n < input.size()) {
89 n = input.find(
'\n', n);
90 if(n != std::string::npos) {
91 input = input.substr(0, n + 1) + leader + input.substr(n + 1);
98CLI11_INLINE std::ostream &format_aliases(std::ostream &out,
const std::vector<std::string> &aliases, std::size_t wid) {
99 if(!aliases.empty()) {
100 out << std::setw(static_cast<int>(wid)) <<
" aliases: ";
102 for(
const auto &alias : aliases) {
108 out << detail::fix_newlines(
" ", alias);
115CLI11_INLINE
bool valid_name_string(
const std::string &str) {
116 if(str.empty() || !valid_first_char(str[0])) {
120 for(
auto c = str.begin() + 1; c != e; ++c)
121 if(!valid_later_char(*c))
126CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) {
128 std::size_t start_pos = 0;
130 while((start_pos = str.find(from, start_pos)) != std::string::npos) {
131 str.replace(start_pos, from.length(), to);
132 start_pos += to.length();
138CLI11_INLINE
void remove_default_flag_values(std::string &flags) {
139 auto loc = flags.find_first_of(
'{', 2);
140 while(loc != std::string::npos) {
141 auto finish = flags.find_first_of(
"},", loc + 1);
142 if((finish != std::string::npos) && (flags[finish] ==
'}')) {
143 flags.erase(flags.begin() +
static_cast<std::ptrdiff_t
>(loc),
144 flags.begin() +
static_cast<std::ptrdiff_t
>(finish) + 1);
146 loc = flags.find_first_of(
'{', loc + 1);
148 flags.erase(std::remove(flags.begin(), flags.end(),
'!'), flags.end());
151CLI11_INLINE std::ptrdiff_t
152find_member(std::string name,
const std::vector<std::string> names,
bool ignore_case,
bool ignore_underscore) {
153 auto it = std::end(names);
155 if(ignore_underscore) {
156 name = detail::to_lower(detail::remove_underscore(name));
157 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
158 return detail::to_lower(detail::remove_underscore(local_name)) == name;
161 name = detail::to_lower(name);
162 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
163 return detail::to_lower(local_name) == name;
167 }
else if(ignore_underscore) {
168 name = detail::remove_underscore(name);
169 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
170 return detail::remove_underscore(local_name) == name;
173 it = std::find(std::begin(names), std::end(names), name);
176 return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
179static const std::string escapedChars(
"\b\t\n\f\r\"\\");
180static const std::string escapedCharsCode(
"btnfr\"\\");
181static const std::string bracketChars{
"\"'`[(<{"};
182static const std::string matchBracketChars(
"\"'`])>}");
184CLI11_INLINE
bool has_escapable_character(
const std::string &str) {
185 return (str.find_first_of(escapedChars) != std::string::npos);
188CLI11_INLINE std::string add_escaped_characters(
const std::string &str) {
190 out.reserve(str.size() + 4);
192 auto sloc = escapedChars.find_first_of(s);
193 if(sloc != std::string::npos) {
195 out.push_back(escapedCharsCode[sloc]);
203CLI11_INLINE std::uint32_t hexConvert(
char hc) {
205 if(hc >=
'0' && hc <=
'9') {
207 }
else if(hc >=
'A' && hc <=
'F') {
208 hcode = (hc -
'A' + 10);
209 }
else if(hc >=
'a' && hc <=
'f') {
210 hcode = (hc -
'a' + 10);
214 return static_cast<uint32_t
>(hcode);
217CLI11_INLINE
char make_char(std::uint32_t code) {
return static_cast<char>(
static_cast<unsigned char>(code)); }
219CLI11_INLINE
void append_codepoint(std::string &str, std::uint32_t code) {
221 str.push_back(
static_cast<char>(code));
222 }
else if(code < 0x800) {
224 str.push_back(make_char(0xC0 | code >> 6));
225 str.push_back(make_char(0x80 | (code & 0x3F)));
226 }
else if(code < 0x10000) {
227 if(0xD800 <= code && code <= 0xDFFF) {
228 throw std::invalid_argument(
"[0xD800, 0xDFFF] are not valid UTF-8.");
231 str.push_back(make_char(0xE0 | code >> 12));
232 str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
233 str.push_back(make_char(0x80 | (code & 0x3F)));
234 }
else if(code < 0x110000) {
236 str.push_back(make_char(0xF0 | code >> 18));
237 str.push_back(make_char(0x80 | (code >> 12 & 0x3F)));
238 str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
239 str.push_back(make_char(0x80 | (code & 0x3F)));
243CLI11_INLINE std::string remove_escaped_characters(
const std::string &str) {
246 out.reserve(str.size());
247 for(
auto loc = str.begin(); loc < str.end(); ++loc) {
249 if(str.end() - loc < 2) {
250 throw std::invalid_argument(
"invalid escape sequence " + str);
252 auto ecloc = escapedCharsCode.find_first_of(*(loc + 1));
253 if(ecloc != std::string::npos) {
254 out.push_back(escapedChars[ecloc]);
256 }
else if(*(loc + 1) ==
'u') {
258 if(str.end() - loc < 6) {
259 throw std::invalid_argument(
"unicode sequence must have 4 hex codes " + str);
261 std::uint32_t code{0};
262 std::uint32_t mplier{16 * 16 * 16};
263 for(
int ii = 2; ii < 6; ++ii) {
264 std::uint32_t res = hexConvert(*(loc + ii));
266 throw std::invalid_argument(
"unicode sequence must have 4 hex codes " + str);
268 code += res * mplier;
269 mplier = mplier / 16;
271 append_codepoint(out, code);
273 }
else if(*(loc + 1) ==
'U') {
275 if(str.end() - loc < 10) {
276 throw std::invalid_argument(
"unicode sequence must have 8 hex codes " + str);
278 std::uint32_t code{0};
279 std::uint32_t mplier{16 * 16 * 16 * 16 * 16 * 16 * 16};
280 for(
int ii = 2; ii < 10; ++ii) {
281 std::uint32_t res = hexConvert(*(loc + ii));
283 throw std::invalid_argument(
"unicode sequence must have 8 hex codes " + str);
285 code += res * mplier;
286 mplier = mplier / 16;
288 append_codepoint(out, code);
290 }
else if(*(loc + 1) ==
'0') {
294 throw std::invalid_argument(std::string(
"unrecognized escape sequence \\") + *(loc + 1) +
" in " + str);
303CLI11_INLINE std::size_t close_string_quote(
const std::string &str, std::size_t start,
char closure_char) {
305 for(loc = start + 1; loc < str.size(); ++loc) {
306 if(str[loc] == closure_char) {
309 if(str[loc] ==
'\\') {
317CLI11_INLINE std::size_t close_literal_quote(
const std::string &str, std::size_t start,
char closure_char) {
318 auto loc = str.find_first_of(closure_char, start + 1);
319 return (loc != std::string::npos ? loc : str.size());
322CLI11_INLINE std::size_t close_sequence(
const std::string &str, std::size_t start,
char closure_char) {
324 auto bracket_loc = matchBracketChars.find(closure_char);
325 switch(bracket_loc) {
327 return close_string_quote(str, start, closure_char);
330 case std::string::npos:
331 return close_literal_quote(str, start, closure_char);
336 std::string closures(1, closure_char);
337 auto loc = start + 1;
339 while(loc < str.size()) {
340 if(str[loc] == closures.back()) {
342 if(closures.empty()) {
346 bracket_loc = bracketChars.find(str[loc]);
347 if(bracket_loc != std::string::npos) {
348 switch(bracket_loc) {
350 loc = close_string_quote(str, loc, str[loc]);
354 loc = close_literal_quote(str, loc, str[loc]);
357 closures.push_back(matchBracketChars[bracket_loc]);
363 if(loc > str.size()) {
369CLI11_INLINE std::vector<std::string> split_up(std::string str,
char delimiter) {
371 auto find_ws = [delimiter](
char ch) {
372 return (delimiter ==
'\0') ? std::isspace<char>(ch, std::locale()) : (ch == delimiter);
376 std::vector<std::string> output;
377 while(!str.empty()) {
378 if(bracketChars.find_first_of(str[0]) != std::string::npos) {
379 auto bracketLoc = bracketChars.find_first_of(str[0]);
380 auto end = close_sequence(str, 0, matchBracketChars[bracketLoc]);
381 if(end >= str.size()) {
382 output.push_back(std::move(str));
385 output.push_back(str.substr(0, end + 1));
386 if(end + 2 < str.size()) {
387 str = str.substr(end + 2);
394 auto it = std::find_if(std::begin(str), std::end(str), find_ws);
395 if(it != std::end(str)) {
396 std::string value = std::string(str.begin(), it);
397 output.push_back(value);
398 str = std::string(it + 1, str.end());
400 output.push_back(str);
409CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset) {
410 auto next = str[offset + 1];
411 if((next ==
'\"') || (next ==
'\'') || (next ==
'`')) {
412 auto astart = str.find_last_of(
"-/ \"\'`", offset - 1);
413 if(astart != std::string::npos) {
414 if(str[astart] == ((str[offset] ==
'=') ?
'-' :
'/'))
421CLI11_INLINE std::string binary_escape_string(
const std::string &string_to_escape) {
423 std::string escaped_string{};
425 for(
char c : string_to_escape) {
428 if(isprint(
static_cast<unsigned char>(c)) == 0) {
429 std::stringstream stream;
433 stream << std::hex << static_cast<unsigned int>(
static_cast<unsigned char>(c));
434 std::string code = stream.str();
435 escaped_string += std::string(
"\\x") + (code.size() < 2 ?
"0" :
"") + code;
436 }
else if(c ==
'x' || c ==
'X') {
438 if(!escaped_string.empty() && escaped_string.back() ==
'\\') {
439 escaped_string += std::string(
"\\x") + (c ==
'x' ?
"78" :
"58");
441 escaped_string.push_back(c);
445 escaped_string.push_back(c);
448 if(escaped_string != string_to_escape) {
449 auto sqLoc = escaped_string.find(
'\'');
450 while(sqLoc != std::string::npos) {
451 escaped_string[sqLoc] =
'\\';
452 escaped_string.insert(sqLoc + 1,
"x27");
453 sqLoc = escaped_string.find(
'\'');
455 escaped_string.insert(0,
"'B\"(");
456 escaped_string.push_back(
')');
457 escaped_string.push_back(
'"');
458 escaped_string.push_back(
'\'');
460 return escaped_string;
463CLI11_INLINE
bool is_binary_escaped_string(
const std::string &escaped_string) {
464 size_t ssize = escaped_string.size();
465 if(escaped_string.compare(0, 3,
"B\"(") == 0 && escaped_string.compare(ssize - 2, 2,
")\"") == 0) {
468 return (escaped_string.compare(0, 4,
"'B\"(") == 0 && escaped_string.compare(ssize - 3, 3,
")\"'") == 0);
471CLI11_INLINE std::string extract_binary_string(
const std::string &escaped_string) {
472 std::size_t start{0};
474 size_t ssize = escaped_string.size();
475 if(escaped_string.compare(0, 3,
"B\"(") == 0 && escaped_string.compare(ssize - 2, 2,
")\"") == 0) {
478 }
else if(escaped_string.compare(0, 4,
"'B\"(") == 0 && escaped_string.compare(ssize - 3, 3,
")\"'") == 0) {
484 return escaped_string;
486 std::string outstring;
488 outstring.reserve(ssize - start - tail);
489 std::size_t loc = start;
490 while(loc < ssize - tail) {
492 if(escaped_string[loc] ==
'\\' && (escaped_string[loc + 1] ==
'x' || escaped_string[loc + 1] ==
'X')) {
493 auto c1 = escaped_string[loc + 2];
494 auto c2 = escaped_string[loc + 3];
496 std::uint32_t res1 = hexConvert(c1);
497 std::uint32_t res2 = hexConvert(c2);
498 if(res1 <= 0x0F && res2 <= 0x0F) {
500 outstring.push_back(
static_cast<char>(res1 * 16 + res2));
504 outstring.push_back(escaped_string[loc]);
510CLI11_INLINE
void remove_quotes(std::vector<std::string> &args) {
511 for(
auto &arg : args) {
512 if(arg.front() ==
'\"' && arg.back() ==
'\"') {
515 arg = remove_escaped_characters(arg);
522CLI11_INLINE
bool process_quoted_string(std::string &str,
char string_char,
char literal_char) {
523 if(str.size() <= 1) {
526 if(detail::is_binary_escaped_string(str)) {
527 str = detail::extract_binary_string(str);
530 if(str.front() == string_char && str.back() == string_char) {
531 detail::remove_outer(str, string_char);
532 if(str.find_first_of(
'\\') != std::string::npos) {
533 str = detail::remove_escaped_characters(str);
537 if((str.front() == literal_char || str.front() ==
'`') && str.back() == str.front()) {
538 detail::remove_outer(str, str.front());
544std::string get_environment_value(
const std::string &env_name) {
545 char *buffer =
nullptr;
546 std::string ename_string;
551 if(_dupenv_s(&buffer, &sz, env_name.c_str()) == 0 && buffer !=
nullptr) {
552 ename_string = std::string(buffer);
557 buffer = std::getenv(env_name.c_str());
558 if(buffer !=
nullptr) {
559 ename_string = std::string(buffer);
565CLI11_INLINE std::ostream &streamOutAsParagraph(std::ostream &out,
566 const std::string &text,
567 std::size_t paragraphWidth,
568 const std::string &linePrefix,
569 bool skipPrefixOnFirstLine) {
570 if(!skipPrefixOnFirstLine)
573 std::istringstream lss(text);
574 std::string line =
"";
575 while(std::getline(lss, line)) {
576 std::istringstream iss(line);
577 std::string word =
"";
578 std::size_t charsWritten = 0;
581 if(word.length() + charsWritten > paragraphWidth) {
582 out <<
'\n' << linePrefix;
587 charsWritten += word.length() + 1;
591 out <<
'\n' << linePrefix;