Coverage Report

Created: 2026-06-15 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmListCommand.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
#include "cmListCommand.h"
4
5
#include <cassert>
6
#include <cstdio>
7
#include <functional>
8
#include <set>
9
#include <sstream>
10
#include <stdexcept>
11
#include <utility>
12
#include <vector>
13
14
#include <cm/memory>
15
#include <cm/optional>
16
#include <cmext/algorithm>
17
#include <cmext/string_view>
18
19
#include "cmExecutionStatus.h"
20
#include "cmList.h"
21
#include "cmMakefile.h"
22
#include "cmPolicies.h"
23
#include "cmRange.h"
24
#include "cmStringAlgorithms.h"
25
#include "cmSubcommandTable.h"
26
#include "cmValue.h"
27
28
namespace {
29
30
bool GetIndexArg(std::string const& arg, int* idx, cmMakefile& mf)
31
0
{
32
0
  long value;
33
0
  if (!cmStrToLong(arg, &value)) {
34
0
    switch (mf.GetPolicyStatus(cmPolicies::CMP0121)) {
35
0
      case cmPolicies::WARN: {
36
        // Default is to warn and use old behavior OLD behavior is to allow
37
        // compatibility, so issue a warning and use the previous behavior.
38
0
        mf.IssuePolicyWarning(
39
0
          cmPolicies::CMP0121, {},
40
0
          cmStrCat("Invalid list index \""_s, arg, "\"."_s));
41
0
        CM_FALLTHROUGH;
42
0
      }
43
0
      case cmPolicies::OLD:
44
        // OLD behavior is to allow compatibility, so just ignore the
45
        // situation.
46
0
        break;
47
0
      case cmPolicies::NEW:
48
0
        return false;
49
0
    }
50
0
  }
51
52
  // Truncation is happening here, but it had always been happening here.
53
0
  *idx = static_cast<int>(value);
54
55
0
  return true;
56
0
}
57
58
bool GetListString(std::string& listString, std::string const& var,
59
                   cmMakefile const& makefile)
60
0
{
61
  // get the old value
62
0
  cmValue cacheValue = makefile.GetDefinition(var);
63
0
  if (!cacheValue) {
64
0
    return false;
65
0
  }
66
0
  listString = *cacheValue;
67
0
  return true;
68
0
}
69
70
cm::optional<cmList> GetList(std::string const& var,
71
                             cmMakefile const& makefile)
72
0
{
73
0
  cm::optional<cmList> list;
74
75
0
  std::string listString;
76
0
  if (!GetListString(listString, var, makefile)) {
77
0
    return list;
78
0
  }
79
  // if the size of the list
80
0
  if (listString.empty()) {
81
0
    list.emplace();
82
0
    return list;
83
0
  }
84
  // expand the variable into a list
85
0
  list.emplace(listString, cmList::EmptyElements::Yes);
86
  // if no empty elements then just return
87
0
  if (!cm::contains(*list, std::string())) {
88
0
    return list;
89
0
  }
90
0
  return list;
91
0
}
92
93
bool HandleLengthCommand(std::vector<std::string> const& args,
94
                         cmExecutionStatus& status)
95
0
{
96
0
  if (args.size() != 3) {
97
0
    status.SetError("sub-command LENGTH requires two arguments.");
98
0
    return false;
99
0
  }
100
101
0
  std::string const& listName = args[1];
102
0
  std::string const& variableName = args.back();
103
104
0
  auto list = GetList(listName, status.GetMakefile());
105
0
  status.GetMakefile().AddDefinition(variableName,
106
0
                                     std::to_string(list ? list->size() : 0));
107
108
0
  return true;
109
0
}
110
111
bool HandleGetCommand(std::vector<std::string> const& args,
112
                      cmExecutionStatus& status)
113
0
{
114
0
  if (args.size() < 4) {
115
0
    status.SetError("sub-command GET requires at least three arguments.");
116
0
    return false;
117
0
  }
118
119
0
  std::string const& listName = args[1];
120
0
  std::string const& variableName = args.back();
121
  // expand the variable
122
0
  auto list = GetList(listName, status.GetMakefile());
123
0
  if (!list) {
124
0
    status.GetMakefile().AddDefinition(variableName, "NOTFOUND");
125
0
    return true;
126
0
  }
127
  // FIXME: Add policy to make non-existing lists an error like empty lists.
128
0
  if (list->empty()) {
129
0
    status.SetError("GET given empty list");
130
0
    return false;
131
0
  }
132
133
0
  std::vector<int> indexes;
134
0
  for (std::size_t cc = 2; cc < args.size() - 1; cc++) {
135
0
    int index;
136
0
    if (!GetIndexArg(args[cc], &index, status.GetMakefile())) {
137
0
      status.SetError(cmStrCat("index: ", args[cc], " is not a valid index"));
138
0
      return false;
139
0
    }
140
0
    indexes.push_back(index);
141
0
  }
142
143
0
  try {
144
0
    auto values = list->get_items(indexes.begin(), indexes.end());
145
0
    status.GetMakefile().AddDefinition(variableName, values.to_string());
146
0
    return true;
147
0
  } catch (std::out_of_range& e) {
148
0
    status.SetError(e.what());
149
0
    return false;
150
0
  }
151
0
}
152
153
bool HandleAppendCommand(std::vector<std::string> const& args,
154
                         cmExecutionStatus& status)
155
0
{
156
0
  assert(args.size() >= 2);
157
158
  // Skip if nothing to append.
159
0
  if (args.size() < 3) {
160
0
    return true;
161
0
  }
162
163
0
  cmMakefile& makefile = status.GetMakefile();
164
0
  std::string const& listName = args[1];
165
  // expand the variable
166
0
  std::string listString;
167
0
  GetListString(listString, listName, makefile);
168
169
0
  makefile.AddDefinition(
170
0
    listName, cmList::append(listString, args.begin() + 2, args.end()));
171
0
  return true;
172
0
}
173
174
bool HandlePrependCommand(std::vector<std::string> const& args,
175
                          cmExecutionStatus& status)
176
0
{
177
0
  assert(args.size() >= 2);
178
179
  // Skip if nothing to prepend.
180
0
  if (args.size() < 3) {
181
0
    return true;
182
0
  }
183
184
0
  cmMakefile& makefile = status.GetMakefile();
185
0
  std::string const& listName = args[1];
186
  // expand the variable
187
0
  std::string listString;
188
0
  GetListString(listString, listName, makefile);
189
190
0
  makefile.AddDefinition(
191
0
    listName, cmList::prepend(listString, args.begin() + 2, args.end()));
192
0
  return true;
193
0
}
194
195
bool HandlePopBackCommand(std::vector<std::string> const& args,
196
                          cmExecutionStatus& status)
197
0
{
198
0
  assert(args.size() >= 2);
199
200
0
  cmMakefile& makefile = status.GetMakefile();
201
0
  auto ai = args.cbegin();
202
0
  ++ai; // Skip subcommand name
203
0
  std::string const& listName = *ai++;
204
0
  auto list = GetList(listName, makefile);
205
206
0
  if (!list) {
207
    // Can't get the list definition... undefine any vars given after.
208
0
    for (; ai != args.cend(); ++ai) {
209
0
      makefile.RemoveDefinition(*ai);
210
0
    }
211
0
    return true;
212
0
  }
213
214
0
  if (!list->empty()) {
215
0
    if (ai == args.cend()) {
216
      // No variables are given... Just remove one element.
217
0
      list->pop_back();
218
0
    } else {
219
      // Ok, assign elements to be removed to the given variables
220
0
      for (; !list->empty() && ai != args.cend(); ++ai) {
221
0
        assert(!ai->empty());
222
0
        makefile.AddDefinition(*ai, list->back());
223
0
        list->pop_back();
224
0
      }
225
      // Undefine the rest variables if the list gets empty earlier...
226
0
      for (; ai != args.cend(); ++ai) {
227
0
        makefile.RemoveDefinition(*ai);
228
0
      }
229
0
    }
230
231
0
    makefile.AddDefinition(listName, list->to_string());
232
233
0
  } else if (ai !=
234
0
             args.cend()) { // The list is empty, but some args were given
235
    // Need to *undefine* 'em all, cuz there are no items to assign...
236
0
    for (; ai != args.cend(); ++ai) {
237
0
      makefile.RemoveDefinition(*ai);
238
0
    }
239
0
  }
240
241
0
  return true;
242
0
}
243
244
bool HandlePopFrontCommand(std::vector<std::string> const& args,
245
                           cmExecutionStatus& status)
246
0
{
247
0
  assert(args.size() >= 2);
248
249
0
  cmMakefile& makefile = status.GetMakefile();
250
0
  auto ai = args.cbegin();
251
0
  ++ai; // Skip subcommand name
252
0
  std::string const& listName = *ai++;
253
0
  auto list = GetList(listName, makefile);
254
255
0
  if (!list) {
256
    // Can't get the list definition... undefine any vars given after.
257
0
    for (; ai != args.cend(); ++ai) {
258
0
      makefile.RemoveDefinition(*ai);
259
0
    }
260
0
    return true;
261
0
  }
262
263
0
  if (!list->empty()) {
264
0
    if (ai == args.cend()) {
265
      // No variables are given... Just remove one element.
266
0
      list->pop_front();
267
0
    } else {
268
      // Ok, assign elements to be removed to the given variables
269
0
      auto vi = list->begin();
270
0
      for (; vi != list->end() && ai != args.cend(); ++ai, ++vi) {
271
0
        assert(!ai->empty());
272
0
        makefile.AddDefinition(*ai, *vi);
273
0
      }
274
0
      list->erase(list->begin(), vi);
275
      // Undefine the rest variables if the list gets empty earlier...
276
0
      for (; ai != args.cend(); ++ai) {
277
0
        makefile.RemoveDefinition(*ai);
278
0
      }
279
0
    }
280
281
0
    makefile.AddDefinition(listName, list->to_string());
282
283
0
  } else if (ai !=
284
0
             args.cend()) { // The list is empty, but some args were given
285
    // Need to *undefine* 'em all, cuz there are no items to assign...
286
0
    for (; ai != args.cend(); ++ai) {
287
0
      makefile.RemoveDefinition(*ai);
288
0
    }
289
0
  }
290
291
0
  return true;
292
0
}
293
294
bool HandleFindCommand(std::vector<std::string> const& args,
295
                       cmExecutionStatus& status)
296
0
{
297
0
  if (args.size() != 4) {
298
0
    status.SetError("sub-command FIND requires three arguments.");
299
0
    return false;
300
0
  }
301
302
0
  std::string const& listName = args[1];
303
0
  std::string const& variableName = args.back();
304
  // expand the variable
305
0
  auto list = GetList(listName, status.GetMakefile());
306
307
0
  if (!list) {
308
0
    status.GetMakefile().AddDefinition(variableName, "-1");
309
0
    return true;
310
0
  }
311
312
0
  auto index = list->find(args[2]);
313
0
  status.GetMakefile().AddDefinition(
314
0
    variableName, index == cmList::npos ? "-1" : std::to_string(index));
315
0
  return true;
316
0
}
317
318
bool HandleInsertCommand(std::vector<std::string> const& args,
319
                         cmExecutionStatus& status)
320
0
{
321
0
  if (args.size() < 4) {
322
0
    status.SetError("sub-command INSERT requires at least three arguments.");
323
0
    return false;
324
0
  }
325
326
0
  std::string const& listName = args[1];
327
328
  // expand the variable
329
0
  int index;
330
0
  if (!GetIndexArg(args[2], &index, status.GetMakefile())) {
331
0
    status.SetError(cmStrCat("index: ", args[2], " is not a valid index"));
332
0
    return false;
333
0
  }
334
0
  auto list = GetList(listName, status.GetMakefile());
335
0
  if (!list) {
336
0
    list = cmList{};
337
0
  }
338
339
0
  try {
340
0
    list->insert_items(index, args.begin() + 3, args.end(),
341
0
                       cmList::ExpandElements::No, cmList::EmptyElements::Yes);
342
0
    status.GetMakefile().AddDefinition(listName, list->to_string());
343
0
    return true;
344
0
  } catch (std::out_of_range& e) {
345
0
    status.SetError(e.what());
346
0
    return false;
347
0
  }
348
0
}
349
350
bool HandleJoinCommand(std::vector<std::string> const& args,
351
                       cmExecutionStatus& status)
352
0
{
353
0
  if (args.size() != 4) {
354
0
    status.SetError(cmStrCat("sub-command JOIN requires three arguments (",
355
0
                             args.size() - 1, " found)."));
356
0
    return false;
357
0
  }
358
359
0
  std::string const& listName = args[1];
360
0
  std::string const& glue = args[2];
361
0
  std::string const& variableName = args[3];
362
363
  // expand the variable
364
0
  auto list = GetList(listName, status.GetMakefile());
365
366
0
  if (!list) {
367
0
    status.GetMakefile().AddDefinition(variableName, "");
368
0
    return true;
369
0
  }
370
371
0
  status.GetMakefile().AddDefinition(variableName, list->join(glue));
372
0
  return true;
373
0
}
374
375
bool HandleRemoveItemCommand(std::vector<std::string> const& args,
376
                             cmExecutionStatus& status)
377
0
{
378
0
  assert(args.size() >= 2);
379
380
0
  if (args.size() == 2) {
381
0
    return true;
382
0
  }
383
384
0
  std::string const& listName = args[1];
385
  // expand the variable
386
0
  auto list = GetList(listName, status.GetMakefile());
387
388
0
  if (!list) {
389
0
    return true;
390
0
  }
391
392
0
  status.GetMakefile().AddDefinition(
393
0
    listName, list->remove_items(args.begin() + 2, args.end()).to_string());
394
0
  return true;
395
0
}
396
397
bool HandleReverseCommand(std::vector<std::string> const& args,
398
                          cmExecutionStatus& status)
399
0
{
400
0
  assert(args.size() >= 2);
401
0
  if (args.size() > 2) {
402
0
    status.SetError("sub-command REVERSE only takes one argument.");
403
0
    return false;
404
0
  }
405
406
0
  std::string const& listName = args[1];
407
  // expand the variable
408
0
  auto list = GetList(listName, status.GetMakefile());
409
410
0
  if (!list) {
411
0
    return true;
412
0
  }
413
414
0
  status.GetMakefile().AddDefinition(listName, list->reverse().to_string());
415
0
  return true;
416
0
}
417
418
bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args,
419
                                   cmExecutionStatus& status)
420
0
{
421
0
  assert(args.size() >= 2);
422
0
  if (args.size() > 2) {
423
0
    status.SetError("sub-command REMOVE_DUPLICATES only takes one argument.");
424
0
    return false;
425
0
  }
426
427
0
  std::string const& listName = args[1];
428
  // expand the variable
429
0
  auto list = GetList(listName, status.GetMakefile());
430
431
0
  if (!list) {
432
0
    return true;
433
0
  }
434
435
0
  status.GetMakefile().AddDefinition(listName,
436
0
                                     list->remove_duplicates().to_string());
437
0
  return true;
438
0
}
439
440
bool HandleTransformCommand(std::vector<std::string> const& args,
441
                            cmExecutionStatus& status)
442
0
{
443
0
  if (args.size() < 3) {
444
0
    status.SetError(
445
0
      "sub-command TRANSFORM requires an action to be specified.");
446
0
    return false;
447
0
  }
448
449
  // Descriptor of action
450
  // Action: enum value identifying action
451
  // Arity: number of arguments required for the action
452
0
  struct ActionDescriptor
453
0
  {
454
0
    ActionDescriptor(std::string name)
455
0
      : Name(std::move(name))
456
0
    {
457
0
    }
458
0
    ActionDescriptor(std::string name, cmList::TransformAction action,
459
0
                     int arity)
460
0
      : Name(std::move(name))
461
0
      , Action(action)
462
0
      , Arity(arity)
463
0
    {
464
0
    }
465
466
0
    operator std::string const&() const { return this->Name; }
467
468
0
    std::string Name;
469
0
    cmList::TransformAction Action;
470
0
    int Arity = 0;
471
0
  };
472
473
  // Build a set of supported actions.
474
0
  std::set<ActionDescriptor,
475
0
           std::function<bool(std::string const&, std::string const&)>>
476
0
    descriptors{ { { "APPEND", cmList::TransformAction::APPEND, 1 },
477
0
                   { "PREPEND", cmList::TransformAction::PREPEND, 1 },
478
0
                   { "TOUPPER", cmList::TransformAction::TOUPPER, 0 },
479
0
                   { "TOLOWER", cmList::TransformAction::TOLOWER, 0 },
480
0
                   { "STRIP", cmList::TransformAction::STRIP, 0 },
481
0
                   { "GENEX_STRIP", cmList::TransformAction::GENEX_STRIP, 0 },
482
0
                   { "REPLACE", cmList::TransformAction::REPLACE, 2 },
483
0
                   { "APPLY", cmList::TransformAction::APPLY, 1 } },
484
0
                 [](std::string const& x, std::string const& y) {
485
0
                   return x < y;
486
0
                 } };
487
488
0
  std::string const& listName = args[1];
489
490
  // Parse all possible function parameters
491
0
  using size_type = std::vector<std::string>::size_type;
492
0
  size_type index = 2;
493
494
0
  auto descriptor = descriptors.find(args[index]);
495
496
0
  if (descriptor == descriptors.end()) {
497
0
    status.SetError(
498
0
      cmStrCat(" sub-command TRANSFORM, ", args[index], " invalid action."));
499
0
    return false;
500
0
  }
501
502
  // Action arguments
503
0
  index += 1;
504
0
  if (args.size() < index + descriptor->Arity) {
505
0
    status.SetError(cmStrCat("sub-command TRANSFORM, action ",
506
0
                             descriptor->Name, " expects ", descriptor->Arity,
507
0
                             " argument(s)."));
508
0
    return false;
509
0
  }
510
511
0
  std::vector<std::string> arguments;
512
0
  index += descriptor->Arity;
513
0
  if (descriptor->Arity > 0) {
514
0
    arguments =
515
0
      std::vector<std::string>(args.begin() + 3, args.begin() + index);
516
0
  }
517
518
0
  std::string const REGEX{ "REGEX" };
519
0
  std::string const AT{ "AT" };
520
0
  std::string const FOR{ "FOR" };
521
0
  std::string const PREDICATE{ "PREDICATE" };
522
0
  std::string const OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
523
0
  std::unique_ptr<cmList::TransformSelector> selector;
524
0
  std::string outputName = listName;
525
526
0
  try {
527
    // handle optional arguments
528
0
    while (args.size() > index) {
529
0
      if ((args[index] == REGEX || args[index] == AT || args[index] == FOR ||
530
0
           args[index] == PREDICATE) &&
531
0
          selector) {
532
0
        status.SetError(
533
0
          cmStrCat("sub-command TRANSFORM, selector already specified (",
534
0
                   selector->GetTag(), ")."));
535
536
0
        return false;
537
0
      }
538
539
      // REGEX selector
540
0
      if (args[index] == REGEX) {
541
0
        if (args.size() == ++index) {
542
0
          status.SetError("sub-command TRANSFORM, selector REGEX expects "
543
0
                          "'regular expression' argument.");
544
0
          return false;
545
0
        }
546
547
0
        selector =
548
0
          cmList::TransformSelector::New<cmList::TransformSelector::REGEX>(
549
0
            args[index]);
550
551
0
        index += 1;
552
0
        continue;
553
0
      }
554
555
      // AT selector
556
0
      if (args[index] == AT) {
557
        // get all specified indexes
558
0
        std::vector<cmList::index_type> indexes;
559
0
        while (args.size() > ++index) {
560
0
          std::size_t pos;
561
0
          int value;
562
563
0
          try {
564
0
            value = std::stoi(args[index], &pos);
565
0
            if (pos != args[index].length()) {
566
              // this is not a number, stop processing
567
0
              break;
568
0
            }
569
0
            indexes.push_back(value);
570
0
          } catch (std::invalid_argument const&) {
571
            // this is not a number, stop processing
572
0
            break;
573
0
          }
574
0
        }
575
576
0
        if (indexes.empty()) {
577
0
          status.SetError(
578
0
            "sub-command TRANSFORM, selector AT expects at least one "
579
0
            "numeric value.");
580
0
          return false;
581
0
        }
582
583
0
        selector =
584
0
          cmList::TransformSelector::New<cmList::TransformSelector::AT>(
585
0
            std::move(indexes));
586
587
0
        continue;
588
0
      }
589
590
      // FOR selector
591
0
      if (args[index] == FOR) {
592
0
        if (args.size() <= ++index + 1) {
593
0
          status.SetError(
594
0
            "sub-command TRANSFORM, selector FOR expects, at least,"
595
0
            " two arguments.");
596
0
          return false;
597
0
        }
598
599
0
        cmList::index_type start = 0;
600
0
        cmList::index_type stop = 0;
601
0
        cmList::index_type step = 1;
602
0
        bool valid = true;
603
0
        try {
604
0
          std::size_t pos;
605
606
0
          start = std::stoi(args[index], &pos);
607
0
          if (pos != args[index].length()) {
608
            // this is not a number
609
0
            valid = false;
610
0
          } else {
611
0
            stop = std::stoi(args[++index], &pos);
612
0
            if (pos != args[index].length()) {
613
              // this is not a number
614
0
              valid = false;
615
0
            }
616
0
          }
617
0
        } catch (std::invalid_argument const&) {
618
          // this is not numbers
619
0
          valid = false;
620
0
        }
621
0
        if (!valid) {
622
0
          status.SetError("sub-command TRANSFORM, selector FOR expects, "
623
0
                          "at least, two numeric values.");
624
0
          return false;
625
0
        }
626
        // try to read a third numeric value for step
627
0
        if (args.size() > ++index) {
628
0
          try {
629
0
            std::size_t pos;
630
631
0
            step = std::stoi(args[index], &pos);
632
0
            if (pos != args[index].length()) {
633
              // this is not a number
634
0
              step = 1;
635
0
            } else {
636
0
              index += 1;
637
0
            }
638
0
          } catch (std::invalid_argument const&) {
639
            // this is not number, ignore exception
640
0
          }
641
0
        }
642
643
0
        if (step <= 0) {
644
0
          status.SetError("sub-command TRANSFORM, selector FOR expects "
645
0
                          "positive numeric value for <step>.");
646
0
          return false;
647
0
        }
648
649
0
        selector =
650
0
          cmList::TransformSelector::New<cmList::TransformSelector::FOR>(
651
0
            { start, stop, step });
652
653
0
        continue;
654
0
      }
655
656
      // PREDICATE selector
657
0
      if (args[index] == PREDICATE) {
658
0
        if (args.size() == ++index) {
659
0
          status.SetError("sub-command TRANSFORM, selector PREDICATE expects "
660
0
                          "'function name' argument.");
661
0
          return false;
662
0
        }
663
664
0
        selector = cmList::TransformSelector::NewPREDICATE(
665
0
          args[index], status.GetMakefile());
666
667
0
        index += 1;
668
0
        continue;
669
0
      }
670
671
      // output variable
672
0
      if (args[index] == OUTPUT_VARIABLE) {
673
0
        if (args.size() == ++index) {
674
0
          status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
675
0
                          "expects variable name argument.");
676
0
          return false;
677
0
        }
678
679
0
        outputName = args[index++];
680
0
        continue;
681
0
      }
682
683
0
      status.SetError(cmStrCat("sub-command TRANSFORM, '",
684
0
                               cmJoin(cmMakeRange(args).advance(index), " "),
685
0
                               "': unexpected argument(s)."));
686
0
      return false;
687
0
    }
688
689
    // expand the list variable
690
0
    auto list = GetList(listName, status.GetMakefile());
691
692
0
    if (!list) {
693
0
      status.GetMakefile().AddDefinition(outputName, "");
694
0
      return true;
695
0
    }
696
697
0
    if (!selector) {
698
0
      selector = cmList::TransformSelector::New();
699
0
    }
700
0
    selector->Makefile = &status.GetMakefile();
701
702
0
    if (descriptor->Action == cmList::TransformAction::APPLY) {
703
0
      list->transform(descriptor->Action, arguments.front(),
704
0
                      status.GetMakefile(), std::move(selector));
705
0
    } else {
706
0
      list->transform(descriptor->Action, arguments, std::move(selector));
707
0
    }
708
0
    status.GetMakefile().AddDefinition(outputName, list->to_string());
709
0
    return true;
710
0
  } catch (cmList::transform_error& e) {
711
0
    status.SetError(e.what());
712
0
    return false;
713
0
  }
714
0
}
715
716
bool HandleSortCommand(std::vector<std::string> const& args,
717
                       cmExecutionStatus& status)
718
0
{
719
0
  assert(args.size() >= 2);
720
0
  if (args.size() > 8) {
721
0
    status.SetError("sub-command SORT only takes up to six arguments.");
722
0
    return false;
723
0
  }
724
725
0
  using SortConfig = cmList::SortConfiguration;
726
0
  SortConfig sortConfig;
727
0
  bool hasComparator = false;
728
729
0
  size_t argumentIndex = 2;
730
0
  std::string const messageHint = "sub-command SORT ";
731
732
0
  while (argumentIndex < args.size()) {
733
0
    std::string const& option = args[argumentIndex++];
734
0
    if (option == "COMPARE") {
735
0
      if (hasComparator) {
736
0
        status.SetError(cmStrCat(messageHint,
737
0
                                 "option \"COMPARE\" is incompatible "
738
0
                                 "with \"COMPARATOR\"."));
739
0
        return false;
740
0
      }
741
0
      if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) {
742
0
        std::string error = cmStrCat(messageHint, "option \"", option,
743
0
                                     "\" has been specified multiple times.");
744
0
        status.SetError(error);
745
0
        return false;
746
0
      }
747
0
      if (argumentIndex < args.size()) {
748
0
        std::string const& argument = args[argumentIndex++];
749
0
        if (argument == "STRING") {
750
0
          sortConfig.Compare = SortConfig::CompareMethod::STRING;
751
0
        } else if (argument == "FILE_BASENAME") {
752
0
          sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME;
753
0
        } else if (argument == "NATURAL") {
754
0
          sortConfig.Compare = SortConfig::CompareMethod::NATURAL;
755
0
        } else {
756
0
          std::string error =
757
0
            cmStrCat(messageHint, "value \"", argument, "\" for option \"",
758
0
                     option, "\" is invalid.");
759
0
          status.SetError(error);
760
0
          return false;
761
0
        }
762
0
      } else {
763
0
        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
764
0
                                 option, "\"."));
765
0
        return false;
766
0
      }
767
0
    } else if (option == "CASE") {
768
0
      if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) {
769
0
        status.SetError(cmStrCat(messageHint, "option \"", option,
770
0
                                 "\" has been specified multiple times."));
771
0
        return false;
772
0
      }
773
0
      if (argumentIndex < args.size()) {
774
0
        std::string const& argument = args[argumentIndex++];
775
0
        if (argument == "SENSITIVE") {
776
0
          sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE;
777
0
        } else if (argument == "INSENSITIVE") {
778
0
          sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE;
779
0
        } else {
780
0
          status.SetError(cmStrCat(messageHint, "value \"", argument,
781
0
                                   "\" for option \"", option,
782
0
                                   "\" is invalid."));
783
0
          return false;
784
0
        }
785
0
      } else {
786
0
        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
787
0
                                 option, "\"."));
788
0
        return false;
789
0
      }
790
0
    } else if (option == "ORDER") {
791
792
0
      if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) {
793
0
        status.SetError(cmStrCat(messageHint, "option \"", option,
794
0
                                 "\" has been specified multiple times."));
795
0
        return false;
796
0
      }
797
0
      if (argumentIndex < args.size()) {
798
0
        std::string const& argument = args[argumentIndex++];
799
0
        if (argument == "ASCENDING") {
800
0
          sortConfig.Order = SortConfig::OrderMode::ASCENDING;
801
0
        } else if (argument == "DESCENDING") {
802
0
          sortConfig.Order = SortConfig::OrderMode::DESCENDING;
803
0
        } else {
804
0
          status.SetError(cmStrCat(messageHint, "value \"", argument,
805
0
                                   "\" for option \"", option,
806
0
                                   "\" is invalid."));
807
0
          return false;
808
0
        }
809
0
      } else {
810
0
        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
811
0
                                 option, "\"."));
812
0
        return false;
813
0
      }
814
0
    } else if (option == "COMPARATOR") {
815
0
      if (hasComparator) {
816
0
        status.SetError(cmStrCat(messageHint, "option \"", option,
817
0
                                 "\" has been specified multiple times."));
818
0
        return false;
819
0
      }
820
0
      if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) {
821
0
        status.SetError(cmStrCat(messageHint,
822
0
                                 "option \"COMPARATOR\" is incompatible "
823
0
                                 "with \"COMPARE\"."));
824
0
        return false;
825
0
      }
826
0
      if (argumentIndex < args.size()) {
827
0
        sortConfig.ComparatorFunction = args[argumentIndex++];
828
0
        sortConfig.Compare = SortConfig::CompareMethod::COMPARATOR;
829
0
        hasComparator = true;
830
0
      } else {
831
0
        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
832
0
                                 option, "\"."));
833
0
        return false;
834
0
      }
835
0
    } else {
836
0
      status.SetError(
837
0
        cmStrCat(messageHint, "option \"", option, "\" is unknown."));
838
0
      return false;
839
0
    }
840
0
  }
841
842
0
  std::string const& listName = args[1];
843
  // expand the variable
844
0
  auto list = GetList(listName, status.GetMakefile());
845
846
0
  if (!list) {
847
0
    return true;
848
0
  }
849
850
0
  if (hasComparator) {
851
0
    try {
852
0
      status.GetMakefile().AddDefinition(
853
0
        listName, list->sort(sortConfig, status.GetMakefile()).to_string());
854
0
      return true;
855
0
    } catch (std::invalid_argument& e) {
856
0
      status.SetError(e.what());
857
0
      return false;
858
0
    }
859
0
  }
860
861
0
  status.GetMakefile().AddDefinition(listName,
862
0
                                     list->sort(sortConfig).to_string());
863
0
  return true;
864
0
}
865
866
bool HandleSublistCommand(std::vector<std::string> const& args,
867
                          cmExecutionStatus& status)
868
0
{
869
0
  if (args.size() != 5) {
870
0
    status.SetError(cmStrCat("sub-command SUBLIST requires four arguments (",
871
0
                             args.size() - 1, " found)."));
872
0
    return false;
873
0
  }
874
875
0
  std::string const& listName = args[1];
876
0
  std::string const& variableName = args.back();
877
878
  // expand the variable
879
0
  auto list = GetList(listName, status.GetMakefile());
880
881
0
  if (!list || list->empty()) {
882
0
    status.GetMakefile().AddDefinition(variableName, "");
883
0
    return true;
884
0
  }
885
886
0
  int start;
887
0
  int length;
888
0
  if (!GetIndexArg(args[2], &start, status.GetMakefile())) {
889
0
    status.SetError(cmStrCat("index: ", args[2], " is not a valid index"));
890
0
    return false;
891
0
  }
892
0
  if (!GetIndexArg(args[3], &length, status.GetMakefile())) {
893
0
    status.SetError(cmStrCat("index: ", args[3], " is not a valid index"));
894
0
    return false;
895
0
  }
896
897
0
  if (start < 0) {
898
0
    status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ",
899
0
                             list->size() - 1));
900
0
    return false;
901
0
  }
902
0
  if (length < -1) {
903
0
    status.SetError(cmStrCat("length: ", length, " should be -1 or greater"));
904
0
    return false;
905
0
  }
906
907
0
  using size_type = cmList::size_type;
908
909
0
  try {
910
0
    auto sublist = list->sublist(static_cast<size_type>(start),
911
0
                                 static_cast<size_type>(length));
912
0
    status.GetMakefile().AddDefinition(variableName, sublist.to_string());
913
0
    return true;
914
0
  } catch (std::out_of_range& e) {
915
0
    status.SetError(e.what());
916
0
    return false;
917
0
  }
918
0
}
919
920
bool HandleRemoveAtCommand(std::vector<std::string> const& args,
921
                           cmExecutionStatus& status)
922
0
{
923
0
  if (args.size() < 3) {
924
0
    status.SetError("sub-command REMOVE_AT requires at least "
925
0
                    "two arguments.");
926
0
    return false;
927
0
  }
928
929
0
  std::string const& listName = args[1];
930
  // expand the variable
931
0
  auto list = GetList(listName, status.GetMakefile());
932
933
0
  if (!list || list->empty()) {
934
0
    std::ostringstream str;
935
0
    str << "index: ";
936
0
    for (size_t i = 1; i < args.size(); ++i) {
937
0
      str << args[i];
938
0
      if (i != args.size() - 1) {
939
0
        str << ", ";
940
0
      }
941
0
    }
942
0
    str << " out of range (0, 0)";
943
0
    status.SetError(str.str());
944
0
    return false;
945
0
  }
946
947
0
  size_t cc;
948
0
  std::vector<cmList::index_type> removed;
949
0
  for (cc = 2; cc < args.size(); ++cc) {
950
0
    int index;
951
0
    if (!GetIndexArg(args[cc], &index, status.GetMakefile())) {
952
0
      status.SetError(cmStrCat("index: ", args[cc], " is not a valid index"));
953
0
      return false;
954
0
    }
955
0
    removed.push_back(index);
956
0
  }
957
958
0
  try {
959
0
    status.GetMakefile().AddDefinition(
960
0
      listName,
961
0
      list->remove_items(removed.begin(), removed.end()).to_string());
962
0
    return true;
963
0
  } catch (std::out_of_range& e) {
964
0
    status.SetError(e.what());
965
0
    return false;
966
0
  }
967
0
}
968
969
bool HandleFilterCommand(std::vector<std::string> const& args,
970
                         cmExecutionStatus& status)
971
0
{
972
0
  if (args.size() < 2) {
973
0
    status.SetError("sub-command FILTER requires a list to be specified.");
974
0
    return false;
975
0
  }
976
977
0
  if (args.size() < 3) {
978
0
    status.SetError(
979
0
      "sub-command FILTER requires an operator to be specified.");
980
0
    return false;
981
0
  }
982
983
0
  if (args.size() < 4) {
984
0
    status.SetError("sub-command FILTER requires a mode to be specified.");
985
0
    return false;
986
0
  }
987
988
0
  std::string const& op = args[2];
989
0
  cmList::FilterMode filterMode;
990
0
  if (op == "INCLUDE") {
991
0
    filterMode = cmList::FilterMode::INCLUDE;
992
0
  } else if (op == "EXCLUDE") {
993
0
    filterMode = cmList::FilterMode::EXCLUDE;
994
0
  } else {
995
0
    status.SetError("sub-command FILTER does not recognize operator " + op);
996
0
    return false;
997
0
  }
998
999
0
  std::string const& listName = args[1];
1000
  // expand the variable
1001
0
  auto list = GetList(listName, status.GetMakefile());
1002
1003
0
  if (!list) {
1004
0
    return true;
1005
0
  }
1006
1007
0
  std::string const& mode = args[3];
1008
0
  if (mode == "REGEX") {
1009
0
    if (args.size() != 5) {
1010
0
      status.SetError("sub-command FILTER, mode REGEX "
1011
0
                      "requires five arguments.");
1012
0
      return false;
1013
0
    }
1014
0
    std::string const& pattern = args[4];
1015
1016
0
    try {
1017
0
      status.GetMakefile().AddDefinition(
1018
0
        listName, list->filter(pattern, filterMode).to_string());
1019
0
      return true;
1020
0
    } catch (std::invalid_argument& e) {
1021
0
      status.SetError(e.what());
1022
0
      return false;
1023
0
    }
1024
0
  }
1025
1026
0
  if (mode == "PREDICATE") {
1027
0
    if (args.size() != 5) {
1028
0
      status.SetError("sub-command FILTER, mode PREDICATE "
1029
0
                      "requires five arguments.");
1030
0
      return false;
1031
0
    }
1032
0
    std::string const& functionName = args[4];
1033
1034
0
    try {
1035
0
      status.GetMakefile().AddDefinition(
1036
0
        listName,
1037
0
        list->filter(functionName, filterMode, status.GetMakefile())
1038
0
          .to_string());
1039
0
      return true;
1040
0
    } catch (std::invalid_argument& e) {
1041
0
      status.SetError(e.what());
1042
0
      return false;
1043
0
    }
1044
0
  }
1045
1046
0
  status.SetError("sub-command FILTER does not recognize mode " + mode);
1047
0
  return false;
1048
0
}
1049
} // namespace
1050
1051
bool cmListCommand(std::vector<std::string> const& args,
1052
                   cmExecutionStatus& status)
1053
0
{
1054
0
  if (args.size() < 2) {
1055
0
    status.SetError("must be called with at least two arguments.");
1056
0
    return false;
1057
0
  }
1058
1059
0
  static cmSubcommandTable const subcommand{
1060
0
    { "LENGTH"_s, HandleLengthCommand },
1061
0
    { "GET"_s, HandleGetCommand },
1062
0
    { "APPEND"_s, HandleAppendCommand },
1063
0
    { "PREPEND"_s, HandlePrependCommand },
1064
0
    { "POP_BACK"_s, HandlePopBackCommand },
1065
0
    { "POP_FRONT"_s, HandlePopFrontCommand },
1066
0
    { "FIND"_s, HandleFindCommand },
1067
0
    { "INSERT"_s, HandleInsertCommand },
1068
0
    { "JOIN"_s, HandleJoinCommand },
1069
0
    { "REMOVE_AT"_s, HandleRemoveAtCommand },
1070
0
    { "REMOVE_ITEM"_s, HandleRemoveItemCommand },
1071
0
    { "REMOVE_DUPLICATES"_s, HandleRemoveDuplicatesCommand },
1072
0
    { "TRANSFORM"_s, HandleTransformCommand },
1073
0
    { "SORT"_s, HandleSortCommand },
1074
0
    { "SUBLIST"_s, HandleSublistCommand },
1075
0
    { "REVERSE"_s, HandleReverseCommand },
1076
0
    { "FILTER"_s, HandleFilterCommand },
1077
0
  };
1078
1079
0
  return subcommand(args[0], args, status);
1080
0
}