Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 <utility>
7
8
#include "cmMakefile.h"
9
#include "cmPolicies.h"
10
#include "cmStringAlgorithms.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
          this->ErrorString =
57
0
            cmStrCat("replace expression \"", this->ReplaceExpression,
58
0
                     "\" contains an out-of-range escape for regex \"",
59
0
                     this->RegExString, '"');
60
0
          return false;
61
0
        }
62
0
        output += re.match(n);
63
0
      }
64
0
    }
65
66
    // Move past the match.
67
0
    base = re.end();
68
69
0
    if (re.start() == input.length()) {
70
0
      break;
71
0
    }
72
0
    if (re.start() == re.end()) {
73
0
      optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET;
74
0
    } else {
75
0
      optNonEmpty = 0;
76
0
    }
77
0
  }
78
79
  // Concatenate the text after the last match.
80
0
  output += input.substr(base);
81
82
0
  return true;
83
0
}
84
85
void cmStringReplaceHelper::ParseReplaceExpression()
86
0
{
87
0
  std::string::size_type l = 0;
88
0
  while (l < this->ReplaceExpression.length()) {
89
0
    auto r = this->ReplaceExpression.find('\\', l);
90
0
    if (r == std::string::npos) {
91
0
      r = this->ReplaceExpression.length();
92
0
      this->Replacements.emplace_back(
93
0
        this->ReplaceExpression.substr(l, r - l));
94
0
    } else {
95
0
      if (r > l) {
96
0
        this->Replacements.emplace_back(
97
0
          this->ReplaceExpression.substr(l, r - l));
98
0
      }
99
0
      if (r == (this->ReplaceExpression.length() - 1)) {
100
0
        this->ValidReplaceExpression = false;
101
0
        this->ErrorString = "replace-expression ends in a backslash";
102
0
        return;
103
0
      }
104
0
      if ((this->ReplaceExpression[r + 1] >= '0') &&
105
0
          (this->ReplaceExpression[r + 1] <= '9')) {
106
0
        this->Replacements.emplace_back(this->ReplaceExpression[r + 1] - '0');
107
0
      } else if (this->ReplaceExpression[r + 1] == 'n') {
108
0
        this->Replacements.emplace_back("\n");
109
0
      } else if (this->ReplaceExpression[r + 1] == '\\') {
110
0
        this->Replacements.emplace_back("\\");
111
0
      } else {
112
0
        this->ValidReplaceExpression = false;
113
0
        this->ErrorString =
114
0
          cmStrCat("Unknown escape \"", this->ReplaceExpression.substr(r, 2),
115
0
                   "\" in replace-expression");
116
0
        return;
117
0
      }
118
0
      r += 2;
119
0
    }
120
0
    l = r;
121
0
  }
122
0
}