| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmStringReplaceHelper.h" | |
| #include <utility> | |
| #include "cmMakefile.h" | |
| #include "cmPolicies.h" | |
| #include "cmStringAlgorithms.h" | |
| cmStringReplaceHelper::cmStringReplaceHelper(std::string const& regex, | |
| std::string replace_expr, | |
| cmMakefile* makefile) | |
| : RegExString(regex) | |
| , RegularExpression(regex) | |
| , ReplaceExpression(std::move(replace_expr)) | |
| , Makefile(makefile) | |
| { | |
| this->ParseReplaceExpression(); | |
| } | |
| bool cmStringReplaceHelper::Replace(cm::string_view input, std::string& output) | |
| { | |
| output.clear(); | |
| unsigned optAnchor = 0; | |
| if (this->Makefile && | |
| this->Makefile->GetPolicyStatus(cmPolicies::CMP0186) != | |
| cmPolicies::NEW) { | |
| optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET; | |
| } | |
| // Scan through the input for all matches. | |
| auto& re = this->RegularExpression; | |
| std::string::size_type base = 0; | |
| unsigned optNonEmpty = 0; | |
| while (re.find(input.data(), base, optAnchor | optNonEmpty)) { | |
| if (this->Makefile) { | |
| this->Makefile->ClearMatches(); | |
| this->Makefile->StoreMatches(re); | |
| } | |
| // Concatenate the part of the input that was not matched. | |
| output += input.substr(base, re.start() - base); | |
| // Concatenate the replacement for the match. | |
| for (auto const& replacement : this->Replacements) { | |
| if (replacement.Number < 0) { | |
| // This is just a plain-text part of the replacement. | |
| output += replacement.Value; | |
| } else { | |
| // Replace with part of the match. | |
| auto n = replacement.Number; | |
| if (n > re.num_groups()) { | |
| this->ErrorString = | |
| cmStrCat("replace expression \"", this->ReplaceExpression, | |
| "\" contains an out-of-range escape for regex \"", | |
| this->RegExString, '"'); | |
| return false; | |
| } | |
| output += re.match(n); | |
| } | |
| } | |
| // Move past the match. | |
| base = re.end(); | |
| if (re.start() == input.length()) { | |
| break; | |
| } | |
| if (re.start() == re.end()) { | |
| optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET; | |
| } else { | |
| optNonEmpty = 0; | |
| } | |
| } | |
| // Concatenate the text after the last match. | |
| output += input.substr(base); | |
| return true; | |
| } | |
| void cmStringReplaceHelper::ParseReplaceExpression() | |
| { | |
| std::string::size_type l = 0; | |
| while (l < this->ReplaceExpression.length()) { | |
| auto r = this->ReplaceExpression.find('\\', l); | |
| if (r == std::string::npos) { | |
| r = this->ReplaceExpression.length(); | |
| this->Replacements.emplace_back( | |
| this->ReplaceExpression.substr(l, r - l)); | |
| } else { | |
| if (r > l) { | |
| this->Replacements.emplace_back( | |
| this->ReplaceExpression.substr(l, r - l)); | |
| } | |
| if (r == (this->ReplaceExpression.length() - 1)) { | |
| this->ValidReplaceExpression = false; | |
| this->ErrorString = "replace-expression ends in a backslash"; | |
| return; | |
| } | |
| if ((this->ReplaceExpression[r + 1] >= '0') && | |
| (this->ReplaceExpression[r + 1] <= '9')) { | |
| this->Replacements.emplace_back(this->ReplaceExpression[r + 1] - '0'); | |
| } else if (this->ReplaceExpression[r + 1] == 'n') { | |
| this->Replacements.emplace_back("\n"); | |
| } else if (this->ReplaceExpression[r + 1] == '\\') { | |
| this->Replacements.emplace_back("\\"); | |
| } else { | |
| this->ValidReplaceExpression = false; | |
| this->ErrorString = | |
| cmStrCat("Unknown escape \"", this->ReplaceExpression.substr(r, 2), | |
| "\" in replace-expression"); | |
| return; | |
| } | |
| r += 2; | |
| } | |
| l = r; | |
| } | |
| } |