Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-09-28 07:02:18

0001 // Copyright (c) 2017-2020, 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 // [CLI11:public_includes:set]
0010 #include <algorithm>
0011 #include <iomanip>
0012 #include <locale>
0013 #include <sstream>
0014 #include <stdexcept>
0015 #include <string>
0016 #include <type_traits>
0017 #include <vector>
0018 // [CLI11:public_includes:end]
0019 
0020 namespace CLI {
0021 
0022 // [CLI11:string_tools_hpp:verbatim]
0023 
0024 /// Include the items in this namespace to get free conversion of enums to/from streams.
0025 /// (This is available inside CLI as well, so CLI11 will use this without a using statement).
0026 namespace enums {
0027 
0028 /// output streaming for enumerations
0029 template <typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
0030 std::ostream &operator<<(std::ostream &in, const T &item) {
0031     // make sure this is out of the detail namespace otherwise it won't be found when needed
0032     return in << static_cast<typename std::underlying_type<T>::type>(item);
0033 }
0034 
0035 }  // namespace enums
0036 
0037 /// Export to CLI namespace
0038 using enums::operator<<;
0039 
0040 namespace detail {
0041 /// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not
0042 /// produce overflow for some expected uses
0043 constexpr int expected_max_vector_size{1 << 29};
0044 // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
0045 /// Split a string by a delim
0046 inline std::vector<std::string> split(const std::string &s, char delim) {
0047     std::vector<std::string> elems;
0048     // Check to see if empty string, give consistent result
0049     if(s.empty()) {
0050         elems.emplace_back();
0051     } else {
0052         std::stringstream ss;
0053         ss.str(s);
0054         std::string item;
0055         while(std::getline(ss, item, delim)) {
0056             elems.push_back(item);
0057         }
0058     }
0059     return elems;
0060 }
0061 
0062 /// Simple function to join a string
0063 template <typename T> std::string join(const T &v, std::string delim = ",") {
0064     std::ostringstream s;
0065     auto beg = std::begin(v);
0066     auto end = std::end(v);
0067     if(beg != end)
0068         s << *beg++;
0069     while(beg != end) {
0070         s << delim << *beg++;
0071     }
0072     return s.str();
0073 }
0074 
0075 /// Simple function to join a string from processed elements
0076 template <typename T,
0077           typename Callable,
0078           typename = typename std::enable_if<!std::is_constructible<std::string, Callable>::value>::type>
0079 std::string join(const T &v, Callable func, std::string delim = ",") {
0080     std::ostringstream s;
0081     auto beg = std::begin(v);
0082     auto end = std::end(v);
0083     auto loc = s.tellp();
0084     while(beg != end) {
0085         auto nloc = s.tellp();
0086         if(nloc > loc) {
0087             s << delim;
0088             loc = nloc;
0089         }
0090         s << func(*beg++);
0091     }
0092     return s.str();
0093 }
0094 
0095 /// Join a string in reverse order
0096 template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
0097     std::ostringstream s;
0098     for(std::size_t start = 0; start < v.size(); start++) {
0099         if(start > 0)
0100             s << delim;
0101         s << v[v.size() - start - 1];
0102     }
0103     return s.str();
0104 }
0105 
0106 // Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
0107 
0108 /// Trim whitespace from left of string
0109 inline std::string &ltrim(std::string &str) {
0110     auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
0111     str.erase(str.begin(), it);
0112     return str;
0113 }
0114 
0115 /// Trim anything from left of string
0116 inline std::string &ltrim(std::string &str, const std::string &filter) {
0117     auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
0118     str.erase(str.begin(), it);
0119     return str;
0120 }
0121 
0122 /// Trim whitespace from right of string
0123 inline std::string &rtrim(std::string &str) {
0124     auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
0125     str.erase(it.base(), str.end());
0126     return str;
0127 }
0128 
0129 /// Trim anything from right of string
0130 inline std::string &rtrim(std::string &str, const std::string &filter) {
0131     auto it =
0132         std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
0133     str.erase(it.base(), str.end());
0134     return str;
0135 }
0136 
0137 /// Trim whitespace from string
0138 inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }
0139 
0140 /// Trim anything from string
0141 inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }
0142 
0143 /// Make a copy of the string and then trim it
0144 inline std::string trim_copy(const std::string &str) {
0145     std::string s = str;
0146     return trim(s);
0147 }
0148 
0149 /// remove quotes at the front and back of a string either '"' or '\''
0150 inline std::string &remove_quotes(std::string &str) {
0151     if(str.length() > 1 && (str.front() == '"' || str.front() == '\'')) {
0152         if(str.front() == str.back()) {
0153             str.pop_back();
0154             str.erase(str.begin(), str.begin() + 1);
0155         }
0156     }
0157     return str;
0158 }
0159 
0160 /// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered)
0161 inline std::string trim_copy(const std::string &str, const std::string &filter) {
0162     std::string s = str;
0163     return trim(s, filter);
0164 }
0165 /// Print a two part "help" string
0166 inline std::ostream &format_help(std::ostream &out, std::string name, const std::string &description, std::size_t wid) {
0167     name = "  " + name;
0168     out << std::setw(static_cast<int>(wid)) << std::left << name;
0169     if(!description.empty()) {
0170         if(name.length() >= wid)
0171             out << "\n" << std::setw(static_cast<int>(wid)) << "";
0172         for(const char c : description) {
0173             out.put(c);
0174             if(c == '\n') {
0175                 out << std::setw(static_cast<int>(wid)) << "";
0176             }
0177         }
0178     }
0179     out << "\n";
0180     return out;
0181 }
0182 
0183 /// Print subcommand aliases
0184 inline std::ostream &format_aliases(std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid) {
0185     if(!aliases.empty()) {
0186         out << std::setw(static_cast<int>(wid)) << "     aliases: ";
0187         bool front = true;
0188         for(const auto &alias : aliases) {
0189             if(!front) {
0190                 out << ", ";
0191             } else {
0192                 front = false;
0193             }
0194             out << alias;
0195         }
0196         out << "\n";
0197     }
0198     return out;
0199 }
0200 
0201 /// Verify the first character of an option
0202 template <typename T> bool valid_first_char(T c) {
0203     return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@';
0204 }
0205 
0206 /// Verify following characters of an option
0207 template <typename T> bool valid_later_char(T c) { return valid_first_char(c) || c == '.' || c == '-'; }
0208 
0209 /// Verify an option name
0210 inline bool valid_name_string(const std::string &str) {
0211     if(str.empty() || !valid_first_char(str[0]))
0212         return false;
0213     for(auto c : str.substr(1))
0214         if(!valid_later_char(c))
0215             return false;
0216     return true;
0217 }
0218 
0219 /// check if a string is a container segment separator (empty or "%%")
0220 inline bool is_separator(const std::string &str) {
0221     static const std::string sep("%%");
0222     return (str.empty() || str == sep);
0223 }
0224 
0225 /// Verify that str consists of letters only
0226 inline bool isalpha(const std::string &str) {
0227     return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); });
0228 }
0229 
0230 /// Return a lower case version of a string
0231 inline std::string to_lower(std::string str) {
0232     std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
0233         return std::tolower(x, std::locale());
0234     });
0235     return str;
0236 }
0237 
0238 /// remove underscores from a string
0239 inline std::string remove_underscore(std::string str) {
0240     str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str));
0241     return str;
0242 }
0243 
0244 /// Find and replace a substring with another substring
0245 inline std::string find_and_replace(std::string str, std::string from, std::string to) {
0246 
0247     std::size_t start_pos = 0;
0248 
0249     while((start_pos = str.find(from, start_pos)) != std::string::npos) {
0250         str.replace(start_pos, from.length(), to);
0251         start_pos += to.length();
0252     }
0253 
0254     return str;
0255 }
0256 
0257 /// check if the flag definitions has possible false flags
0258 inline bool has_default_flag_values(const std::string &flags) {
0259     return (flags.find_first_of("{!") != std::string::npos);
0260 }
0261 
0262 inline void remove_default_flag_values(std::string &flags) {
0263     auto loc = flags.find_first_of('{');
0264     while(loc != std::string::npos) {
0265         auto finish = flags.find_first_of("},", loc + 1);
0266         if((finish != std::string::npos) && (flags[finish] == '}')) {
0267             flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
0268                         flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
0269         }
0270         loc = flags.find_first_of('{', loc + 1);
0271     }
0272     flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
0273 }
0274 
0275 /// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores
0276 inline std::ptrdiff_t find_member(std::string name,
0277                                   const std::vector<std::string> names,
0278                                   bool ignore_case = false,
0279                                   bool ignore_underscore = false) {
0280     auto it = std::end(names);
0281     if(ignore_case) {
0282         if(ignore_underscore) {
0283             name = detail::to_lower(detail::remove_underscore(name));
0284             it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0285                 return detail::to_lower(detail::remove_underscore(local_name)) == name;
0286             });
0287         } else {
0288             name = detail::to_lower(name);
0289             it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0290                 return detail::to_lower(local_name) == name;
0291             });
0292         }
0293 
0294     } else if(ignore_underscore) {
0295         name = detail::remove_underscore(name);
0296         it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
0297             return detail::remove_underscore(local_name) == name;
0298         });
0299     } else {
0300         it = std::find(std::begin(names), std::end(names), name);
0301     }
0302 
0303     return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
0304 }
0305 
0306 /// Find a trigger string and call a modify callable function that takes the current string and starting position of the
0307 /// trigger and returns the position in the string to search for the next trigger string
0308 template <typename Callable> inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) {
0309     std::size_t start_pos = 0;
0310     while((start_pos = str.find(trigger, start_pos)) != std::string::npos) {
0311         start_pos = modify(str, start_pos);
0312     }
0313     return str;
0314 }
0315 
0316 /// Split a string '"one two" "three"' into 'one two', 'three'
0317 /// Quote characters can be ` ' or "
0318 inline std::vector<std::string> split_up(std::string str, char delimiter = '\0') {
0319 
0320     const std::string delims("\'\"`");
0321     auto find_ws = [delimiter](char ch) {
0322         return (delimiter == '\0') ? (std::isspace<char>(ch, std::locale()) != 0) : (ch == delimiter);
0323     };
0324     trim(str);
0325 
0326     std::vector<std::string> output;
0327     bool embeddedQuote = false;
0328     char keyChar = ' ';
0329     while(!str.empty()) {
0330         if(delims.find_first_of(str[0]) != std::string::npos) {
0331             keyChar = str[0];
0332             auto end = str.find_first_of(keyChar, 1);
0333             while((end != std::string::npos) && (str[end - 1] == '\\')) {  // deal with escaped quotes
0334                 end = str.find_first_of(keyChar, end + 1);
0335                 embeddedQuote = true;
0336             }
0337             if(end != std::string::npos) {
0338                 output.push_back(str.substr(1, end - 1));
0339                 if(end + 2 < str.size()) {
0340                     str = str.substr(end + 2);
0341                 } else {
0342                     str.clear();
0343                 }
0344 
0345             } else {
0346                 output.push_back(str.substr(1));
0347                 str = "";
0348             }
0349         } else {
0350             auto it = std::find_if(std::begin(str), std::end(str), find_ws);
0351             if(it != std::end(str)) {
0352                 std::string value = std::string(str.begin(), it);
0353                 output.push_back(value);
0354                 str = std::string(it + 1, str.end());
0355             } else {
0356                 output.push_back(str);
0357                 str = "";
0358             }
0359         }
0360         // transform any embedded quotes into the regular character
0361         if(embeddedQuote) {
0362             output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar));
0363             embeddedQuote = false;
0364         }
0365         trim(str);
0366     }
0367     return output;
0368 }
0369 
0370 /// Add a leader to the beginning of all new lines (nothing is added
0371 /// at the start of the first line). `"; "` would be for ini files
0372 ///
0373 /// Can't use Regex, or this would be a subs.
0374 inline std::string fix_newlines(const std::string &leader, std::string input) {
0375     std::string::size_type n = 0;
0376     while(n != std::string::npos && n < input.size()) {
0377         n = input.find('\n', n);
0378         if(n != std::string::npos) {
0379             input = input.substr(0, n + 1) + leader + input.substr(n + 1);
0380             n += leader.size();
0381         }
0382     }
0383     return input;
0384 }
0385 
0386 /// This function detects an equal or colon followed by an escaped quote after an argument
0387 /// then modifies the string to replace the equality with a space.  This is needed
0388 /// to allow the split up function to work properly and is intended to be used with the find_and_modify function
0389 /// the return value is the offset+1 which is required by the find_and_modify function.
0390 inline std::size_t escape_detect(std::string &str, std::size_t offset) {
0391     auto next = str[offset + 1];
0392     if((next == '\"') || (next == '\'') || (next == '`')) {
0393         auto astart = str.find_last_of("-/ \"\'`", offset - 1);
0394         if(astart != std::string::npos) {
0395             if(str[astart] == ((str[offset] == '=') ? '-' : '/'))
0396                 str[offset] = ' ';  // interpret this as a space so the split_up works properly
0397         }
0398     }
0399     return offset + 1;
0400 }
0401 
0402 /// Add quotes if the string contains spaces
0403 inline std::string &add_quotes_if_needed(std::string &str) {
0404     if((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) {
0405         char quote = str.find('"') < str.find('\'') ? '\'' : '"';
0406         if(str.find(' ') != std::string::npos) {
0407             str.insert(0, 1, quote);
0408             str.append(1, quote);
0409         }
0410     }
0411     return str;
0412 }
0413 
0414 }  // namespace detail
0415 
0416 // [CLI11:string_tools_hpp:end]
0417 
0418 }  // namespace CLI