File indexing completed on 2026-01-08 10:09:18
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('\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 find_and_replace(std::string str, std::string from, std::string to) {
0127
0128 std::size_t start_pos = 0;
0129
0130 while((start_pos = str.find(from, start_pos)) != std::string::npos) {
0131 str.replace(start_pos, from.length(), to);
0132 start_pos += to.length();
0133 }
0134
0135 return str;
0136 }
0137
0138 CLI11_INLINE void remove_default_flag_values(std::string &flags) {
0139 auto loc = flags.find_first_of('{', 2);
0140 while(loc != std::string::npos) {
0141 auto finish = flags.find_first_of("},", loc + 1);
0142 if((finish != std::string::npos) && (flags[finish] == '}')) {
0143 flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
0144 flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
0145 }
0146 loc = flags.find_first_of('{', loc + 1);
0147 }
0148 flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
0149 }
0150
0151 CLI11_INLINE std::ptrdiff_t
0152 find_member(std::string name, const std::vector<std::string> names, bool ignore_case, bool ignore_underscore) {
0153 auto it = std::end(names);
0154 if(ignore_case) {
0155 if(ignore_underscore) {
0156 name = detail::to_lower(detail::remove_underscore(name));
0157 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0158 return detail::to_lower(detail::remove_underscore(local_name)) == name;
0159 });
0160 } else {
0161 name = detail::to_lower(name);
0162 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0163 return detail::to_lower(local_name) == name;
0164 });
0165 }
0166
0167 } else if(ignore_underscore) {
0168 name = detail::remove_underscore(name);
0169 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0170 return detail::remove_underscore(local_name) == name;
0171 });
0172 } else {
0173 it = std::find(std::begin(names), std::end(names), name);
0174 }
0175
0176 return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
0177 }
0178
0179 static const std::string escapedChars("\b\t\n\f\r\"\\");
0180 static const std::string escapedCharsCode("btnfr\"\\");
0181 static const std::string bracketChars{"\"'`[(<{"};
0182 static const std::string matchBracketChars("\"'`])>}");
0183
0184 CLI11_INLINE bool has_escapable_character(const std::string &str) {
0185 return (str.find_first_of(escapedChars) != std::string::npos);
0186 }
0187
0188 CLI11_INLINE std::string add_escaped_characters(const std::string &str) {
0189 std::string out;
0190 out.reserve(str.size() + 4);
0191 for(char s : str) {
0192 auto sloc = escapedChars.find_first_of(s);
0193 if(sloc != std::string::npos) {
0194 out.push_back('\\');
0195 out.push_back(escapedCharsCode[sloc]);
0196 } else {
0197 out.push_back(s);
0198 }
0199 }
0200 return out;
0201 }
0202
0203 CLI11_INLINE std::uint32_t hexConvert(char hc) {
0204 int hcode{0};
0205 if(hc >= '0' && hc <= '9') {
0206 hcode = (hc - '0');
0207 } else if(hc >= 'A' && hc <= 'F') {
0208 hcode = (hc - 'A' + 10);
0209 } else if(hc >= 'a' && hc <= 'f') {
0210 hcode = (hc - 'a' + 10);
0211 } else {
0212 hcode = -1;
0213 }
0214 return static_cast<uint32_t>(hcode);
0215 }
0216
0217 CLI11_INLINE char make_char(std::uint32_t code) { return static_cast<char>(static_cast<unsigned char>(code)); }
0218
0219 CLI11_INLINE void append_codepoint(std::string &str, std::uint32_t code) {
0220 if(code < 0x80) {
0221 str.push_back(static_cast<char>(code));
0222 } else if(code < 0x800) {
0223
0224 str.push_back(make_char(0xC0 | code >> 6));
0225 str.push_back(make_char(0x80 | (code & 0x3F)));
0226 } else if(code < 0x10000) {
0227 if(0xD800 <= code && code <= 0xDFFF) {
0228 throw std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8.");
0229 }
0230
0231 str.push_back(make_char(0xE0 | code >> 12));
0232 str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
0233 str.push_back(make_char(0x80 | (code & 0x3F)));
0234 } else if(code < 0x110000) {
0235
0236 str.push_back(make_char(0xF0 | code >> 18));
0237 str.push_back(make_char(0x80 | (code >> 12 & 0x3F)));
0238 str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
0239 str.push_back(make_char(0x80 | (code & 0x3F)));
0240 }
0241 }
0242
0243 CLI11_INLINE std::string remove_escaped_characters(const std::string &str) {
0244
0245 std::string out;
0246 out.reserve(str.size());
0247 for(auto loc = str.begin(); loc < str.end(); ++loc) {
0248 if(*loc == '\\') {
0249 if(str.end() - loc < 2) {
0250 throw std::invalid_argument("invalid escape sequence " + str);
0251 }
0252 auto ecloc = escapedCharsCode.find_first_of(*(loc + 1));
0253 if(ecloc != std::string::npos) {
0254 out.push_back(escapedChars[ecloc]);
0255 ++loc;
0256 } else if(*(loc + 1) == 'u') {
0257
0258 if(str.end() - loc < 6) {
0259 throw std::invalid_argument("unicode sequence must have 4 hex codes " + str);
0260 }
0261 std::uint32_t code{0};
0262 std::uint32_t mplier{16 * 16 * 16};
0263 for(int ii = 2; ii < 6; ++ii) {
0264 std::uint32_t res = hexConvert(*(loc + ii));
0265 if(res > 0x0F) {
0266 throw std::invalid_argument("unicode sequence must have 4 hex codes " + str);
0267 }
0268 code += res * mplier;
0269 mplier = mplier / 16;
0270 }
0271 append_codepoint(out, code);
0272 loc += 5;
0273 } else if(*(loc + 1) == 'U') {
0274
0275 if(str.end() - loc < 10) {
0276 throw std::invalid_argument("unicode sequence must have 8 hex codes " + str);
0277 }
0278 std::uint32_t code{0};
0279 std::uint32_t mplier{16 * 16 * 16 * 16 * 16 * 16 * 16};
0280 for(int ii = 2; ii < 10; ++ii) {
0281 std::uint32_t res = hexConvert(*(loc + ii));
0282 if(res > 0x0F) {
0283 throw std::invalid_argument("unicode sequence must have 8 hex codes " + str);
0284 }
0285 code += res * mplier;
0286 mplier = mplier / 16;
0287 }
0288 append_codepoint(out, code);
0289 loc += 9;
0290 } else if(*(loc + 1) == '0') {
0291 out.push_back('\0');
0292 ++loc;
0293 } else {
0294 throw std::invalid_argument(std::string("unrecognized escape sequence \\") + *(loc + 1) + " in " + str);
0295 }
0296 } else {
0297 out.push_back(*loc);
0298 }
0299 }
0300 return out;
0301 }
0302
0303 CLI11_INLINE std::size_t close_string_quote(const std::string &str, std::size_t start, char closure_char) {
0304 std::size_t loc{0};
0305 for(loc = start + 1; loc < str.size(); ++loc) {
0306 if(str[loc] == closure_char) {
0307 break;
0308 }
0309 if(str[loc] == '\\') {
0310
0311 ++loc;
0312 }
0313 }
0314 return loc;
0315 }
0316
0317 CLI11_INLINE std::size_t close_literal_quote(const std::string &str, std::size_t start, char closure_char) {
0318 auto loc = str.find_first_of(closure_char, start + 1);
0319 return (loc != std::string::npos ? loc : str.size());
0320 }
0321
0322 CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char) {
0323
0324 auto bracket_loc = matchBracketChars.find(closure_char);
0325 switch(bracket_loc) {
0326 case 0:
0327 return close_string_quote(str, start, closure_char);
0328 case 1:
0329 case 2:
0330 case std::string::npos:
0331 return close_literal_quote(str, start, closure_char);
0332 default:
0333 break;
0334 }
0335
0336 std::string closures(1, closure_char);
0337 auto loc = start + 1;
0338
0339 while(loc < str.size()) {
0340 if(str[loc] == closures.back()) {
0341 closures.pop_back();
0342 if(closures.empty()) {
0343 return loc;
0344 }
0345 }
0346 bracket_loc = bracketChars.find(str[loc]);
0347 if(bracket_loc != std::string::npos) {
0348 switch(bracket_loc) {
0349 case 0:
0350 loc = close_string_quote(str, loc, str[loc]);
0351 break;
0352 case 1:
0353 case 2:
0354 loc = close_literal_quote(str, loc, str[loc]);
0355 break;
0356 default:
0357 closures.push_back(matchBracketChars[bracket_loc]);
0358 break;
0359 }
0360 }
0361 ++loc;
0362 }
0363 if(loc > str.size()) {
0364 loc = str.size();
0365 }
0366 return loc;
0367 }
0368
0369 CLI11_INLINE std::vector<std::string> split_up(std::string str, char delimiter) {
0370
0371 auto find_ws = [delimiter](char ch) {
0372 return (delimiter == '\0') ? std::isspace<char>(ch, std::locale()) : (ch == delimiter);
0373 };
0374 trim(str);
0375
0376 std::vector<std::string> output;
0377 while(!str.empty()) {
0378 if(bracketChars.find_first_of(str[0]) != std::string::npos) {
0379 auto bracketLoc = bracketChars.find_first_of(str[0]);
0380 auto end = close_sequence(str, 0, matchBracketChars[bracketLoc]);
0381 if(end >= str.size()) {
0382 output.push_back(std::move(str));
0383 str.clear();
0384 } else {
0385 output.push_back(str.substr(0, end + 1));
0386 if(end + 2 < str.size()) {
0387 str = str.substr(end + 2);
0388 } else {
0389 str.clear();
0390 }
0391 }
0392
0393 } else {
0394 auto it = std::find_if(std::begin(str), std::end(str), find_ws);
0395 if(it != std::end(str)) {
0396 std::string value = std::string(str.begin(), it);
0397 output.push_back(value);
0398 str = std::string(it + 1, str.end());
0399 } else {
0400 output.push_back(str);
0401 str.clear();
0402 }
0403 }
0404 trim(str);
0405 }
0406 return output;
0407 }
0408
0409 CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset) {
0410 auto next = str[offset + 1];
0411 if((next == '\"') || (next == '\'') || (next == '`')) {
0412 auto astart = str.find_last_of("-/ \"\'`", offset - 1);
0413 if(astart != std::string::npos) {
0414 if(str[astart] == ((str[offset] == '=') ? '-' : '/'))
0415 str[offset] = ' ';
0416 }
0417 }
0418 return offset + 1;
0419 }
0420
0421 CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape) {
0422
0423 std::string escaped_string{};
0424
0425 for(char c : string_to_escape) {
0426
0427
0428 if(isprint(static_cast<unsigned char>(c)) == 0) {
0429 std::stringstream stream;
0430
0431
0432
0433 stream << std::hex << static_cast<unsigned int>(static_cast<unsigned char>(c));
0434 std::string code = stream.str();
0435 escaped_string += std::string("\\x") + (code.size() < 2 ? "0" : "") + code;
0436 } else if(c == 'x' || c == 'X') {
0437
0438 if(!escaped_string.empty() && escaped_string.back() == '\\') {
0439 escaped_string += std::string("\\x") + (c == 'x' ? "78" : "58");
0440 } else {
0441 escaped_string.push_back(c);
0442 }
0443
0444 } else {
0445 escaped_string.push_back(c);
0446 }
0447 }
0448 if(escaped_string != string_to_escape) {
0449 auto sqLoc = escaped_string.find('\'');
0450 while(sqLoc != std::string::npos) {
0451 escaped_string[sqLoc] = '\\';
0452 escaped_string.insert(sqLoc + 1, "x27");
0453 sqLoc = escaped_string.find('\'');
0454 }
0455 escaped_string.insert(0, "'B\"(");
0456 escaped_string.push_back(')');
0457 escaped_string.push_back('"');
0458 escaped_string.push_back('\'');
0459 }
0460 return escaped_string;
0461 }
0462
0463 CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string) {
0464 size_t ssize = escaped_string.size();
0465 if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) {
0466 return true;
0467 }
0468 return (escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0);
0469 }
0470
0471 CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string) {
0472 std::size_t start{0};
0473 std::size_t tail{0};
0474 size_t ssize = escaped_string.size();
0475 if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) {
0476 start = 3;
0477 tail = 2;
0478 } else if(escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0) {
0479 start = 4;
0480 tail = 3;
0481 }
0482
0483 if(start == 0) {
0484 return escaped_string;
0485 }
0486 std::string outstring;
0487
0488 outstring.reserve(ssize - start - tail);
0489 std::size_t loc = start;
0490 while(loc < ssize - tail) {
0491
0492 if(escaped_string[loc] == '\\' && (escaped_string[loc + 1] == 'x' || escaped_string[loc + 1] == 'X')) {
0493 auto c1 = escaped_string[loc + 2];
0494 auto c2 = escaped_string[loc + 3];
0495
0496 std::uint32_t res1 = hexConvert(c1);
0497 std::uint32_t res2 = hexConvert(c2);
0498 if(res1 <= 0x0F && res2 <= 0x0F) {
0499 loc += 4;
0500 outstring.push_back(static_cast<char>(res1 * 16 + res2));
0501 continue;
0502 }
0503 }
0504 outstring.push_back(escaped_string[loc]);
0505 ++loc;
0506 }
0507 return outstring;
0508 }
0509
0510 CLI11_INLINE void remove_quotes(std::vector<std::string> &args) {
0511 for(auto &arg : args) {
0512 if(arg.front() == '\"' && arg.back() == '\"') {
0513 remove_quotes(arg);
0514
0515 arg = remove_escaped_characters(arg);
0516 } else {
0517 remove_quotes(arg);
0518 }
0519 }
0520 }
0521
0522 CLI11_INLINE void handle_secondary_array(std::string &str) {
0523 if(str.size() >= 2 && str.front() == '[' && str.back() == ']') {
0524
0525 std::string tstr{"[["};
0526 for(std::size_t ii = 1; ii < str.size(); ++ii) {
0527 tstr.push_back(str[ii]);
0528 tstr.push_back(str[ii]);
0529 }
0530 str = std::move(tstr);
0531 }
0532 }
0533
0534 CLI11_INLINE bool process_quoted_string(std::string &str, char string_char, char literal_char) {
0535 if(str.size() <= 1) {
0536 return false;
0537 }
0538 if(detail::is_binary_escaped_string(str)) {
0539 str = detail::extract_binary_string(str);
0540 handle_secondary_array(str);
0541 return true;
0542 }
0543 if(str.front() == string_char && str.back() == string_char) {
0544 detail::remove_outer(str, string_char);
0545 if(str.find_first_of('\\') != std::string::npos) {
0546 str = detail::remove_escaped_characters(str);
0547 }
0548 handle_secondary_array(str);
0549 return true;
0550 }
0551 if((str.front() == literal_char || str.front() == '`') && str.back() == str.front()) {
0552 detail::remove_outer(str, str.front());
0553 handle_secondary_array(str);
0554 return true;
0555 }
0556 return false;
0557 }
0558
0559 std::string get_environment_value(const std::string &env_name) {
0560 char *buffer = nullptr;
0561 std::string ename_string;
0562
0563 #ifdef _MSC_VER
0564
0565 std::size_t sz = 0;
0566 if(_dupenv_s(&buffer, &sz, env_name.c_str()) == 0 && buffer != nullptr) {
0567 ename_string = std::string(buffer);
0568 free(buffer);
0569 }
0570 #else
0571
0572 buffer = std::getenv(env_name.c_str());
0573 if(buffer != nullptr) {
0574 ename_string = std::string(buffer);
0575 }
0576 #endif
0577 return ename_string;
0578 }
0579
0580 CLI11_INLINE std::ostream &streamOutAsParagraph(std::ostream &out,
0581 const std::string &text,
0582 std::size_t paragraphWidth,
0583 const std::string &linePrefix,
0584 bool skipPrefixOnFirstLine) {
0585 if(!skipPrefixOnFirstLine)
0586 out << linePrefix;
0587
0588 std::istringstream lss(text);
0589 std::string line = "";
0590 while(std::getline(lss, line)) {
0591 std::istringstream iss(line);
0592 std::string word = "";
0593 std::size_t charsWritten = 0;
0594
0595 while(iss >> word) {
0596 if(word.length() + charsWritten > paragraphWidth) {
0597 out << '\n' << linePrefix;
0598 charsWritten = 0;
0599 }
0600
0601 out << word << " ";
0602 charsWritten += word.length() + 1;
0603 }
0604
0605 if(!lss.eof())
0606 out << '\n' << linePrefix;
0607 }
0608 return out;
0609 }
0610
0611 }
0612
0613 }