Coverage Report

Created: 2026-02-09 06:05

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