Coverage Report

Created: 2026-07-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmList.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 "cmList.h"
7
8
#include <algorithm>
9
#include <cstddef>
10
#include <functional>
11
#include <iterator>
12
#include <set>
13
#include <stdexcept>
14
#include <utility>
15
16
#include <cm/memory>
17
#include <cm/optional>
18
19
#include "cmsys/RegularExpression.hxx"
20
21
#include "cmAlgorithms.h"
22
#include "cmExecutionStatus.h"
23
#include "cmGeneratorExpression.h"
24
#include "cmListFileCache.h"
25
#include "cmMakefile.h"
26
#include "cmRange.h"
27
#include "cmState.h"
28
#include "cmStateTypes.h"
29
#include "cmStringAlgorithms.h"
30
#include "cmStringReplaceHelper.h"
31
#include "cmSystemTools.h"
32
#include "cmValue.h"
33
34
cm::string_view cmList::element_separator{ ";" };
35
36
cmList cmList::sublist(size_type pos, size_type length) const
37
0
{
38
0
  if (pos >= this->Values.size()) {
39
0
    throw std::out_of_range(cmStrCat(
40
0
      "begin index: ", pos, " is out of range 0 - ", this->Values.size() - 1));
41
0
  }
42
43
0
  size_type count = (length == npos || pos + length > this->size())
44
0
    ? this->size()
45
0
    : pos + length;
46
0
  return this->sublist(this->begin() + pos, this->begin() + count);
47
0
}
48
49
cmList::size_type cmList::find(cm::string_view value) const
50
0
{
51
0
  auto res = std::find(this->Values.begin(), this->Values.end(), value);
52
0
  if (res == this->Values.end()) {
53
0
    return npos;
54
0
  }
55
56
0
  return std::distance(this->Values.begin(), res);
57
0
}
58
59
cmList& cmList::remove_duplicates()
60
0
{
61
0
  auto newEnd = cmRemoveDuplicates(this->Values);
62
0
  this->Values.erase(newEnd, this->Values.end());
63
64
0
  return *this;
65
0
}
66
67
namespace {
68
class MatchesRegex
69
{
70
public:
71
  MatchesRegex(cmsys::RegularExpression& regex, cmList::FilterMode mode)
72
0
    : Regex(regex)
73
0
    , IncludeMatches(mode == cmList::FilterMode::INCLUDE)
74
0
  {
75
0
  }
76
77
  bool operator()(std::string const& target)
78
0
  {
79
0
    return this->Regex.find(target) ^ this->IncludeMatches;
80
0
  }
81
82
private:
83
  cmsys::RegularExpression& Regex;
84
  bool const IncludeMatches;
85
};
86
87
// Hash of call site (FilePath:Line) for unique variable names across recursive
88
// calls.
89
std::string OutputVarFor(cm::string_view prefix, cmMakefile& makefile)
90
0
{
91
0
  cmListFileContext context = makefile.GetBacktrace().Top();
92
0
  std::size_t hash =
93
0
    std::hash<std::string>{}(cmStrCat(context.FilePath, ":", context.Line));
94
0
  return cmStrCat(prefix, hash, "_");
95
0
}
96
97
void RequireFunction(cmMakefile const& makefile,
98
                     std::string const& functionName,
99
                     std::string const& errorPrefix)
100
0
{
101
0
  cm::optional<cmStateEnums::CommandType> type =
102
0
    makefile.GetState()->GetCommandType(functionName);
103
0
  if (!type) {
104
0
    throw cmList::transform_error(
105
0
      cmStrCat(errorPrefix, ": unknown function \"", functionName, "\"."));
106
0
  }
107
0
  if (*type == cmStateEnums::CommandType::Macro) {
108
0
    throw cmList::transform_error(
109
0
      cmStrCat(errorPrefix, ": macro \"", functionName,
110
0
               "\" may not be used here;"
111
0
               " define it as a function() instead."));
112
0
  }
113
0
}
114
115
class PredicateEvaluator
116
{
117
public:
118
  PredicateEvaluator(std::string functionName, cmMakefile& makefile,
119
                     std::string errorPrefix = "sub-command TRANSFORM, "
120
                                               "selector PREDICATE")
121
0
    : FunctionName(std::move(functionName))
122
0
    , Makefile(&makefile)
123
0
    , ErrorPrefix(std::move(errorPrefix))
124
0
    , OutputVar(OutputVarFor("_cmake_predicate_out_", makefile))
125
0
  {
126
0
    RequireFunction(makefile, this->FunctionName, this->ErrorPrefix);
127
0
  }
128
129
  bool operator()(std::string const& value)
130
0
  {
131
0
    this->Makefile->RemoveDefinition(this->OutputVar);
132
133
0
    cmListFileContext context = this->Makefile->GetBacktrace().Top();
134
0
    std::vector<cmListFileArgument> funcArgs;
135
0
    funcArgs.emplace_back(value, cmListFileArgument::Quoted, context.Line);
136
0
    funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
137
0
                          context.Line);
138
0
    cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
139
0
                             std::move(funcArgs) };
140
141
0
    cmExecutionStatus status(*this->Makefile);
142
0
    if (!this->Makefile->ExecuteCommand(func, status) ||
143
0
        status.GetNestedError()) {
144
0
      throw cmList::transform_error(
145
0
        cmStrCat(this->ErrorPrefix, ": function \"", this->FunctionName,
146
0
                 "\" failed during execution."));
147
0
    }
148
149
0
    cmValue result = this->Makefile->GetDefinition(this->OutputVar);
150
0
    if (!result) {
151
0
      throw cmList::transform_error(
152
0
        cmStrCat(this->ErrorPrefix, ": function \"", this->FunctionName,
153
0
                 "\" did not set the output variable."));
154
0
    }
155
156
0
    bool boolResult = cmIsOn(*result);
157
0
    this->Makefile->RemoveDefinition(this->OutputVar);
158
0
    return boolResult;
159
0
  }
160
161
private:
162
  std::string FunctionName;
163
  cmMakefile* Makefile = nullptr;
164
  std::string ErrorPrefix;
165
  std::string OutputVar;
166
};
167
168
class MatchesPredicate
169
{
170
public:
171
  MatchesPredicate(PredicateEvaluator& evaluator, cmList::FilterMode mode)
172
0
    : Evaluator(evaluator)
173
0
    , IncludeMatches(mode == cmList::FilterMode::INCLUDE)
174
0
  {
175
0
  }
176
177
  bool operator()(std::string const& target)
178
0
  {
179
0
    return this->Evaluator(target) ^ this->IncludeMatches;
180
0
  }
181
182
private:
183
  PredicateEvaluator& Evaluator;
184
  bool IncludeMatches;
185
};
186
187
class ComparatorEvaluator
188
{
189
public:
190
  ComparatorEvaluator(std::string functionName, cmMakefile& makefile)
191
0
    : FunctionName(std::move(functionName))
192
0
    , Makefile(&makefile)
193
0
    , OutputVar(OutputVarFor("_cmake_comparator_out_", makefile))
194
0
  {
195
0
    RequireFunction(makefile, this->FunctionName,
196
0
                    "sub-command SORT, COMPARATOR");
197
0
  }
198
199
  bool operator()(std::string const& a, std::string const& b)
200
0
  {
201
0
    this->Makefile->RemoveDefinition(this->OutputVar);
202
203
0
    cmListFileContext context = this->Makefile->GetBacktrace().Top();
204
0
    std::vector<cmListFileArgument> funcArgs;
205
0
    funcArgs.emplace_back(a, cmListFileArgument::Quoted, context.Line);
206
0
    funcArgs.emplace_back(b, cmListFileArgument::Quoted, context.Line);
207
0
    funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
208
0
                          context.Line);
209
0
    cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
210
0
                             std::move(funcArgs) };
211
212
0
    cmExecutionStatus status(*this->Makefile);
213
0
    if (!this->Makefile->ExecuteCommand(func, status) ||
214
0
        status.GetNestedError()) {
215
0
      throw cmList::transform_error(
216
0
        cmStrCat("sub-command SORT, COMPARATOR: function \"",
217
0
                 this->FunctionName, "\" failed during execution."));
218
0
    }
219
220
0
    cmValue result = this->Makefile->GetDefinition(this->OutputVar);
221
0
    if (!result) {
222
0
      throw cmList::transform_error(
223
0
        cmStrCat("sub-command SORT, COMPARATOR: function \"",
224
0
                 this->FunctionName, "\" did not set the output variable."));
225
0
    }
226
227
0
    bool boolResult = cmIsOn(*result);
228
0
    this->Makefile->RemoveDefinition(this->OutputVar);
229
0
    return boolResult;
230
0
  }
231
232
private:
233
  std::string FunctionName;
234
  cmMakefile* Makefile = nullptr;
235
  std::string OutputVar;
236
};
237
}
238
239
cmList& cmList::filter(cm::string_view pattern, FilterMode mode)
240
0
{
241
0
  cmsys::RegularExpression regex(std::string{ pattern });
242
0
  if (!regex.is_valid()) {
243
0
    throw std::invalid_argument(
244
0
      cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"",
245
0
               pattern, "\"."));
246
0
  }
247
248
0
  auto it = std::remove_if(this->Values.begin(), this->Values.end(),
249
0
                           MatchesRegex{ regex, mode });
250
0
  this->Values.erase(it, this->Values.end());
251
252
0
  return *this;
253
0
}
254
255
cmList& cmList::filter(std::string const& functionName, FilterMode mode,
256
                       cmMakefile& makefile)
257
0
{
258
0
  try {
259
0
    PredicateEvaluator evaluator(functionName, makefile,
260
0
                                 "sub-command FILTER, mode PREDICATE");
261
262
0
    auto it = std::remove_if(this->Values.begin(), this->Values.end(),
263
0
                             MatchesPredicate{ evaluator, mode });
264
0
    this->Values.erase(it, this->Values.end());
265
0
  } catch (transform_error& e) {
266
0
    throw std::invalid_argument(e.what());
267
0
  }
268
269
0
  return *this;
270
0
}
271
272
namespace {
273
class StringSorter
274
{
275
protected:
276
  using StringFilter = std::function<std::string(std::string const&)>;
277
278
  using OrderMode = cmList::SortConfiguration::OrderMode;
279
  using CompareMethod = cmList::SortConfiguration::CompareMethod;
280
  using CaseSensitivity = cmList::SortConfiguration::CaseSensitivity;
281
282
  StringFilter GetCompareFilter(CompareMethod compare)
283
0
  {
284
0
    return (compare == CompareMethod::FILE_BASENAME)
285
0
      ? cmSystemTools::GetFilenameName
286
0
      : nullptr;
287
0
  }
288
289
  StringFilter GetCaseFilter(CaseSensitivity sensitivity)
290
0
  {
291
0
    return (sensitivity == CaseSensitivity::INSENSITIVE)
292
0
      ? cmsys::SystemTools::LowerCase
293
0
      : nullptr;
294
0
  }
295
296
  using ComparisonFunction =
297
    std::function<bool(std::string const&, std::string const&)>;
298
  ComparisonFunction GetComparisonFunction(CompareMethod compare)
299
0
  {
300
0
    if (compare == CompareMethod::NATURAL) {
301
0
      return std::function<bool(std::string const&, std::string const&)>(
302
0
        [](std::string const& x, std::string const& y) {
303
0
          return cmSystemTools::strverscmp(x, y) < 0;
304
0
        });
305
0
    }
306
0
    return std::function<bool(std::string const&, std::string const&)>(
307
0
      [](std::string const& x, std::string const& y) { return x < y; });
308
0
  }
309
310
public:
311
  StringSorter(cmList::SortConfiguration config)
312
0
    : Filters{ this->GetCompareFilter(config.Compare),
313
0
               this->GetCaseFilter(config.Case) }
314
0
    , SortMethod(this->GetComparisonFunction(config.Compare))
315
0
    , Descending(config.Order == OrderMode::DESCENDING)
316
0
  {
317
0
  }
318
319
  StringSorter(cmList::SortConfiguration config, ComparisonFunction comparator)
320
0
    : Filters{ nullptr, this->GetCaseFilter(config.Case) }
321
0
    , SortMethod(std::move(comparator))
322
0
    , Descending(config.Order == OrderMode::DESCENDING)
323
0
  {
324
0
  }
325
326
  std::string ApplyFilter(std::string const& argument)
327
0
  {
328
0
    std::string result = argument;
329
0
    for (auto const& filter : this->Filters) {
330
0
      if (filter) {
331
0
        result = filter(result);
332
0
      }
333
0
    }
334
0
    return result;
335
0
  }
336
337
  bool operator()(std::string const& a, std::string const& b)
338
0
  {
339
0
    std::string af = this->ApplyFilter(a);
340
0
    std::string bf = this->ApplyFilter(b);
341
0
    bool result;
342
0
    if (this->Descending) {
343
0
      result = this->SortMethod(bf, af);
344
0
    } else {
345
0
      result = this->SortMethod(af, bf);
346
0
    }
347
0
    return result;
348
0
  }
349
350
private:
351
  StringFilter Filters[2] = { nullptr, nullptr };
352
  ComparisonFunction SortMethod;
353
  bool Descending;
354
};
355
}
356
357
0
cmList::SortConfiguration::SortConfiguration() = default;
358
359
cmList& cmList::sort(SortConfiguration cfg)
360
0
{
361
0
  SortConfiguration config{ cfg };
362
363
0
  if (config.Order == SortConfiguration::OrderMode::DEFAULT) {
364
0
    config.Order = SortConfiguration::OrderMode::ASCENDING;
365
0
  }
366
0
  if (config.Compare == SortConfiguration::CompareMethod::DEFAULT) {
367
0
    config.Compare = SortConfiguration::CompareMethod::STRING;
368
0
  }
369
0
  if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) {
370
0
    config.Case = SortConfiguration::CaseSensitivity::SENSITIVE;
371
0
  }
372
373
0
  if ((config.Compare == SortConfiguration::CompareMethod::STRING) &&
374
0
      (config.Case == SortConfiguration::CaseSensitivity::SENSITIVE) &&
375
0
      (config.Order == SortConfiguration::OrderMode::ASCENDING)) {
376
0
    std::sort(this->Values.begin(), this->Values.end());
377
0
  } else {
378
0
    StringSorter sorter(config);
379
0
    std::sort(this->Values.begin(), this->Values.end(), sorter);
380
0
  }
381
382
0
  return *this;
383
0
}
384
385
cmList& cmList::sort(
386
  SortConfiguration cfg,
387
  std::function<bool(std::string const&, std::string const&)> comparator)
388
0
{
389
0
  SortConfiguration config{ cfg };
390
391
0
  if (config.Order == SortConfiguration::OrderMode::DEFAULT) {
392
0
    config.Order = SortConfiguration::OrderMode::ASCENDING;
393
0
  }
394
0
  if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) {
395
0
    config.Case = SortConfiguration::CaseSensitivity::SENSITIVE;
396
0
  }
397
398
0
  try {
399
0
    StringSorter sorter(
400
0
      config, [&comparator](std::string const& a, std::string const& b) {
401
0
        bool result = comparator(a, b);
402
0
        if (result && comparator(b, a)) {
403
0
          throw cmList::transform_error(
404
0
            "sub-command SORT, COMPARATOR: function does not induce a strict "
405
0
            "weak ordering. The comparator returned TRUE for both (a, b) and "
406
0
            "(b, a).");
407
0
        }
408
0
        return result;
409
0
      });
410
0
    std::sort(this->Values.begin(), this->Values.end(), sorter);
411
0
  } catch (transform_error& e) {
412
0
    throw std::invalid_argument(e.what());
413
0
  }
414
415
0
  return *this;
416
0
}
417
418
cmList& cmList::sort(SortConfiguration cfg, cmMakefile& makefile)
419
0
{
420
0
  try {
421
0
    ComparatorEvaluator evaluator(cfg.ComparatorFunction, makefile);
422
0
    return this->sort(
423
0
      cfg, [&evaluator](std::string const& a, std::string const& b) {
424
0
        return evaluator(a, b);
425
0
      });
426
0
  } catch (transform_error& e) {
427
0
    throw std::invalid_argument(e.what());
428
0
  }
429
0
}
430
431
namespace {
432
using transform_type = std::function<std::string(std::string const&)>;
433
using transform_error = cmList::transform_error;
434
435
class TransformSelector : public cmList::TransformSelector
436
{
437
public:
438
0
  ~TransformSelector() override = default;
439
440
  std::string Tag;
441
442
0
  std::string const& GetTag() override { return this->Tag; }
443
444
  virtual bool Validate(std::size_t count = 0) = 0;
445
446
  virtual bool InSelection(std::string const&) = 0;
447
448
  virtual void Transform(cmList::container_type& list,
449
                         transform_type const& transform)
450
0
  {
451
0
    std::transform(list.begin(), list.end(), list.begin(), transform);
452
0
  }
453
454
  // Return, for each element, whether the selector selects it via InSelection.
455
  virtual std::vector<bool> Selection(cmList::container_type const& list)
456
0
  {
457
0
    std::vector<bool> selected;
458
0
    selected.reserve(list.size());
459
0
    for (auto const& value : list) {
460
0
      selected.push_back(this->InSelection(value));
461
0
    }
462
0
    return selected;
463
0
  }
464
465
protected:
466
  TransformSelector(std::string&& tag)
467
0
    : Tag(std::move(tag))
468
0
  {
469
0
  }
470
};
471
472
class TransformNoSelector : public TransformSelector
473
{
474
public:
475
  TransformNoSelector()
476
0
    : TransformSelector("NO SELECTOR")
477
0
  {
478
0
  }
479
480
0
  bool Validate(std::size_t) override { return true; }
481
482
0
  bool InSelection(std::string const&) override { return true; }
483
};
484
class TransformSelectorRegex : public TransformSelector
485
{
486
public:
487
  TransformSelectorRegex(std::string const& regex)
488
0
    : TransformSelector("REGEX")
489
0
    , Regex(regex)
490
0
  {
491
0
  }
492
  TransformSelectorRegex(std::string&& regex)
493
0
    : TransformSelector("REGEX")
494
0
    , Regex(regex)
495
0
  {
496
0
  }
497
498
0
  bool Validate(std::size_t) override { return this->Regex.is_valid(); }
499
500
  bool InSelection(std::string const& value) override
501
0
  {
502
0
    return this->Regex.find(value);
503
0
  }
504
505
  cmsys::RegularExpression Regex;
506
};
507
class TransformSelectorPredicate : public TransformSelector
508
{
509
public:
510
  TransformSelectorPredicate(std::string const& functionName,
511
                             cmMakefile& makefile)
512
0
    : TransformSelector("PREDICATE")
513
0
    , Evaluator(functionName, makefile)
514
0
  {
515
0
  }
516
517
0
  bool Validate(std::size_t) override { return true; }
518
519
  bool InSelection(std::string const& value) override
520
0
  {
521
0
    return this->Evaluator(value);
522
0
  }
523
524
private:
525
  PredicateEvaluator Evaluator;
526
};
527
class TransformSelectorIndexes : public TransformSelector
528
{
529
public:
530
  std::vector<index_type> Indexes;
531
532
0
  bool InSelection(std::string const&) override { return true; }
533
534
  void Transform(std::vector<std::string>& list,
535
                 transform_type const& transform) override
536
0
  {
537
0
    this->Validate(list.size());
538
539
0
    for (auto index : this->Indexes) {
540
0
      list[index] = transform(list[index]);
541
0
    }
542
0
  }
543
544
  // Select the computed Indexes; Validate throws transform_error on an
545
  // out-of-range index.
546
  std::vector<bool> Selection(cmList::container_type const& list) override
547
0
  {
548
0
    this->Validate(list.size());
549
550
0
    std::vector<bool> selected(list.size(), false);
551
0
    for (auto index : this->Indexes) {
552
0
      selected[index] = true;
553
0
    }
554
0
    return selected;
555
0
  }
556
557
protected:
558
  TransformSelectorIndexes(std::string&& tag)
559
0
    : TransformSelector(std::move(tag))
560
0
  {
561
0
  }
562
  TransformSelectorIndexes(std::string&& tag,
563
                           std::vector<index_type> const& indexes)
564
0
    : TransformSelector(std::move(tag))
565
0
    , Indexes(indexes)
566
0
  {
567
0
  }
568
  TransformSelectorIndexes(std::string&& tag,
569
                           std::vector<index_type>&& indexes)
570
0
    : TransformSelector(std::move(tag))
571
0
    , Indexes(indexes)
572
0
  {
573
0
  }
574
575
  index_type NormalizeIndex(index_type index, std::size_t count)
576
0
  {
577
0
    if (index < 0) {
578
0
      index = static_cast<index_type>(count) + index;
579
0
    }
580
0
    if (index < 0 || count <= static_cast<std::size_t>(index)) {
581
0
      throw transform_error(cmStrCat(
582
0
        "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index,
583
0
        " out of range (-", count, ", ", count - 1, ")."));
584
0
    }
585
0
    return index;
586
0
  }
587
};
588
class TransformSelectorAt : public TransformSelectorIndexes
589
{
590
public:
591
  TransformSelectorAt(std::vector<index_type> const& indexes)
592
0
    : TransformSelectorIndexes("AT", indexes)
593
0
  {
594
0
  }
595
  TransformSelectorAt(std::vector<index_type>&& indexes)
596
0
    : TransformSelectorIndexes("AT", std::move(indexes))
597
0
  {
598
0
  }
599
600
  bool Validate(std::size_t count) override
601
0
  {
602
0
    decltype(this->Indexes) indexes;
603
604
0
    for (auto index : this->Indexes) {
605
0
      indexes.push_back(this->NormalizeIndex(index, count));
606
0
    }
607
0
    this->Indexes = std::move(indexes);
608
609
0
    return true;
610
0
  }
611
};
612
class TransformSelectorFor : public TransformSelectorIndexes
613
{
614
public:
615
  TransformSelectorFor(index_type start, index_type stop, index_type step)
616
0
    : TransformSelectorIndexes("FOR")
617
0
    , Start(start)
618
0
    , Stop(stop)
619
0
    , Step(step)
620
0
  {
621
0
  }
622
623
  bool Validate(std::size_t count) override
624
0
  {
625
0
    this->Start = this->NormalizeIndex(this->Start, count);
626
0
    this->Stop = this->NormalizeIndex(this->Stop, count);
627
628
    // Does stepping move us further from the end?
629
0
    if (this->Start > this->Stop) {
630
0
      throw transform_error(
631
0
        cmStrCat("sub-command TRANSFORM, selector FOR "
632
0
                 "expects <start> to be no greater than <stop> (",
633
0
                 this->Start, " > ", this->Stop, ')'));
634
0
    }
635
636
    // compute indexes
637
0
    auto size = (this->Stop - this->Start + 1) / this->Step;
638
0
    if ((this->Stop - this->Start + 1) % this->Step != 0) {
639
0
      size += 1;
640
0
    }
641
642
0
    this->Indexes.resize(size);
643
0
    auto start = this->Start;
644
0
    auto step = this->Step;
645
0
    std::generate(this->Indexes.begin(), this->Indexes.end(),
646
0
                  [&start, step]() -> index_type {
647
0
                    auto r = start;
648
0
                    start += step;
649
0
                    return r;
650
0
                  });
651
652
0
    return true;
653
0
  }
654
655
private:
656
  index_type Start, Stop, Step;
657
};
658
659
class TransformAction
660
{
661
public:
662
0
  virtual ~TransformAction() = default;
663
664
0
  void Initialize(TransformSelector* selector) { this->Selector = selector; }
665
0
  virtual void Initialize(TransformSelector*, std::string const&) {}
666
  virtual void Initialize(TransformSelector*, std::string const&,
667
                          std::string const&)
668
0
  {
669
0
  }
670
  virtual void Initialize(TransformSelector* selector,
671
                          std::vector<std::string> const&)
672
0
  {
673
0
    this->Initialize(selector);
674
0
  }
675
676
  virtual std::string operator()(std::string const& s) = 0;
677
678
protected:
679
  TransformSelector* Selector;
680
};
681
class TransformActionAppend : public TransformAction
682
{
683
public:
684
  using TransformAction::Initialize;
685
686
  void Initialize(TransformSelector* selector,
687
                  std::string const& append) override
688
0
  {
689
0
    TransformAction::Initialize(selector);
690
0
    this->Append = append;
691
0
  }
692
  void Initialize(TransformSelector* selector,
693
                  std::vector<std::string> const& append) override
694
0
  {
695
0
    this->Initialize(selector, append.front());
696
0
  }
697
698
  std::string operator()(std::string const& s) override
699
0
  {
700
0
    if (this->Selector->InSelection(s)) {
701
0
      return cmStrCat(s, this->Append);
702
0
    }
703
704
0
    return s;
705
0
  }
706
707
private:
708
  std::string Append;
709
};
710
class TransformActionPrepend : public TransformAction
711
{
712
public:
713
  using TransformAction::Initialize;
714
715
  void Initialize(TransformSelector* selector,
716
                  std::string const& prepend) override
717
0
  {
718
0
    TransformAction::Initialize(selector);
719
0
    this->Prepend = prepend;
720
0
  }
721
  void Initialize(TransformSelector* selector,
722
                  std::vector<std::string> const& prepend) override
723
0
  {
724
0
    this->Initialize(selector, prepend.front());
725
0
  }
726
727
  std::string operator()(std::string const& s) override
728
0
  {
729
0
    if (this->Selector->InSelection(s)) {
730
0
      return cmStrCat(this->Prepend, s);
731
0
    }
732
733
0
    return s;
734
0
  }
735
736
private:
737
  std::string Prepend;
738
};
739
class TransformActionToUpper : public TransformAction
740
{
741
public:
742
  std::string operator()(std::string const& s) override
743
0
  {
744
0
    if (this->Selector->InSelection(s)) {
745
0
      return cmSystemTools::UpperCase(s);
746
0
    }
747
748
0
    return s;
749
0
  }
750
};
751
class TransformActionToLower : public TransformAction
752
{
753
public:
754
  std::string operator()(std::string const& s) override
755
0
  {
756
0
    if (this->Selector->InSelection(s)) {
757
0
      return cmSystemTools::LowerCase(s);
758
0
    }
759
760
0
    return s;
761
0
  }
762
};
763
class TransformActionStrip : public TransformAction
764
{
765
public:
766
  std::string operator()(std::string const& s) override
767
0
  {
768
0
    if (this->Selector->InSelection(s)) {
769
0
      return cmTrimWhitespace(s);
770
0
    }
771
772
0
    return s;
773
0
  }
774
};
775
class TransformActionGenexStrip : public TransformAction
776
{
777
public:
778
  std::string operator()(std::string const& s) override
779
0
  {
780
0
    if (this->Selector->InSelection(s)) {
781
0
      return cmGeneratorExpression::Preprocess(
782
0
        s, cmGeneratorExpression::StripAllGeneratorExpressions);
783
0
    }
784
785
0
    return s;
786
0
  }
787
};
788
class TransformActionReplace : public TransformAction
789
{
790
public:
791
  using TransformAction::Initialize;
792
793
  void Initialize(TransformSelector* selector, std::string const& regex,
794
                  std::string const& replace) override
795
0
  {
796
0
    TransformAction::Initialize(selector);
797
0
    this->ReplaceHelper = cm::make_unique<cmStringReplaceHelper>(
798
0
      regex, replace, selector->Makefile);
799
800
0
    if (!this->ReplaceHelper->IsRegularExpressionValid()) {
801
0
      throw transform_error(
802
0
        cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
803
0
                 "regex \"",
804
0
                 regex, "\"."));
805
0
    }
806
0
    if (!this->ReplaceHelper->IsReplaceExpressionValid()) {
807
0
      throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
808
0
                                     this->ReplaceHelper->GetError(), '.'));
809
0
    }
810
0
  }
811
  void Initialize(TransformSelector* selector,
812
                  std::vector<std::string> const& args) override
813
0
  {
814
0
    this->Initialize(selector, args[0], args[1]);
815
0
  }
816
817
  std::string operator()(std::string const& s) override
818
0
  {
819
0
    if (this->Selector->InSelection(s)) {
820
      // Scan through the input for all matches.
821
0
      std::string output;
822
823
0
      if (!this->ReplaceHelper->Replace(s, output)) {
824
0
        throw transform_error(
825
0
          cmStrCat("sub-command TRANSFORM, action REPLACE: ",
826
0
                   this->ReplaceHelper->GetError(), '.'));
827
0
      }
828
829
0
      return output;
830
0
    }
831
832
0
    return s;
833
0
  }
834
835
private:
836
  std::unique_ptr<cmStringReplaceHelper> ReplaceHelper;
837
};
838
839
class TransformActionApply : public TransformAction
840
{
841
public:
842
  using TransformAction::Initialize;
843
844
  void Initialize(TransformSelector* selector, std::string const& functionName,
845
                  cmMakefile& makefile)
846
0
  {
847
0
    TransformAction::Initialize(selector);
848
0
    this->FunctionName = functionName;
849
0
    this->Makefile = &makefile;
850
0
    this->OutputVar = OutputVarFor("_cmake_transform_apply_out_", makefile);
851
852
0
    RequireFunction(makefile, this->FunctionName,
853
0
                    "sub-command TRANSFORM, action APPLY");
854
0
  }
855
856
  void Initialize(TransformSelector* /*selector*/,
857
                  std::vector<std::string> const& /*args*/) override
858
0
  {
859
    // This overload must not be used for APPLY — it lacks cmMakefile context.
860
0
    throw transform_error(
861
0
      "sub-command TRANSFORM, action APPLY requires cmMakefile context.");
862
0
  }
863
864
  std::string operator()(std::string const& s) override
865
0
  {
866
0
    if (!this->Selector->InSelection(s)) {
867
0
      return s;
868
0
    }
869
870
    // Unset the output variable before calling
871
0
    this->Makefile->RemoveDefinition(this->OutputVar);
872
873
    // Build the function call: functionName(s, outputVar)
874
0
    cmListFileContext context = this->Makefile->GetBacktrace().Top();
875
0
    std::vector<cmListFileArgument> funcArgs;
876
0
    funcArgs.emplace_back(s, cmListFileArgument::Quoted, context.Line);
877
0
    funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
878
0
                          context.Line);
879
0
    cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
880
0
                             std::move(funcArgs) };
881
882
0
    cmExecutionStatus status(*this->Makefile);
883
0
    if (!this->Makefile->ExecuteCommand(func, status) ||
884
0
        status.GetNestedError()) {
885
0
      throw transform_error(
886
0
        cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
887
0
                 this->FunctionName, "\" failed during execution."));
888
0
    }
889
890
    // Read back the output variable
891
0
    cmValue result = this->Makefile->GetDefinition(this->OutputVar);
892
0
    if (!result) {
893
0
      throw transform_error(
894
0
        cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
895
0
                 this->FunctionName, "\" did not set the output variable."));
896
0
    }
897
898
    // Copy the result before cleaning up (RemoveDefinition invalidates the
899
    // cmValue pointer).
900
0
    std::string output = *result;
901
902
    // Clean up
903
0
    this->Makefile->RemoveDefinition(this->OutputVar);
904
905
0
    return output;
906
0
  }
907
908
private:
909
  std::string FunctionName;
910
  cmMakefile* Makefile = nullptr;
911
  std::string OutputVar;
912
};
913
914
// Descriptor of action
915
// Arity: number of arguments required for the action
916
// Transform: Object implementing the action
917
struct ActionDescriptor
918
{
919
  ActionDescriptor(cmList::TransformAction action)
920
0
    : Action(action)
921
0
  {
922
0
  }
923
  ActionDescriptor(cmList::TransformAction action, std::string name,
924
                   std::size_t arity,
925
                   std::unique_ptr<TransformAction> transform)
926
0
    : Action(action)
927
0
    , Name(std::move(name))
928
0
    , Arity(arity)
929
0
    , Transform(std::move(transform))
930
0
  {
931
0
  }
932
933
0
  operator cmList::TransformAction() const { return this->Action; }
934
935
  cmList::TransformAction Action;
936
  std::string Name;
937
  std::size_t Arity = 0;
938
  std::unique_ptr<TransformAction> Transform;
939
};
940
941
// Build a set of supported actions.
942
using ActionDescriptorSet = std::set<
943
  ActionDescriptor,
944
  std::function<bool(cmList::TransformAction, cmList::TransformAction)>>;
945
946
ActionDescriptorSet Descriptors([](cmList::TransformAction x,
947
0
                                   cmList::TransformAction y) {
948
0
  return x < y;
949
0
});
950
951
ActionDescriptorSet::iterator TransformConfigure(
952
  cmList::TransformAction action,
953
  std::unique_ptr<cmList::TransformSelector>& selector, std::size_t arity)
954
0
{
955
0
  if (Descriptors.empty()) {
956
0
    Descriptors.emplace(cmList::TransformAction::APPEND, "APPEND", 1,
957
0
                        cm::make_unique<TransformActionAppend>());
958
0
    Descriptors.emplace(cmList::TransformAction::PREPEND, "PREPEND", 1,
959
0
                        cm::make_unique<TransformActionPrepend>());
960
0
    Descriptors.emplace(cmList::TransformAction::TOUPPER, "TOUPPER", 0,
961
0
                        cm::make_unique<TransformActionToUpper>());
962
0
    Descriptors.emplace(cmList::TransformAction::TOLOWER, "TOLOWER", 0,
963
0
                        cm::make_unique<TransformActionToLower>());
964
0
    Descriptors.emplace(cmList::TransformAction::STRIP, "STRIP", 0,
965
0
                        cm::make_unique<TransformActionStrip>());
966
0
    Descriptors.emplace(cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0,
967
0
                        cm::make_unique<TransformActionGenexStrip>());
968
0
    Descriptors.emplace(cmList::TransformAction::REPLACE, "REPLACE", 2,
969
0
                        cm::make_unique<TransformActionReplace>());
970
0
    Descriptors.emplace(cmList::TransformAction::APPLY, "APPLY", 1,
971
0
                        cm::make_unique<TransformActionApply>());
972
0
  }
973
974
0
  auto descriptor = Descriptors.find(action);
975
0
  if (descriptor == Descriptors.end()) {
976
0
    throw transform_error(cmStrCat(" sub-command TRANSFORM, ",
977
0
                                   static_cast<int>(action),
978
0
                                   " invalid action."));
979
0
  }
980
981
0
  if (descriptor->Arity != arity) {
982
0
    throw transform_error(cmStrCat("sub-command TRANSFORM, action ",
983
0
                                   descriptor->Name, " expects ",
984
0
                                   descriptor->Arity, " argument(s)."));
985
0
  }
986
0
  if (!selector) {
987
0
    selector = cm::make_unique<TransformNoSelector>();
988
0
  }
989
990
0
  return descriptor;
991
0
}
992
}
993
994
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::New()
995
0
{
996
0
  return cm::make_unique<TransformNoSelector>();
997
0
}
998
999
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
1000
  std::initializer_list<index_type> indexes)
1001
0
{
1002
0
  return cm::make_unique<TransformSelectorAt>(
1003
0
    std::vector<index_type>{ indexes.begin(), indexes.end() });
1004
0
  ;
1005
0
}
1006
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
1007
  std::vector<index_type> const& indexes)
1008
0
{
1009
0
  return cm::make_unique<TransformSelectorAt>(indexes);
1010
0
}
1011
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
1012
  std::vector<index_type>&& indexes)
1013
0
{
1014
0
  return cm::make_unique<TransformSelectorAt>(std::move(indexes));
1015
0
}
1016
1017
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
1018
  std::initializer_list<index_type> indexes)
1019
0
{
1020
0
  if (indexes.size() < 2 || indexes.size() > 3) {
1021
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
1022
0
                          "expects 2 or 3 arguments");
1023
0
  }
1024
0
  if (indexes.size() == 3 && *(indexes.begin() + 2) < 0) {
1025
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
1026
0
                          "positive numeric value for <step>.");
1027
0
  }
1028
1029
0
  return cm::make_unique<TransformSelectorFor>(
1030
0
    *indexes.begin(), *(indexes.begin() + 1),
1031
0
    indexes.size() == 3 ? *(indexes.begin() + 2) : 1);
1032
0
}
1033
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
1034
  std::vector<index_type> const& indexes)
1035
0
{
1036
0
  if (indexes.size() < 2 || indexes.size() > 3) {
1037
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
1038
0
                          "expects 2 or 3 arguments");
1039
0
  }
1040
0
  if (indexes.size() == 3 && indexes[2] < 0) {
1041
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
1042
0
                          "positive numeric value for <step>.");
1043
0
  }
1044
1045
0
  return cm::make_unique<TransformSelectorFor>(
1046
0
    indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
1047
0
}
1048
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
1049
  std::vector<index_type>&& indexes)
1050
0
{
1051
0
  if (indexes.size() < 2 || indexes.size() > 3) {
1052
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
1053
0
                          "expects 2 or 3 arguments");
1054
0
  }
1055
0
  if (indexes.size() == 3 && indexes[2] < 0) {
1056
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
1057
0
                          "positive numeric value for <step>.");
1058
0
  }
1059
1060
0
  return cm::make_unique<TransformSelectorFor>(
1061
0
    indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
1062
0
}
1063
1064
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
1065
  std::string const& regex)
1066
0
{
1067
0
  std::unique_ptr<::TransformSelector> selector =
1068
0
    cm::make_unique<TransformSelectorRegex>(regex);
1069
0
  if (!selector->Validate()) {
1070
0
    throw transform_error(
1071
0
      cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
1072
0
               "regex \"",
1073
0
               regex, "\"."));
1074
0
  }
1075
  // weird construct to please all compilers
1076
0
  return std::unique_ptr<cmList::TransformSelector>(selector.release());
1077
0
}
1078
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
1079
  std::string&& regex)
1080
0
{
1081
0
  std::unique_ptr<::TransformSelector> selector =
1082
0
    cm::make_unique<TransformSelectorRegex>(std::move(regex));
1083
0
  if (!selector->Validate()) {
1084
0
    throw transform_error(
1085
0
      cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
1086
0
               "regex \"",
1087
0
               regex, "\"."));
1088
0
  }
1089
  // weird construct to please all compilers
1090
0
  return std::unique_ptr<cmList::TransformSelector>(selector.release());
1091
0
}
1092
1093
std::unique_ptr<cmList::TransformSelector>
1094
cmList::TransformSelector::NewPREDICATE(std::string const& functionName,
1095
                                        cmMakefile& makefile)
1096
0
{
1097
0
  return std::unique_ptr<cmList::TransformSelector>(
1098
0
    new TransformSelectorPredicate(functionName, makefile));
1099
0
}
1100
1101
cmList& cmList::transform(TransformAction action,
1102
                          std::unique_ptr<TransformSelector> selector)
1103
0
{
1104
0
  auto descriptor = TransformConfigure(action, selector, 0);
1105
1106
0
  descriptor->Transform->Initialize(
1107
0
    static_cast<::TransformSelector*>(selector.get()));
1108
1109
0
  static_cast<::TransformSelector&>(*selector).Transform(
1110
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
1111
0
      return (*descriptor->Transform)(s);
1112
0
    });
1113
1114
0
  return *this;
1115
0
}
1116
1117
cmList& cmList::transform(TransformAction action, std::string const& arg,
1118
                          std::unique_ptr<TransformSelector> selector)
1119
0
{
1120
0
  auto descriptor = TransformConfigure(action, selector, 1);
1121
1122
0
  descriptor->Transform->Initialize(
1123
0
    static_cast<::TransformSelector*>(selector.get()), arg);
1124
1125
0
  static_cast<::TransformSelector&>(*selector).Transform(
1126
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
1127
0
      return (*descriptor->Transform)(s);
1128
0
    });
1129
1130
0
  return *this;
1131
0
}
1132
1133
cmList& cmList::transform(TransformAction action, std::string const& arg1,
1134
                          std::string const& arg2,
1135
                          std::unique_ptr<TransformSelector> selector)
1136
0
{
1137
0
  auto descriptor = TransformConfigure(action, selector, 2);
1138
1139
0
  descriptor->Transform->Initialize(
1140
0
    static_cast<::TransformSelector*>(selector.get()), arg1, arg2);
1141
1142
0
  static_cast<::TransformSelector&>(*selector).Transform(
1143
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
1144
0
      return (*descriptor->Transform)(s);
1145
0
    });
1146
1147
0
  return *this;
1148
0
}
1149
1150
cmList& cmList::transform(TransformAction action,
1151
                          std::vector<std::string> const& args,
1152
                          std::unique_ptr<TransformSelector> selector)
1153
0
{
1154
0
  auto descriptor = TransformConfigure(action, selector, args.size());
1155
1156
0
  descriptor->Transform->Initialize(
1157
0
    static_cast<::TransformSelector*>(selector.get()), args);
1158
1159
0
  static_cast<::TransformSelector&>(*selector).Transform(
1160
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
1161
0
      return (*descriptor->Transform)(s);
1162
0
    });
1163
1164
0
  return *this;
1165
0
}
1166
1167
cmList& cmList::transform(TransformAction action, std::string const& arg,
1168
                          cmMakefile& makefile,
1169
                          std::unique_ptr<TransformSelector> selector)
1170
0
{
1171
  // Validate action and arity via the static registry.
1172
0
  TransformConfigure(action, selector, 1);
1173
1174
  // Create a local instance rather than reusing the singleton from
1175
  // Descriptors.  A user function invoked by APPLY may itself call
1176
  // list(TRANSFORM ... APPLY ...), which would clobber a shared instance.
1177
0
  TransformActionApply applyAction;
1178
0
  applyAction.Initialize(static_cast<::TransformSelector*>(selector.get()),
1179
0
                         arg, makefile);
1180
1181
0
  static_cast<::TransformSelector&>(*selector).Transform(
1182
0
    this->Values, [&applyAction](std::string const& s) -> std::string {
1183
0
      return applyAction(s);
1184
0
    });
1185
1186
0
  return *this;
1187
0
}
1188
1189
std::vector<bool> cmList::GetTransformSelection(
1190
  cmList::TransformSelector& selector) const
1191
0
{
1192
0
  return static_cast<::TransformSelector&>(selector).Selection(this->Values);
1193
0
}
1194
1195
std::string& cmList::append(std::string& list, std::string&& value)
1196
0
{
1197
0
  if (list.empty()) {
1198
0
    list = std::move(value);
1199
0
  } else {
1200
0
    list += cmStrCat(cmList::element_separator, value);
1201
0
  }
1202
1203
0
  return list;
1204
0
}
1205
std::string& cmList::append(std::string& list, cm::string_view value)
1206
0
{
1207
0
  return cmList::append(list, std::string{ value });
1208
0
}
1209
1210
std::string& cmList::prepend(std::string& list, std::string&& value)
1211
0
{
1212
0
  if (list.empty()) {
1213
0
    list = std::move(value);
1214
0
  } else {
1215
0
    list.insert(0, cmStrCat(value, cmList::element_separator));
1216
0
  }
1217
1218
0
  return list;
1219
0
}
1220
std::string& cmList::prepend(std::string& list, cm::string_view value)
1221
0
{
1222
0
  return cmList::prepend(list, std::string{ value });
1223
0
}
1224
1225
cmList::size_type cmList::ComputeIndex(index_type pos, bool boundCheck) const
1226
0
{
1227
0
  if (boundCheck) {
1228
0
    if (this->Values.empty()) {
1229
0
      throw std::out_of_range(
1230
0
        cmStrCat("index: ", pos, " out of range (0, 0)"));
1231
0
    }
1232
1233
0
    auto index = pos;
1234
0
    if (!this->Values.empty()) {
1235
0
      auto length = this->Values.size();
1236
0
      if (index < 0) {
1237
0
        index = static_cast<index_type>(length) + index;
1238
0
      }
1239
0
      if (index < 0 || length <= static_cast<size_type>(index)) {
1240
0
        throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
1241
0
                                         this->Values.size(), ", ",
1242
0
                                         this->Values.size() - 1, ')'));
1243
0
      }
1244
0
    }
1245
0
    return index;
1246
0
  }
1247
1248
0
  return pos < 0 ? this->Values.size() + pos : pos;
1249
0
}
1250
cmList::size_type cmList::ComputeInsertIndex(index_type pos,
1251
                                             bool boundCheck) const
1252
0
{
1253
0
  if (boundCheck) {
1254
0
    if (this->Values.empty() && pos != 0) {
1255
0
      throw std::out_of_range(
1256
0
        cmStrCat("index: ", pos, " out of range (0, 0)"));
1257
0
    }
1258
1259
0
    auto index = pos;
1260
0
    if (!this->Values.empty()) {
1261
0
      auto length = this->Values.size();
1262
0
      if (index < 0) {
1263
0
        index = static_cast<index_type>(length) + index;
1264
0
      }
1265
0
      if (index < 0 || length < static_cast<size_type>(index)) {
1266
0
        throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
1267
0
                                         this->Values.size(), ", ",
1268
0
                                         this->Values.size(), ')'));
1269
0
      }
1270
0
    }
1271
0
    return index;
1272
0
  }
1273
1274
0
  return pos < 0 ? this->Values.size() + pos : pos;
1275
0
}
1276
1277
cmList cmList::GetItems(std::vector<index_type>&& indexes) const
1278
0
{
1279
0
  cmList listItems;
1280
1281
0
  for (auto index : indexes) {
1282
0
    listItems.emplace_back(this->get_item(index));
1283
0
  }
1284
1285
0
  return listItems;
1286
0
}
1287
1288
cmList& cmList::RemoveItems(std::vector<index_type>&& indexes)
1289
0
{
1290
0
  if (indexes.empty()) {
1291
0
    return *this;
1292
0
  }
1293
1294
  // compute all indexes
1295
0
  std::vector<size_type> idx(indexes.size());
1296
0
  std::transform(indexes.cbegin(), indexes.cend(), idx.begin(),
1297
0
                 [this](index_type index) -> size_type {
1298
0
                   return this->ComputeIndex(index);
1299
0
                 });
1300
1301
0
  std::sort(idx.begin(), idx.end(),
1302
0
            [](size_type l, size_type r) { return l > r; });
1303
0
  auto newEnd = std::unique(idx.begin(), idx.end());
1304
0
  idx.erase(newEnd, idx.end());
1305
1306
0
  for (auto index : idx) {
1307
0
    this->erase(this->begin() + index);
1308
0
  }
1309
1310
0
  return *this;
1311
0
}
1312
1313
cmList& cmList::RemoveItems(std::vector<std::string>&& items)
1314
0
{
1315
0
  std::sort(items.begin(), items.end());
1316
0
  auto last = std::unique(items.begin(), items.end());
1317
0
  auto first = items.begin();
1318
1319
0
  auto newEnd = cmRemoveMatching(this->Values, cmMakeRange(first, last));
1320
0
  this->Values.erase(newEnd, this->Values.end());
1321
1322
0
  return *this;
1323
0
}
1324
1325
cmList::container_type::iterator cmList::Insert(
1326
  container_type& container, container_type::const_iterator pos,
1327
  std::string&& value, ExpandElements expandElements,
1328
  EmptyElements emptyElements)
1329
25.1k
{
1330
25.1k
  auto delta = std::distance(container.cbegin(), pos);
1331
25.1k
  auto insertPos = container.begin() + delta;
1332
1333
25.1k
  if (expandElements == ExpandElements::Yes) {
1334
    // If argument is empty, it is an empty list.
1335
25.1k
    if (emptyElements == EmptyElements::No && value.empty()) {
1336
0
      return insertPos;
1337
0
    }
1338
1339
    // if there are no ; in the name then just copy the current string
1340
25.1k
    if (value.find(';') == std::string::npos) {
1341
20.6k
      return container.insert(insertPos, std::move(value));
1342
20.6k
    }
1343
1344
4.50k
    std::string newValue;
1345
    // Break the string at non-escaped semicolons not nested in [].
1346
4.50k
    int squareNesting = 0;
1347
4.50k
    auto last = value.begin();
1348
4.50k
    auto const cend = value.end();
1349
670k
    for (auto c = last; c != cend; ++c) {
1350
665k
      switch (*c) {
1351
13.3k
        case '\\': {
1352
          // We only want to allow escaping of semicolons.  Other
1353
          // escapes should not be processed here.
1354
13.3k
          auto cnext = c + 1;
1355
13.3k
          if ((cnext != cend) && *cnext == ';') {
1356
6.37k
            newValue.append(last, c);
1357
            // Skip over the escape character
1358
6.37k
            last = cnext;
1359
6.37k
            c = cnext;
1360
6.37k
          }
1361
13.3k
        } break;
1362
26.3k
        case '[': {
1363
26.3k
          ++squareNesting;
1364
26.3k
        } break;
1365
55.3k
        case ']': {
1366
55.3k
          --squareNesting;
1367
55.3k
        } break;
1368
31.3k
        case ';': {
1369
          // brackets.
1370
31.3k
          if (squareNesting == 0) {
1371
25.2k
            newValue.append(last, c);
1372
            // Skip over the semicolon
1373
25.2k
            last = c + 1;
1374
25.2k
            if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
1375
              // Add the last argument.
1376
20.3k
              insertPos = container.insert(insertPos, newValue);
1377
20.3k
              insertPos++;
1378
20.3k
              newValue.clear();
1379
20.3k
            }
1380
25.2k
          }
1381
31.3k
        } break;
1382
539k
        default: {
1383
          // Just append this character.
1384
539k
        } break;
1385
665k
      }
1386
665k
    }
1387
4.50k
    newValue.append(last, cend);
1388
4.50k
    if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
1389
      // Add the last argument.
1390
2.49k
      container.insert(insertPos, std::move(newValue));
1391
2.49k
    }
1392
4.50k
  } else if (!value.empty() || emptyElements == EmptyElements::Yes) {
1393
0
    return container.insert(insertPos, std::move(value));
1394
0
  }
1395
4.50k
  return container.begin() + delta;
1396
25.1k
}
1397
1398
std::string const& cmList::ToString(BT<std::string> const& s)
1399
0
{
1400
0
  return s.Value;
1401
0
}