CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmStringReplaceHelper.cxx
blob768effd8a8d0819c87e15ad0a117019c46b7107a
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
4 #include "cmStringReplaceHelper.h"
6 #include <sstream>
7 #include <utility>
9 #include "cmMakefile.h"
11 cmStringReplaceHelper::cmStringReplaceHelper(const std::string& regex,
12 std::string replace_expr,
13 cmMakefile* makefile)
14 : RegExString(regex)
15 , RegularExpression(regex)
16 , ReplaceExpression(std::move(replace_expr))
17 , Makefile(makefile)
19 this->ParseReplaceExpression();
22 bool cmStringReplaceHelper::Replace(const std::string& input,
23 std::string& output)
25 output.clear();
27 // Scan through the input for all matches.
28 std::string::size_type base = 0;
29 while (this->RegularExpression.find(input.c_str() + base)) {
30 if (this->Makefile) {
31 this->Makefile->ClearMatches();
32 this->Makefile->StoreMatches(this->RegularExpression);
34 auto l2 = this->RegularExpression.start();
35 auto r = this->RegularExpression.end();
37 // Concatenate the part of the input that was not matched.
38 output += input.substr(base, l2);
40 // Make sure the match had some text.
41 if (r - l2 == 0) {
42 std::ostringstream error;
43 error << "regex \"" << this->RegExString << "\" matched an empty string";
44 this->ErrorString = error.str();
45 return false;
48 // Concatenate the replacement for the match.
49 for (const auto& replacement : this->Replacements) {
50 if (replacement.Number < 0) {
51 // This is just a plain-text part of the replacement.
52 output += replacement.Value;
53 } else {
54 // Replace with part of the match.
55 auto n = replacement.Number;
56 auto start = this->RegularExpression.start(n);
57 auto end = this->RegularExpression.end(n);
58 auto len = input.length() - base;
59 if ((start != std::string::npos) && (end != std::string::npos) &&
60 (start <= len) && (end <= len)) {
61 output += input.substr(base + start, end - start);
62 } else {
63 std::ostringstream error;
64 error << "replace expression \"" << this->ReplaceExpression
65 << "\" contains an out-of-range escape for regex \""
66 << this->RegExString << "\"";
67 this->ErrorString = error.str();
68 return false;
73 // Move past the match.
74 base += r;
77 // Concatenate the text after the last match.
78 output += input.substr(base, input.length() - base);
80 return true;
83 void cmStringReplaceHelper::ParseReplaceExpression()
85 std::string::size_type l = 0;
86 while (l < this->ReplaceExpression.length()) {
87 auto r = this->ReplaceExpression.find('\\', l);
88 if (r == std::string::npos) {
89 r = this->ReplaceExpression.length();
90 this->Replacements.emplace_back(
91 this->ReplaceExpression.substr(l, r - l));
92 } else {
93 if (r - l > 0) {
94 this->Replacements.emplace_back(
95 this->ReplaceExpression.substr(l, r - l));
97 if (r == (this->ReplaceExpression.length() - 1)) {
98 this->ValidReplaceExpression = false;
99 this->ErrorString = "replace-expression ends in a backslash";
100 return;
102 if ((this->ReplaceExpression[r + 1] >= '0') &&
103 (this->ReplaceExpression[r + 1] <= '9')) {
104 this->Replacements.emplace_back(this->ReplaceExpression[r + 1] - '0');
105 } else if (this->ReplaceExpression[r + 1] == 'n') {
106 this->Replacements.emplace_back("\n");
107 } else if (this->ReplaceExpression[r + 1] == '\\') {
108 this->Replacements.emplace_back("\\");
109 } else {
110 this->ValidReplaceExpression = false;
111 std::ostringstream error;
112 error << "Unknown escape \"" << this->ReplaceExpression.substr(r, 2)
113 << "\" in replace-expression";
114 this->ErrorString = error.str();
115 return;
117 r += 2;
119 l = r;