|
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
|
|
|
|
#include "cmStringReplaceHelper.h"
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include "cmMakefile.h"
|
|
|
|
|
|
|
|
cmStringReplaceHelper::cmStringReplaceHelper(const std::string& regex,
|
|
|
|
std::string replace_expr,
|
|
|
|
cmMakefile* makefile)
|
|
|
|
: RegExString(regex)
|
|
|
|
, RegularExpression(regex)
|
|
|
|
, ReplaceExpression(std::move(replace_expr))
|
|
|
|
, Makefile(makefile)
|
|
|
|
{
|
|
|
|
this->ParseReplaceExpression();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmStringReplaceHelper::Replace(const std::string& input,
|
|
|
|
std::string& output)
|
|
|
|
{
|
|
|
|
output.clear();
|
|
|
|
|
|
|
|
// Scan through the input for all matches.
|
|
|
|
std::string::size_type base = 0;
|
|
|
|
while (this->RegularExpression.find(input.c_str() + base)) {
|
|
|
|
if (this->Makefile) {
|
|
|
|
this->Makefile->ClearMatches();
|
|
|
|
this->Makefile->StoreMatches(this->RegularExpression);
|
|
|
|
}
|
|
|
|
auto l2 = this->RegularExpression.start();
|
|
|
|
auto r = this->RegularExpression.end();
|
|
|
|
|
|
|
|
// Concatenate the part of the input that was not matched.
|
|
|
|
output += input.substr(base, l2);
|
|
|
|
|
|
|
|
// Make sure the match had some text.
|
|
|
|
if (r - l2 == 0) {
|
|
|
|
std::ostringstream error;
|
|
|
|
error << "regex \"" << this->RegExString << "\" matched an empty string";
|
|
|
|
this->ErrorString = error.str();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Concatenate the replacement for the match.
|
|
|
|
for (const auto& 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;
|
|
|
|
auto start = this->RegularExpression.start(n);
|
|
|
|
auto end = this->RegularExpression.end(n);
|
|
|
|
auto len = input.length() - base;
|
|
|
|
if ((start != std::string::npos) && (end != std::string::npos) &&
|
|
|
|
(start <= len) && (end <= len)) {
|
|
|
|
output += input.substr(base + start, end - start);
|
|
|
|
} else {
|
|
|
|
std::ostringstream error;
|
|
|
|
error << "replace expression \"" << this->ReplaceExpression
|
|
|
|
<< "\" contains an out-of-range escape for regex \""
|
|
|
|
<< this->RegExString << "\"";
|
|
|
|
this->ErrorString = error.str();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move past the match.
|
|
|
|
base += r;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Concatenate the text after the last match.
|
|
|
|
output += input.substr(base, input.length() - 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 > 0) {
|
|
|
|
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;
|
|
|
|
std::ostringstream error;
|
|
|
|
error << "Unknown escape \"" << this->ReplaceExpression.substr(r, 2)
|
|
|
|
<< "\" in replace-expression";
|
|
|
|
this->ErrorString = error.str();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
r += 2;
|
|
|
|
}
|
|
|
|
l = r;
|
|
|
|
}
|
|
|
|
}
|