Coverage Report

Created: 2026-09-14 06:43

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