Coverage Report

Created: 2026-04-29 07:01

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
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 "cmStringAlgorithms.h"
28
#include "cmStringReplaceHelper.h"
29
#include "cmSystemTools.h"
30
#include "cmValue.h"
31
32
cm::string_view cmList::element_separator{ ";" };
33
34
cmList cmList::sublist(size_type pos, size_type length) const
35
0
{
36
0
  if (pos >= this->Values.size()) {
37
0
    throw std::out_of_range(cmStrCat(
38
0
      "begin index: ", pos, " is out of range 0 - ", this->Values.size() - 1));
39
0
  }
40
41
0
  size_type count = (length == npos || pos + length > this->size())
42
0
    ? this->size()
43
0
    : pos + length;
44
0
  return this->sublist(this->begin() + pos, this->begin() + count);
45
0
}
46
47
cmList::size_type cmList::find(cm::string_view value) const
48
0
{
49
0
  auto res = std::find(this->Values.begin(), this->Values.end(), value);
50
0
  if (res == this->Values.end()) {
51
0
    return npos;
52
0
  }
53
54
0
  return std::distance(this->Values.begin(), res);
55
0
}
56
57
cmList& cmList::remove_duplicates()
58
0
{
59
0
  auto newEnd = cmRemoveDuplicates(this->Values);
60
0
  this->Values.erase(newEnd, this->Values.end());
61
62
0
  return *this;
63
0
}
64
65
namespace {
66
class MatchesRegex
67
{
68
public:
69
  MatchesRegex(cmsys::RegularExpression& regex, cmList::FilterMode mode)
70
0
    : Regex(regex)
71
0
    , IncludeMatches(mode == cmList::FilterMode::INCLUDE)
72
0
  {
73
0
  }
74
75
  bool operator()(std::string const& target)
76
0
  {
77
0
    return this->Regex.find(target) ^ this->IncludeMatches;
78
0
  }
79
80
private:
81
  cmsys::RegularExpression& Regex;
82
  bool const IncludeMatches;
83
};
84
}
85
86
cmList& cmList::filter(cm::string_view pattern, FilterMode mode)
87
0
{
88
0
  cmsys::RegularExpression regex(std::string{ pattern });
89
0
  if (!regex.is_valid()) {
90
0
    throw std::invalid_argument(
91
0
      cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"",
92
0
               pattern, "\"."));
93
0
  }
94
95
0
  auto it = std::remove_if(this->Values.begin(), this->Values.end(),
96
0
                           MatchesRegex{ regex, mode });
97
0
  this->Values.erase(it, this->Values.end());
98
99
0
  return *this;
100
0
}
101
102
namespace {
103
class StringSorter
104
{
105
protected:
106
  using StringFilter = std::function<std::string(std::string const&)>;
107
108
  using OrderMode = cmList::SortConfiguration::OrderMode;
109
  using CompareMethod = cmList::SortConfiguration::CompareMethod;
110
  using CaseSensitivity = cmList::SortConfiguration::CaseSensitivity;
111
112
  StringFilter GetCompareFilter(CompareMethod compare)
113
0
  {
114
0
    return (compare == CompareMethod::FILE_BASENAME)
115
0
      ? cmSystemTools::GetFilenameName
116
0
      : nullptr;
117
0
  }
118
119
  StringFilter GetCaseFilter(CaseSensitivity sensitivity)
120
0
  {
121
0
    return (sensitivity == CaseSensitivity::INSENSITIVE)
122
0
      ? cmsys::SystemTools::LowerCase
123
0
      : nullptr;
124
0
  }
125
126
  using ComparisonFunction =
127
    std::function<bool(std::string const&, std::string const&)>;
128
  ComparisonFunction GetComparisonFunction(CompareMethod compare)
129
0
  {
130
0
    if (compare == CompareMethod::NATURAL) {
131
0
      return std::function<bool(std::string const&, std::string const&)>(
132
0
        [](std::string const& x, std::string const& y) {
133
0
          return cmSystemTools::strverscmp(x, y) < 0;
134
0
        });
135
0
    }
136
0
    return std::function<bool(std::string const&, std::string const&)>(
137
0
      [](std::string const& x, std::string const& y) { return x < y; });
138
0
  }
139
140
public:
141
  StringSorter(cmList::SortConfiguration config)
142
0
    : Filters{ this->GetCompareFilter(config.Compare),
143
0
               this->GetCaseFilter(config.Case) }
144
0
    , SortMethod(this->GetComparisonFunction(config.Compare))
145
0
    , Descending(config.Order == OrderMode::DESCENDING)
146
0
  {
147
0
  }
148
149
  std::string ApplyFilter(std::string const& argument)
150
0
  {
151
0
    std::string result = argument;
152
0
    for (auto const& filter : this->Filters) {
153
0
      if (filter) {
154
0
        result = filter(result);
155
0
      }
156
0
    }
157
0
    return result;
158
0
  }
159
160
  bool operator()(std::string const& a, std::string const& b)
161
0
  {
162
0
    std::string af = this->ApplyFilter(a);
163
0
    std::string bf = this->ApplyFilter(b);
164
0
    bool result;
165
0
    if (this->Descending) {
166
0
      result = this->SortMethod(bf, af);
167
0
    } else {
168
0
      result = this->SortMethod(af, bf);
169
0
    }
170
0
    return result;
171
0
  }
172
173
private:
174
  StringFilter Filters[2] = { nullptr, nullptr };
175
  ComparisonFunction SortMethod;
176
  bool Descending;
177
};
178
}
179
180
0
cmList::SortConfiguration::SortConfiguration() = default;
181
182
cmList& cmList::sort(SortConfiguration cfg)
183
0
{
184
0
  SortConfiguration config{ cfg };
185
186
0
  if (config.Order == SortConfiguration::OrderMode::DEFAULT) {
187
0
    config.Order = SortConfiguration::OrderMode::ASCENDING;
188
0
  }
189
0
  if (config.Compare == SortConfiguration::CompareMethod::DEFAULT) {
190
0
    config.Compare = SortConfiguration::CompareMethod::STRING;
191
0
  }
192
0
  if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) {
193
0
    config.Case = SortConfiguration::CaseSensitivity::SENSITIVE;
194
0
  }
195
196
0
  if ((config.Compare == SortConfiguration::CompareMethod::STRING) &&
197
0
      (config.Case == SortConfiguration::CaseSensitivity::SENSITIVE) &&
198
0
      (config.Order == SortConfiguration::OrderMode::ASCENDING)) {
199
0
    std::sort(this->Values.begin(), this->Values.end());
200
0
  } else {
201
0
    StringSorter sorter(config);
202
0
    std::sort(this->Values.begin(), this->Values.end(), sorter);
203
0
  }
204
205
0
  return *this;
206
0
}
207
208
namespace {
209
using transform_type = std::function<std::string(std::string const&)>;
210
using transform_error = cmList::transform_error;
211
212
class TransformSelector : public cmList::TransformSelector
213
{
214
public:
215
0
  ~TransformSelector() override = default;
216
217
  std::string Tag;
218
219
0
  std::string const& GetTag() override { return this->Tag; }
220
221
  virtual bool Validate(std::size_t count = 0) = 0;
222
223
  virtual bool InSelection(std::string const&) = 0;
224
225
  virtual void Transform(cmList::container_type& list,
226
                         transform_type const& transform)
227
0
  {
228
0
    std::transform(list.begin(), list.end(), list.begin(), transform);
229
0
  }
230
231
protected:
232
  TransformSelector(std::string&& tag)
233
0
    : Tag(std::move(tag))
234
0
  {
235
0
  }
236
};
237
238
class TransformNoSelector : public TransformSelector
239
{
240
public:
241
  TransformNoSelector()
242
0
    : TransformSelector("NO SELECTOR")
243
0
  {
244
0
  }
245
246
0
  bool Validate(std::size_t) override { return true; }
247
248
0
  bool InSelection(std::string const&) override { return true; }
249
};
250
class TransformSelectorRegex : public TransformSelector
251
{
252
public:
253
  TransformSelectorRegex(std::string const& regex)
254
0
    : TransformSelector("REGEX")
255
0
    , Regex(regex)
256
0
  {
257
0
  }
258
  TransformSelectorRegex(std::string&& regex)
259
0
    : TransformSelector("REGEX")
260
0
    , Regex(regex)
261
0
  {
262
0
  }
263
264
0
  bool Validate(std::size_t) override { return this->Regex.is_valid(); }
265
266
  bool InSelection(std::string const& value) override
267
0
  {
268
0
    return this->Regex.find(value);
269
0
  }
270
271
  cmsys::RegularExpression Regex;
272
};
273
class TransformSelectorIndexes : public TransformSelector
274
{
275
public:
276
  std::vector<index_type> Indexes;
277
278
0
  bool InSelection(std::string const&) override { return true; }
279
280
  void Transform(std::vector<std::string>& list,
281
                 transform_type const& transform) override
282
0
  {
283
0
    this->Validate(list.size());
284
285
0
    for (auto index : this->Indexes) {
286
0
      list[index] = transform(list[index]);
287
0
    }
288
0
  }
289
290
protected:
291
  TransformSelectorIndexes(std::string&& tag)
292
0
    : TransformSelector(std::move(tag))
293
0
  {
294
0
  }
295
  TransformSelectorIndexes(std::string&& tag,
296
                           std::vector<index_type> const& indexes)
297
0
    : TransformSelector(std::move(tag))
298
0
    , Indexes(indexes)
299
0
  {
300
0
  }
301
  TransformSelectorIndexes(std::string&& tag,
302
                           std::vector<index_type>&& indexes)
303
0
    : TransformSelector(std::move(tag))
304
0
    , Indexes(indexes)
305
0
  {
306
0
  }
307
308
  index_type NormalizeIndex(index_type index, std::size_t count)
309
0
  {
310
0
    if (index < 0) {
311
0
      index = static_cast<index_type>(count) + index;
312
0
    }
313
0
    if (index < 0 || count <= static_cast<std::size_t>(index)) {
314
0
      throw transform_error(cmStrCat(
315
0
        "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index,
316
0
        " out of range (-", count, ", ", count - 1, ")."));
317
0
    }
318
0
    return index;
319
0
  }
320
};
321
class TransformSelectorAt : public TransformSelectorIndexes
322
{
323
public:
324
  TransformSelectorAt(std::vector<index_type> const& indexes)
325
0
    : TransformSelectorIndexes("AT", indexes)
326
0
  {
327
0
  }
328
  TransformSelectorAt(std::vector<index_type>&& indexes)
329
0
    : TransformSelectorIndexes("AT", std::move(indexes))
330
0
  {
331
0
  }
332
333
  bool Validate(std::size_t count) override
334
0
  {
335
0
    decltype(this->Indexes) indexes;
336
337
0
    for (auto index : this->Indexes) {
338
0
      indexes.push_back(this->NormalizeIndex(index, count));
339
0
    }
340
0
    this->Indexes = std::move(indexes);
341
342
0
    return true;
343
0
  }
344
};
345
class TransformSelectorFor : public TransformSelectorIndexes
346
{
347
public:
348
  TransformSelectorFor(index_type start, index_type stop, index_type step)
349
0
    : TransformSelectorIndexes("FOR")
350
0
    , Start(start)
351
0
    , Stop(stop)
352
0
    , Step(step)
353
0
  {
354
0
  }
355
356
  bool Validate(std::size_t count) override
357
0
  {
358
0
    this->Start = this->NormalizeIndex(this->Start, count);
359
0
    this->Stop = this->NormalizeIndex(this->Stop, count);
360
361
    // Does stepping move us further from the end?
362
0
    if (this->Start > this->Stop) {
363
0
      throw transform_error(
364
0
        cmStrCat("sub-command TRANSFORM, selector FOR "
365
0
                 "expects <start> to be no greater than <stop> (",
366
0
                 this->Start, " > ", this->Stop, ')'));
367
0
    }
368
369
    // compute indexes
370
0
    auto size = (this->Stop - this->Start + 1) / this->Step;
371
0
    if ((this->Stop - this->Start + 1) % this->Step != 0) {
372
0
      size += 1;
373
0
    }
374
375
0
    this->Indexes.resize(size);
376
0
    auto start = this->Start;
377
0
    auto step = this->Step;
378
0
    std::generate(this->Indexes.begin(), this->Indexes.end(),
379
0
                  [&start, step]() -> index_type {
380
0
                    auto r = start;
381
0
                    start += step;
382
0
                    return r;
383
0
                  });
384
385
0
    return true;
386
0
  }
387
388
private:
389
  index_type Start, Stop, Step;
390
};
391
392
class TransformAction
393
{
394
public:
395
0
  virtual ~TransformAction() = default;
396
397
0
  void Initialize(TransformSelector* selector) { this->Selector = selector; }
398
0
  virtual void Initialize(TransformSelector*, std::string const&) {}
399
  virtual void Initialize(TransformSelector*, std::string const&,
400
                          std::string const&)
401
0
  {
402
0
  }
403
  virtual void Initialize(TransformSelector* selector,
404
                          std::vector<std::string> const&)
405
0
  {
406
0
    this->Initialize(selector);
407
0
  }
408
409
  virtual std::string operator()(std::string const& s) = 0;
410
411
protected:
412
  TransformSelector* Selector;
413
};
414
class TransformActionAppend : public TransformAction
415
{
416
public:
417
  using TransformAction::Initialize;
418
419
  void Initialize(TransformSelector* selector,
420
                  std::string const& append) override
421
0
  {
422
0
    TransformAction::Initialize(selector);
423
0
    this->Append = append;
424
0
  }
425
  void Initialize(TransformSelector* selector,
426
                  std::vector<std::string> const& append) override
427
0
  {
428
0
    this->Initialize(selector, append.front());
429
0
  }
430
431
  std::string operator()(std::string const& s) override
432
0
  {
433
0
    if (this->Selector->InSelection(s)) {
434
0
      return cmStrCat(s, this->Append);
435
0
    }
436
437
0
    return s;
438
0
  }
439
440
private:
441
  std::string Append;
442
};
443
class TransformActionPrepend : public TransformAction
444
{
445
public:
446
  using TransformAction::Initialize;
447
448
  void Initialize(TransformSelector* selector,
449
                  std::string const& prepend) override
450
0
  {
451
0
    TransformAction::Initialize(selector);
452
0
    this->Prepend = prepend;
453
0
  }
454
  void Initialize(TransformSelector* selector,
455
                  std::vector<std::string> const& prepend) override
456
0
  {
457
0
    this->Initialize(selector, prepend.front());
458
0
  }
459
460
  std::string operator()(std::string const& s) override
461
0
  {
462
0
    if (this->Selector->InSelection(s)) {
463
0
      return cmStrCat(this->Prepend, s);
464
0
    }
465
466
0
    return s;
467
0
  }
468
469
private:
470
  std::string Prepend;
471
};
472
class TransformActionToUpper : public TransformAction
473
{
474
public:
475
  std::string operator()(std::string const& s) override
476
0
  {
477
0
    if (this->Selector->InSelection(s)) {
478
0
      return cmSystemTools::UpperCase(s);
479
0
    }
480
481
0
    return s;
482
0
  }
483
};
484
class TransformActionToLower : public TransformAction
485
{
486
public:
487
  std::string operator()(std::string const& s) override
488
0
  {
489
0
    if (this->Selector->InSelection(s)) {
490
0
      return cmSystemTools::LowerCase(s);
491
0
    }
492
493
0
    return s;
494
0
  }
495
};
496
class TransformActionStrip : public TransformAction
497
{
498
public:
499
  std::string operator()(std::string const& s) override
500
0
  {
501
0
    if (this->Selector->InSelection(s)) {
502
0
      return cmTrimWhitespace(s);
503
0
    }
504
505
0
    return s;
506
0
  }
507
};
508
class TransformActionGenexStrip : public TransformAction
509
{
510
public:
511
  std::string operator()(std::string const& s) override
512
0
  {
513
0
    if (this->Selector->InSelection(s)) {
514
0
      return cmGeneratorExpression::Preprocess(
515
0
        s, cmGeneratorExpression::StripAllGeneratorExpressions);
516
0
    }
517
518
0
    return s;
519
0
  }
520
};
521
class TransformActionReplace : public TransformAction
522
{
523
public:
524
  using TransformAction::Initialize;
525
526
  void Initialize(TransformSelector* selector, std::string const& regex,
527
                  std::string const& replace) override
528
0
  {
529
0
    TransformAction::Initialize(selector);
530
0
    this->ReplaceHelper = cm::make_unique<cmStringReplaceHelper>(
531
0
      regex, replace, selector->Makefile);
532
533
0
    if (!this->ReplaceHelper->IsRegularExpressionValid()) {
534
0
      throw transform_error(
535
0
        cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
536
0
                 "regex \"",
537
0
                 regex, "\"."));
538
0
    }
539
0
    if (!this->ReplaceHelper->IsReplaceExpressionValid()) {
540
0
      throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
541
0
                                     this->ReplaceHelper->GetError(), '.'));
542
0
    }
543
0
  }
544
  void Initialize(TransformSelector* selector,
545
                  std::vector<std::string> const& args) override
546
0
  {
547
0
    this->Initialize(selector, args[0], args[1]);
548
0
  }
549
550
  std::string operator()(std::string const& s) override
551
0
  {
552
0
    if (this->Selector->InSelection(s)) {
553
      // Scan through the input for all matches.
554
0
      std::string output;
555
556
0
      if (!this->ReplaceHelper->Replace(s, output)) {
557
0
        throw transform_error(
558
0
          cmStrCat("sub-command TRANSFORM, action REPLACE: ",
559
0
                   this->ReplaceHelper->GetError(), '.'));
560
0
      }
561
562
0
      return output;
563
0
    }
564
565
0
    return s;
566
0
  }
567
568
private:
569
  std::unique_ptr<cmStringReplaceHelper> ReplaceHelper;
570
};
571
572
class TransformActionApply : public TransformAction
573
{
574
public:
575
  using TransformAction::Initialize;
576
577
  void Initialize(TransformSelector* selector, std::string const& functionName,
578
                  cmMakefile& makefile)
579
0
  {
580
0
    TransformAction::Initialize(selector);
581
0
    this->FunctionName = functionName;
582
0
    this->Makefile = &makefile;
583
584
    // Validate: command must exist
585
0
    if (!makefile.GetState()->GetCommand(this->FunctionName)) {
586
0
      throw transform_error(
587
0
        cmStrCat("sub-command TRANSFORM, action APPLY: unknown function \"",
588
0
                 this->FunctionName, "\"."));
589
0
    }
590
0
  }
591
592
  void Initialize(TransformSelector* /*selector*/,
593
                  std::vector<std::string> const& /*args*/) override
594
0
  {
595
    // This overload must not be used for APPLY — it lacks cmMakefile context.
596
0
    throw transform_error(
597
0
      "sub-command TRANSFORM, action APPLY requires cmMakefile context.");
598
0
  }
599
600
  std::string operator()(std::string const& s) override
601
0
  {
602
0
    if (!this->Selector->InSelection(s)) {
603
0
      return s;
604
0
    }
605
606
    // Use a unique output variable name to avoid collisions
607
0
    std::string const outputVar = "_list_transform_apply_out_";
608
609
    // Unset the output variable before calling
610
0
    this->Makefile->RemoveDefinition(outputVar);
611
612
    // Build the function call: functionName(s, outputVar)
613
0
    cmListFileContext context = this->Makefile->GetBacktrace().Top();
614
0
    std::vector<cmListFileArgument> funcArgs;
615
0
    funcArgs.emplace_back(s, cmListFileArgument::Quoted, context.Line);
616
0
    funcArgs.emplace_back(outputVar, cmListFileArgument::Quoted, context.Line);
617
0
    cmListFileFunction func{ this->FunctionName, context.Line, context.Line,
618
0
                             std::move(funcArgs) };
619
620
0
    cmExecutionStatus status(*this->Makefile);
621
0
    if (!this->Makefile->ExecuteCommand(func, status) ||
622
0
        status.GetNestedError()) {
623
0
      throw transform_error(
624
0
        cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
625
0
                 this->FunctionName, "\" failed during execution."));
626
0
    }
627
628
    // Read back the output variable
629
0
    cmValue result = this->Makefile->GetDefinition(outputVar);
630
0
    if (!result) {
631
0
      throw transform_error(
632
0
        cmStrCat("sub-command TRANSFORM, action APPLY: function \"",
633
0
                 this->FunctionName, "\" did not set the output variable."));
634
0
    }
635
636
    // Copy the result before cleaning up (RemoveDefinition invalidates the
637
    // cmValue pointer).
638
0
    std::string output = *result;
639
640
    // Clean up
641
0
    this->Makefile->RemoveDefinition(outputVar);
642
643
0
    return output;
644
0
  }
645
646
private:
647
  std::string FunctionName;
648
  cmMakefile* Makefile = nullptr;
649
};
650
651
// Descriptor of action
652
// Arity: number of arguments required for the action
653
// Transform: Object implementing the action
654
struct ActionDescriptor
655
{
656
  ActionDescriptor(cmList::TransformAction action)
657
0
    : Action(action)
658
0
  {
659
0
  }
660
  ActionDescriptor(cmList::TransformAction action, std::string name,
661
                   std::size_t arity,
662
                   std::unique_ptr<TransformAction> transform)
663
0
    : Action(action)
664
0
    , Name(std::move(name))
665
0
    , Arity(arity)
666
0
    , Transform(std::move(transform))
667
0
  {
668
0
  }
669
670
0
  operator cmList::TransformAction() const { return this->Action; }
671
672
  cmList::TransformAction Action;
673
  std::string Name;
674
  std::size_t Arity = 0;
675
  std::unique_ptr<TransformAction> Transform;
676
};
677
678
// Build a set of supported actions.
679
using ActionDescriptorSet = std::set<
680
  ActionDescriptor,
681
  std::function<bool(cmList::TransformAction, cmList::TransformAction)>>;
682
683
ActionDescriptorSet Descriptors([](cmList::TransformAction x,
684
0
                                   cmList::TransformAction y) {
685
0
  return x < y;
686
0
});
687
688
ActionDescriptorSet::iterator TransformConfigure(
689
  cmList::TransformAction action,
690
  std::unique_ptr<cmList::TransformSelector>& selector, std::size_t arity)
691
0
{
692
0
  if (Descriptors.empty()) {
693
0
    Descriptors.emplace(cmList::TransformAction::APPEND, "APPEND", 1,
694
0
                        cm::make_unique<TransformActionAppend>());
695
0
    Descriptors.emplace(cmList::TransformAction::PREPEND, "PREPEND", 1,
696
0
                        cm::make_unique<TransformActionPrepend>());
697
0
    Descriptors.emplace(cmList::TransformAction::TOUPPER, "TOUPPER", 0,
698
0
                        cm::make_unique<TransformActionToUpper>());
699
0
    Descriptors.emplace(cmList::TransformAction::TOLOWER, "TOLOWER", 0,
700
0
                        cm::make_unique<TransformActionToLower>());
701
0
    Descriptors.emplace(cmList::TransformAction::STRIP, "STRIP", 0,
702
0
                        cm::make_unique<TransformActionStrip>());
703
0
    Descriptors.emplace(cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0,
704
0
                        cm::make_unique<TransformActionGenexStrip>());
705
0
    Descriptors.emplace(cmList::TransformAction::REPLACE, "REPLACE", 2,
706
0
                        cm::make_unique<TransformActionReplace>());
707
0
    Descriptors.emplace(cmList::TransformAction::APPLY, "APPLY", 1,
708
0
                        cm::make_unique<TransformActionApply>());
709
0
  }
710
711
0
  auto descriptor = Descriptors.find(action);
712
0
  if (descriptor == Descriptors.end()) {
713
0
    throw transform_error(cmStrCat(" sub-command TRANSFORM, ",
714
0
                                   static_cast<int>(action),
715
0
                                   " invalid action."));
716
0
  }
717
718
0
  if (descriptor->Arity != arity) {
719
0
    throw transform_error(cmStrCat("sub-command TRANSFORM, action ",
720
0
                                   descriptor->Name, " expects ",
721
0
                                   descriptor->Arity, " argument(s)."));
722
0
  }
723
0
  if (!selector) {
724
0
    selector = cm::make_unique<TransformNoSelector>();
725
0
  }
726
727
0
  return descriptor;
728
0
}
729
}
730
731
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::New()
732
0
{
733
0
  return cm::make_unique<TransformNoSelector>();
734
0
}
735
736
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
737
  std::initializer_list<index_type> indexes)
738
0
{
739
0
  return cm::make_unique<TransformSelectorAt>(
740
0
    std::vector<index_type>{ indexes.begin(), indexes.end() });
741
0
  ;
742
0
}
743
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
744
  std::vector<index_type> const& indexes)
745
0
{
746
0
  return cm::make_unique<TransformSelectorAt>(indexes);
747
0
}
748
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
749
  std::vector<index_type>&& indexes)
750
0
{
751
0
  return cm::make_unique<TransformSelectorAt>(std::move(indexes));
752
0
}
753
754
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
755
  std::initializer_list<index_type> indexes)
756
0
{
757
0
  if (indexes.size() < 2 || indexes.size() > 3) {
758
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
759
0
                          "expects 2 or 3 arguments");
760
0
  }
761
0
  if (indexes.size() == 3 && *(indexes.begin() + 2) < 0) {
762
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
763
0
                          "positive numeric value for <step>.");
764
0
  }
765
766
0
  return cm::make_unique<TransformSelectorFor>(
767
0
    *indexes.begin(), *(indexes.begin() + 1),
768
0
    indexes.size() == 3 ? *(indexes.begin() + 2) : 1);
769
0
}
770
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
771
  std::vector<index_type> const& indexes)
772
0
{
773
0
  if (indexes.size() < 2 || indexes.size() > 3) {
774
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
775
0
                          "expects 2 or 3 arguments");
776
0
  }
777
0
  if (indexes.size() == 3 && indexes[2] < 0) {
778
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
779
0
                          "positive numeric value for <step>.");
780
0
  }
781
782
0
  return cm::make_unique<TransformSelectorFor>(
783
0
    indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
784
0
}
785
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
786
  std::vector<index_type>&& indexes)
787
0
{
788
0
  if (indexes.size() < 2 || indexes.size() > 3) {
789
0
    throw transform_error("sub-command TRANSFORM, selector FOR "
790
0
                          "expects 2 or 3 arguments");
791
0
  }
792
0
  if (indexes.size() == 3 && indexes[2] < 0) {
793
0
    throw transform_error("sub-command TRANSFORM, selector FOR expects "
794
0
                          "positive numeric value for <step>.");
795
0
  }
796
797
0
  return cm::make_unique<TransformSelectorFor>(
798
0
    indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
799
0
}
800
801
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
802
  std::string const& regex)
803
0
{
804
0
  std::unique_ptr<::TransformSelector> selector =
805
0
    cm::make_unique<TransformSelectorRegex>(regex);
806
0
  if (!selector->Validate()) {
807
0
    throw transform_error(
808
0
      cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
809
0
               "regex \"",
810
0
               regex, "\"."));
811
0
  }
812
  // weird construct to please all compilers
813
0
  return std::unique_ptr<cmList::TransformSelector>(selector.release());
814
0
}
815
std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
816
  std::string&& regex)
817
0
{
818
0
  std::unique_ptr<::TransformSelector> selector =
819
0
    cm::make_unique<TransformSelectorRegex>(std::move(regex));
820
0
  if (!selector->Validate()) {
821
0
    throw transform_error(
822
0
      cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
823
0
               "regex \"",
824
0
               regex, "\"."));
825
0
  }
826
  // weird construct to please all compilers
827
0
  return std::unique_ptr<cmList::TransformSelector>(selector.release());
828
0
}
829
830
cmList& cmList::transform(TransformAction action,
831
                          std::unique_ptr<TransformSelector> selector)
832
0
{
833
0
  auto descriptor = TransformConfigure(action, selector, 0);
834
835
0
  descriptor->Transform->Initialize(
836
0
    static_cast<::TransformSelector*>(selector.get()));
837
838
0
  static_cast<::TransformSelector&>(*selector).Transform(
839
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
840
0
      return (*descriptor->Transform)(s);
841
0
    });
842
843
0
  return *this;
844
0
}
845
846
cmList& cmList::transform(TransformAction action, std::string const& arg,
847
                          std::unique_ptr<TransformSelector> selector)
848
0
{
849
0
  auto descriptor = TransformConfigure(action, selector, 1);
850
851
0
  descriptor->Transform->Initialize(
852
0
    static_cast<::TransformSelector*>(selector.get()), arg);
853
854
0
  static_cast<::TransformSelector&>(*selector).Transform(
855
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
856
0
      return (*descriptor->Transform)(s);
857
0
    });
858
859
0
  return *this;
860
0
}
861
862
cmList& cmList::transform(TransformAction action, std::string const& arg1,
863
                          std::string const& arg2,
864
                          std::unique_ptr<TransformSelector> selector)
865
0
{
866
0
  auto descriptor = TransformConfigure(action, selector, 2);
867
868
0
  descriptor->Transform->Initialize(
869
0
    static_cast<::TransformSelector*>(selector.get()), arg1, arg2);
870
871
0
  static_cast<::TransformSelector&>(*selector).Transform(
872
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
873
0
      return (*descriptor->Transform)(s);
874
0
    });
875
876
0
  return *this;
877
0
}
878
879
cmList& cmList::transform(TransformAction action,
880
                          std::vector<std::string> const& args,
881
                          std::unique_ptr<TransformSelector> selector)
882
0
{
883
0
  auto descriptor = TransformConfigure(action, selector, args.size());
884
885
0
  descriptor->Transform->Initialize(
886
0
    static_cast<::TransformSelector*>(selector.get()), args);
887
888
0
  static_cast<::TransformSelector&>(*selector).Transform(
889
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
890
0
      return (*descriptor->Transform)(s);
891
0
    });
892
893
0
  return *this;
894
0
}
895
896
cmList& cmList::transform(TransformAction action, std::string const& arg,
897
                          cmMakefile& makefile,
898
                          std::unique_ptr<TransformSelector> selector)
899
0
{
900
0
  auto descriptor = TransformConfigure(action, selector, 1);
901
902
0
  auto* applyAction =
903
0
    static_cast<TransformActionApply*>(descriptor->Transform.get());
904
0
  applyAction->Initialize(static_cast<::TransformSelector*>(selector.get()),
905
0
                          arg, makefile);
906
907
0
  static_cast<::TransformSelector&>(*selector).Transform(
908
0
    this->Values, [&descriptor](std::string const& s) -> std::string {
909
0
      return (*descriptor->Transform)(s);
910
0
    });
911
912
0
  return *this;
913
0
}
914
915
std::string& cmList::append(std::string& list, std::string&& value)
916
0
{
917
0
  if (list.empty()) {
918
0
    list = std::move(value);
919
0
  } else {
920
0
    list += cmStrCat(cmList::element_separator, value);
921
0
  }
922
923
0
  return list;
924
0
}
925
std::string& cmList::append(std::string& list, cm::string_view value)
926
0
{
927
0
  return cmList::append(list, std::string{ value });
928
0
}
929
930
std::string& cmList::prepend(std::string& list, std::string&& value)
931
0
{
932
0
  if (list.empty()) {
933
0
    list = std::move(value);
934
0
  } else {
935
0
    list.insert(0, cmStrCat(value, cmList::element_separator));
936
0
  }
937
938
0
  return list;
939
0
}
940
std::string& cmList::prepend(std::string& list, cm::string_view value)
941
0
{
942
0
  return cmList::prepend(list, std::string{ value });
943
0
}
944
945
cmList::size_type cmList::ComputeIndex(index_type pos, bool boundCheck) const
946
0
{
947
0
  if (boundCheck) {
948
0
    if (this->Values.empty()) {
949
0
      throw std::out_of_range(
950
0
        cmStrCat("index: ", pos, " out of range (0, 0)"));
951
0
    }
952
953
0
    auto index = pos;
954
0
    if (!this->Values.empty()) {
955
0
      auto length = this->Values.size();
956
0
      if (index < 0) {
957
0
        index = static_cast<index_type>(length) + index;
958
0
      }
959
0
      if (index < 0 || length <= static_cast<size_type>(index)) {
960
0
        throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
961
0
                                         this->Values.size(), ", ",
962
0
                                         this->Values.size() - 1, ')'));
963
0
      }
964
0
    }
965
0
    return index;
966
0
  }
967
968
0
  return pos < 0 ? this->Values.size() + pos : pos;
969
0
}
970
cmList::size_type cmList::ComputeInsertIndex(index_type pos,
971
                                             bool boundCheck) const
972
0
{
973
0
  if (boundCheck) {
974
0
    if (this->Values.empty() && pos != 0) {
975
0
      throw std::out_of_range(
976
0
        cmStrCat("index: ", pos, " out of range (0, 0)"));
977
0
    }
978
979
0
    auto index = pos;
980
0
    if (!this->Values.empty()) {
981
0
      auto length = this->Values.size();
982
0
      if (index < 0) {
983
0
        index = static_cast<index_type>(length) + index;
984
0
      }
985
0
      if (index < 0 || length < static_cast<size_type>(index)) {
986
0
        throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
987
0
                                         this->Values.size(), ", ",
988
0
                                         this->Values.size(), ')'));
989
0
      }
990
0
    }
991
0
    return index;
992
0
  }
993
994
0
  return pos < 0 ? this->Values.size() + pos : pos;
995
0
}
996
997
cmList cmList::GetItems(std::vector<index_type>&& indexes) const
998
0
{
999
0
  cmList listItems;
1000
1001
0
  for (auto index : indexes) {
1002
0
    listItems.emplace_back(this->get_item(index));
1003
0
  }
1004
1005
0
  return listItems;
1006
0
}
1007
1008
cmList& cmList::RemoveItems(std::vector<index_type>&& indexes)
1009
0
{
1010
0
  if (indexes.empty()) {
1011
0
    return *this;
1012
0
  }
1013
1014
  // compute all indexes
1015
0
  std::vector<size_type> idx(indexes.size());
1016
0
  std::transform(indexes.cbegin(), indexes.cend(), idx.begin(),
1017
0
                 [this](index_type index) -> size_type {
1018
0
                   return this->ComputeIndex(index);
1019
0
                 });
1020
1021
0
  std::sort(idx.begin(), idx.end(),
1022
0
            [](size_type l, size_type r) { return l > r; });
1023
0
  auto newEnd = std::unique(idx.begin(), idx.end());
1024
0
  idx.erase(newEnd, idx.end());
1025
1026
0
  for (auto index : idx) {
1027
0
    this->erase(this->begin() + index);
1028
0
  }
1029
1030
0
  return *this;
1031
0
}
1032
1033
cmList& cmList::RemoveItems(std::vector<std::string>&& items)
1034
0
{
1035
0
  std::sort(items.begin(), items.end());
1036
0
  auto last = std::unique(items.begin(), items.end());
1037
0
  auto first = items.begin();
1038
1039
0
  auto newEnd = cmRemoveMatching(this->Values, cmMakeRange(first, last));
1040
0
  this->Values.erase(newEnd, this->Values.end());
1041
1042
0
  return *this;
1043
0
}
1044
1045
cmList::container_type::iterator cmList::Insert(
1046
  container_type& container, container_type::const_iterator pos,
1047
  std::string&& value, ExpandElements expandElements,
1048
  EmptyElements emptyElements)
1049
14.2k
{
1050
14.2k
  auto delta = std::distance(container.cbegin(), pos);
1051
14.2k
  auto insertPos = container.begin() + delta;
1052
1053
14.2k
  if (expandElements == ExpandElements::Yes) {
1054
    // If argument is empty, it is an empty list.
1055
14.2k
    if (emptyElements == EmptyElements::No && value.empty()) {
1056
0
      return insertPos;
1057
0
    }
1058
1059
    // if there are no ; in the name then just copy the current string
1060
14.2k
    if (value.find(';') == std::string::npos) {
1061
9.91k
      return container.insert(insertPos, std::move(value));
1062
9.91k
    }
1063
1064
4.32k
    std::string newValue;
1065
    // Break the string at non-escaped semicolons not nested in [].
1066
4.32k
    int squareNesting = 0;
1067
4.32k
    auto last = value.begin();
1068
4.32k
    auto const cend = value.end();
1069
664k
    for (auto c = last; c != cend; ++c) {
1070
660k
      switch (*c) {
1071
20.2k
        case '\\': {
1072
          // We only want to allow escaping of semicolons.  Other
1073
          // escapes should not be processed here.
1074
20.2k
          auto cnext = c + 1;
1075
20.2k
          if ((cnext != cend) && *cnext == ';') {
1076
9.06k
            newValue.append(last, c);
1077
            // Skip over the escape character
1078
9.06k
            last = cnext;
1079
9.06k
            c = cnext;
1080
9.06k
          }
1081
20.2k
        } break;
1082
17.7k
        case '[': {
1083
17.7k
          ++squareNesting;
1084
17.7k
        } break;
1085
51.5k
        case ']': {
1086
51.5k
          --squareNesting;
1087
51.5k
        } break;
1088
41.5k
        case ';': {
1089
          // brackets.
1090
41.5k
          if (squareNesting == 0) {
1091
32.0k
            newValue.append(last, c);
1092
            // Skip over the semicolon
1093
32.0k
            last = c + 1;
1094
32.0k
            if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
1095
              // Add the last argument.
1096
25.8k
              insertPos = container.insert(insertPos, newValue);
1097
25.8k
              insertPos++;
1098
25.8k
              newValue.clear();
1099
25.8k
            }
1100
32.0k
          }
1101
41.5k
        } break;
1102
529k
        default: {
1103
          // Just append this character.
1104
529k
        } break;
1105
660k
      }
1106
660k
    }
1107
4.32k
    newValue.append(last, cend);
1108
4.32k
    if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
1109
      // Add the last argument.
1110
2.62k
      container.insert(insertPos, std::move(newValue));
1111
2.62k
    }
1112
4.32k
  } else if (!value.empty() || emptyElements == EmptyElements::Yes) {
1113
0
    return container.insert(insertPos, std::move(value));
1114
0
  }
1115
4.32k
  return container.begin() + delta;
1116
14.2k
}
1117
1118
std::string const& cmList::ToString(BT<std::string> const& s)
1119
0
{
1120
0
  return s.Value;
1121
0
}