Coverage Report

Created: 2026-03-12 06:35

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