File indexing completed on 2026-07-14 08:32:14
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009
0010
0011
0012 #include "../StringTools.hpp"
0013
0014
0015 #include <cstdint>
0016 #include <string>
0017 #include <utility>
0018 #include <vector>
0019
0020
0021 namespace CLI {
0022
0023
0024 namespace detail {
0025 CLI11_INLINE std::vector<std::string> split(const std::string &s, char delim) {
0026 std::vector<std::string> elems;
0027
0028 if(s.empty()) {
0029 elems.emplace_back();
0030 } else {
0031 std::stringstream ss;
0032 ss.str(s);
0033 std::string item;
0034 while(std::getline(ss, item, delim)) {
0035 elems.push_back(item);
0036 }
0037 }
0038 return elems;
0039 }
0040
0041 CLI11_INLINE std::string <rim(std::string &str) {
0042 auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
0043 str.erase(str.begin(), it);
0044 return str;
0045 }
0046
0047 CLI11_INLINE std::string <rim(std::string &str, const std::string &filter) {
0048 auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
0049 str.erase(str.begin(), it);
0050 return str;
0051 }
0052
0053 CLI11_INLINE std::string &rtrim(std::string &str) {
0054 auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
0055 str.erase(it.base(), str.end());
0056 return str;
0057 }
0058
0059 CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter) {
0060 auto it =
0061 std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
0062 str.erase(it.base(), str.end());
0063 return str;
0064 }
0065
0066 CLI11_INLINE std::string &remove_quotes(std::string &str) {
0067 if(str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) {
0068 if(str.front() == str.back()) {
0069 str.pop_back();
0070 str.erase(str.begin(), str.begin() + 1);
0071 }
0072 }
0073 return str;
0074 }
0075
0076 CLI11_INLINE std::string &remove_outer(std::string &str, char key) {
0077 if(str.length() > 1 && (str.front() == key)) {
0078 if(str.front() == str.back()) {
0079 str.pop_back();
0080 str.erase(str.begin(), str.begin() + 1);
0081 }
0082 }
0083 return str;
0084 }
0085
0086 CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input) {
0087 std::string::size_type n = 0;
0088 while(n != std::string::npos && n < input.size()) {
0089 n = input.find_first_of("\r\n", n);
0090 if(n != std::string::npos) {
0091 input = input.substr(0, n + 1) + leader + input.substr(n + 1);
0092 n += leader.size();
0093 }
0094 }
0095 return input;
0096 }
0097
0098 CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid) {
0099 if(!aliases.empty()) {
0100 out << std::setw(static_cast<int>(wid)) << " aliases: ";
0101 bool front = true;
0102 for(const auto &alias : aliases) {
0103 if(!front) {
0104 out << ", ";
0105 } else {
0106 front = false;
0107 }
0108 out << detail::fix_newlines(" ", alias);
0109 }
0110 out << "\n";
0111 }
0112 return out;
0113 }
0114
0115 CLI11_INLINE bool valid_name_string(const std::string &str) {
0116 if(str.empty() || !valid_first_char(str[0])) {
0117 return false;
0118 }
0119 auto e = str.end();
0120 for(auto c = str.begin() + 1; c != e; ++c)
0121 if(!valid_later_char(*c))
0122 return false;
0123 return true;
0124 }
0125
0126 CLI11_INLINE std::string get_group_separators() {
0127 std::string separators{"_'"};
0128 #if CLI11_HAS_RTTI != 0
0129 char group_separator = std::use_facet<std::numpunct<char>>(std::locale()).thousands_sep();
0130 separators.push_back(group_separator);
0131 #endif
0132 return separators;
0133 }
0134
0135 CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) {
0136
0137 std::size_t start_pos = 0;
0138
0139 while((start_pos = str.find(from, start_pos)) != std::string::npos) {
0140 str.replace(start_pos, from.length(), to);
0141 start_pos += to.length();
0142 }
0143
0144 return str;
0145 }
0146
0147 CLI11_INLINE void remove_default_flag_values(std::string &flags) {
0148 auto loc = flags.find_first_of('{', 2);
0149 while(loc != std::string::npos) {
0150 auto finish = flags.find_first_of("},", loc + 1);
0151 if((finish != std::string::npos) && (flags[finish] == '}')) {
0152 flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
0153 flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
0154 }
0155 loc = flags.find_first_of('{', loc + 1);
0156 }
0157 flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
0158 }
0159
0160 CLI11_INLINE std::ptrdiff_t
0161 find_member(std::string name, const std::vector<std::string> names, bool ignore_case, bool ignore_underscore) {
0162 auto it = std::end(names);
0163 if(ignore_case) {
0164 if(ignore_underscore) {
0165 name = detail::to_lower(detail::remove_underscore(name));
0166 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0167 return detail::to_lower(detail::remove_underscore(local_name)) == name;
0168 });
0169 } else {
0170 name = detail::to_lower(name);
0171 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0172 return detail::to_lower(local_name) == name;
0173 });
0174 }
0175
0176 } else if(ignore_underscore) {
0177 name = detail::remove_underscore(name);
0178 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0179 return detail::remove_underscore(local_name) == name;
0180 });
0181 } else {
0182 it = std::find(std::begin(names), std::end(names), name);
0183 }
0184
0185 return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
0186 }
0187
0188 static const std::string escapedChars("\b\t\n\f\r\"\\");
0189 static const std::string escapedCharsCode("btnfr\"\\");
0190 static const std::string bracketChars{"\"'`[(<{"};
0191 static const std::string matchBracketChars("\"'`])>}");
0192
0193 CLI11_INLINE bool has_escapable_character(const std::string &str) {
0194 return (str.find_first_of(escapedChars) != std::string::npos);
0195 }
0196
0197 CLI11_INLINE std::string add_escaped_characters(const std::string &str) {
0198 std::string out;
0199 out.reserve(str.size() + 4);
0200 for(char s : str) {
0201 auto sloc = escapedChars.find_first_of(s);
0202 if(sloc != std::string::npos) {
0203 out.push_back('\\');
0204 out.push_back(escapedCharsCode[sloc]);
0205 } else {
0206 out.push_back(s);
0207 }
0208 }
0209 return out;
0210 }
0211
0212 CLI11_INLINE std::uint32_t hexConvert(char hc) {
0213 int hcode{0};
0214 if(hc >= '0' && hc <= '9') {
0215 hcode = (hc - '0');
0216 } else if(hc >= 'A' && hc <= 'F') {
0217 hcode = (hc - 'A' + 10);
0218 } else if(hc >= 'a' && hc <= 'f') {
0219 hcode = (hc - 'a' + 10);
0220 } else {
0221 hcode = -1;
0222 }
0223 return static_cast<uint32_t>(hcode);
0224 }
0225
0226 CLI11_INLINE char make_char(std::uint32_t code) { return static_cast<char>(static_cast<unsigned char>(code)); }
0227
0228 CLI11_INLINE void append_codepoint(std::string &str, std::uint32_t code) {
0229 if(code < 0x80) {
0230 str.push_back(static_cast<char>(code));
0231 } else if(code < 0x800) {
0232
0233 str.push_back(make_char(0xC0 | code >> 6));
0234 str.push_back(make_char(0x80 | (code & 0x3F)));
0235 } else if(code < 0x10000) {
0236 if(0xD800 <= code && code <= 0xDFFF) {
0237 throw std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8.");
0238 }
0239
0240 str.push_back(make_char(0xE0 | code >> 12));
0241 str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
0242 str.push_back(make_char(0x80 | (code & 0x3F)));
0243 } else if(code < 0x110000) {
0244
0245 str.push_back(make_char(0xF0 | code >> 18));
0246 str.push_back(make_char(0x80 | (code >> 12 & 0x3F)));
0247 str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
0248 str.push_back(make_char(0x80 | (code & 0x3F)));
0249 }
0250 }
0251
0252 CLI11_INLINE std::string remove_escaped_characters(const std::string &str) {
0253
0254 std::string out;
0255 out.reserve(str.size());
0256 for(auto loc = str.begin(); loc < str.end(); ++loc) {
0257 if(*loc == '\\') {
0258 if(str.end() - loc < 2) {
0259 throw std::invalid_argument("invalid escape sequence " + str);
0260 }
0261 auto ecloc = escapedCharsCode.find_first_of(*(loc + 1));
0262 if(ecloc != std::string::npos) {
0263 out.push_back(escapedChars[ecloc]);
0264 ++loc;
0265 } else if(*(loc + 1) == 'u') {
0266
0267 if(str.end() - loc < 6) {
0268 throw std::invalid_argument("unicode sequence must have 4 hex codes " + str);
0269 }
0270 std::uint32_t code{0};
0271 std::uint32_t mplier{16 * 16 * 16};
0272 for(int ii = 2; ii < 6; ++ii) {
0273 std::uint32_t res = hexConvert(*(loc + ii));
0274 if(res > 0x0F) {
0275 throw std::invalid_argument("unicode sequence must have 4 hex codes " + str);
0276 }
0277 code += res * mplier;
0278 mplier = mplier / 16;
0279 }
0280 append_codepoint(out, code);
0281 loc += 5;
0282 } else if(*(loc + 1) == 'U') {
0283
0284 if(str.end() - loc < 10) {
0285 throw std::invalid_argument("unicode sequence must have 8 hex codes " + str);
0286 }
0287 std::uint32_t code{0};
0288 std::uint32_t mplier{16 * 16 * 16 * 16 * 16 * 16 * 16};
0289 for(int ii = 2; ii < 10; ++ii) {
0290 std::uint32_t res = hexConvert(*(loc + ii));
0291 if(res > 0x0F) {
0292 throw std::invalid_argument("unicode sequence must have 8 hex codes " + str);
0293 }
0294 code += res * mplier;
0295 mplier = mplier / 16;
0296 }
0297 append_codepoint(out, code);
0298 loc += 9;
0299 } else if(*(loc + 1) == '0') {
0300 out.push_back('\0');
0301 ++loc;
0302 } else {
0303 throw std::invalid_argument(std::string("unrecognized escape sequence \\") + *(loc + 1) + " in " + str);
0304 }
0305 } else {
0306 out.push_back(*loc);
0307 }
0308 }
0309 return out;
0310 }
0311
0312 CLI11_INLINE std::size_t close_string_quote(const std::string &str, std::size_t start, char closure_char) {
0313 std::size_t loc{0};
0314 for(loc = start + 1; loc < str.size(); ++loc) {
0315 if(str[loc] == closure_char) {
0316 break;
0317 }
0318 if(str[loc] == '\\') {
0319
0320 ++loc;
0321 }
0322 }
0323 return loc;
0324 }
0325
0326 CLI11_INLINE std::size_t close_literal_quote(const std::string &str, std::size_t start, char closure_char) {
0327 auto loc = str.find_first_of(closure_char, start + 1);
0328 return (loc != std::string::npos ? loc : str.size());
0329 }
0330
0331 CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char) {
0332
0333 auto bracket_loc = matchBracketChars.find(closure_char);
0334 switch(bracket_loc) {
0335 case 0:
0336 return close_string_quote(str, start, closure_char);
0337 case 1:
0338 case 2:
0339 #if defined(_MSC_VER) && _MSC_VER < 1920
0340 case(std::size_t)-1:
0341 #else
0342 case std::string::npos:
0343 #endif
0344 return close_literal_quote(str, start, closure_char);
0345 default:
0346 break;
0347 }
0348
0349 std::string closures(1, closure_char);
0350 auto loc = start + 1;
0351
0352 while(loc < str.size()) {
0353 if(str[loc] == closures.back()) {
0354 closures.pop_back();
0355 if(closures.empty()) {
0356 return loc;
0357 }
0358 }
0359 bracket_loc = bracketChars.find(str[loc]);
0360 if(bracket_loc != std::string::npos) {
0361 switch(bracket_loc) {
0362 case 0:
0363 loc = close_string_quote(str, loc, str[loc]);
0364 break;
0365 case 1:
0366 case 2:
0367 loc = close_literal_quote(str, loc, str[loc]);
0368 break;
0369 default:
0370 closures.push_back(matchBracketChars[bracket_loc]);
0371 break;
0372 }
0373 }
0374 ++loc;
0375 }
0376 if(loc > str.size()) {
0377 loc = str.size();
0378 }
0379 return loc;
0380 }
0381
0382 CLI11_INLINE std::vector<std::string> split_up(std::string str, char delimiter) {
0383
0384 auto find_ws = [delimiter](char ch) {
0385 return (delimiter == '\0') ? std::isspace<char>(ch, std::locale()) : (ch == delimiter);
0386 };
0387 trim(str);
0388
0389 std::vector<std::string> output;
0390 while(!str.empty()) {
0391 if(bracketChars.find_first_of(str[0]) != std::string::npos) {
0392 auto bracketLoc = bracketChars.find_first_of(str[0]);
0393 auto end = close_sequence(str, 0, matchBracketChars[bracketLoc]);
0394 if(end >= str.size()) {
0395 output.push_back(std::move(str));
0396 str.clear();
0397 } else {
0398 output.push_back(str.substr(0, end + 1));
0399 if(end + 2 < str.size()) {
0400 str = str.substr(end + 2);
0401 } else {
0402 str.clear();
0403 }
0404 }
0405
0406 } else {
0407 auto it = std::find_if(std::begin(str), std::end(str), find_ws);
0408 if(it != std::end(str)) {
0409 std::string value = std::string(str.begin(), it);
0410 output.push_back(value);
0411 str = std::string(it + 1, str.end());
0412 } else {
0413 output.push_back(str);
0414 str.clear();
0415 }
0416 }
0417 trim(str);
0418 }
0419 return output;
0420 }
0421
0422 CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset) {
0423 auto next = str[offset + 1];
0424 if((next == '\"') || (next == '\'') || (next == '`')) {
0425 auto astart = str.find_last_of("-/ \"\'`", offset - 1);
0426 if(astart != std::string::npos) {
0427 if(str[astart] == ((str[offset] == '=') ? '-' : '/'))
0428 str[offset] = ' ';
0429 }
0430 }
0431 return offset + 1;
0432 }
0433
0434 CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape, bool force) {
0435
0436 std::string escaped_string{};
0437
0438 for(char c : string_to_escape) {
0439
0440
0441 if(isprint(static_cast<unsigned char>(c)) == 0) {
0442 std::stringstream stream;
0443
0444
0445
0446 stream << std::hex << static_cast<unsigned int>(static_cast<unsigned char>(c));
0447 std::string code = stream.str();
0448 escaped_string += std::string("\\x") + (code.size() < 2 ? "0" : "") + code;
0449 } else if(c == 'x' || c == 'X') {
0450
0451 if(!escaped_string.empty() && escaped_string.back() == '\\') {
0452 escaped_string += std::string("\\x") + (c == 'x' ? "78" : "58");
0453 } else {
0454 escaped_string.push_back(c);
0455 }
0456
0457 } else {
0458 escaped_string.push_back(c);
0459 }
0460 }
0461 if(escaped_string != string_to_escape || force) {
0462 auto sqLoc = escaped_string.find('\'');
0463 while(sqLoc != std::string::npos) {
0464 escaped_string[sqLoc] = '\\';
0465 escaped_string.insert(sqLoc + 1, "x27");
0466 sqLoc = escaped_string.find('\'');
0467 }
0468 escaped_string.insert(0, "'B\"(");
0469 escaped_string.push_back(')');
0470 escaped_string.push_back('"');
0471 escaped_string.push_back('\'');
0472 }
0473 return escaped_string;
0474 }
0475
0476 CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string) {
0477 size_t ssize = escaped_string.size();
0478 if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) {
0479 return true;
0480 }
0481 return (escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0);
0482 }
0483
0484 CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string) {
0485 std::size_t start{0};
0486 std::size_t tail{0};
0487 size_t ssize = escaped_string.size();
0488 if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) {
0489 start = 3;
0490 tail = 2;
0491 } else if(escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0) {
0492 start = 4;
0493 tail = 3;
0494 }
0495
0496 if(start == 0) {
0497 return escaped_string;
0498 }
0499 std::string outstring;
0500
0501 outstring.reserve(ssize - start - tail);
0502 std::size_t loc = start;
0503 while(loc < ssize - tail) {
0504
0505 if(escaped_string[loc] == '\\' && (escaped_string[loc + 1] == 'x' || escaped_string[loc + 1] == 'X')) {
0506 auto c1 = escaped_string[loc + 2];
0507 auto c2 = escaped_string[loc + 3];
0508
0509 std::uint32_t res1 = hexConvert(c1);
0510 std::uint32_t res2 = hexConvert(c2);
0511 if(res1 <= 0x0F && res2 <= 0x0F) {
0512 loc += 4;
0513 outstring.push_back(static_cast<char>(res1 * 16 + res2));
0514 continue;
0515 }
0516 }
0517 outstring.push_back(escaped_string[loc]);
0518 ++loc;
0519 }
0520 return outstring;
0521 }
0522
0523 CLI11_INLINE void remove_quotes(std::vector<std::string> &args) {
0524 for(auto &arg : args) {
0525 if(arg.front() == '\"' && arg.back() == '\"') {
0526 remove_quotes(arg);
0527
0528 arg = remove_escaped_characters(arg);
0529 } else {
0530 remove_quotes(arg);
0531 }
0532 }
0533 }
0534
0535 CLI11_INLINE void handle_secondary_array(std::string &str) {
0536 if(str.size() >= 2 && str.front() == '[' && str.back() == ']') {
0537
0538 std::string tstr{"[["};
0539 for(std::size_t ii = 1; ii < str.size(); ++ii) {
0540 tstr.push_back(str[ii]);
0541 tstr.push_back(str[ii]);
0542 }
0543 str = std::move(tstr);
0544 }
0545 }
0546
0547 CLI11_INLINE bool
0548 process_quoted_string(std::string &str, char string_char, char literal_char, bool disable_secondary_array_processing) {
0549 if(str.size() <= 1) {
0550 return false;
0551 }
0552 if(detail::is_binary_escaped_string(str)) {
0553 str = detail::extract_binary_string(str);
0554 if(!disable_secondary_array_processing)
0555 handle_secondary_array(str);
0556 return true;
0557 }
0558 if(str.front() == string_char && str.back() == string_char) {
0559 detail::remove_outer(str, string_char);
0560 if(str.find_first_of('\\') != std::string::npos) {
0561 str = detail::remove_escaped_characters(str);
0562 }
0563 if(!disable_secondary_array_processing)
0564 handle_secondary_array(str);
0565 return true;
0566 }
0567 if((str.front() == literal_char || str.front() == '`') && str.back() == str.front()) {
0568 detail::remove_outer(str, str.front());
0569 if(!disable_secondary_array_processing)
0570 handle_secondary_array(str);
0571 return true;
0572 }
0573 return false;
0574 }
0575
0576 std::string get_environment_value(const std::string &env_name) {
0577 char *buffer = nullptr;
0578 std::string ename_string;
0579
0580 #ifdef _MSC_VER
0581
0582 std::size_t sz = 0;
0583 if(_dupenv_s(&buffer, &sz, env_name.c_str()) == 0 && buffer != nullptr) {
0584 ename_string = std::string(buffer);
0585 free(buffer);
0586 }
0587 #else
0588
0589 buffer = std::getenv(env_name.c_str());
0590 if(buffer != nullptr) {
0591 ename_string = std::string(buffer);
0592 }
0593 #endif
0594 return ename_string;
0595 }
0596
0597 CLI11_INLINE std::ostream &streamOutAsParagraph(std::ostream &out,
0598 const std::string &text,
0599 std::size_t paragraphWidth,
0600 const std::string &linePrefix,
0601 bool skipPrefixOnFirstLine) {
0602 if(!skipPrefixOnFirstLine)
0603 out << linePrefix;
0604
0605 std::istringstream lss(text);
0606 std::string line = "";
0607 while(std::getline(lss, line)) {
0608 std::istringstream iss(line);
0609 std::string word = "";
0610 std::size_t charsWritten = 0;
0611
0612 while(iss >> word) {
0613 if(word.length() + charsWritten > paragraphWidth) {
0614 out << '\n' << linePrefix;
0615 charsWritten = 0;
0616 }
0617
0618 out << word << " ";
0619 charsWritten += word.length() + 1;
0620 }
0621
0622 if(!lss.eof())
0623 out << '\n' << linePrefix;
0624 }
0625 return out;
0626 }
0627
0628 }
0629
0630 }