Coverage Report

Created: 2026-04-29 07:01

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 "cmDiagnostics.h"
20
#include "cmExecutionStatus.h"
21
#include "cmList.h"
22
#include "cmMakefile.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.IssueDiagnostic(cmDiagnostics::CMD_AUTHOR, 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
                   { "APPLY", cmList::TransformAction::APPLY, 1 } },
486
0
                 [](std::string const& x, std::string const& y) {
487
0
                   return x < y;
488
0
                 } };
489
490
0
  std::string const& listName = args[1];
491
492
  // Parse all possible function parameters
493
0
  using size_type = std::vector<std::string>::size_type;
494
0
  size_type index = 2;
495
496
0
  auto descriptor = descriptors.find(args[index]);
497
498
0
  if (descriptor == descriptors.end()) {
499
0
    status.SetError(
500
0
      cmStrCat(" sub-command TRANSFORM, ", args[index], " invalid action."));
501
0
    return false;
502
0
  }
503
504
  // Action arguments
505
0
  index += 1;
506
0
  if (args.size() < index + descriptor->Arity) {
507
0
    status.SetError(cmStrCat("sub-command TRANSFORM, action ",
508
0
                             descriptor->Name, " expects ", descriptor->Arity,
509
0
                             " argument(s)."));
510
0
    return false;
511
0
  }
512
513
0
  std::vector<std::string> arguments;
514
0
  index += descriptor->Arity;
515
0
  if (descriptor->Arity > 0) {
516
0
    arguments =
517
0
      std::vector<std::string>(args.begin() + 3, args.begin() + index);
518
0
  }
519
520
0
  std::string const REGEX{ "REGEX" };
521
0
  std::string const AT{ "AT" };
522
0
  std::string const FOR{ "FOR" };
523
0
  std::string const OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
524
0
  std::unique_ptr<cmList::TransformSelector> selector;
525
0
  std::string outputName = listName;
526
527
0
  try {
528
    // handle optional arguments
529
0
    while (args.size() > index) {
530
0
      if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) &&
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
      // output variable
657
0
      if (args[index] == OUTPUT_VARIABLE) {
658
0
        if (args.size() == ++index) {
659
0
          status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
660
0
                          "expects variable name argument.");
661
0
          return false;
662
0
        }
663
664
0
        outputName = args[index++];
665
0
        continue;
666
0
      }
667
668
0
      status.SetError(cmStrCat("sub-command TRANSFORM, '",
669
0
                               cmJoin(cmMakeRange(args).advance(index), " "),
670
0
                               "': unexpected argument(s)."));
671
0
      return false;
672
0
    }
673
674
    // expand the list variable
675
0
    auto list = GetList(listName, status.GetMakefile());
676
677
0
    if (!list) {
678
0
      status.GetMakefile().AddDefinition(outputName, "");
679
0
      return true;
680
0
    }
681
682
0
    if (!selector) {
683
0
      selector = cmList::TransformSelector::New();
684
0
    }
685
0
    selector->Makefile = &status.GetMakefile();
686
687
0
    if (descriptor->Action == cmList::TransformAction::APPLY) {
688
0
      list->transform(descriptor->Action, arguments.front(),
689
0
                      status.GetMakefile(), std::move(selector));
690
0
    } else {
691
0
      list->transform(descriptor->Action, arguments, std::move(selector));
692
0
    }
693
0
    status.GetMakefile().AddDefinition(outputName, list->to_string());
694
0
    return true;
695
0
  } catch (cmList::transform_error& e) {
696
0
    status.SetError(e.what());
697
0
    return false;
698
0
  }
699
0
}
700
701
bool HandleSortCommand(std::vector<std::string> const& args,
702
                       cmExecutionStatus& status)
703
0
{
704
0
  assert(args.size() >= 2);
705
0
  if (args.size() > 8) {
706
0
    status.SetError("sub-command SORT only takes up to six arguments.");
707
0
    return false;
708
0
  }
709
710
0
  using SortConfig = cmList::SortConfiguration;
711
0
  SortConfig sortConfig;
712
713
0
  size_t argumentIndex = 2;
714
0
  std::string const messageHint = "sub-command SORT ";
715
716
0
  while (argumentIndex < args.size()) {
717
0
    std::string const& option = args[argumentIndex++];
718
0
    if (option == "COMPARE") {
719
0
      if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) {
720
0
        std::string error = cmStrCat(messageHint, "option \"", option,
721
0
                                     "\" has been specified multiple times.");
722
0
        status.SetError(error);
723
0
        return false;
724
0
      }
725
0
      if (argumentIndex < args.size()) {
726
0
        std::string const& argument = args[argumentIndex++];
727
0
        if (argument == "STRING") {
728
0
          sortConfig.Compare = SortConfig::CompareMethod::STRING;
729
0
        } else if (argument == "FILE_BASENAME") {
730
0
          sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME;
731
0
        } else if (argument == "NATURAL") {
732
0
          sortConfig.Compare = SortConfig::CompareMethod::NATURAL;
733
0
        } else {
734
0
          std::string error =
735
0
            cmStrCat(messageHint, "value \"", argument, "\" for option \"",
736
0
                     option, "\" is invalid.");
737
0
          status.SetError(error);
738
0
          return false;
739
0
        }
740
0
      } else {
741
0
        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
742
0
                                 option, "\"."));
743
0
        return false;
744
0
      }
745
0
    } else if (option == "CASE") {
746
0
      if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) {
747
0
        status.SetError(cmStrCat(messageHint, "option \"", option,
748
0
                                 "\" has been specified multiple times."));
749
0
        return false;
750
0
      }
751
0
      if (argumentIndex < args.size()) {
752
0
        std::string const& argument = args[argumentIndex++];
753
0
        if (argument == "SENSITIVE") {
754
0
          sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE;
755
0
        } else if (argument == "INSENSITIVE") {
756
0
          sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE;
757
0
        } else {
758
0
          status.SetError(cmStrCat(messageHint, "value \"", argument,
759
0
                                   "\" for option \"", option,
760
0
                                   "\" is invalid."));
761
0
          return false;
762
0
        }
763
0
      } else {
764
0
        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
765
0
                                 option, "\"."));
766
0
        return false;
767
0
      }
768
0
    } else if (option == "ORDER") {
769
770
0
      if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) {
771
0
        status.SetError(cmStrCat(messageHint, "option \"", option,
772
0
                                 "\" has been specified multiple times."));
773
0
        return false;
774
0
      }
775
0
      if (argumentIndex < args.size()) {
776
0
        std::string const& argument = args[argumentIndex++];
777
0
        if (argument == "ASCENDING") {
778
0
          sortConfig.Order = SortConfig::OrderMode::ASCENDING;
779
0
        } else if (argument == "DESCENDING") {
780
0
          sortConfig.Order = SortConfig::OrderMode::DESCENDING;
781
0
        } else {
782
0
          status.SetError(cmStrCat(messageHint, "value \"", argument,
783
0
                                   "\" for option \"", option,
784
0
                                   "\" is invalid."));
785
0
          return false;
786
0
        }
787
0
      } else {
788
0
        status.SetError(cmStrCat(messageHint, "missing argument for option \"",
789
0
                                 option, "\"."));
790
0
        return false;
791
0
      }
792
0
    } else {
793
0
      status.SetError(
794
0
        cmStrCat(messageHint, "option \"", option, "\" is unknown."));
795
0
      return false;
796
0
    }
797
0
  }
798
799
0
  std::string const& listName = args[1];
800
  // expand the variable
801
0
  auto list = GetList(listName, status.GetMakefile());
802
803
0
  if (!list) {
804
0
    return true;
805
0
  }
806
807
0
  status.GetMakefile().AddDefinition(listName,
808
0
                                     list->sort(sortConfig).to_string());
809
0
  return true;
810
0
}
811
812
bool HandleSublistCommand(std::vector<std::string> const& args,
813
                          cmExecutionStatus& status)
814
0
{
815
0
  if (args.size() != 5) {
816
0
    status.SetError(cmStrCat("sub-command SUBLIST requires four arguments (",
817
0
                             args.size() - 1, " found)."));
818
0
    return false;
819
0
  }
820
821
0
  std::string const& listName = args[1];
822
0
  std::string const& variableName = args.back();
823
824
  // expand the variable
825
0
  auto list = GetList(listName, status.GetMakefile());
826
827
0
  if (!list || list->empty()) {
828
0
    status.GetMakefile().AddDefinition(variableName, "");
829
0
    return true;
830
0
  }
831
832
0
  int start;
833
0
  int length;
834
0
  if (!GetIndexArg(args[2], &start, status.GetMakefile())) {
835
0
    status.SetError(cmStrCat("index: ", args[2], " is not a valid index"));
836
0
    return false;
837
0
  }
838
0
  if (!GetIndexArg(args[3], &length, status.GetMakefile())) {
839
0
    status.SetError(cmStrCat("index: ", args[3], " is not a valid index"));
840
0
    return false;
841
0
  }
842
843
0
  if (start < 0) {
844
0
    status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ",
845
0
                             list->size() - 1));
846
0
    return false;
847
0
  }
848
0
  if (length < -1) {
849
0
    status.SetError(cmStrCat("length: ", length, " should be -1 or greater"));
850
0
    return false;
851
0
  }
852
853
0
  using size_type = cmList::size_type;
854
855
0
  try {
856
0
    auto sublist = list->sublist(static_cast<size_type>(start),
857
0
                                 static_cast<size_type>(length));
858
0
    status.GetMakefile().AddDefinition(variableName, sublist.to_string());
859
0
    return true;
860
0
  } catch (std::out_of_range& e) {
861
0
    status.SetError(e.what());
862
0
    return false;
863
0
  }
864
0
}
865
866
bool HandleRemoveAtCommand(std::vector<std::string> const& args,
867
                           cmExecutionStatus& status)
868
0
{
869
0
  if (args.size() < 3) {
870
0
    status.SetError("sub-command REMOVE_AT requires at least "
871
0
                    "two arguments.");
872
0
    return false;
873
0
  }
874
875
0
  std::string const& listName = args[1];
876
  // expand the variable
877
0
  auto list = GetList(listName, status.GetMakefile());
878
879
0
  if (!list || list->empty()) {
880
0
    std::ostringstream str;
881
0
    str << "index: ";
882
0
    for (size_t i = 1; i < args.size(); ++i) {
883
0
      str << args[i];
884
0
      if (i != args.size() - 1) {
885
0
        str << ", ";
886
0
      }
887
0
    }
888
0
    str << " out of range (0, 0)";
889
0
    status.SetError(str.str());
890
0
    return false;
891
0
  }
892
893
0
  size_t cc;
894
0
  std::vector<cmList::index_type> removed;
895
0
  for (cc = 2; cc < args.size(); ++cc) {
896
0
    int index;
897
0
    if (!GetIndexArg(args[cc], &index, status.GetMakefile())) {
898
0
      status.SetError(cmStrCat("index: ", args[cc], " is not a valid index"));
899
0
      return false;
900
0
    }
901
0
    removed.push_back(index);
902
0
  }
903
904
0
  try {
905
0
    status.GetMakefile().AddDefinition(
906
0
      listName,
907
0
      list->remove_items(removed.begin(), removed.end()).to_string());
908
0
    return true;
909
0
  } catch (std::out_of_range& e) {
910
0
    status.SetError(e.what());
911
0
    return false;
912
0
  }
913
0
}
914
915
bool HandleFilterCommand(std::vector<std::string> const& args,
916
                         cmExecutionStatus& status)
917
0
{
918
0
  if (args.size() < 2) {
919
0
    status.SetError("sub-command FILTER requires a list to be specified.");
920
0
    return false;
921
0
  }
922
923
0
  if (args.size() < 3) {
924
0
    status.SetError(
925
0
      "sub-command FILTER requires an operator to be specified.");
926
0
    return false;
927
0
  }
928
929
0
  if (args.size() < 4) {
930
0
    status.SetError("sub-command FILTER requires a mode to be specified.");
931
0
    return false;
932
0
  }
933
934
0
  std::string const& op = args[2];
935
0
  cmList::FilterMode filterMode;
936
0
  if (op == "INCLUDE") {
937
0
    filterMode = cmList::FilterMode::INCLUDE;
938
0
  } else if (op == "EXCLUDE") {
939
0
    filterMode = cmList::FilterMode::EXCLUDE;
940
0
  } else {
941
0
    status.SetError("sub-command FILTER does not recognize operator " + op);
942
0
    return false;
943
0
  }
944
945
0
  std::string const& listName = args[1];
946
  // expand the variable
947
0
  auto list = GetList(listName, status.GetMakefile());
948
949
0
  if (!list) {
950
0
    return true;
951
0
  }
952
953
0
  std::string const& mode = args[3];
954
0
  if (mode != "REGEX") {
955
0
    status.SetError("sub-command FILTER does not recognize mode " + mode);
956
0
    return false;
957
0
  }
958
0
  if (args.size() != 5) {
959
0
    status.SetError("sub-command FILTER, mode REGEX "
960
0
                    "requires five arguments.");
961
0
    return false;
962
0
  }
963
0
  std::string const& pattern = args[4];
964
965
0
  try {
966
0
    status.GetMakefile().AddDefinition(
967
0
      listName, list->filter(pattern, filterMode).to_string());
968
0
    return true;
969
0
  } catch (std::invalid_argument& e) {
970
0
    status.SetError(e.what());
971
0
    return false;
972
0
  }
973
0
}
974
} // namespace
975
976
bool cmListCommand(std::vector<std::string> const& args,
977
                   cmExecutionStatus& status)
978
0
{
979
0
  if (args.size() < 2) {
980
0
    status.SetError("must be called with at least two arguments.");
981
0
    return false;
982
0
  }
983
984
0
  static cmSubcommandTable const subcommand{
985
0
    { "LENGTH"_s, HandleLengthCommand },
986
0
    { "GET"_s, HandleGetCommand },
987
0
    { "APPEND"_s, HandleAppendCommand },
988
0
    { "PREPEND"_s, HandlePrependCommand },
989
0
    { "POP_BACK"_s, HandlePopBackCommand },
990
0
    { "POP_FRONT"_s, HandlePopFrontCommand },
991
0
    { "FIND"_s, HandleFindCommand },
992
0
    { "INSERT"_s, HandleInsertCommand },
993
0
    { "JOIN"_s, HandleJoinCommand },
994
0
    { "REMOVE_AT"_s, HandleRemoveAtCommand },
995
0
    { "REMOVE_ITEM"_s, HandleRemoveItemCommand },
996
0
    { "REMOVE_DUPLICATES"_s, HandleRemoveDuplicatesCommand },
997
0
    { "TRANSFORM"_s, HandleTransformCommand },
998
0
    { "SORT"_s, HandleSortCommand },
999
0
    { "SUBLIST"_s, HandleSublistCommand },
1000
0
    { "REVERSE"_s, HandleReverseCommand },
1001
0
    { "FILTER"_s, HandleFilterCommand },
1002
0
  };
1003
1004
0
  return subcommand(args[0], args, status);
1005
0
}