Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-08 10:09:18

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('\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) {  // ascii code equivalent
0221         str.push_back(static_cast<char>(code));
0222     } else if(code < 0x800) {  // \u0080 to \u07FF
0223         // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111
0224         str.push_back(make_char(0xC0 | code >> 6));
0225         str.push_back(make_char(0x80 | (code & 0x3F)));
0226     } else if(code < 0x10000) {  // U+0800...U+FFFF
0227         if(0xD800 <= code && code <= 0xDFFF) {
0228             throw std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8.");
0229         }
0230         // 1110yyyy 10yxxxxx 10xxxxxx
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) {  // U+010000 ... U+10FFFF
0235         // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
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                 // must have 4 hex characters
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                 // must have 8 hex characters
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             // skip the next character for escaped sequences
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] = ' ';  // interpret this as a space so the split_up works properly
0416         }
0417     }
0418     return offset + 1;
0419 }
0420 
0421 CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape) {
0422     // s is our escaped output string
0423     std::string escaped_string{};
0424     // loop through all characters
0425     for(char c : string_to_escape) {
0426         // check if a given character is printable
0427         // the cast is necessary to avoid undefined behaviour
0428         if(isprint(static_cast<unsigned char>(c)) == 0) {
0429             std::stringstream stream;
0430             // if the character is not printable
0431             // we'll convert it to a hex string using a stringstream
0432             // note that since char is signed we have to cast it to unsigned first
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             // need to check for inadvertent binary sequences
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         // ssize-2 to skip )" at the end
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             // only remove escaped for string arguments not literal strings
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         // handle some special array processing for arguments if it might be interpreted as a secondary array
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     // Windows version
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     // This also works on Windows, but gives a warning
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;  // First line prefix
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 }  // namespace detail
0612 // [CLI11:string_tools_inl_hpp:end]
0613 }  // namespace CLI