Coverage Report

Created: 2026-02-09 06:05

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 <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
}