/src/CMake/Source/cmStringReplaceHelper.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | |
4 | | #include "cmStringReplaceHelper.h" |
5 | | |
6 | | #include <sstream> |
7 | | #include <utility> |
8 | | |
9 | | #include "cmMakefile.h" |
10 | | #include "cmPolicies.h" |
11 | | |
12 | | cmStringReplaceHelper::cmStringReplaceHelper(std::string const& regex, |
13 | | std::string replace_expr, |
14 | | cmMakefile* makefile) |
15 | 0 | : RegExString(regex) |
16 | 0 | , RegularExpression(regex) |
17 | 0 | , ReplaceExpression(std::move(replace_expr)) |
18 | 0 | , Makefile(makefile) |
19 | 0 | { |
20 | 0 | this->ParseReplaceExpression(); |
21 | 0 | } |
22 | | |
23 | | bool cmStringReplaceHelper::Replace(cm::string_view input, std::string& output) |
24 | 0 | { |
25 | 0 | output.clear(); |
26 | |
|
27 | 0 | unsigned optAnchor = 0; |
28 | 0 | if (this->Makefile && |
29 | 0 | this->Makefile->GetPolicyStatus(cmPolicies::CMP0186) != |
30 | 0 | cmPolicies::NEW) { |
31 | 0 | optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET; |
32 | 0 | } |
33 | | |
34 | | // Scan through the input for all matches. |
35 | 0 | auto& re = this->RegularExpression; |
36 | 0 | std::string::size_type base = 0; |
37 | 0 | unsigned optNonEmpty = 0; |
38 | 0 | while (re.find(input.data(), base, optAnchor | optNonEmpty)) { |
39 | 0 | if (this->Makefile) { |
40 | 0 | this->Makefile->ClearMatches(); |
41 | 0 | this->Makefile->StoreMatches(re); |
42 | 0 | } |
43 | | |
44 | | // Concatenate the part of the input that was not matched. |
45 | 0 | output += input.substr(base, re.start() - base); |
46 | | |
47 | | // Concatenate the replacement for the match. |
48 | 0 | for (auto const& replacement : this->Replacements) { |
49 | 0 | if (replacement.Number < 0) { |
50 | | // This is just a plain-text part of the replacement. |
51 | 0 | output += replacement.Value; |
52 | 0 | } else { |
53 | | // Replace with part of the match. |
54 | 0 | auto n = replacement.Number; |
55 | 0 | if (n > re.num_groups()) { |
56 | 0 | std::ostringstream error; |
57 | 0 | error << "replace expression \"" << this->ReplaceExpression |
58 | 0 | << "\" contains an out-of-range escape for regex \"" |
59 | 0 | << this->RegExString << "\""; |
60 | 0 | this->ErrorString = error.str(); |
61 | 0 | return false; |
62 | 0 | } |
63 | 0 | output += re.match(n); |
64 | 0 | } |
65 | 0 | } |
66 | | |
67 | | // Move past the match. |
68 | 0 | base = re.end(); |
69 | |
|
70 | 0 | if (re.start() == input.length()) { |
71 | 0 | break; |
72 | 0 | } |
73 | 0 | if (re.start() == re.end()) { |
74 | 0 | optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET; |
75 | 0 | } else { |
76 | 0 | optNonEmpty = 0; |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | // Concatenate the text after the last match. |
81 | 0 | output += input.substr(base); |
82 | |
|
83 | 0 | return true; |
84 | 0 | } |
85 | | |
86 | | void cmStringReplaceHelper::ParseReplaceExpression() |
87 | 0 | { |
88 | 0 | std::string::size_type l = 0; |
89 | 0 | while (l < this->ReplaceExpression.length()) { |
90 | 0 | auto r = this->ReplaceExpression.find('\\', l); |
91 | 0 | if (r == std::string::npos) { |
92 | 0 | r = this->ReplaceExpression.length(); |
93 | 0 | this->Replacements.emplace_back( |
94 | 0 | this->ReplaceExpression.substr(l, r - l)); |
95 | 0 | } else { |
96 | 0 | if (r - l > 0) { |
97 | 0 | this->Replacements.emplace_back( |
98 | 0 | this->ReplaceExpression.substr(l, r - l)); |
99 | 0 | } |
100 | 0 | if (r == (this->ReplaceExpression.length() - 1)) { |
101 | 0 | this->ValidReplaceExpression = false; |
102 | 0 | this->ErrorString = "replace-expression ends in a backslash"; |
103 | 0 | return; |
104 | 0 | } |
105 | 0 | if ((this->ReplaceExpression[r + 1] >= '0') && |
106 | 0 | (this->ReplaceExpression[r + 1] <= '9')) { |
107 | 0 | this->Replacements.emplace_back(this->ReplaceExpression[r + 1] - '0'); |
108 | 0 | } else if (this->ReplaceExpression[r + 1] == 'n') { |
109 | 0 | this->Replacements.emplace_back("\n"); |
110 | 0 | } else if (this->ReplaceExpression[r + 1] == '\\') { |
111 | 0 | this->Replacements.emplace_back("\\"); |
112 | 0 | } else { |
113 | 0 | this->ValidReplaceExpression = false; |
114 | 0 | std::ostringstream error; |
115 | 0 | error << "Unknown escape \"" << this->ReplaceExpression.substr(r, 2) |
116 | 0 | << "\" in replace-expression"; |
117 | 0 | this->ErrorString = error.str(); |
118 | 0 | return; |
119 | 0 | } |
120 | 0 | r += 2; |
121 | 0 | } |
122 | 0 | l = r; |
123 | 0 | } |
124 | 0 | } |