Coverage Report

Created: 2026-06-15 07:03

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(SortConfiguration cfg, cmMakefile& makefile)
386
0
{
387
0
  SortConfiguration config{ cfg };
388
389
0
  if (config.Order == SortConfiguration::OrderMode::DEFAULT) {
390
0
    config.Order = SortConfiguration::OrderMode::ASCENDING;
391
0
  }
392
0
  if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) {
393
0
    config.Case = SortConfiguration::CaseSensitivity::SENSITIVE;
394
0
  }
395
396
0
  try {
397
0
    ComparatorEvaluator evaluator(config.ComparatorFunction, makefile);
398
0
    StringSorter sorter(
399
0
      config, [&evaluator](std::string const& a, std::string const& b) {
400
0
        bool result = evaluator(a, b);
401
0
        if (result && evaluator(b, a)) {
402
0
          throw cmList::transform_error(
403
0
            "sub-command SORT, COMPARATOR: function does not induce a strict "
404
0
            "weak ordering. The comparator returned TRUE for both (a, b) and "
405
0
            "(b, a).");
406
0
        }
407
0
        return result;
408
0
      });
409
0
    std::sort(this->Values.begin(), this->Values.end(), sorter);
410
0
  } catch (transform_error& e) {
411
0
    throw std::invalid_argument(e.what());
412
0
  }
413
414
0
  return *this;
415
0
}
416
417
namespace {
418
using transform_type = std::function<std::string(std::string const&)>;
419
using transform_error = cmList::transform_error;
420
421
class TransformSelector : public cmList::TransformSelector
422
{
423
public:
424
0
  ~TransformSelector() override = default;
425
426
  std::string Tag;
427
428
0
  std::string const& GetTag() override { return this->Tag; }
429
430
  virtual bool Validate(std::size_t count = 0) = 0;
431
432
  virtual bool InSelection(std::string const&) = 0;
433
434
  virtual void Transform(cmList::container_type& list,
435
                         transform_type const& transform)
436
0
  {
437
0
    std::transform(list.begin(), list.end(), list.begin(), transform);
438
0
  }
439
440
protected:
441
  TransformSelector(std::string&& tag)
442
0
    : Tag(std::move(tag))
443
0
  {
444
0
  }
445
};
446
447
class TransformNoSelector : public TransformSelector
448
{
449
public:
450
  TransformNoSelector()
451
0
    : TransformSelector("NO SELECTOR")
452
0
  {
453
0
  }
454
455
0
  bool Validate(std::size_t) override { return true; }
456
457
0
  bool InSelection(std::string const&) override { return true; }
458
};
459
class TransformSelectorRegex : public TransformSelector
460
{
461
public:
462
  TransformSelectorRegex(std::string const& regex)
463
0
    : TransformSelector("REGEX")
464
0
    , Regex(regex)
465
0
  {
466
0
  }
467
  TransformSelectorRegex(std::string&& regex)
468
0
    : TransformSelector("REGEX")
469
0
    , Regex(regex)
470
0
  {
471
0
  }
472
473
0
  bool Validate(std::size_t) override { return this->Regex.is_valid(); }
474
475
  bool InSelection(std::string const& value) override
476
0
  {
477
0
    return this->Regex.find(value);
478
0
  }
479
480
  cmsys::RegularExpression Regex;
481
};
482
class TransformSelectorPredicate : public TransformSelector
483
{
484
public:
485
  TransformSelectorPredicate(std::string const& functionName,
486
                             cmMakefile& makefile)
487
0
    : TransformSelector("PREDICATE")
488
0
    , Evaluator(functionName, makefile)
489
0
  {
490
0
  }
491
492
0
  bool Validate(std::size_t) override { return true; }
493
494
  bool InSelection(std::string const& value) override
495
0
  {
496
0
    return this->Evaluator(value);
497
0
  }
498
499
private:
500
  PredicateEvaluator Evaluator;
501
};
502
class TransformSelectorIndexes : public TransformSelector
503
{
504
public:
505
  std::vector<index_type> Indexes;
506
507
0
  bool InSelection(std::string const&) override { return true; }
508
509
  void Transform(std::vector<std::string>& list,
510
                 transform_type const& transform) override
511
0
  {
512
0
    this->Validate(list.size());
513
514
0
    for (auto index : this->Indexes) {
515
0
      list[index] = transform(list[index]);
516
0
    }
517
0
  }
518
519
protected:
520
  TransformSelectorIndexes(std::string&& tag)
521
0
    : TransformSelector(std::move(tag))
522
0
  {
523
0
  }
524
  TransformSelectorIndexes(std::string&& tag,
525
                           std::vector<index_type> const& indexes)
526
0
    : TransformSelector(std::move(tag))
527
0
    , Indexes(indexes)
528
0
  {
529
0
  }
530
  TransformSelectorIndexes(std::string&& tag,
531
                           std::vector<index_type>&& indexes)
532
0
    : TransformSelector(std::move(tag))
533
0
    , Indexes(indexes)
534
0
  {
535
0
  }
536
537
  index_type NormalizeIndex(index_type index, std::size_t count)
538
0
  {
539
0
    if (index < 0) {
540
0
      index = static_cast<index_type>(count) + index;
541
0
    }
542
0
    if (index < 0 || count <= static_cast<std::size_t>(index)) {
543
0
      throw transform_error(cmStrCat(
544
0
        "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index,
545
0
        " out of range (-", count, ", ", count - 1, ")."));
546
0
    }
547
0
    return index;
548
0
  }
549
};
550
class TransformSelectorAt : public TransformSelectorIndexes
551
{
552
public:
553
  TransformSelectorAt(std::vector<index_type> const& indexes)
554
0
    : TransformSelectorIndexes("AT", indexes)
555
0
  {
556
0
  }
557
  TransformSelectorAt(std::vector<index_type>&& indexes)
558
0
    : TransformSelectorIndexes("AT", std::move(indexes))
559
0
  {
560
0
  }
561
562
  bool Validate(std::size_t count) override
563
0
  {
564
0
    decltype(this->Indexes) indexes;
565
566
0
    for (auto index : this->Indexes) {
567
0
      indexes.push_back(this->NormalizeIndex(index, count));
568
0
    }
569
0
    this->Indexes = std::move(indexes);
570
571
0
    return true;
572
0
  }
573
};
574
class TransformSelectorFor : public TransformSelectorIndexes
575
{
576
public:
577
  TransformSelectorFor(index_type start, index_type stop, index_type step)
578
0
    : TransformSelectorIndexes("FOR")
579
0
    , Start(start)
580
0
    , Stop(stop)
581
0
    , Step(step)
582
0
  {
583
0
  }
584
585
  bool Validate(std::size_t count) override
586
0
  {
587
0
    this->Start = this->NormalizeIndex(this->Start, count);
588
0
    this->Stop = this->NormalizeIndex(this->Stop, count);
589
590
    // Does stepping move us further from the end?
591
0
    if (this->Start > this->Stop) {
592
0
      throw transform_error(
593
0
        cmStrCat("sub-command TRANSFORM, selector FOR "
594
0
                 "expects <start> to be no greater than <stop> (",
595
0
                 this->Start, " > ", this->Stop, ')'));
596
0
    }
597
598
    // compute indexes
599
0
    auto size = (this->Stop - this->Start + 1) / this->Step;
600
0
    if ((this->Stop - this->Start + 1) % this->Step != 0) {
601
0
      size += 1;
602
0
    }
603
604
0
    this->Indexes.resize(size);
605
0
    auto start = this->Start;
606
0
    auto step = this->Step;
607
0
    std::generate(this->Indexes.begin(), this->Indexes.end(),
608
0
                  [&start, step]() -> index_type {
609
0
                    auto r = start;
610
0
                    start += step;
611
0
                    return r;
612
0
                  });
613
614
0
    return true;
615
0
  }
616
617
private:
618
  index_type Start, Stop, Step;
619
};
620
621
class TransformAction
622
{
623
public:
624
0
  virtual ~TransformAction() = default;
625
626
0
  void Initialize(TransformSelector* selector) { this->Selector = selector; }
627
0
  virtual void Initialize(TransformSelector*, std::string const&) {}
628
  virtual void Initialize(TransformSelector*, std::string const&,
629
                          std::string const&)
630
0
  {
631
0
  }
632
  virtual void Initialize(TransformSelector* selector,
633
                          std::vector<std::string> const&)
634
0
  {
635
0
    this->Initialize(selector);
636
0
  }
637
638
  virtual std::string operator()(std::string const& s) = 0;
639
640
protected:
641
  TransformSelector* Selector;
642
};
643
class TransformActionAppend : public TransformAction
644
{
645
public:
646
  using TransformAction::Initialize;
647
648
  void Initialize(TransformSelector* selector,
649
                  std::string const& append) override
650
0
  {
651
0
    TransformAction::Initialize(selector);
652
0
    this->Append = append;
653
0
  }
654
  void Initialize(TransformSelector* selector,
655
                  std::vector<std::string> const& append) override
656
0
  {
657
0
    this->Initialize(selector, append.front());
658
0
  }
659
660
  std::string operator()(std::string const& s) override
661
0
  {
662
0
    if (this->Selector->InSelection(s)) {
663
0
      return cmStrCat(s, this->Append);
664
0
    }
665
666
0
    return s;
667
0
  }
668
669
private:
670
  std::string Append;
671
};
672
class TransformActionPrepend : public TransformAction
673
{
674
public:
675
  using TransformAction::Initialize;
676
677
  void Initialize(TransformSelector* selector,
678
                  std::string const& prepend) override
679
0
  {
680
0
    TransformAction::Initialize(selector);
681
0
    this->Prepend = prepend;
682
0
  }
683
  void Initialize(TransformSelector* selector,
684
                  std::vector<std::string> const& prepend) override
685
0
  {
686
0
    this->Initialize(selector, prepend.front());
687
0
  }
688
689
  std::string operator()(std::string const& s) override
690
0
  {
691
0
    if (this->Selector->InSelection(s)) {
692
0
      return cmStrCat(this->Prepend, s);
693
0
    }
694
695
0
    return s;
696
0
  }
697
698
private:
699
  std::string Prepend;
700
};
701
class TransformActionToUpper : public TransformAction
702
{
703
public:
704
  std::string operator()(std::string const& s) override
705
0
  {
706
0
    if (this->Selector->InSelection(s)) {
707
0
      return cmSystemTools::UpperCase(s);
708
0
    }
709
710
0
    return s;
711
0
  }
712
};
713
class TransformActionToLower : public TransformAction
714
{
715
public:
716
  std::string operator()(std::string const& s) override
717
0
  {
718
0
    if (this->Selector->InSelection(s)) {
719
0
      return cmSystemTools::LowerCase(s);
720
0
    }
721
722
0
    return s;
723
0
  }
724
};
725
class TransformActionStrip : public TransformAction
726
{
727
public:
728
  std::string operator()(std::string const& s) override
729
0
  {
730
0
    if (this->Selector->InSelection(s)) {
731
0
      return cmTrimWhitespace(s);
732
0
    }
733
734
0
    return s;
735
0
  }
736
};
737
class TransformActionGenexStrip : public TransformAction
738
{
739
public:
740
  std::string operator()(std::string const& s) override
741
0
  {
742
0
    if (this->Selector->InSelection(s)) {
743
0
      return cmGeneratorExpression::Preprocess(
744
0
        s, cmGeneratorExpression::StripAllGeneratorExpressions);
745
0
    }
746
747
0
    return s;
748
0
  }
749
};
750
class TransformActionReplace : public TransformAction
751
{
752
public:
753
  using TransformAction::Initialize;
754
755
  void Initialize(TransformSelector* selector, std::string const& regex,
756
                  std::string const& replace) override
757
0
  {
758
0
    TransformAction::Initialize(selector);
759
0
    this->ReplaceHelper = cm::make_unique<cmStringReplaceHelper>(
760
0
      regex, replace, selector->Makefile);
761
762
0
    if (!this->ReplaceHelper->IsRegularExpressionValid()) {
763
0
      throw transform_error(
764
0
        cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
765
0
                 "regex \"",
766
0
                 regex, "\"."));
767
0
    }
768
0
    if (!this->ReplaceHelper->IsReplaceExpressionValid()) {
769
0
      throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
770
0
                                     this->ReplaceHelper->GetError(), '.'));
771
0
    }
772
0
  }
773
  void Initialize(TransformSelector* selector,
774
                  std::vector<std::string> const& args) override
775
0
  {
776
0
    this->Initialize(selector, args[0], args[1]);
777
0
  }
778
779
  std::string operator()(std::string const& s) override
780
0
  {
781
0
    if (this->Selector->InSelection(s)) {
782
      // Scan through the input for all matches.
783
0
      std::string output;
784
785
0
      if (!this->ReplaceHelper->Replace(s, output)) {
786
0
        throw transform_error(
787
0
          cmStrCat("sub-command TRANSFORM, action REPLACE: ",
788
0
                   this->ReplaceHelper->GetError(), '.'));
789
0
      }
790
791
0
      return output;
792
0
    }
793
794
0
    return s;
795
0
  }
796
797
private:
798
  std::unique_ptr<cmStringReplaceHelper> ReplaceHelper;
799
};
800
801
class TransformActionApply : public TransformAction
802
{
803
public:
804
  using TransformAction::Initialize;
805
806
  void Initialize(TransformSelector* selector, std::string const& functionName,
807
                  cmMakefile& makefile)
808
0
  {
809
0
    TransformAction::Initialize(selector);
810
0
    this->FunctionName = functionName;
811
0
    this->Makefile = &makefile;
812
0
    this->OutputVar = OutputVarFor("_cmake_transform_apply_out_", makefile);
813
814
0
    RequireFunction(makefile, this->FunctionName,
815
0
                    "sub-command TRANSFORM, action APPLY");
816
0
  }
817
818
  void Initialize(TransformSelector* /*selector*/,
819
                  std::vector<std::string> const& /*args*/) override
820
0
  {
821
    // This overload must not be used for APPLY — it lacks cmMakefile context.
822
0
    throw transform_error(
823
0
      "sub-command TRANSFORM, action APPLY requires cmMakefile context.");
824
0
  }
825
826
  std::string operator()(std::string const& s) override
827
0
  {
828
0
    if (!this->Selector->InSelection(s)) {
829
0
      return s;
830
0
    }
831
832
    // Unset the output variable before calling
833
0
    this->Makefile->RemoveDefinition(this->OutputVar);
834
835
    // Build the function call: functionName(s, outputVar)
836
0
    cmListFileContext context = this->Makefile->GetBacktrace().Top();
837
0
    std::vector<cmListFileArgument> funcArgs;
838
0
    funcArgs.emplace_back(s, cmListFileArgument::Quoted, context.Line);
839
0
    funcArgs.emplace_back(this->OutputVar, cmListFileArgument::Quoted,
840
0
                          context.Line);
841
0
    cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
842
0
                             std::move(funcArgs) };
843
844
0
    cmExecutionStatus status(*this->Makefile);
845
0
    if (!this->Makefile->ExecuteCommand(func, status) ||
846
0
        status.GetNestedError()) {
847
0
      throw transform_error(
848
0
        cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
849
0
                 this->FunctionName, "\" failed during execution."));
850
0
    }
851
852
    // Read back the output variable
853
0
    cmValue result = this->Makefile->GetDefinition(this->OutputVar);
854
0
    if (!result) {
855
0
      throw transform_error(
856
0
        cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
857
0
                 this->FunctionName, "\" did not set the output variable."));
858
0
    }
859
860
    // Copy the result before cleaning up (RemoveDefinition invalidates the
861
    // cmValue pointer).
862
0
    std::string output = *result;
863
864
    // Clean up
865
0
    this->Makefile->RemoveDefinition(this->OutputVar);
866
867
0
    return output;
868
0
  }
869
870
private:
871
  std::string FunctionName;
872
  cmMakefile* Makefile = nullptr;
873
  std::string OutputVar;
874
};
875
876
// Descriptor of action
877
// Arity: number of arguments required for the action
878
// Transform: Object implementing the action
879
struct ActionDescriptor
880
{
881
  ActionDescriptor(cmList::TransformAction action)
882
0
    : Action(action)
883
0
  {
884
0
  }
885
  ActionDescriptor(cmList::TransformAction action, std::string name,
886
                   std::size_t arity,
887
                   std::unique_ptr<TransformAction> transform)
888
0
    : Action(action)
889
0
    , Name(std::move(name))
890
0
    , Arity(arity)
891
0
    , Transform(std::move(transform))
892
0
  {
893
0
  }
894
895
0
  operator cmList::TransformAction() const { return this->Action; }
896
897
  cmList::TransformAction Action;
898
  std::string Name;
899
  std::size_t Arity = 0;
900
  std::unique_ptr<TransformAction> Transform;
901
};
902
903
// Build a set of supported actions.
904
using ActionDescriptorSet = std::set<
905
  ActionDescriptor,
906
  std::function<bool(cmList::TransformAction, cmList::TransformAction)>>;
907
908
ActionDescriptorSet Descriptors([](cmList::TransformAction x,
909
0
                                   cmList::TransformAction y) {
910
0
  return x < y;
911
0
});
912
913
ActionDescriptorSet::iterator TransformConfigure(
914
  cmList::TransformAction action,
915
  std::unique_ptr<cmList::TransformSelector>& selector, std::size_t arity)
916
0
{
917
0
  if (Descriptors.empty()) {
918
0
    Descriptors.emplace(cmList::TransformAction::APPEND, "APPEND", 1,
919
0
                        cm::make_unique<TransformActionAppend>());
920
0
    Descriptors.emplace(cmList::TransformAction::PREPEND, "PREPEND", 1,
921
0
                        cm::make_unique<TransformActionPrepend>());
922
0
    Descriptors.emplace(cmList::TransformAction::TOUPPER, "TOUPPER", 0,
923
0
                        cm::make_unique<TransformActionToUpper>());
924
0
    Descriptors.emplace(cmList::TransformAction::TOLOWER, "TOLOWER", 0,
925
0
                        cm::make_unique<TransformActionToLower>());
926
0
    Descriptors.emplace(cmList::TransformAction::STRIP, "STRIP", 0,
927
0
                        cm::make_unique<TransformActionStrip>());
928
0
    Descriptors.emplace(cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0,
929
0
                        cm::make_unique<TransformActionGenexStrip>());
930
0
    Descriptors.emplace(cmList::TransformAction::REPLACE, "REPLACE", 2,
931
0
                        cm::make_unique<TransformActionReplace>());
932
0
    Descriptors.emplace(cmList::TransformAction::APPLY, "APPLY", 1,
933
0
                        cm::make_unique<TransformActionApply>());
934
0
  }
935
936
0
  auto descriptor = Descriptors.find(action);
937
0
  if (descriptor == Descriptors.end()) {
938
0
    throw transform_error(cmStrCat(" sub-command TRANSFORM, ",
939
0
                                   static_cast<int>(action),
940
0
                                   " invalid action."));
941
0
  }
942
943
0
  if (descriptor->Arity != arity) {
944
0
    throw transform_error(cmStrCat("sub-command TRANSFORM, action ",
945
0
                                   descriptor->Name, " expects ",
946
0
                                   descriptor->Arity, " argument(s)."));
947
0
  }
948
0
  if (!selector) {
949
0
    selector = cm::make_unique<TransformNoSelector>();
950
0
  }
951
952
0
  return descriptor;
953
0
}
954
}
955
956
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::New()
957
0
{
958
0
  return cm::make_unique<TransformNoSelector>();
959
0
}
960
961
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
962
  std::initializer_list<index_type> indexes)
963
0
{
964
0
  return cm::make_unique<TransformSelectorAt>(
965
0
    std::vector<index_type>{ indexes.begin(), indexes.end() });
966
0
  ;
967
0
}
968
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
969
  std::vector<index_type> const& indexes)
970
0
{
971
0
  return cm::make_unique<TransformSelectorAt>(indexes);
972
0
}
973
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
974
  std::vector<index_type>&& indexes)
975
0
{
976
0
  return cm::make_unique<TransformSelectorAt>(std::move(indexes));
977
0
}
978
979
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
980
  std::initializer_list<index_type> indexes)
981
0
{
982
0
  if (indexes.size() < 2 || indexes.size() > 3) {
983
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
984
0
                          "expects 2 or 3 arguments");
985
0
  }
986
0
  if (indexes.size() == 3 && *(indexes.begin() + 2) < 0) {
987
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
988
0
                          "positive numeric value for <step>.");
989
0
  }
990
991
0
  return cm::make_unique<TransformSelectorFor>(
992
0
    *indexes.begin(), *(indexes.begin() + 1),
993
0
    indexes.size() == 3 ? *(indexes.begin() + 2) : 1);
994
0
}
995
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
996
  std::vector<index_type> const& indexes)
997
0
{
998
0
  if (indexes.size() < 2 || indexes.size() > 3) {
999
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
1000
0
                          "expects 2 or 3 arguments");
1001
0
  }
1002
0
  if (indexes.size() == 3 && indexes[2] < 0) {
1003
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
1004
0
                          "positive numeric value for <step>.");
1005
0
  }
1006
1007
0
  return cm::make_unique<TransformSelectorFor>(
1008
0
    indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
1009
0
}
1010
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
1011
  std::vector<index_type>&& indexes)
1012
0
{
1013
0
  if (indexes.size() < 2 || indexes.size() > 3) {
1014
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
1015
0
                          "expects 2 or 3 arguments");
1016
0
  }
1017
0
  if (indexes.size() == 3 && indexes[2] < 0) {
1018
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
1019
0
                          "positive numeric value for <step>.");
1020
0
  }
1021
1022
0
  return cm::make_unique<TransformSelectorFor>(
1023
0
    indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
1024
0
}
1025
1026
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
1027
  std::string const& regex)
1028
0
{
1029
0
  std::unique_ptr<::TransformSelector> selector =
1030
0
    cm::make_unique<TransformSelectorRegex>(regex);
1031
0
  if (!selector->Validate()) {
1032
0
    throw transform_error(
1033
0
      cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
1034
0
               "regex \"",
1035
0
               regex, "\"."));
1036
0
  }
1037
  // weird construct to please all compilers
1038
0
  return std::unique_ptr<cmList::TransformSelector>(selector.release());
1039
0
}
1040
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
1041
  std::string&& regex)
1042
0
{
1043
0
  std::unique_ptr<::TransformSelector> selector =
1044
0
    cm::make_unique<TransformSelectorRegex>(std::move(regex));
1045
0
  if (!selector->Validate()) {
1046
0
    throw transform_error(
1047
0
      cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
1048
0
               "regex \"",
1049
0
               regex, "\"."));
1050
0
  }
1051
  // weird construct to please all compilers
1052
0
  return std::unique_ptr<cmList::TransformSelector>(selector.release());
1053
0
}
1054
1055
std::unique_ptr<cmList::TransformSelector>
1056
cmList::TransformSelector::NewPREDICATE(std::string const& functionName,
1057
                                        cmMakefile& makefile)
1058
0
{
1059
0
  return std::unique_ptr<cmList::TransformSelector>(
1060
0
    new TransformSelectorPredicate(functionName, makefile));
1061
0
}
1062
1063
cmList& cmList::transform(TransformAction action,
1064
                          std::unique_ptr<TransformSelector> selector)
1065
0
{
1066
0
  auto descriptor = TransformConfigure(action, selector, 0);
1067
1068
0
  descriptor->Transform->Initialize(
1069
0
    static_cast<::TransformSelector*>(selector.get()));
1070
1071
0
  static_cast<::TransformSelector&>(*selector).Transform(
1072
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
1073
0
      return (*descriptor->Transform)(s);
1074
0
    });
1075
1076
0
  return *this;
1077
0
}
1078
1079
cmList& cmList::transform(TransformAction action, std::string const& arg,
1080
                          std::unique_ptr<TransformSelector> selector)
1081
0
{
1082
0
  auto descriptor = TransformConfigure(action, selector, 1);
1083
1084
0
  descriptor->Transform->Initialize(
1085
0
    static_cast<::TransformSelector*>(selector.get()), arg);
1086
1087
0
  static_cast<::TransformSelector&>(*selector).Transform(
1088
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
1089
0
      return (*descriptor->Transform)(s);
1090
0
    });
1091
1092
0
  return *this;
1093
0
}
1094
1095
cmList& cmList::transform(TransformAction action, std::string const& arg1,
1096
                          std::string const& arg2,
1097
                          std::unique_ptr<TransformSelector> selector)
1098
0
{
1099
0
  auto descriptor = TransformConfigure(action, selector, 2);
1100
1101
0
  descriptor->Transform->Initialize(
1102
0
    static_cast<::TransformSelector*>(selector.get()), arg1, arg2);
1103
1104
0
  static_cast<::TransformSelector&>(*selector).Transform(
1105
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
1106
0
      return (*descriptor->Transform)(s);
1107
0
    });
1108
1109
0
  return *this;
1110
0
}
1111
1112
cmList& cmList::transform(TransformAction action,
1113
                          std::vector<std::string> const& args,
1114
                          std::unique_ptr<TransformSelector> selector)
1115
0
{
1116
0
  auto descriptor = TransformConfigure(action, selector, args.size());
1117
1118
0
  descriptor->Transform->Initialize(
1119
0
    static_cast<::TransformSelector*>(selector.get()), args);
1120
1121
0
  static_cast<::TransformSelector&>(*selector).Transform(
1122
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
1123
0
      return (*descriptor->Transform)(s);
1124
0
    });
1125
1126
0
  return *this;
1127
0
}
1128
1129
cmList& cmList::transform(TransformAction action, std::string const& arg,
1130
                          cmMakefile& makefile,
1131
                          std::unique_ptr<TransformSelector> selector)
1132
0
{
1133
  // Validate action and arity via the static registry.
1134
0
  TransformConfigure(action, selector, 1);
1135
1136
  // Create a local instance rather than reusing the singleton from
1137
  // Descriptors.  A user function invoked by APPLY may itself call
1138
  // list(TRANSFORM ... APPLY ...), which would clobber a shared instance.
1139
0
  TransformActionApply applyAction;
1140
0
  applyAction.Initialize(static_cast<::TransformSelector*>(selector.get()),
1141
0
                         arg, makefile);
1142
1143
0
  static_cast<::TransformSelector&>(*selector).Transform(
1144
0
    this->Values, [&applyAction](std::string const& s) -> std::string {
1145
0
      return applyAction(s);
1146
0
    });
1147
1148
0
  return *this;
1149
0
}
1150
1151
std::string& cmList::append(std::string& list, std::string&& value)
1152
0
{
1153
0
  if (list.empty()) {
1154
0
    list = std::move(value);
1155
0
  } else {
1156
0
    list += cmStrCat(cmList::element_separator, value);
1157
0
  }
1158
1159
0
  return list;
1160
0
}
1161
std::string& cmList::append(std::string& list, cm::string_view value)
1162
0
{
1163
0
  return cmList::append(list, std::string{ value });
1164
0
}
1165
1166
std::string& cmList::prepend(std::string& list, std::string&& value)
1167
0
{
1168
0
  if (list.empty()) {
1169
0
    list = std::move(value);
1170
0
  } else {
1171
0
    list.insert(0, cmStrCat(value, cmList::element_separator));
1172
0
  }
1173
1174
0
  return list;
1175
0
}
1176
std::string& cmList::prepend(std::string& list, cm::string_view value)
1177
0
{
1178
0
  return cmList::prepend(list, std::string{ value });
1179
0
}
1180
1181
cmList::size_type cmList::ComputeIndex(index_type pos, bool boundCheck) const
1182
0
{
1183
0
  if (boundCheck) {
1184
0
    if (this->Values.empty()) {
1185
0
      throw std::out_of_range(
1186
0
        cmStrCat("index: ", pos, " out of range (0, 0)"));
1187
0
    }
1188
1189
0
    auto index = pos;
1190
0
    if (!this->Values.empty()) {
1191
0
      auto length = this->Values.size();
1192
0
      if (index < 0) {
1193
0
        index = static_cast<index_type>(length) + index;
1194
0
      }
1195
0
      if (index < 0 || length <= static_cast<size_type>(index)) {
1196
0
        throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
1197
0
                                         this->Values.size(), ", ",
1198
0
                                         this->Values.size() - 1, ')'));
1199
0
      }
1200
0
    }
1201
0
    return index;
1202
0
  }
1203
1204
0
  return pos < 0 ? this->Values.size() + pos : pos;
1205
0
}
1206
cmList::size_type cmList::ComputeInsertIndex(index_type pos,
1207
                                             bool boundCheck) const
1208
0
{
1209
0
  if (boundCheck) {
1210
0
    if (this->Values.empty() && pos != 0) {
1211
0
      throw std::out_of_range(
1212
0
        cmStrCat("index: ", pos, " out of range (0, 0)"));
1213
0
    }
1214
1215
0
    auto index = pos;
1216
0
    if (!this->Values.empty()) {
1217
0
      auto length = this->Values.size();
1218
0
      if (index < 0) {
1219
0
        index = static_cast<index_type>(length) + index;
1220
0
      }
1221
0
      if (index < 0 || length < static_cast<size_type>(index)) {
1222
0
        throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
1223
0
                                         this->Values.size(), ", ",
1224
0
                                         this->Values.size(), ')'));
1225
0
      }
1226
0
    }
1227
0
    return index;
1228
0
  }
1229
1230
0
  return pos < 0 ? this->Values.size() + pos : pos;
1231
0
}
1232
1233
cmList cmList::GetItems(std::vector<index_type>&& indexes) const
1234
0
{
1235
0
  cmList listItems;
1236
1237
0
  for (auto index : indexes) {
1238
0
    listItems.emplace_back(this->get_item(index));
1239
0
  }
1240
1241
0
  return listItems;
1242
0
}
1243
1244
cmList& cmList::RemoveItems(std::vector<index_type>&& indexes)
1245
0
{
1246
0
  if (indexes.empty()) {
1247
0
    return *this;
1248
0
  }
1249
1250
  // compute all indexes
1251
0
  std::vector<size_type> idx(indexes.size());
1252
0
  std::transform(indexes.cbegin(), indexes.cend(), idx.begin(),
1253
0
                 [this](index_type index) -> size_type {
1254
0
                   return this->ComputeIndex(index);
1255
0
                 });
1256
1257
0
  std::sort(idx.begin(), idx.end(),
1258
0
            [](size_type l, size_type r) { return l > r; });
1259
0
  auto newEnd = std::unique(idx.begin(), idx.end());
1260
0
  idx.erase(newEnd, idx.end());
1261
1262
0
  for (auto index : idx) {
1263
0
    this->erase(this->begin() + index);
1264
0
  }
1265
1266
0
  return *this;
1267
0
}
1268
1269
cmList& cmList::RemoveItems(std::vector<std::string>&& items)
1270
0
{
1271
0
  std::sort(items.begin(), items.end());
1272
0
  auto last = std::unique(items.begin(), items.end());
1273
0
  auto first = items.begin();
1274
1275
0
  auto newEnd = cmRemoveMatching(this->Values, cmMakeRange(first, last));
1276
0
  this->Values.erase(newEnd, this->Values.end());
1277
1278
0
  return *this;
1279
0
}
1280
1281
cmList::container_type::iterator cmList::Insert(
1282
  container_type& container, container_type::const_iterator pos,
1283
  std::string&& value, ExpandElements expandElements,
1284
  EmptyElements emptyElements)
1285
21.7k
{
1286
21.7k
  auto delta = std::distance(container.cbegin(), pos);
1287
21.7k
  auto insertPos = container.begin() + delta;
1288
1289
21.7k
  if (expandElements == ExpandElements::Yes) {
1290
    // If argument is empty, it is an empty list.
1291
21.7k
    if (emptyElements == EmptyElements::No && value.empty()) {
1292
0
      return insertPos;
1293
0
    }
1294
1295
    // if there are no ; in the name then just copy the current string
1296
21.7k
    if (value.find(';') == std::string::npos) {
1297
17.2k
      return container.insert(insertPos, std::move(value));
1298
17.2k
    }
1299
1300
4.49k
    std::string newValue;
1301
    // Break the string at non-escaped semicolons not nested in [].
1302
4.49k
    int squareNesting = 0;
1303
4.49k
    auto last = value.begin();
1304
4.49k
    auto const cend = value.end();
1305
680k
    for (auto c = last; c != cend; ++c) {
1306
675k
      switch (*c) {
1307
18.4k
        case '\\': {
1308
          // We only want to allow escaping of semicolons.  Other
1309
          // escapes should not be processed here.
1310
18.4k
          auto cnext = c + 1;
1311
18.4k
          if ((cnext != cend) && *cnext == ';') {
1312
9.83k
            newValue.append(last, c);
1313
            // Skip over the escape character
1314
9.83k
            last = cnext;
1315
9.83k
            c = cnext;
1316
9.83k
          }
1317
18.4k
        } break;
1318
1.99k
        case '[': {
1319
1.99k
          ++squareNesting;
1320
1.99k
        } break;
1321
34.3k
        case ']': {
1322
34.3k
          --squareNesting;
1323
34.3k
        } break;
1324
32.8k
        case ';': {
1325
          // brackets.
1326
32.8k
          if (squareNesting == 0) {
1327
25.3k
            newValue.append(last, c);
1328
            // Skip over the semicolon
1329
25.3k
            last = c + 1;
1330
25.3k
            if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
1331
              // Add the last argument.
1332
19.4k
              insertPos = container.insert(insertPos, newValue);
1333
19.4k
              insertPos++;
1334
19.4k
              newValue.clear();
1335
19.4k
            }
1336
25.3k
          }
1337
32.8k
        } break;
1338
588k
        default: {
1339
          // Just append this character.
1340
588k
        } break;
1341
675k
      }
1342
675k
    }
1343
4.49k
    newValue.append(last, cend);
1344
4.49k
    if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
1345
      // Add the last argument.
1346
2.56k
      container.insert(insertPos, std::move(newValue));
1347
2.56k
    }
1348
4.49k
  } else if (!value.empty() || emptyElements == EmptyElements::Yes) {
1349
0
    return container.insert(insertPos, std::move(value));
1350
0
  }
1351
4.49k
  return container.begin() + delta;
1352
21.7k
}
1353
1354
std::string const& cmList::ToString(BT<std::string> const& s)
1355
0
{
1356
0
  return s.Value;
1357
0
}