| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file LICENSE.rst or https://cmake.org/licensing for details. */ |
| |
| #include "cmConfigure.h" // IWYU pragma: keep |
| |
| #include "cmCMakeString.hxx" |
| |
| #include <cstdio> |
| #include <cstdlib> |
| #include <memory> |
| #include <stdexcept> |
| #include <vector> |
| |
| #include "cmsys/RegularExpression.hxx" |
| |
| #include "cmCryptoHash.h" |
| #include "cmGeneratorExpression.h" |
| #include "cmMakefile.h" |
| #include "cmPolicies.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmStringReplaceHelper.h" |
| #include "cmSystemTools.h" |
| #include "cmTimestamp.h" |
| #include "cmUuid.h" |
| |
| namespace cm { |
| bool CMakeString::Compare(CompOperator op, cm::string_view other) |
| { |
| switch (op) { |
| case CompOperator::EQUAL: |
| return this->String_ == other; |
| case CompOperator::LESS: |
| return this->String_ < other; |
| case CompOperator::LESS_EQUAL: |
| return this->String_ <= other; |
| case CompOperator::GREATER: |
| return this->String_ > other; |
| case CompOperator::GREATER_EQUAL: |
| return this->String_ >= other; |
| default: |
| return false; |
| } |
| } |
| |
| CMakeString& CMakeString::Replace(std::string const& matchExpression, |
| std::string const& replaceExpression, |
| Regex regex, cmMakefile* makefile) |
| { |
| if (regex == Regex::Yes) { |
| if (makefile) { |
| makefile->ClearMatches(); |
| } |
| |
| cmStringReplaceHelper replaceHelper(matchExpression, replaceExpression, |
| makefile); |
| if (!replaceHelper.IsReplaceExpressionValid()) { |
| throw std::invalid_argument(replaceHelper.GetError()); |
| } |
| if (!replaceHelper.IsRegularExpressionValid()) { |
| throw std::invalid_argument( |
| cmStrCat("Failed to compile regex \"", matchExpression, '"')); |
| } |
| |
| std ::string output; |
| if (!replaceHelper.Replace(this->String_, output)) { |
| throw std::runtime_error(replaceHelper.GetError()); |
| } |
| this->String_ = std::move(output); |
| } else { |
| std::string output = this->String_.str(); |
| cmsys::SystemTools::ReplaceString(output, matchExpression, |
| replaceExpression); |
| this->String_ = std::move(output); |
| } |
| |
| return *this; |
| }; |
| |
| cmList CMakeString::Match(std::string const& matchExpression, |
| MatchItems matchItems, cmMakefile* makefile) const |
| { |
| if (makefile) { |
| makefile->ClearMatches(); |
| } |
| |
| // Compile the regular expression. |
| cmsys::RegularExpression re; |
| if (!re.compile(matchExpression)) { |
| throw std::invalid_argument( |
| cmStrCat("Failed to compile regex \"", matchExpression, '"')); |
| } |
| |
| cmList output; |
| if (matchItems == MatchItems::Once) { |
| if (re.find(this->String_.data())) { |
| if (makefile) { |
| makefile->StoreMatches(re); |
| } |
| output = re.match(); |
| } |
| } else { |
| unsigned optAnchor = 0; |
| if (makefile && |
| makefile->GetPolicyStatus(cmPolicies::CMP0186) != cmPolicies::NEW) { |
| optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET; |
| } |
| |
| // Scan through the input for all matches. |
| std::string::size_type base = 0; |
| unsigned optNonEmpty = 0; |
| while (re.find(this->String_.data(), base, optAnchor | optNonEmpty)) { |
| if (makefile) { |
| makefile->ClearMatches(); |
| makefile->StoreMatches(re); |
| } |
| |
| output.push_back(re.match()); |
| base = re.end(); |
| |
| if (re.start() == this->String_.length()) { |
| break; |
| } |
| if (re.start() == re.end()) { |
| optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET; |
| } else { |
| optNonEmpty = 0; |
| } |
| } |
| } |
| |
| return output; |
| } |
| |
| CMakeString CMakeString::Substring(long begin, long count) const |
| { |
| if (begin < 0 || static_cast<size_type>(begin) > this->String_.size()) { |
| throw std::out_of_range(cmStrCat( |
| "begin index: ", begin, " is out of range 0 - ", this->String_.size())); |
| } |
| if (count < -1) { |
| throw std::out_of_range( |
| cmStrCat("end index: ", count, " should be -1 or greater")); |
| } |
| |
| return this->String_.substr(static_cast<size_type>(begin), |
| count == -1 ? npos |
| : static_cast<size_type>(count)); |
| } |
| |
| CMakeString& CMakeString::Strip(StripItems stripItems) |
| { |
| if (stripItems == StripItems::Space) { |
| this->String_ = cmTrimWhitespace(this->String_); |
| } else { |
| this->String_ = cmGeneratorExpression::Preprocess( |
| this->String_, cmGeneratorExpression::StripAllGeneratorExpressions); |
| } |
| |
| return *this; |
| } |
| |
| CMakeString& CMakeString::Repeat(size_type count) |
| { |
| switch (this->Size()) { |
| case 0u: |
| // Nothing to do for zero length input strings |
| break; |
| case 1u: |
| // NOTE If the string to repeat consists of the only character, |
| // use the appropriate constructor. |
| this->String_ = std::string(count, this->String_[0]); |
| break; |
| default: |
| std::string result; |
| auto size = this->Size(); |
| |
| result.reserve(size * count); |
| for (auto i = 0u; i < count; ++i) { |
| result.insert(i * size, this->String_.data(), size); |
| } |
| this->String_ = std::move(result); |
| break; |
| } |
| return *this; |
| } |
| |
| CMakeString& CMakeString::Quote(QuoteItems) |
| { |
| std ::string output; |
| // Escape all regex special characters |
| cmStringReplaceHelper replaceHelper("([][()+*^.$?|\\\\])", R"(\\\1)"); |
| if (!replaceHelper.Replace(this->String_, output)) { |
| throw std::runtime_error(replaceHelper.GetError()); |
| } |
| |
| this->String_ = std::move(output); |
| return *this; |
| } |
| |
| CMakeString& CMakeString::Hash(cm::string_view hashAlgorithm) |
| { |
| std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(hashAlgorithm)); |
| if (hash) { |
| this->String_ = hash->HashString(this->String_); |
| return *this; |
| } |
| |
| throw std::invalid_argument( |
| cmStrCat(hashAlgorithm, ": invalid hash algorithm.")); |
| } |
| |
| CMakeString& CMakeString::FromASCII(string_range codes) |
| { |
| std::string output; |
| output.reserve(codes.size()); |
| |
| for (auto const& code : codes) { |
| try { |
| auto ch = std::stoi(code); |
| if (ch > 0 && ch < 256) { |
| output += static_cast<char>(ch); |
| } else { |
| throw std::invalid_argument( |
| cmStrCat("Character with code ", code, " does not exist.")); |
| } |
| } catch (...) { |
| throw std::invalid_argument( |
| cmStrCat("Character with code ", code, " does not exist.")); |
| } |
| } |
| this->String_ = std::move(output); |
| return *this; |
| } |
| |
| CMakeString& CMakeString::ToHexadecimal(cm::string_view str) |
| { |
| std::string output(str.size() * 2, ' '); |
| std::string::size_type hexIndex = 0; |
| |
| for (auto const& c : str) { |
| std::snprintf(&output[hexIndex], 3, "%.2x", c & 0xFFu); |
| hexIndex += 2; |
| } |
| this->String_ = output; |
| return *this; |
| } |
| |
| cm::string_view const CMakeString::RandomDefaultAlphabet{ |
| "qwertyuiopasdfghjklzxcvbnm" |
| "QWERTYUIOPASDFGHJKLZXCVBNM" |
| "0123456789" |
| }; |
| bool CMakeString::Seeded = false; |
| |
| CMakeString& CMakeString::Random(unsigned int seed, std::size_t length, |
| cm::string_view alphabet) |
| { |
| if (alphabet.empty()) { |
| alphabet = RandomDefaultAlphabet; |
| } |
| |
| if (length < 1) { |
| throw std::out_of_range("Invoked with bad length."); |
| } |
| |
| if (!this->Seeded) { |
| this->Seeded = true; |
| std::srand(seed); |
| } |
| |
| double alphabetSize = static_cast<double>(alphabet.size()); |
| std::vector<char> result; |
| result.reserve(length + 1); |
| |
| for (std::size_t i = 0; i < length; i++) { |
| auto index = static_cast<std::string::size_type>( |
| alphabetSize * std::rand() / (RAND_MAX + 1.0)); |
| result.push_back(alphabet[index]); |
| } |
| result.push_back(0); |
| |
| this->String_ = result.data(); |
| return *this; |
| } |
| |
| CMakeString& CMakeString::Timestamp(cm::string_view format, UTC utc) |
| { |
| cmTimestamp timestamp; |
| this->String_ = timestamp.CurrentTime(format, utc == UTC::Yes); |
| return *this; |
| } |
| |
| CMakeString& CMakeString::UUID(cm::string_view nameSpace, cm::string_view name, |
| UUIDType type, Case uuidCase) |
| { |
| #if !defined(CMAKE_BOOTSTRAP) |
| cmUuid uuidGenerator; |
| std::vector<unsigned char> uuidNamespace; |
| std::string uuid; |
| |
| if (!uuidGenerator.StringToBinary(nameSpace, uuidNamespace)) { |
| throw std::invalid_argument("malformed NAMESPACE UUID"); |
| } |
| |
| if (type == UUIDType::MD5) { |
| uuid = uuidGenerator.FromMd5(uuidNamespace, name); |
| } else if (type == UUIDType::SHA1) { |
| uuid = uuidGenerator.FromSha1(uuidNamespace, name); |
| } |
| |
| if (uuid.empty()) { |
| throw std::runtime_error("generation failed"); |
| } |
| |
| if (uuidCase == Case::Upper) { |
| uuid = cmSystemTools::UpperCase(uuid); |
| } |
| |
| this->String_ = std::move(uuid); |
| return *this; |
| #else |
| throw std::runtime_error("not available during bootstrap"); |
| #endif |
| } |
| |
| } |