/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmStringAlgorithms.h" #include #include #include // IWYU pragma: keep #include #include #include std::string cmTrimWhitespace(cm::string_view str) { auto start = str.begin(); while (start != str.end() && cmIsSpace(*start)) { ++start; } if (start == str.end()) { return std::string(); } auto stop = str.end() - 1; while (cmIsSpace(*stop)) { --stop; } return std::string(start, stop + 1); } std::string cmRemoveQuotes(cm::string_view str) { // We process only strings that have two quotes at least. // Also front() and back() are only defined behavior on non empty strings. if (str.size() >= 2 && // str.front() == '"' && // str.back() == '"') { // Remove a quote from the front and back str.remove_prefix(1); str.remove_suffix(1); } return std::string(str); } std::string cmEscapeQuotes(cm::string_view str) { std::string result; result.reserve(str.size()); for (const char ch : str) { if (ch == '"') { result += '\\'; } result += ch; } return result; } std::vector cmTokenize(cm::string_view str, cm::string_view sep) { std::vector tokens; cm::string_view::size_type tokend = 0; do { cm::string_view::size_type tokstart = str.find_first_not_of(sep, tokend); if (tokstart == cm::string_view::npos) { break; // no more tokens } tokend = str.find_first_of(sep, tokstart); if (tokend == cm::string_view::npos) { tokens.emplace_back(str.substr(tokstart)); } else { tokens.emplace_back(str.substr(tokstart, tokend - tokstart)); } } while (tokend != cm::string_view::npos); if (tokens.empty()) { tokens.emplace_back(); } return tokens; } void cmExpandList(cm::string_view arg, std::vector& argsOut, bool emptyArgs) { // If argument is empty, it is an empty list. if (!emptyArgs && arg.empty()) { return; } // if there are no ; in the name then just copy the current string if (arg.find(';') == cm::string_view::npos) { argsOut.emplace_back(arg); return; } std::string newArg; // Break the string at non-escaped semicolons not nested in []. int squareNesting = 0; cm::string_view::iterator last = arg.begin(); cm::string_view::iterator const cend = arg.end(); for (cm::string_view::iterator c = last; c != cend; ++c) { switch (*c) { case '\\': { // We only want to allow escaping of semicolons. Other // escapes should not be processed here. cm::string_view::iterator cnext = c + 1; if ((cnext != cend) && *cnext == ';') { newArg.append(last, c); // Skip over the escape character last = cnext; c = cnext; } } break; case '[': { ++squareNesting; } break; case ']': { --squareNesting; } break; case ';': { // Break the string here if we are not nested inside square // brackets. if (squareNesting == 0) { newArg.append(last, c); // Skip over the semicolon last = c + 1; if (!newArg.empty() || emptyArgs) { // Add the last argument if the string is not empty. argsOut.push_back(newArg); newArg.clear(); } } } break; default: { // Just append this character. } break; } } newArg.append(last, cend); if (!newArg.empty() || emptyArgs) { // Add the last argument if the string is not empty. argsOut.push_back(std::move(newArg)); } } std::vector cmExpandedList(cm::string_view arg, bool emptyArgs) { std::vector argsOut; cmExpandList(arg, argsOut, emptyArgs); return argsOut; } namespace { template inline void MakeDigits(cm::string_view& view, char (&digits)[N], const char* pattern, T value) { int res = std::snprintf(digits, N, pattern, value); if (res > 0 && res < static_cast(N)) { view = cm::string_view(digits, static_cast(res)); } } } // unnamed namespace cmAlphaNum::cmAlphaNum(int val) { MakeDigits(this->View_, this->Digits_, "%i", val); } cmAlphaNum::cmAlphaNum(unsigned int val) { MakeDigits(this->View_, this->Digits_, "%u", val); } cmAlphaNum::cmAlphaNum(long int val) { MakeDigits(this->View_, this->Digits_, "%li", val); } cmAlphaNum::cmAlphaNum(unsigned long int val) { MakeDigits(this->View_, this->Digits_, "%lu", val); } cmAlphaNum::cmAlphaNum(long long int val) { MakeDigits(this->View_, this->Digits_, "%lli", val); } cmAlphaNum::cmAlphaNum(unsigned long long int val) { MakeDigits(this->View_, this->Digits_, "%llu", val); } cmAlphaNum::cmAlphaNum(float val) { MakeDigits(this->View_, this->Digits_, "%g", static_cast(val)); } cmAlphaNum::cmAlphaNum(double val) { MakeDigits(this->View_, this->Digits_, "%g", val); } std::string cmCatViews(std::initializer_list views) { std::size_t total_size = 0; for (cm::string_view const& view : views) { total_size += view.size(); } std::string result(total_size, '\0'); std::string::iterator sit = result.begin(); for (cm::string_view const& view : views) { sit = std::copy_n(view.data(), view.size(), sit); } return result; } bool cmIsInternallyOn(cm::string_view val) { return (val.size() == 4) && // (val[0] == 'I' || val[0] == 'i') && // (val[1] == '_') && // (val[2] == 'O' || val[2] == 'o') && // (val[3] == 'N' || val[3] == 'n'); } bool cmIsNOTFOUND(cm::string_view val) { return (val == "NOTFOUND") || cmHasLiteralSuffix(val, "-NOTFOUND"); } bool cmIsOn(cm::string_view val) { switch (val.size()) { case 1: return val[0] == '1' || val[0] == 'Y' || val[0] == 'y'; case 2: return // (val[0] == 'O' || val[0] == 'o') && // (val[1] == 'N' || val[1] == 'n'); case 3: return // (val[0] == 'Y' || val[0] == 'y') && // (val[1] == 'E' || val[1] == 'e') && // (val[2] == 'S' || val[2] == 's'); case 4: return // (val[0] == 'T' || val[0] == 't') && // (val[1] == 'R' || val[1] == 'r') && // (val[2] == 'U' || val[2] == 'u') && // (val[3] == 'E' || val[3] == 'e'); default: break; } return false; } bool cmIsOff(cm::string_view val) { switch (val.size()) { case 0: return true; case 1: return val[0] == '0' || val[0] == 'N' || val[0] == 'n'; case 2: return // (val[0] == 'N' || val[0] == 'n') && // (val[1] == 'O' || val[1] == 'o'); case 3: return // (val[0] == 'O' || val[0] == 'o') && // (val[1] == 'F' || val[1] == 'f') && // (val[2] == 'F' || val[2] == 'f'); case 5: return // (val[0] == 'F' || val[0] == 'f') && // (val[1] == 'A' || val[1] == 'a') && // (val[2] == 'L' || val[2] == 'l') && // (val[3] == 'S' || val[3] == 's') && // (val[4] == 'E' || val[4] == 'e'); case 6: return // (val[0] == 'I' || val[0] == 'i') && // (val[1] == 'G' || val[1] == 'g') && // (val[2] == 'N' || val[2] == 'n') && // (val[3] == 'O' || val[3] == 'o') && // (val[4] == 'R' || val[4] == 'r') && // (val[5] == 'E' || val[5] == 'e'); default: break; } return cmIsNOTFOUND(val); } bool cmStrToLong(const char* str, long* value) { errno = 0; char* endp; *value = strtol(str, &endp, 10); return (*endp == '\0') && (endp != str) && (errno == 0); } bool cmStrToLong(std::string const& str, long* value) { return cmStrToLong(str.c_str(), value); } bool cmStrToULong(const char* str, unsigned long* value) { errno = 0; char* endp; while (cmIsSpace(*str)) { ++str; } if (*str == '-') { return false; } *value = strtoul(str, &endp, 10); return (*endp == '\0') && (endp != str) && (errno == 0); } bool cmStrToULong(std::string const& str, unsigned long* value) { return cmStrToULong(str.c_str(), value); } template std::size_t getJoinedLength(Range const& rng, cm::string_view separator) { std::size_t rangeLength{}; for (auto const& item : rng) { rangeLength += item.size(); } auto const separatorsLength = (rng.size() - 1) * separator.size(); return rangeLength + separatorsLength; } template std::string cmJoinImpl(Range const& rng, cm::string_view separator, cm::string_view initial) { if (rng.empty()) { return { std::begin(initial), std::end(initial) }; } std::string result; result.reserve(initial.size() + getJoinedLength(rng, separator)); result.append(std::begin(initial), std::end(initial)); auto begin = std::begin(rng); auto end = std::end(rng); result += *begin; for (++begin; begin != end; ++begin) { result.append(std::begin(separator), std::end(separator)); result += *begin; } return result; } std::string cmJoin(std::vector const& rng, cm::string_view separator, cm::string_view initial) { return cmJoinImpl(rng, separator, initial); } std::string cmJoin(cmStringRange const& rng, cm::string_view separator, cm::string_view initial) { return cmJoinImpl(rng, separator, initial); }