Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmCMakeString.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 "cmConfigure.h" // IWYU pragma: keep
5
6
#include "cmCMakeString.hxx"
7
8
#include <cstdio>
9
#include <cstdlib>
10
#include <memory>
11
#include <stdexcept>
12
#include <vector>
13
14
#include "cmsys/RegularExpression.hxx"
15
16
#include "cmCryptoHash.h"
17
#include "cmGeneratorExpression.h"
18
#include "cmMakefile.h"
19
#include "cmPolicies.h"
20
#include "cmStringAlgorithms.h"
21
#include "cmStringReplaceHelper.h"
22
#include "cmSystemTools.h"
23
#include "cmTimestamp.h"
24
#include "cmUuid.h"
25
26
namespace cm {
27
bool CMakeString::Compare(CompOperator op, cm::string_view other)
28
0
{
29
0
  switch (op) {
30
0
    case CompOperator::EQUAL:
31
0
      return this->String_ == other;
32
0
    case CompOperator::LESS:
33
0
      return this->String_ < other;
34
0
    case CompOperator::LESS_EQUAL:
35
0
      return this->String_ <= other;
36
0
    case CompOperator::GREATER:
37
0
      return this->String_ > other;
38
0
    case CompOperator::GREATER_EQUAL:
39
0
      return this->String_ >= other;
40
0
    default:
41
0
      return false;
42
0
  }
43
0
}
44
45
CMakeString& CMakeString::Replace(std::string const& matchExpression,
46
                                  std::string const& replaceExpression,
47
                                  Regex regex, cmMakefile* makefile)
48
0
{
49
0
  if (regex == Regex::Yes) {
50
0
    if (makefile) {
51
0
      makefile->ClearMatches();
52
0
    }
53
54
0
    cmStringReplaceHelper replaceHelper(matchExpression, replaceExpression,
55
0
                                        makefile);
56
0
    if (!replaceHelper.IsReplaceExpressionValid()) {
57
0
      throw std::invalid_argument(replaceHelper.GetError());
58
0
    }
59
0
    if (!replaceHelper.IsRegularExpressionValid()) {
60
0
      throw std::invalid_argument(
61
0
        cmStrCat("Failed to compile regex \"", matchExpression, '"'));
62
0
    }
63
64
0
    std ::string output;
65
0
    if (!replaceHelper.Replace(this->String_, output)) {
66
0
      throw std::runtime_error(replaceHelper.GetError());
67
0
    }
68
0
    this->String_ = std::move(output);
69
0
  } else {
70
0
    std::string output = this->String_.str();
71
0
    cmsys::SystemTools::ReplaceString(output, matchExpression,
72
0
                                      replaceExpression);
73
0
    this->String_ = std::move(output);
74
0
  }
75
76
0
  return *this;
77
0
};
78
79
cmList CMakeString::Match(std::string const& matchExpression,
80
                          MatchItems matchItems, cmMakefile* makefile) const
81
0
{
82
0
  if (makefile) {
83
0
    makefile->ClearMatches();
84
0
  }
85
86
  // Compile the regular expression.
87
0
  cmsys::RegularExpression re;
88
0
  if (!re.compile(matchExpression)) {
89
0
    throw std::invalid_argument(
90
0
      cmStrCat("Failed to compile regex \"", matchExpression, '"'));
91
0
  }
92
93
0
  cmList output;
94
0
  if (matchItems == MatchItems::Once) {
95
0
    if (re.find(this->String_.data())) {
96
0
      if (makefile) {
97
0
        makefile->StoreMatches(re);
98
0
      }
99
0
      output = re.match();
100
0
    }
101
0
  } else {
102
0
    unsigned optAnchor = 0;
103
0
    if (makefile &&
104
0
        makefile->GetPolicyStatus(cmPolicies::CMP0186) != cmPolicies::NEW) {
105
0
      optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET;
106
0
    }
107
108
    // Scan through the input for all matches.
109
0
    std::string::size_type base = 0;
110
0
    unsigned optNonEmpty = 0;
111
0
    while (re.find(this->String_.data(), base, optAnchor | optNonEmpty)) {
112
0
      if (makefile) {
113
0
        makefile->ClearMatches();
114
0
        makefile->StoreMatches(re);
115
0
      }
116
117
0
      output.push_back(re.match());
118
0
      base = re.end();
119
120
0
      if (re.start() == this->String_.length()) {
121
0
        break;
122
0
      }
123
0
      if (re.start() == re.end()) {
124
0
        optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET;
125
0
      } else {
126
0
        optNonEmpty = 0;
127
0
      }
128
0
    }
129
0
  }
130
131
0
  return output;
132
0
}
133
134
CMakeString CMakeString::Substring(long begin, long count) const
135
0
{
136
0
  if (begin < 0 || static_cast<size_type>(begin) > this->String_.size()) {
137
0
    throw std::out_of_range(cmStrCat(
138
0
      "begin index: ", begin, " is out of range 0 - ", this->String_.size()));
139
0
  }
140
0
  if (count < -1) {
141
0
    throw std::out_of_range(
142
0
      cmStrCat("end index: ", count, " should be -1 or greater"));
143
0
  }
144
145
0
  return this->String_.substr(static_cast<size_type>(begin),
146
0
                              count == -1 ? npos
147
0
                                          : static_cast<size_type>(count));
148
0
}
149
150
CMakeString& CMakeString::Strip(StripItems stripItems)
151
0
{
152
0
  if (stripItems == StripItems::Space) {
153
0
    this->String_ = cmTrimWhitespace(this->String_);
154
0
  } else {
155
0
    this->String_ = cmGeneratorExpression::Preprocess(
156
0
      this->String_, cmGeneratorExpression::StripAllGeneratorExpressions);
157
0
  }
158
159
0
  return *this;
160
0
}
161
162
CMakeString& CMakeString::Repeat(size_type count)
163
0
{
164
0
  switch (this->Size()) {
165
0
    case 0u:
166
      // Nothing to do for zero length input strings
167
0
      break;
168
0
    case 1u:
169
      // NOTE If the string to repeat consists of the only character,
170
      // use the appropriate constructor.
171
0
      this->String_ = std::string(count, this->String_[0]);
172
0
      break;
173
0
    default:
174
0
      std::string result;
175
0
      auto size = this->Size();
176
177
0
      result.reserve(size * count);
178
0
      for (auto i = 0u; i < count; ++i) {
179
0
        result.insert(i * size, this->String_.data(), size);
180
0
      }
181
0
      this->String_ = std::move(result);
182
0
      break;
183
0
  }
184
0
  return *this;
185
0
}
186
187
CMakeString& CMakeString::Quote(QuoteItems)
188
0
{
189
0
  std ::string output;
190
  // Escape all regex special characters
191
0
  cmStringReplaceHelper replaceHelper("([][()+*^.$?|\\\\])", R"(\\\1)");
192
0
  if (!replaceHelper.Replace(this->String_, output)) {
193
0
    throw std::runtime_error(replaceHelper.GetError());
194
0
  }
195
196
0
  this->String_ = std::move(output);
197
0
  return *this;
198
0
}
199
200
CMakeString& CMakeString::Hash(cm::string_view hashAlgorithm)
201
0
{
202
0
  std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(hashAlgorithm));
203
0
  if (hash) {
204
0
    this->String_ = hash->HashString(this->String_);
205
0
    return *this;
206
0
  }
207
208
0
  throw std::invalid_argument(
209
0
    cmStrCat(hashAlgorithm, ": invalid hash algorithm."));
210
0
}
211
212
CMakeString& CMakeString::FromASCII(string_range codes)
213
0
{
214
0
  std::string output;
215
0
  output.reserve(codes.size());
216
217
0
  for (auto const& code : codes) {
218
0
    try {
219
0
      auto ch = std::stoi(code);
220
0
      if (ch > 0 && ch < 256) {
221
0
        output += static_cast<char>(ch);
222
0
      } else {
223
0
        throw std::invalid_argument(
224
0
          cmStrCat("Character with code ", code, " does not exist."));
225
0
      }
226
0
    } catch (...) {
227
0
      throw std::invalid_argument(
228
0
        cmStrCat("Character with code ", code, " does not exist."));
229
0
    }
230
0
  }
231
0
  this->String_ = std::move(output);
232
0
  return *this;
233
0
}
234
235
CMakeString& CMakeString::ToHexadecimal(cm::string_view str)
236
0
{
237
0
  std::string output(str.size() * 2, ' ');
238
0
  std::string::size_type hexIndex = 0;
239
240
0
  for (auto const& c : str) {
241
0
    std::snprintf(&output[hexIndex], 3, "%.2x", c & 0xFFu);
242
0
    hexIndex += 2;
243
0
  }
244
0
  this->String_ = output;
245
0
  return *this;
246
0
}
247
248
cm::string_view const CMakeString::RandomDefaultAlphabet{
249
  "qwertyuiopasdfghjklzxcvbnm"
250
  "QWERTYUIOPASDFGHJKLZXCVBNM"
251
  "0123456789"
252
};
253
bool CMakeString::Seeded = false;
254
255
CMakeString& CMakeString::Random(unsigned int seed, std::size_t length,
256
                                 cm::string_view alphabet)
257
0
{
258
0
  if (alphabet.empty()) {
259
0
    alphabet = RandomDefaultAlphabet;
260
0
  }
261
262
0
  if (length < 1) {
263
0
    throw std::out_of_range("Invoked with bad length.");
264
0
  }
265
266
0
  if (!this->Seeded) {
267
0
    this->Seeded = true;
268
0
    std::srand(seed);
269
0
  }
270
271
0
  double alphabetSize = static_cast<double>(alphabet.size());
272
0
  std::vector<char> result;
273
0
  result.reserve(length + 1);
274
275
0
  for (std::size_t i = 0; i < length; i++) {
276
0
    auto index = static_cast<std::string::size_type>(
277
0
      alphabetSize * std::rand() / (RAND_MAX + 1.0));
278
0
    result.push_back(alphabet[index]);
279
0
  }
280
0
  result.push_back(0);
281
282
0
  this->String_ = result.data();
283
0
  return *this;
284
0
}
285
286
CMakeString& CMakeString::Timestamp(cm::string_view format, UTC utc)
287
0
{
288
0
  cmTimestamp timestamp;
289
0
  this->String_ = timestamp.CurrentTime(format, utc == UTC::Yes);
290
0
  return *this;
291
0
}
292
293
CMakeString& CMakeString::UUID(cm::string_view nameSpace, cm::string_view name,
294
                               UUIDType type, Case uuidCase)
295
0
{
296
0
#if !defined(CMAKE_BOOTSTRAP)
297
0
  cmUuid uuidGenerator;
298
0
  std::vector<unsigned char> uuidNamespace;
299
0
  std::string uuid;
300
301
0
  if (!uuidGenerator.StringToBinary(nameSpace, uuidNamespace)) {
302
0
    throw std::invalid_argument("malformed NAMESPACE UUID");
303
0
  }
304
305
0
  if (type == UUIDType::MD5) {
306
0
    uuid = uuidGenerator.FromMd5(uuidNamespace, name);
307
0
  } else if (type == UUIDType::SHA1) {
308
0
    uuid = uuidGenerator.FromSha1(uuidNamespace, name);
309
0
  }
310
311
0
  if (uuid.empty()) {
312
0
    throw std::runtime_error("generation failed");
313
0
  }
314
315
0
  if (uuidCase == Case::Upper) {
316
0
    uuid = cmSystemTools::UpperCase(uuid);
317
0
  }
318
319
0
  this->String_ = std::move(uuid);
320
0
  return *this;
321
#else
322
  throw std::runtime_error("not available during bootstrap");
323
#endif
324
0
}
325
326
}