Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-14 08:32:14

0001 // Copyright (c) 2017-2025, University of Cincinnati, developed by Henry Schreiner
0002 // under NSF AWARD 1414736 and by the respective contributors.
0003 // All rights reserved.
0004 //
0005 // SPDX-License-Identifier: BSD-3-Clause
0006 
0007 #pragma once
0008 
0009 // IWYU pragma: private, include "CLI/CLI.hpp"
0010 
0011 // This include is only needed for IDEs to discover symbols
0012 #include "../StringTools.hpp"
0013 
0014 // [CLI11:public_includes:set]
0015 #include <cstdint>
0016 #include <string>
0017 #include <utility>
0018 #include <vector>
0019 // [CLI11:public_includes:end]
0020 
0021 namespace CLI {
0022 // [CLI11:string_tools_inl_hpp:verbatim]
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     // Check to see if empty string, give consistent result
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 &ltrim(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 &ltrim(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) {  // ascii code equivalent
0230         str.push_back(static_cast<char>(code));
0231     } else if(code < 0x800) {  // \u0080 to \u07FF
0232         // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111
0233         str.push_back(make_char(0xC0 | code >> 6));
0234         str.push_back(make_char(0x80 | (code & 0x3F)));
0235     } else if(code < 0x10000) {  // U+0800...U+FFFF
0236         if(0xD800 <= code && code <= 0xDFFF) {
0237             throw std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8.");
0238         }
0239         // 1110yyyy 10yxxxxx 10xxxxxx
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) {  // U+010000 ... U+10FFFF
0244         // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
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                 // must have 4 hex characters
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                 // must have 8 hex characters
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             // skip the next character for escaped sequences
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] = ' ';  // interpret this as a space so the split_up works properly
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     // s is our escaped output string
0436     std::string escaped_string{};
0437     // loop through all characters
0438     for(char c : string_to_escape) {
0439         // check if a given character is printable
0440         // the cast is necessary to avoid undefined behaviour
0441         if(isprint(static_cast<unsigned char>(c)) == 0) {
0442             std::stringstream stream;
0443             // if the character is not printable
0444             // we'll convert it to a hex string using a stringstream
0445             // note that since char is signed we have to cast it to unsigned first
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             // need to check for inadvertent binary sequences
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         // ssize-2 to skip )" at the end
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             // only remove escaped for string arguments not literal strings
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         // handle some special array processing for arguments if it might be interpreted as a secondary array
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     // Windows version
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     // This also works on Windows, but gives a warning
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;  // First line prefix
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 }  // namespace detail
0629 // [CLI11:string_tools_inl_hpp:end]
0630 }  // namespace CLI