Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmFileCommand.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 "cmFileCommand.h"
4
5
#include <algorithm>
6
#include <cassert>
7
#include <cmath>
8
#include <cstdio>
9
#include <cstdlib>
10
#include <iterator>
11
#include <map>
12
#include <set>
13
#include <sstream>
14
#include <utility>
15
#include <vector>
16
17
#include <cm/memory>
18
#include <cm/optional>
19
#include <cm/string_view>
20
#include <cmext/algorithm>
21
#include <cmext/string_view>
22
23
#include <cm3p/kwiml/int.h>
24
25
#include "cmsys/FStream.hxx"
26
#include "cmsys/Glob.hxx"
27
#include "cmsys/RegularExpression.hxx"
28
#include "cmsys/String.h"
29
30
#include "cm_sys_stat.h"
31
32
#include "cmArgumentParser.h"
33
#include "cmArgumentParserTypes.h"
34
#include "cmCMakePath.h"
35
#include "cmCryptoHash.h"
36
#include "cmELF.h"
37
#include "cmExecutionStatus.h"
38
#include "cmFSPermissions.h"
39
#include "cmFileCommand_ReadMacho.h"
40
#include "cmFileCopier.h"
41
#include "cmFileInstaller.h"
42
#include "cmFileLockPool.h"
43
#include "cmFileTimes.h"
44
#include "cmGeneratedFileStream.h"
45
#include "cmGeneratorExpression.h"
46
#include "cmGlobCacheEntry.h"
47
#include "cmGlobalGenerator.h"
48
#include "cmHexFileConverter.h"
49
#include "cmList.h"
50
#include "cmListFileCache.h"
51
#include "cmMakefile.h"
52
#include "cmMessageType.h"
53
#include "cmNewLineStyle.h"
54
#include "cmPolicies.h"
55
#include "cmRange.h"
56
#include "cmRuntimeDependencyArchive.h"
57
#include "cmState.h"
58
#include "cmStringAlgorithms.h"
59
#include "cmSubcommandTable.h"
60
#include "cmSystemTools.h"
61
#include "cmTimestamp.h"
62
#include "cmValue.h"
63
#include "cmWorkingDirectory.h"
64
#include "cmake.h"
65
66
#if !defined(CMAKE_BOOTSTRAP)
67
#  include <cm3p/curl/curl.h>
68
69
#  include "cmCurl.h"
70
#  include "cmFileLockResult.h"
71
#endif
72
73
namespace {
74
75
bool HandleWriteImpl(std::vector<std::string> const& args, bool append,
76
                     cmExecutionStatus& status)
77
0
{
78
0
  if (args.size() < 2) {
79
0
    status.SetError(cmStrCat(
80
0
      args[0], " must be called with at least one additional argument."));
81
0
    return false;
82
0
  }
83
0
  auto i = args.begin();
84
85
0
  i++; // Get rid of subcommand
86
87
0
  std::string fileName = *i;
88
0
  if (!cmsys::SystemTools::FileIsFullPath(*i)) {
89
0
    fileName =
90
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', *i);
91
0
  }
92
93
0
  i++;
94
95
0
  if (!status.GetMakefile().CanIWriteThisFile(fileName)) {
96
0
    std::string e = cmStrCat("attempted to write a file: ", fileName,
97
0
                             " into a source directory.");
98
0
    status.SetError(e);
99
0
    cmSystemTools::SetFatalErrorOccurred();
100
0
    return false;
101
0
  }
102
0
  std::string dir = cmSystemTools::GetFilenamePath(fileName);
103
0
  cmSystemTools::MakeDirectory(dir);
104
105
0
  mode_t mode = 0;
106
0
  bool writable = false;
107
108
  // Set permissions to writable
109
0
  if (cmSystemTools::GetPermissions(fileName, mode)) {
110
#if defined(_MSC_VER) || defined(__MINGW32__)
111
    writable = (mode & S_IWRITE) != 0;
112
    mode_t newMode = mode | S_IWRITE;
113
#else
114
0
    writable = mode & S_IWUSR;
115
0
    mode_t newMode = mode | S_IWUSR | S_IWGRP;
116
0
#endif
117
0
    if (!writable) {
118
0
      cmSystemTools::SetPermissions(fileName, newMode);
119
0
    }
120
0
  }
121
  // If GetPermissions fails, pretend like it is ok. File open will fail if
122
  // the file is not writable
123
0
  cmsys::ofstream file(fileName.c_str(),
124
0
                       append ? std::ios::app : std::ios::out);
125
0
  if (!file) {
126
0
    std::string error =
127
0
      cmStrCat("failed to open for writing (",
128
0
               cmSystemTools::GetLastSystemError(), "):\n  ", fileName);
129
0
    status.SetError(error);
130
0
    return false;
131
0
  }
132
0
  std::string message = cmJoin(cmMakeRange(i, args.end()), std::string());
133
0
  file << message;
134
0
  if (!file) {
135
0
    std::string error =
136
0
      cmStrCat("write failed (", cmSystemTools::GetLastSystemError(), "):\n  ",
137
0
               fileName);
138
0
    status.SetError(error);
139
0
    return false;
140
0
  }
141
0
  file.close();
142
0
  if (mode && !writable) {
143
0
    cmSystemTools::SetPermissions(fileName, mode);
144
0
  }
145
0
  return true;
146
0
}
147
148
bool HandleWriteCommand(std::vector<std::string> const& args,
149
                        cmExecutionStatus& status)
150
0
{
151
0
  return HandleWriteImpl(args, false, status);
152
0
}
153
154
bool HandleAppendCommand(std::vector<std::string> const& args,
155
                         cmExecutionStatus& status)
156
0
{
157
0
  return HandleWriteImpl(args, true, status);
158
0
}
159
160
bool HandleReadCommand(std::vector<std::string> const& args,
161
                       cmExecutionStatus& status)
162
0
{
163
0
  if (args.size() < 3) {
164
0
    status.SetError("READ must be called with at least two additional "
165
0
                    "arguments");
166
0
    return false;
167
0
  }
168
169
0
  std::string const& fileNameArg = args[1];
170
0
  std::string const& variable = args[2];
171
172
0
  struct Arguments
173
0
  {
174
0
    std::string Offset;
175
0
    std::string Limit;
176
0
    bool Hex = false;
177
0
  };
178
179
0
  static auto const parser = cmArgumentParser<Arguments>{}
180
0
                               .Bind("OFFSET"_s, &Arguments::Offset)
181
0
                               .Bind("LIMIT"_s, &Arguments::Limit)
182
0
                               .Bind("HEX"_s, &Arguments::Hex);
183
184
0
  Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3),
185
0
                                           /*unparsedArguments=*/nullptr);
186
187
0
  std::string fileName = fileNameArg;
188
0
  if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
189
0
    fileName = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/',
190
0
                        fileNameArg);
191
0
  }
192
193
// Open the specified file.
194
#if defined(_WIN32) || defined(__CYGWIN__)
195
  cmsys::ifstream file(fileName.c_str(),
196
                       arguments.Hex ? (std::ios::binary | std::ios::in)
197
                                     : std::ios::in);
198
#else
199
0
  cmsys::ifstream file(fileName.c_str());
200
0
#endif
201
202
0
  if (!file) {
203
0
    std::string error =
204
0
      cmStrCat("failed to open for reading (",
205
0
               cmSystemTools::GetLastSystemError(), "):\n  ", fileName);
206
0
    status.SetError(error);
207
0
    return false;
208
0
  }
209
210
  // is there a limit?
211
0
  std::string::size_type sizeLimit = std::string::npos;
212
0
  if (!arguments.Limit.empty()) {
213
0
    unsigned long long limit;
214
0
    if (cmStrToULongLong(arguments.Limit, &limit)) {
215
0
      sizeLimit = static_cast<std::string::size_type>(limit);
216
0
    }
217
0
  }
218
219
  // is there an offset?
220
0
  cmsys::ifstream::off_type offset = 0;
221
0
  if (!arguments.Offset.empty()) {
222
0
    long long off;
223
0
    if (cmStrToLongLong(arguments.Offset, &off)) {
224
0
      offset = static_cast<cmsys::ifstream::off_type>(off);
225
0
    }
226
0
  }
227
228
0
  file.seekg(offset, std::ios::beg); // explicit ios::beg for IBM VisualAge 6
229
230
0
  std::string output;
231
232
0
  if (arguments.Hex) {
233
    // Convert part of the file into hex code
234
0
    char c;
235
0
    while ((sizeLimit > 0) && (file.get(c))) {
236
0
      char hex[4];
237
0
      snprintf(hex, sizeof(hex), "%.2x", c & 0xFFu);
238
0
      output += hex;
239
0
      sizeLimit--;
240
0
    }
241
0
  } else {
242
0
    std::string line;
243
0
    bool has_newline = false;
244
0
    while (
245
0
      sizeLimit > 0 &&
246
0
      cmSystemTools::GetLineFromStream(file, line, &has_newline, sizeLimit)) {
247
0
      sizeLimit = sizeLimit - line.size();
248
0
      if (has_newline && sizeLimit > 0) {
249
0
        sizeLimit--;
250
0
      }
251
0
      output += line;
252
0
      if (has_newline) {
253
0
        output += "\n";
254
0
      }
255
0
    }
256
0
  }
257
0
  status.GetMakefile().AddDefinition(variable, output);
258
0
  return true;
259
0
}
260
261
bool HandleHashCommand(std::vector<std::string> const& args,
262
                       cmExecutionStatus& status)
263
0
{
264
0
#if !defined(CMAKE_BOOTSTRAP)
265
0
  if (args.size() != 3) {
266
0
    status.SetError(
267
0
      cmStrCat(args[0], " requires a file name and output variable"));
268
0
    return false;
269
0
  }
270
271
0
  std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0]));
272
0
  if (hash) {
273
0
    std::string out = hash->HashFile(args[1]);
274
0
    if (!out.empty()) {
275
0
      status.GetMakefile().AddDefinition(args[2], out);
276
0
      return true;
277
0
    }
278
0
    status.SetError(cmStrCat(args[0], " failed to read file \"", args[1],
279
0
                             "\": ", cmSystemTools::GetLastSystemError()));
280
0
  }
281
0
  return false;
282
#else
283
  status.SetError(cmStrCat(args[0], " not available during bootstrap"));
284
  return false;
285
#endif
286
0
}
287
288
bool HandleStringsCommand(std::vector<std::string> const& args,
289
                          cmExecutionStatus& status)
290
0
{
291
0
  if (args.size() < 3) {
292
0
    status.SetError("STRINGS requires a file name and output variable");
293
0
    return false;
294
0
  }
295
296
  // Get the file to read.
297
0
  std::string fileName = args[1];
298
0
  if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
299
0
    fileName =
300
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
301
0
  }
302
303
  // Get the variable in which to store the results.
304
0
  std::string const& outVar = args[2];
305
306
  // Parse the options.
307
0
  enum
308
0
  {
309
0
    arg_none,
310
0
    arg_limit_input,
311
0
    arg_limit_output,
312
0
    arg_limit_count,
313
0
    arg_length_minimum,
314
0
    arg_length_maximum,
315
0
    arg_maximum,
316
0
    arg_regex,
317
0
    arg_encoding
318
0
  };
319
0
  unsigned int minlen = 0;
320
0
  unsigned int maxlen = 0;
321
0
  int limit_input = -1;
322
0
  int limit_output = -1;
323
0
  unsigned int limit_count = 0;
324
0
  cmsys::RegularExpression regex;
325
0
  bool have_regex = false;
326
0
  bool store_regex = true;
327
0
  bool newline_consume = false;
328
0
  bool hex_conversion_enabled = true;
329
0
  enum
330
0
  {
331
0
    encoding_none = cmsys::FStream::BOM_None,
332
0
    encoding_utf8 = cmsys::FStream::BOM_UTF8,
333
0
    encoding_utf16le = cmsys::FStream::BOM_UTF16LE,
334
0
    encoding_utf16be = cmsys::FStream::BOM_UTF16BE,
335
0
    encoding_utf32le = cmsys::FStream::BOM_UTF32LE,
336
0
    encoding_utf32be = cmsys::FStream::BOM_UTF32BE
337
0
  };
338
0
  int encoding = encoding_none;
339
0
  int arg_mode = arg_none;
340
0
  for (unsigned int i = 3; i < args.size(); ++i) {
341
0
    if (args[i] == "LIMIT_INPUT") {
342
0
      arg_mode = arg_limit_input;
343
0
    } else if (args[i] == "LIMIT_OUTPUT") {
344
0
      arg_mode = arg_limit_output;
345
0
    } else if (args[i] == "LIMIT_COUNT") {
346
0
      arg_mode = arg_limit_count;
347
0
    } else if (args[i] == "LENGTH_MINIMUM") {
348
0
      arg_mode = arg_length_minimum;
349
0
    } else if (args[i] == "LENGTH_MAXIMUM") {
350
0
      arg_mode = arg_length_maximum;
351
0
    } else if (args[i] == "REGEX") {
352
0
      arg_mode = arg_regex;
353
0
    } else if (args[i] == "NEWLINE_CONSUME") {
354
0
      newline_consume = true;
355
0
      arg_mode = arg_none;
356
0
    } else if (args[i] == "NO_HEX_CONVERSION") {
357
0
      hex_conversion_enabled = false;
358
0
      arg_mode = arg_none;
359
0
    } else if (args[i] == "ENCODING") {
360
0
      arg_mode = arg_encoding;
361
0
    } else if (arg_mode == arg_limit_input) {
362
0
      if (sscanf(args[i].c_str(), "%d", &limit_input) != 1 ||
363
0
          limit_input < 0) {
364
0
        status.SetError(cmStrCat("STRINGS option LIMIT_INPUT value \"",
365
0
                                 args[i], "\" is not an unsigned integer."));
366
0
        return false;
367
0
      }
368
0
      arg_mode = arg_none;
369
0
    } else if (arg_mode == arg_limit_output) {
370
0
      if (sscanf(args[i].c_str(), "%d", &limit_output) != 1 ||
371
0
          limit_output < 0) {
372
0
        status.SetError(cmStrCat("STRINGS option LIMIT_OUTPUT value \"",
373
0
                                 args[i], "\" is not an unsigned integer."));
374
0
        return false;
375
0
      }
376
0
      arg_mode = arg_none;
377
0
    } else if (arg_mode == arg_limit_count) {
378
0
      int count;
379
0
      if (sscanf(args[i].c_str(), "%d", &count) != 1 || count < 0) {
380
0
        status.SetError(cmStrCat("STRINGS option LIMIT_COUNT value \"",
381
0
                                 args[i], "\" is not an unsigned integer."));
382
0
        return false;
383
0
      }
384
0
      limit_count = count;
385
0
      arg_mode = arg_none;
386
0
    } else if (arg_mode == arg_length_minimum) {
387
0
      int len;
388
0
      if (sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0) {
389
0
        status.SetError(cmStrCat("STRINGS option LENGTH_MINIMUM value \"",
390
0
                                 args[i], "\" is not an unsigned integer."));
391
0
        return false;
392
0
      }
393
0
      minlen = len;
394
0
      arg_mode = arg_none;
395
0
    } else if (arg_mode == arg_length_maximum) {
396
0
      int len;
397
0
      if (sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0) {
398
0
        status.SetError(cmStrCat("STRINGS option LENGTH_MAXIMUM value \"",
399
0
                                 args[i], "\" is not an unsigned integer."));
400
0
        return false;
401
0
      }
402
0
      maxlen = len;
403
0
      arg_mode = arg_none;
404
0
    } else if (arg_mode == arg_regex) {
405
0
      if (!regex.compile(args[i])) {
406
0
        status.SetError(cmStrCat("STRINGS option REGEX value \"", args[i],
407
0
                                 "\" could not be compiled."));
408
0
        return false;
409
0
      }
410
0
      have_regex = true;
411
0
      switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0159)) {
412
0
        case cmPolicies::NEW:
413
          // store_regex = true
414
0
          break;
415
0
        case cmPolicies::WARN:
416
0
          if (status.GetMakefile().PolicyOptionalWarningEnabled(
417
0
                "CMAKE_POLICY_WARNING_CMP0159")) {
418
0
            status.GetMakefile().IssueMessage(
419
0
              MessageType::AUTHOR_WARNING,
420
0
              cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0159),
421
0
                       "\n"
422
0
                       "For compatibility, CMake is leaving CMAKE_MATCH_<n> "
423
0
                       "unchanged."));
424
0
          }
425
0
          CM_FALLTHROUGH;
426
0
        case cmPolicies::OLD:
427
0
          store_regex = false;
428
0
          break;
429
0
      }
430
0
      arg_mode = arg_none;
431
0
    } else if (arg_mode == arg_encoding) {
432
0
      if (args[i] == "UTF-8") {
433
0
        encoding = encoding_utf8;
434
0
      } else if (args[i] == "UTF-16LE") {
435
0
        encoding = encoding_utf16le;
436
0
      } else if (args[i] == "UTF-16BE") {
437
0
        encoding = encoding_utf16be;
438
0
      } else if (args[i] == "UTF-32LE") {
439
0
        encoding = encoding_utf32le;
440
0
      } else if (args[i] == "UTF-32BE") {
441
0
        encoding = encoding_utf32be;
442
0
      } else {
443
0
        status.SetError(cmStrCat("STRINGS option ENCODING \"", args[i],
444
0
                                 "\" not recognized."));
445
0
        return false;
446
0
      }
447
0
      arg_mode = arg_none;
448
0
    } else {
449
0
      status.SetError(
450
0
        cmStrCat("STRINGS given unknown argument \"", args[i], '"'));
451
0
      return false;
452
0
    }
453
0
  }
454
455
0
  if (hex_conversion_enabled) {
456
    // TODO: should work without temp file, but just on a memory buffer
457
0
    std::string binaryFileName =
458
0
      cmStrCat(status.GetMakefile().GetCurrentBinaryDirectory(),
459
0
               "/CMakeFiles/FileCommandStringsBinaryFile");
460
0
    if (cmHexFileConverter::TryConvert(fileName, binaryFileName)) {
461
0
      fileName = binaryFileName;
462
0
    }
463
0
  }
464
465
// Open the specified file.
466
#if defined(_WIN32) || defined(__CYGWIN__)
467
  cmsys::ifstream fin(fileName.c_str(), std::ios::in | std::ios::binary);
468
#else
469
0
  cmsys::ifstream fin(fileName.c_str());
470
0
#endif
471
0
  if (!fin) {
472
0
    status.SetError(
473
0
      cmStrCat("STRINGS file \"", fileName, "\" cannot be read."));
474
0
    return false;
475
0
  }
476
477
  // If BOM is found and encoding was not specified, use the BOM
478
0
  int bom_found = cmsys::FStream::ReadBOM(fin);
479
0
  if (encoding == encoding_none && bom_found != cmsys::FStream::BOM_None) {
480
0
    encoding = bom_found;
481
0
  }
482
483
0
  unsigned int bytes_rem = 0;
484
0
  if (encoding == encoding_utf16le || encoding == encoding_utf16be) {
485
0
    bytes_rem = 1;
486
0
  }
487
0
  if (encoding == encoding_utf32le || encoding == encoding_utf32be) {
488
0
    bytes_rem = 3;
489
0
  }
490
491
  // Parse strings out of the file.
492
0
  int output_size = 0;
493
0
  std::vector<std::string> strings;
494
0
  std::string s;
495
0
  while ((!limit_count || strings.size() < limit_count) &&
496
0
         (limit_input < 0 || static_cast<int>(fin.tellg()) < limit_input) &&
497
0
         fin) {
498
0
    std::string current_str;
499
500
0
    int c = fin.get();
501
0
    for (unsigned int i = 0; i < bytes_rem; ++i) {
502
0
      int c1 = fin.get();
503
0
      if (!fin) {
504
0
        fin.putback(static_cast<char>(c1));
505
0
        break;
506
0
      }
507
0
      c = (c << 8) | c1;
508
0
    }
509
0
    if (encoding == encoding_utf16le) {
510
0
      c = ((c & 0xFF) << 8) | ((c & 0xFF00) >> 8);
511
0
    } else if (encoding == encoding_utf32le) {
512
0
      c = (((c & 0xFF) << 24) | ((c & 0xFF00) << 8) | ((c & 0xFF0000) >> 8) |
513
0
           ((c & 0xFF000000) >> 24));
514
0
    }
515
516
0
    if (c == '\r') {
517
      // Ignore CR character to make output always have UNIX newlines.
518
0
      continue;
519
0
    }
520
521
0
    if (c >= 0 && c <= 0xFF &&
522
0
        (cmsysString_isprint(static_cast<char>(c)) || c == '\t' ||
523
0
         (c == '\n' && newline_consume))) {
524
      // This is an ASCII character that may be part of a string.
525
      // Cast added to avoid compiler warning. Cast is ok because
526
      // c is guaranteed to fit in char by the above if...
527
0
      current_str += static_cast<char>(c);
528
0
    } else if (encoding == encoding_utf8) {
529
      // Check for UTF-8 encoded string (up to 4 octets)
530
0
      static unsigned char const utf8_check_table[3][2] = {
531
0
        { 0xE0, 0xC0 },
532
0
        { 0xF0, 0xE0 },
533
0
        { 0xF8, 0xF0 },
534
0
      };
535
536
      // how many octets are there?
537
0
      unsigned int num_utf8_bytes = 0;
538
0
      for (unsigned int j = 0; num_utf8_bytes == 0 && j < 3; j++) {
539
0
        if ((c & utf8_check_table[j][0]) == utf8_check_table[j][1]) {
540
0
          num_utf8_bytes = j + 2;
541
0
        }
542
0
      }
543
544
      // get subsequent octets and check that they are valid
545
0
      for (unsigned int j = 0; j < num_utf8_bytes; j++) {
546
0
        if (j != 0) {
547
0
          c = fin.get();
548
0
          if (!fin || (c & 0xC0) != 0x80) {
549
0
            fin.putback(static_cast<char>(c));
550
0
            break;
551
0
          }
552
0
        }
553
0
        current_str += static_cast<char>(c);
554
0
      }
555
556
      // if this was an invalid utf8 sequence, discard the data, and put
557
      // back subsequent characters
558
0
      if ((current_str.length() != num_utf8_bytes)) {
559
0
        for (unsigned int j = 0; j < current_str.size() - 1; j++) {
560
0
          fin.putback(current_str[current_str.size() - 1 - j]);
561
0
        }
562
0
        current_str.clear();
563
0
      }
564
0
    }
565
566
0
    if (c == '\n' && !newline_consume) {
567
      // The current line has been terminated.  Check if the current
568
      // string matches the requirements.  The length may now be as
569
      // low as zero since blank lines are allowed.
570
0
      if (s.length() >= minlen && (!have_regex || regex.find(s))) {
571
0
        if (store_regex) {
572
0
          status.GetMakefile().ClearMatches();
573
0
          status.GetMakefile().StoreMatches(regex);
574
0
        }
575
0
        output_size += static_cast<int>(s.size()) + 1;
576
0
        if (limit_output >= 0 && output_size >= limit_output) {
577
0
          s.clear();
578
0
          break;
579
0
        }
580
0
        strings.push_back(s);
581
0
      }
582
583
      // Reset the string to empty.
584
0
      s.clear();
585
0
    } else if (current_str.empty()) {
586
      // A non-string character has been found.  Check if the current
587
      // string matches the requirements.  We require that the length
588
      // be at least one no matter what the user specified.
589
0
      if (s.length() >= minlen && !s.empty() &&
590
0
          (!have_regex || regex.find(s))) {
591
0
        if (store_regex) {
592
0
          status.GetMakefile().ClearMatches();
593
0
          status.GetMakefile().StoreMatches(regex);
594
0
        }
595
0
        output_size += static_cast<int>(s.size()) + 1;
596
0
        if (limit_output >= 0 && output_size >= limit_output) {
597
0
          s.clear();
598
0
          break;
599
0
        }
600
0
        strings.push_back(s);
601
0
      }
602
603
      // Reset the string to empty.
604
0
      s.clear();
605
0
    } else {
606
0
      s += current_str;
607
0
    }
608
609
0
    if (maxlen > 0 && s.size() == maxlen) {
610
      // Terminate a string if the maximum length is reached.
611
0
      if (s.length() >= minlen && (!have_regex || regex.find(s))) {
612
0
        if (store_regex) {
613
0
          status.GetMakefile().ClearMatches();
614
0
          status.GetMakefile().StoreMatches(regex);
615
0
        }
616
0
        output_size += static_cast<int>(s.size()) + 1;
617
0
        if (limit_output >= 0 && output_size >= limit_output) {
618
0
          s.clear();
619
0
          break;
620
0
        }
621
0
        strings.push_back(s);
622
0
      }
623
0
      s.clear();
624
0
    }
625
0
  }
626
627
  // If there is a non-empty current string we have hit the end of the
628
  // input file or the input size limit.  Check if the current string
629
  // matches the requirements.
630
0
  if ((!limit_count || strings.size() < limit_count) && !s.empty() &&
631
0
      s.length() >= minlen && (!have_regex || regex.find(s))) {
632
0
    if (store_regex) {
633
0
      status.GetMakefile().ClearMatches();
634
0
      status.GetMakefile().StoreMatches(regex);
635
0
    }
636
0
    output_size += static_cast<int>(s.size()) + 1;
637
0
    if (limit_output < 0 || output_size < limit_output) {
638
0
      strings.push_back(s);
639
0
    }
640
0
  }
641
642
  // Encode the result in a CMake list.
643
0
  char const* sep = "";
644
0
  std::string output;
645
0
  for (std::string const& sr : strings) {
646
    // Separate the strings in the output to make it a list.
647
0
    output += sep;
648
0
    sep = ";";
649
650
    // Store the string in the output, but escape semicolons to
651
    // make sure it is a list.
652
0
    for (char i : sr) {
653
0
      if (i == ';') {
654
0
        output += '\\';
655
0
      }
656
0
      output += i;
657
0
    }
658
0
  }
659
660
  // Save the output in a makefile variable.
661
0
  status.GetMakefile().AddDefinition(outVar, output);
662
0
  return true;
663
0
}
664
665
bool HandleGlobImpl(std::vector<std::string> const& args, bool recurse,
666
                    cmExecutionStatus& status)
667
0
{
668
0
  if (args.size() < 2) {
669
0
    status.SetError(cmStrCat(
670
0
      args[0], " must be called with at least one additional argument."));
671
0
    return false;
672
0
  }
673
674
0
  auto i = args.begin();
675
676
0
  i++; // Get rid of subcommand
677
678
0
  std::string variable = *i;
679
0
  i++;
680
0
  cmsys::Glob g;
681
0
  g.SetRecurse(recurse);
682
0
  if (recurse) {
683
0
    g.RecurseThroughSymlinksOff();
684
0
  }
685
686
0
  cmake* cm = status.GetMakefile().GetCMakeInstance();
687
0
  std::vector<std::string> files;
688
0
  bool configureDepends = false;
689
0
  bool warnConfigureLate = false;
690
0
  while (i != args.end()) {
691
0
    if (*i == "LIST_DIRECTORIES") {
692
0
      ++i; // skip LIST_DIRECTORIES
693
0
      if (i != args.end()) {
694
0
        if (cmIsOn(*i)) {
695
0
          g.SetListDirs(true);
696
0
          g.SetRecurseListDirs(true);
697
0
        } else if (cmIsOff(*i)) {
698
0
          g.SetListDirs(false);
699
0
          g.SetRecurseListDirs(false);
700
0
        } else {
701
0
          status.SetError("LIST_DIRECTORIES missing bool value.");
702
0
          return false;
703
0
        }
704
0
        ++i;
705
0
      } else {
706
0
        status.SetError("LIST_DIRECTORIES missing bool value.");
707
0
        return false;
708
0
      }
709
0
    } else if (*i == "FOLLOW_SYMLINKS") {
710
0
      ++i; // skip FOLLOW_SYMLINKS
711
0
      if (recurse) {
712
0
        g.RecurseThroughSymlinksOn();
713
0
        if (i == args.end()) {
714
0
          status.SetError(
715
0
            "GLOB_RECURSE requires a glob expression after FOLLOW_SYMLINKS.");
716
0
          return false;
717
0
        }
718
0
      }
719
0
    } else if (*i == "RELATIVE") {
720
0
      ++i; // skip RELATIVE
721
0
      if (i == args.end()) {
722
0
        status.SetError("GLOB requires a directory after the RELATIVE tag.");
723
0
        return false;
724
0
      }
725
0
      g.SetRelative(i->c_str());
726
0
      ++i;
727
0
      if (i == args.end()) {
728
0
        status.SetError(
729
0
          "GLOB requires a glob expression after the directory.");
730
0
        return false;
731
0
      }
732
0
    } else if (*i == "CONFIGURE_DEPENDS") {
733
      // Generated build system depends on glob results
734
0
      if (!configureDepends && warnConfigureLate) {
735
0
        status.GetMakefile().IssueMessage(
736
0
          MessageType::AUTHOR_WARNING,
737
0
          "CONFIGURE_DEPENDS flag was given after a glob expression was "
738
0
          "already evaluated.");
739
0
      }
740
0
      if (cm->GetState()->GetRole() != cmState::Role::Project) {
741
0
        status.GetMakefile().IssueMessage(
742
0
          MessageType::FATAL_ERROR,
743
0
          "CONFIGURE_DEPENDS is invalid for script and find package modes.");
744
0
        return false;
745
0
      }
746
0
      configureDepends = true;
747
0
      ++i;
748
0
      if (i == args.end()) {
749
0
        status.SetError(
750
0
          "GLOB requires a glob expression after CONFIGURE_DEPENDS.");
751
0
        return false;
752
0
      }
753
0
    } else {
754
0
      std::string expr = *i;
755
0
      if (!cmsys::SystemTools::FileIsFullPath(*i)) {
756
0
        expr = status.GetMakefile().GetCurrentSourceDirectory();
757
        // Handle script mode
758
0
        if (!expr.empty()) {
759
0
          expr += "/" + *i;
760
0
        } else {
761
0
          expr = *i;
762
0
        }
763
0
      }
764
765
0
      cmsys::Glob::GlobMessages globMessages;
766
0
      g.FindFiles(expr, &globMessages);
767
768
0
      if (!globMessages.empty()) {
769
0
        bool shouldExit = false;
770
0
        for (cmsys::Glob::Message const& globMessage : globMessages) {
771
0
          if (globMessage.type == cmsys::Glob::cyclicRecursion) {
772
0
            status.GetMakefile().IssueMessage(
773
0
              MessageType::AUTHOR_WARNING,
774
0
              cmStrCat("Cyclic recursion detected while globbing for '", *i,
775
0
                       "':\n", globMessage.content));
776
0
          } else if (globMessage.type == cmsys::Glob::error) {
777
0
            status.GetMakefile().IssueMessage(
778
0
              MessageType::FATAL_ERROR,
779
0
              cmStrCat("Error has occurred while globbing for '", *i, "' - ",
780
0
                       globMessage.content));
781
0
            shouldExit = true;
782
0
          } else if (cm->GetDebugOutput() || cm->GetTrace()) {
783
0
            status.GetMakefile().IssueMessage(
784
0
              MessageType::LOG,
785
0
              cmStrCat("Globbing for\n  ", *i, "\nEncountered an error:\n ",
786
0
                       globMessage.content));
787
0
          }
788
0
        }
789
0
        if (shouldExit) {
790
0
          return false;
791
0
        }
792
0
      }
793
794
0
      std::vector<std::string>& foundFiles = g.GetFiles();
795
0
      cm::append(files, foundFiles);
796
797
0
      if (configureDepends) {
798
0
        std::sort(foundFiles.begin(), foundFiles.end());
799
0
        foundFiles.erase(std::unique(foundFiles.begin(), foundFiles.end()),
800
0
                         foundFiles.end());
801
0
        auto entry = cmGlobCacheEntry{
802
0
          recurse,
803
0
          (recurse ? g.GetRecurseListDirs() : g.GetListDirs()),
804
0
          (recurse ? g.GetRecurseThroughSymlinks() : false),
805
0
          (g.GetRelative() ? g.GetRelative() : ""),
806
0
          expr,
807
0
          foundFiles
808
0
        };
809
0
        cm->AddGlobCacheEntry(entry, variable,
810
0
                              status.GetMakefile().GetBacktrace());
811
0
      } else {
812
0
        warnConfigureLate = true;
813
0
      }
814
0
      ++i;
815
0
    }
816
0
  }
817
818
0
  std::sort(files.begin(), files.end());
819
0
  files.erase(std::unique(files.begin(), files.end()), files.end());
820
0
  status.GetMakefile().AddDefinition(variable, cmList::to_string(files));
821
0
  return true;
822
0
}
823
824
bool HandleGlobCommand(std::vector<std::string> const& args,
825
                       cmExecutionStatus& status)
826
0
{
827
0
  return HandleGlobImpl(args, false, status);
828
0
}
829
830
bool HandleGlobRecurseCommand(std::vector<std::string> const& args,
831
                              cmExecutionStatus& status)
832
0
{
833
0
  return HandleGlobImpl(args, true, status);
834
0
}
835
836
bool HandleMakeDirectoryCommand(std::vector<std::string> const& args,
837
                                cmExecutionStatus& status)
838
0
{
839
  // Projects might pass a dynamically generated list of directories, and it
840
  // could be an empty list. We should not assume there is at least one.
841
842
0
  cmRange<std::vector<std::string>::const_iterator> argsRange =
843
0
    cmMakeRange(args).advance(1); // Get rid of subcommand
844
845
0
  struct Arguments : public ArgumentParser::ParseResult
846
0
  {
847
0
    std::string Result;
848
0
  };
849
0
  Arguments arguments;
850
851
0
  auto resultPosItr =
852
0
    std::find(cm::begin(argsRange), cm::end(argsRange), "RESULT");
853
0
  if (resultPosItr != cm::end(argsRange)) {
854
0
    static auto const parser =
855
0
      cmArgumentParser<Arguments>{}.Bind("RESULT"_s, &Arguments::Result);
856
0
    std::vector<std::string> unparsedArguments;
857
0
    auto resultDistanceFromBegin =
858
0
      std::distance(cm::begin(argsRange), resultPosItr);
859
0
    arguments =
860
0
      parser.Parse(cmMakeRange(argsRange).advance(resultDistanceFromBegin),
861
0
                   &unparsedArguments);
862
863
0
    if (!unparsedArguments.empty()) {
864
0
      std::string unexpectedArgsStr = cmJoin(
865
0
        cmMakeRange(cm::begin(unparsedArguments), cm::end(unparsedArguments)),
866
0
        "\n");
867
0
      status.SetError("MAKE_DIRECTORY called with unexpected\n"
868
0
                      "arguments:\n  " +
869
0
                      unexpectedArgsStr);
870
0
      return false;
871
0
    }
872
873
0
    auto resultDistanceFromEnd =
874
0
      std::distance(cm::end(argsRange), resultPosItr);
875
0
    argsRange = argsRange.retreat(-resultDistanceFromEnd);
876
0
  }
877
878
0
  std::string expr;
879
0
  for (std::string const& arg : argsRange) {
880
0
    std::string const* cdir = &arg;
881
0
    if (!cmsys::SystemTools::FileIsFullPath(arg)) {
882
0
      expr =
883
0
        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
884
0
      cdir = &expr;
885
0
    }
886
0
    if (!status.GetMakefile().CanIWriteThisFile(*cdir)) {
887
0
      std::string e = cmStrCat("attempted to create a directory: ", *cdir,
888
0
                               " into a source directory.");
889
0
      if (arguments.Result.empty()) {
890
0
        status.SetError(e);
891
0
        cmSystemTools::SetFatalErrorOccurred();
892
0
        return false;
893
0
      }
894
0
      status.GetMakefile().AddDefinition(arguments.Result, e);
895
0
      return true;
896
0
    }
897
0
    cmsys::Status mkdirStatus = cmSystemTools::MakeDirectory(*cdir);
898
0
    if (!mkdirStatus) {
899
0
      if (arguments.Result.empty()) {
900
0
        std::string errorOutput =
901
0
          cmStrCat("failed to create directory:\n  ", *cdir,
902
0
                   "\nbecause: ", mkdirStatus.GetString());
903
0
        status.SetError(errorOutput);
904
0
        return false;
905
0
      }
906
0
      std::string errorResult = cmStrCat("Failed to create directory: ", *cdir,
907
0
                                         " Error: ", mkdirStatus.GetString());
908
0
      status.GetMakefile().AddDefinition(arguments.Result, errorResult);
909
0
      return true;
910
0
    }
911
0
  }
912
0
  if (!arguments.Result.empty()) {
913
0
    status.GetMakefile().AddDefinition(arguments.Result, "0");
914
0
  }
915
0
  return true;
916
0
}
917
918
bool HandleTouchImpl(std::vector<std::string> const& args, bool create,
919
                     cmExecutionStatus& status)
920
0
{
921
  // Projects might pass a dynamically generated list of files, and it
922
  // could be an empty list. We should not assume there is at least one.
923
924
0
  for (std::string const& arg :
925
0
       cmMakeRange(args).advance(1)) // Get rid of subcommand
926
0
  {
927
0
    std::string tfile = arg;
928
0
    if (!cmsys::SystemTools::FileIsFullPath(tfile)) {
929
0
      tfile =
930
0
        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
931
0
    }
932
0
    if (!status.GetMakefile().CanIWriteThisFile(tfile)) {
933
0
      std::string e = cmStrCat("attempted to touch a file: ", tfile,
934
0
                               " in a source directory.");
935
0
      status.SetError(e);
936
0
      cmSystemTools::SetFatalErrorOccurred();
937
0
      return false;
938
0
    }
939
0
    if (!cmSystemTools::Touch(tfile, create)) {
940
0
      std::string error = "problem touching file: " + tfile;
941
0
      status.SetError(error);
942
0
      return false;
943
0
    }
944
0
  }
945
0
  return true;
946
0
}
947
948
bool HandleTouchCommand(std::vector<std::string> const& args,
949
                        cmExecutionStatus& status)
950
0
{
951
0
  return HandleTouchImpl(args, true, status);
952
0
}
953
954
bool HandleTouchNocreateCommand(std::vector<std::string> const& args,
955
                                cmExecutionStatus& status)
956
0
{
957
0
  return HandleTouchImpl(args, false, status);
958
0
}
959
960
bool HandleDifferentCommand(std::vector<std::string> const& args,
961
                            cmExecutionStatus& status)
962
0
{
963
  /*
964
    FILE(DIFFERENT <variable> FILES <lhs> <rhs>)
965
   */
966
967
  // Evaluate arguments.
968
0
  char const* file_lhs = nullptr;
969
0
  char const* file_rhs = nullptr;
970
0
  char const* var = nullptr;
971
0
  enum Doing
972
0
  {
973
0
    DoingNone,
974
0
    DoingVar,
975
0
    DoingFileLHS,
976
0
    DoingFileRHS
977
0
  };
978
0
  Doing doing = DoingVar;
979
0
  for (unsigned int i = 1; i < args.size(); ++i) {
980
0
    if (args[i] == "FILES") {
981
0
      doing = DoingFileLHS;
982
0
    } else if (doing == DoingVar) {
983
0
      var = args[i].c_str();
984
0
      doing = DoingNone;
985
0
    } else if (doing == DoingFileLHS) {
986
0
      file_lhs = args[i].c_str();
987
0
      doing = DoingFileRHS;
988
0
    } else if (doing == DoingFileRHS) {
989
0
      file_rhs = args[i].c_str();
990
0
      doing = DoingNone;
991
0
    } else {
992
0
      status.SetError(cmStrCat("DIFFERENT given unknown argument ", args[i]));
993
0
      return false;
994
0
    }
995
0
  }
996
0
  if (!var) {
997
0
    status.SetError("DIFFERENT not given result variable name.");
998
0
    return false;
999
0
  }
1000
0
  if (!file_lhs || !file_rhs) {
1001
0
    status.SetError("DIFFERENT not given FILES option with two file names.");
1002
0
    return false;
1003
0
  }
1004
1005
  // Compare the files.
1006
0
  char const* result =
1007
0
    cmSystemTools::FilesDiffer(file_lhs, file_rhs) ? "1" : "0";
1008
0
  status.GetMakefile().AddDefinition(var, result);
1009
0
  return true;
1010
0
}
1011
1012
bool HandleCopyCommand(std::vector<std::string> const& args,
1013
                       cmExecutionStatus& status)
1014
0
{
1015
0
  cmFileCopier copier(status);
1016
0
  return copier.Run(args);
1017
0
}
1018
1019
bool HandleRPathChangeCommand(std::vector<std::string> const& args,
1020
                              cmExecutionStatus& status)
1021
0
{
1022
  // Evaluate arguments.
1023
0
  std::string file;
1024
0
  cm::optional<std::string> oldRPath;
1025
0
  cm::optional<std::string> newRPath;
1026
0
  bool removeEnvironmentRPath = false;
1027
0
  cmArgumentParser<void> parser;
1028
0
  std::vector<std::string> unknownArgs;
1029
0
  parser.Bind("FILE"_s, file)
1030
0
    .Bind("OLD_RPATH"_s, oldRPath)
1031
0
    .Bind("NEW_RPATH"_s, newRPath)
1032
0
    .Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath);
1033
0
  ArgumentParser::ParseResult parseResult =
1034
0
    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
1035
0
  if (!unknownArgs.empty()) {
1036
0
    status.SetError(
1037
0
      cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front()));
1038
0
    return false;
1039
0
  }
1040
0
  if (parseResult.MaybeReportError(status.GetMakefile())) {
1041
0
    return true;
1042
0
  }
1043
0
  if (file.empty()) {
1044
0
    status.SetError("RPATH_CHANGE not given FILE option.");
1045
0
    return false;
1046
0
  }
1047
0
  if (!oldRPath) {
1048
0
    status.SetError("RPATH_CHANGE not given OLD_RPATH option.");
1049
0
    return false;
1050
0
  }
1051
0
  if (!newRPath) {
1052
0
    status.SetError("RPATH_CHANGE not given NEW_RPATH option.");
1053
0
    return false;
1054
0
  }
1055
0
  if (!cmSystemTools::FileExists(file, true)) {
1056
0
    status.SetError(
1057
0
      cmStrCat("RPATH_CHANGE given FILE \"", file, "\" that does not exist."));
1058
0
    return false;
1059
0
  }
1060
0
  bool success = true;
1061
0
  cmFileTimes const ft(file);
1062
0
  std::string emsg;
1063
0
  bool changed;
1064
1065
0
  if (!cmSystemTools::ChangeRPath(file, *oldRPath, *newRPath,
1066
0
                                  removeEnvironmentRPath, &emsg, &changed)) {
1067
0
    status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n  ",
1068
0
                             *newRPath, "\nto the file:\n  ", file, '\n',
1069
0
                             emsg));
1070
0
    success = false;
1071
0
  }
1072
0
  if (success) {
1073
0
    if (changed) {
1074
0
      std::string message =
1075
0
        cmStrCat("Set non-toolchain portion of runtime path of \"", file,
1076
0
                 "\" to \"", *newRPath, '"');
1077
0
      status.GetMakefile().DisplayStatus(message, -1);
1078
0
    }
1079
0
    ft.Store(file);
1080
0
  }
1081
0
  return success;
1082
0
}
1083
1084
bool HandleRPathSetCommand(std::vector<std::string> const& args,
1085
                           cmExecutionStatus& status)
1086
0
{
1087
  // Evaluate arguments.
1088
0
  std::string file;
1089
0
  cm::optional<std::string> newRPath;
1090
0
  cmArgumentParser<void> parser;
1091
0
  std::vector<std::string> unknownArgs;
1092
0
  parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath);
1093
0
  ArgumentParser::ParseResult parseResult =
1094
0
    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
1095
0
  if (!unknownArgs.empty()) {
1096
0
    status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"",
1097
0
                             unknownArgs.front(), "\"."));
1098
0
    return false;
1099
0
  }
1100
0
  if (parseResult.MaybeReportError(status.GetMakefile())) {
1101
0
    return true;
1102
0
  }
1103
0
  if (file.empty()) {
1104
0
    status.SetError("RPATH_SET not given FILE option.");
1105
0
    return false;
1106
0
  }
1107
0
  if (!newRPath) {
1108
0
    status.SetError("RPATH_SET not given NEW_RPATH option.");
1109
0
    return false;
1110
0
  }
1111
0
  if (!cmSystemTools::FileExists(file, true)) {
1112
0
    status.SetError(
1113
0
      cmStrCat("RPATH_SET given FILE \"", file, "\" that does not exist."));
1114
0
    return false;
1115
0
  }
1116
0
  bool success = true;
1117
0
  cmFileTimes const ft(file);
1118
0
  std::string emsg;
1119
0
  bool changed;
1120
1121
0
  if (!cmSystemTools::SetRPath(file, *newRPath, &emsg, &changed)) {
1122
0
    status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n  ",
1123
0
                             *newRPath, "\nto the file:\n  ", file, '\n',
1124
0
                             emsg));
1125
0
    success = false;
1126
0
  }
1127
0
  if (success) {
1128
0
    if (changed) {
1129
0
      std::string message =
1130
0
        cmStrCat("Set non-toolchain portion of runtime path of \"", file,
1131
0
                 "\" to \"", *newRPath, '"');
1132
0
      status.GetMakefile().DisplayStatus(message, -1);
1133
0
    }
1134
0
    ft.Store(file);
1135
0
  }
1136
0
  return success;
1137
0
}
1138
1139
bool HandleRPathRemoveCommand(std::vector<std::string> const& args,
1140
                              cmExecutionStatus& status)
1141
0
{
1142
  // Evaluate arguments.
1143
0
  std::string file;
1144
0
  cmArgumentParser<void> parser;
1145
0
  std::vector<std::string> unknownArgs;
1146
0
  parser.Bind("FILE"_s, file);
1147
0
  ArgumentParser::ParseResult parseResult =
1148
0
    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
1149
0
  if (!unknownArgs.empty()) {
1150
0
    status.SetError(
1151
0
      cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front()));
1152
0
    return false;
1153
0
  }
1154
0
  if (parseResult.MaybeReportError(status.GetMakefile())) {
1155
0
    return true;
1156
0
  }
1157
0
  if (file.empty()) {
1158
0
    status.SetError("RPATH_REMOVE not given FILE option.");
1159
0
    return false;
1160
0
  }
1161
0
  if (!cmSystemTools::FileExists(file, true)) {
1162
0
    status.SetError(
1163
0
      cmStrCat("RPATH_REMOVE given FILE \"", file, "\" that does not exist."));
1164
0
    return false;
1165
0
  }
1166
0
  bool success = true;
1167
0
  cmFileTimes const ft(file);
1168
0
  std::string emsg;
1169
0
  bool removed;
1170
0
  if (!cmSystemTools::RemoveRPath(file, &emsg, &removed)) {
1171
0
    status.SetError(
1172
0
      cmStrCat("RPATH_REMOVE could not remove RPATH from file: \n  ", file,
1173
0
               '\n', emsg));
1174
0
    success = false;
1175
0
  }
1176
0
  if (success) {
1177
0
    if (removed) {
1178
0
      std::string message =
1179
0
        cmStrCat("Removed runtime path from \"", file, '"');
1180
0
      status.GetMakefile().DisplayStatus(message, -1);
1181
0
    }
1182
0
    ft.Store(file);
1183
0
  }
1184
0
  return success;
1185
0
}
1186
1187
bool HandleRPathCheckCommand(std::vector<std::string> const& args,
1188
                             cmExecutionStatus& status)
1189
0
{
1190
  // Evaluate arguments.
1191
0
  std::string file;
1192
0
  cm::optional<std::string> rpath;
1193
0
  cmArgumentParser<void> parser;
1194
0
  std::vector<std::string> unknownArgs;
1195
0
  parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath);
1196
0
  ArgumentParser::ParseResult parseResult =
1197
0
    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
1198
0
  if (!unknownArgs.empty()) {
1199
0
    status.SetError(
1200
0
      cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front()));
1201
0
    return false;
1202
0
  }
1203
0
  if (parseResult.MaybeReportError(status.GetMakefile())) {
1204
0
    return true;
1205
0
  }
1206
0
  if (file.empty()) {
1207
0
    status.SetError("RPATH_CHECK not given FILE option.");
1208
0
    return false;
1209
0
  }
1210
0
  if (!rpath) {
1211
0
    status.SetError("RPATH_CHECK not given RPATH option.");
1212
0
    return false;
1213
0
  }
1214
1215
  // If the file exists but does not have the desired RPath then
1216
  // delete it.  This is used during installation to re-install a file
1217
  // if its RPath will change.
1218
0
  if (cmSystemTools::FileExists(file, true) &&
1219
0
      !cmSystemTools::CheckRPath(file, *rpath)) {
1220
0
    cmSystemTools::RemoveFile(file);
1221
0
  }
1222
1223
0
  return true;
1224
0
}
1225
1226
bool HandleReadElfCommand(std::vector<std::string> const& args,
1227
                          cmExecutionStatus& status)
1228
0
{
1229
0
  if (args.size() < 4) {
1230
0
    status.SetError("READ_ELF must be called with at least three additional "
1231
0
                    "arguments.");
1232
0
    return false;
1233
0
  }
1234
1235
0
  std::string const& fileNameArg = args[1];
1236
1237
0
  struct Arguments
1238
0
  {
1239
0
    std::string RPath;
1240
0
    std::string RunPath;
1241
0
    std::string Error;
1242
0
  };
1243
1244
0
  static auto const parser = cmArgumentParser<Arguments>{}
1245
0
                               .Bind("RPATH"_s, &Arguments::RPath)
1246
0
                               .Bind("RUNPATH"_s, &Arguments::RunPath)
1247
0
                               .Bind("CAPTURE_ERROR"_s, &Arguments::Error);
1248
0
  Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2),
1249
0
                                           /*unparsedArguments=*/nullptr);
1250
1251
0
  if (!cmSystemTools::FileExists(fileNameArg, true)) {
1252
0
    status.SetError(cmStrCat("READ_ELF given FILE \"", fileNameArg,
1253
0
                             "\" that does not exist."));
1254
0
    return false;
1255
0
  }
1256
1257
0
  cmELF elf(fileNameArg.c_str());
1258
0
  if (!elf) {
1259
0
    if (arguments.Error.empty()) {
1260
0
      status.SetError(cmStrCat("READ_ELF given FILE:\n  ", fileNameArg,
1261
0
                               "\nthat is not a valid ELF file."));
1262
0
      return false;
1263
0
    }
1264
0
    status.GetMakefile().AddDefinition(arguments.Error,
1265
0
                                       "not a valid ELF file");
1266
0
    return true;
1267
0
  }
1268
1269
0
  if (!arguments.RPath.empty()) {
1270
0
    if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
1271
0
      std::string rpath(se_rpath->Value);
1272
0
      std::replace(rpath.begin(), rpath.end(), ':', ';');
1273
0
      status.GetMakefile().AddDefinition(arguments.RPath, rpath);
1274
0
    }
1275
0
  }
1276
0
  if (!arguments.RunPath.empty()) {
1277
0
    if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
1278
0
      std::string runpath(se_runpath->Value);
1279
0
      std::replace(runpath.begin(), runpath.end(), ':', ';');
1280
0
      status.GetMakefile().AddDefinition(arguments.RunPath, runpath);
1281
0
    }
1282
0
  }
1283
1284
0
  return true;
1285
0
}
1286
1287
bool HandleInstallCommand(std::vector<std::string> const& args,
1288
                          cmExecutionStatus& status)
1289
0
{
1290
0
  cmFileInstaller installer(status);
1291
0
  return installer.Run(args);
1292
0
}
1293
1294
bool HandleRealPathCommand(std::vector<std::string> const& args,
1295
                           cmExecutionStatus& status)
1296
0
{
1297
0
  if (args.size() < 3) {
1298
0
    status.SetError("REAL_PATH requires a path and an output variable");
1299
0
    return false;
1300
0
  }
1301
1302
0
  struct Arguments : public ArgumentParser::ParseResult
1303
0
  {
1304
0
    cm::optional<std::string> BaseDirectory;
1305
0
    bool ExpandTilde = false;
1306
0
  };
1307
0
  static auto const parser =
1308
0
    cmArgumentParser<Arguments>{}
1309
0
      .Bind("BASE_DIRECTORY"_s, &Arguments::BaseDirectory)
1310
0
      .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde);
1311
1312
0
  std::vector<std::string> unparsedArguments;
1313
0
  auto arguments =
1314
0
    parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments);
1315
1316
0
  if (!unparsedArguments.empty()) {
1317
0
    status.SetError("REAL_PATH called with unexpected arguments");
1318
0
    return false;
1319
0
  }
1320
0
  if (arguments.MaybeReportError(status.GetMakefile())) {
1321
0
    return true;
1322
0
  }
1323
1324
0
  if (!arguments.BaseDirectory) {
1325
0
    arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
1326
0
  }
1327
1328
0
  auto input = args[1];
1329
0
  if (arguments.ExpandTilde && !input.empty()) {
1330
0
    if (input[0] == '~' && (input.length() == 1 || input[1] == '/')) {
1331
0
      std::string home;
1332
0
      if (
1333
#if defined(_WIN32) && !defined(__CYGWIN__)
1334
        cmSystemTools::GetEnv("USERPROFILE", home) ||
1335
#endif
1336
0
        cmSystemTools::GetEnv("HOME", home)) {
1337
0
        input.replace(0, 1, home);
1338
0
      }
1339
0
    }
1340
0
  }
1341
1342
0
  bool warnAbout152 = false;
1343
0
  bool use152New = true;
1344
0
  cmPolicies::PolicyStatus policyStatus =
1345
0
    status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0152);
1346
0
  switch (policyStatus) {
1347
0
    case cmPolicies::NEW:
1348
0
      break;
1349
0
    case cmPolicies::WARN:
1350
0
      use152New = false;
1351
0
      warnAbout152 = true;
1352
0
      break;
1353
0
    case cmPolicies::OLD:
1354
0
      use152New = false;
1355
0
      warnAbout152 = false;
1356
0
      break;
1357
0
  }
1358
1359
0
  auto computeNewPath = [=](std::string const& in, std::string& result) {
1360
0
    auto path = cmCMakePath{ in };
1361
0
    if (path.IsRelative()) {
1362
0
      auto basePath = cmCMakePath{ *arguments.BaseDirectory };
1363
0
      path = basePath.Append(path);
1364
0
    }
1365
0
    result = cmSystemTools::GetRealPath(path.String());
1366
0
  };
1367
1368
0
  std::string realPath;
1369
0
  if (use152New) {
1370
0
    computeNewPath(input, realPath);
1371
0
  } else {
1372
0
    std::string oldPolicyPath =
1373
0
      cmSystemTools::CollapseFullPath(input, *arguments.BaseDirectory);
1374
0
    oldPolicyPath = cmSystemTools::GetRealPath(oldPolicyPath);
1375
0
    if (warnAbout152) {
1376
0
      computeNewPath(input, realPath);
1377
0
      if (oldPolicyPath != realPath) {
1378
0
        status.GetMakefile().IssueMessage(
1379
0
          MessageType::AUTHOR_WARNING,
1380
0
          cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0152),
1381
0
                   "\n"
1382
0
                   "From input path:\n  ",
1383
0
                   input, "\nthe policy OLD behavior produces path:\n  ",
1384
0
                   oldPolicyPath,
1385
0
                   "\nbut the policy NEW behavior produces path:\n  ",
1386
0
                   realPath,
1387
0
                   "\nSince the policy is not set, CMake is using the OLD "
1388
0
                   "behavior for compatibility."));
1389
0
      }
1390
0
    }
1391
0
    realPath = oldPolicyPath;
1392
0
  }
1393
1394
0
  if (!cmSystemTools::FileExists(realPath)) {
1395
0
    status.GetMakefile().IssueMessage(
1396
0
      MessageType::AUTHOR_WARNING,
1397
0
      cmStrCat("Given path:\n  ", input,
1398
0
               "\ndoes not refer to an existing path on disk."));
1399
0
  }
1400
1401
0
  status.GetMakefile().AddDefinition(args[2], realPath);
1402
1403
0
  return true;
1404
0
}
1405
1406
bool HandleRelativePathCommand(std::vector<std::string> const& args,
1407
                               cmExecutionStatus& status)
1408
0
{
1409
0
  if (args.size() != 4) {
1410
0
    status.SetError("RELATIVE_PATH called with incorrect number of arguments");
1411
0
    return false;
1412
0
  }
1413
1414
0
  std::string const& outVar = args[1];
1415
0
  std::string const& directoryName = args[2];
1416
0
  std::string const& fileName = args[3];
1417
1418
0
  if (!cmSystemTools::FileIsFullPath(directoryName)) {
1419
0
    std::string errstring =
1420
0
      "RELATIVE_PATH must be passed a full path to the directory: " +
1421
0
      directoryName;
1422
0
    status.SetError(errstring);
1423
0
    return false;
1424
0
  }
1425
0
  if (!cmSystemTools::FileIsFullPath(fileName)) {
1426
0
    std::string errstring =
1427
0
      "RELATIVE_PATH must be passed a full path to the file: " + fileName;
1428
0
    status.SetError(errstring);
1429
0
    return false;
1430
0
  }
1431
1432
0
  std::string res = cmSystemTools::RelativePath(directoryName, fileName);
1433
0
  status.GetMakefile().AddDefinition(outVar, res);
1434
0
  return true;
1435
0
}
1436
1437
bool HandleRename(std::vector<std::string> const& args,
1438
                  cmExecutionStatus& status)
1439
0
{
1440
0
  if (args.size() < 3) {
1441
0
    status.SetError("RENAME must be called with at least two additional "
1442
0
                    "arguments");
1443
0
    return false;
1444
0
  }
1445
1446
  // Compute full path for old and new names.
1447
0
  std::string oldname = args[1];
1448
0
  if (!cmsys::SystemTools::FileIsFullPath(oldname)) {
1449
0
    oldname =
1450
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
1451
0
  }
1452
0
  std::string newname = args[2];
1453
0
  if (!cmsys::SystemTools::FileIsFullPath(newname)) {
1454
0
    newname =
1455
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[2]);
1456
0
  }
1457
1458
0
  struct Arguments
1459
0
  {
1460
0
    bool NoReplace = false;
1461
0
    std::string Result;
1462
0
  };
1463
1464
0
  static auto const parser = cmArgumentParser<Arguments>{}
1465
0
                               .Bind("NO_REPLACE"_s, &Arguments::NoReplace)
1466
0
                               .Bind("RESULT"_s, &Arguments::Result);
1467
1468
0
  std::vector<std::string> unconsumedArgs;
1469
0
  Arguments const arguments =
1470
0
    parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
1471
0
  if (!unconsumedArgs.empty()) {
1472
0
    status.SetError("RENAME unknown argument:\n  " + unconsumedArgs.front());
1473
0
    return false;
1474
0
  }
1475
1476
0
  std::string err;
1477
0
  switch (cmSystemTools::RenameFile(oldname, newname,
1478
0
                                    arguments.NoReplace
1479
0
                                      ? cmSystemTools::Replace::No
1480
0
                                      : cmSystemTools::Replace::Yes,
1481
0
                                    &err)) {
1482
0
    case cmSystemTools::RenameResult::Success:
1483
0
      if (!arguments.Result.empty()) {
1484
0
        status.GetMakefile().AddDefinition(arguments.Result, "0");
1485
0
      }
1486
0
      return true;
1487
0
    case cmSystemTools::RenameResult::NoReplace:
1488
0
      if (!arguments.Result.empty()) {
1489
0
        err = "NO_REPLACE";
1490
0
      } else {
1491
0
        err = "path not replaced";
1492
0
      }
1493
0
      CM_FALLTHROUGH;
1494
0
    case cmSystemTools::RenameResult::Failure:
1495
0
      if (!arguments.Result.empty()) {
1496
0
        status.GetMakefile().AddDefinition(arguments.Result, err);
1497
0
        return true;
1498
0
      }
1499
0
      break;
1500
0
  }
1501
0
  status.SetError(cmStrCat("RENAME failed to rename\n  ", oldname, "\nto\n  ",
1502
0
                           newname, "\nbecause: ", err, '\n'));
1503
0
  return false;
1504
0
}
1505
1506
bool HandleCopyFile(std::vector<std::string> const& args,
1507
                    cmExecutionStatus& status)
1508
0
{
1509
0
  if (args.size() < 3) {
1510
0
    status.SetError("COPY_FILE must be called with at least two additional "
1511
0
                    "arguments");
1512
0
    return false;
1513
0
  }
1514
1515
  // Compute full path for old and new names.
1516
0
  std::string oldname = args[1];
1517
0
  if (!cmsys::SystemTools::FileIsFullPath(oldname)) {
1518
0
    oldname =
1519
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[1]);
1520
0
  }
1521
0
  std::string newname = args[2];
1522
0
  if (!cmsys::SystemTools::FileIsFullPath(newname)) {
1523
0
    newname =
1524
0
      cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', args[2]);
1525
0
  }
1526
1527
0
  struct Arguments
1528
0
  {
1529
0
    bool InputMayBeRecent = false;
1530
0
    bool OnlyIfDifferent = false;
1531
0
    std::string Result;
1532
0
  };
1533
1534
0
  static auto const parser =
1535
0
    cmArgumentParser<Arguments>{}
1536
0
      .Bind("INPUT_MAY_BE_RECENT"_s, &Arguments::InputMayBeRecent)
1537
0
      .Bind("ONLY_IF_DIFFERENT"_s, &Arguments::OnlyIfDifferent)
1538
0
      .Bind("RESULT"_s, &Arguments::Result);
1539
1540
0
  std::vector<std::string> unconsumedArgs;
1541
0
  Arguments const arguments =
1542
0
    parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
1543
0
  if (!unconsumedArgs.empty()) {
1544
0
    status.SetError("COPY_FILE unknown argument:\n  " +
1545
0
                    unconsumedArgs.front());
1546
0
    return false;
1547
0
  }
1548
1549
0
  bool result = true;
1550
0
  if (cmsys::SystemTools::FileIsDirectory(oldname)) {
1551
0
    if (!arguments.Result.empty()) {
1552
0
      status.GetMakefile().AddDefinition(arguments.Result,
1553
0
                                         "cannot copy a directory");
1554
0
    } else {
1555
0
      status.SetError(
1556
0
        cmStrCat("COPY_FILE cannot copy a directory\n  ", oldname));
1557
0
      result = false;
1558
0
    }
1559
0
    return result;
1560
0
  }
1561
0
  if (cmsys::SystemTools::FileIsDirectory(newname)) {
1562
0
    if (!arguments.Result.empty()) {
1563
0
      status.GetMakefile().AddDefinition(arguments.Result,
1564
0
                                         "cannot copy to a directory");
1565
0
    } else {
1566
0
      status.SetError(
1567
0
        cmStrCat("COPY_FILE cannot copy to a directory\n  ", newname));
1568
0
      result = false;
1569
0
    }
1570
0
    return result;
1571
0
  }
1572
1573
0
  cmSystemTools::CopyWhen when;
1574
0
  if (arguments.OnlyIfDifferent) {
1575
0
    when = cmSystemTools::CopyWhen::OnlyIfDifferent;
1576
0
  } else {
1577
0
    when = cmSystemTools::CopyWhen::Always;
1578
0
  }
1579
0
  cmSystemTools::CopyInputRecent const inputRecent = arguments.InputMayBeRecent
1580
0
    ? cmSystemTools::CopyInputRecent::Yes
1581
0
    : cmSystemTools::CopyInputRecent::No;
1582
1583
0
  std::string err;
1584
0
  if (cmSystemTools::CopySingleFile(oldname, newname, when, inputRecent,
1585
0
                                    &err) ==
1586
0
      cmSystemTools::CopyResult::Success) {
1587
0
    if (!arguments.Result.empty()) {
1588
0
      status.GetMakefile().AddDefinition(arguments.Result, "0");
1589
0
    }
1590
0
  } else {
1591
0
    if (!arguments.Result.empty()) {
1592
0
      status.GetMakefile().AddDefinition(arguments.Result, err);
1593
0
    } else {
1594
0
      status.SetError(cmStrCat("COPY_FILE failed to copy\n  ", oldname,
1595
0
                               "\nto\n  ", newname, "\nbecause: ", err, '\n'));
1596
0
      result = false;
1597
0
    }
1598
0
  }
1599
1600
0
  return result;
1601
0
}
1602
1603
bool HandleRemoveImpl(std::vector<std::string> const& args, bool recurse,
1604
                      cmExecutionStatus& status)
1605
0
{
1606
0
  for (std::string const& arg :
1607
0
       cmMakeRange(args).advance(1)) // Get rid of subcommand
1608
0
  {
1609
0
    std::string fileName = arg;
1610
0
    if (fileName.empty()) {
1611
0
      std::string r = recurse ? "REMOVE_RECURSE" : "REMOVE";
1612
0
      status.GetMakefile().IssueMessage(
1613
0
        MessageType::AUTHOR_WARNING,
1614
0
        cmStrCat("Ignoring empty file name in ", std::move(r), '.'));
1615
0
      continue;
1616
0
    }
1617
0
    if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
1618
0
      fileName =
1619
0
        cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/', arg);
1620
0
    }
1621
1622
0
    if (cmSystemTools::FileIsDirectory(fileName) &&
1623
0
        !cmSystemTools::FileIsSymlink(fileName) && recurse) {
1624
0
      cmSystemTools::RepeatedRemoveDirectory(fileName);
1625
0
    } else {
1626
0
      cmSystemTools::RemoveFile(fileName);
1627
0
    }
1628
0
  }
1629
0
  return true;
1630
0
}
1631
1632
bool HandleRemove(std::vector<std::string> const& args,
1633
                  cmExecutionStatus& status)
1634
0
{
1635
0
  return HandleRemoveImpl(args, false, status);
1636
0
}
1637
1638
bool HandleRemoveRecurse(std::vector<std::string> const& args,
1639
                         cmExecutionStatus& status)
1640
0
{
1641
0
  return HandleRemoveImpl(args, true, status);
1642
0
}
1643
1644
std::string ToNativePath(std::string const& path)
1645
0
{
1646
0
  auto const& outPath = cmSystemTools::ConvertToOutputPath(path);
1647
0
  if (outPath.size() > 1 && outPath.front() == '\"' &&
1648
0
      outPath.back() == '\"') {
1649
0
    return outPath.substr(1, outPath.size() - 2);
1650
0
  }
1651
0
  return outPath;
1652
0
}
1653
1654
std::string ToCMakePath(std::string const& path)
1655
0
{
1656
0
  auto temp = path;
1657
0
  cmSystemTools::ConvertToUnixSlashes(temp);
1658
0
  return temp;
1659
0
}
1660
1661
bool HandlePathCommand(std::vector<std::string> const& args,
1662
                       std::string (*convert)(std::string const&),
1663
                       cmExecutionStatus& status)
1664
0
{
1665
0
  if (args.size() != 3) {
1666
0
    status.SetError("FILE([TO_CMAKE_PATH|TO_NATIVE_PATH] path result) must be "
1667
0
                    "called with exactly three arguments.");
1668
0
    return false;
1669
0
  }
1670
#if defined(_WIN32) && !defined(__CYGWIN__)
1671
  char pathSep = ';';
1672
#else
1673
0
  char pathSep = ':';
1674
0
#endif
1675
0
  std::vector<std::string> path = cmSystemTools::SplitString(args[1], pathSep);
1676
1677
0
  std::string value = cmList::to_string(cmMakeRange(path).transform(convert));
1678
0
  status.GetMakefile().AddDefinition(args[2], value);
1679
0
  return true;
1680
0
}
1681
1682
bool HandleCMakePathCommand(std::vector<std::string> const& args,
1683
                            cmExecutionStatus& status)
1684
0
{
1685
0
  return HandlePathCommand(args, ToCMakePath, status);
1686
0
}
1687
1688
bool HandleNativePathCommand(std::vector<std::string> const& args,
1689
                             cmExecutionStatus& status)
1690
0
{
1691
0
  return HandlePathCommand(args, ToNativePath, status);
1692
0
}
1693
1694
#if !defined(CMAKE_BOOTSTRAP)
1695
1696
bool const TLS_VERIFY_DEFAULT = true;
1697
std::string const TLS_VERSION_DEFAULT = "1.2";
1698
1699
// Stuff for curl download/upload
1700
using cmFileCommandVectorOfChar = std::vector<char>;
1701
1702
std::string CurlCompatFileURL(std::string url)
1703
0
{
1704
0
  if (cmHasLiteralPrefix(url, "file://")) {
1705
    // libcurl 7.77 and below accidentally allowed spaces in file:// URLs,
1706
    // which CMake has long accepted as a result.  Explicitly encode spaces.
1707
0
    cmSystemTools::ReplaceString(url, " ", "%20");
1708
0
  }
1709
0
  return url;
1710
0
}
1711
1712
size_t cmWriteToFileCallback(void* ptr, size_t size, size_t nmemb, void* data)
1713
0
{
1714
0
  int realsize = static_cast<int>(size * nmemb);
1715
0
  cmsys::ofstream* fout = static_cast<cmsys::ofstream*>(data);
1716
0
  if (fout) {
1717
0
    char const* chPtr = static_cast<char*>(ptr);
1718
0
    if (!fout->write(chPtr, realsize)) {
1719
0
      return CURL_WRITEFUNC_ERROR;
1720
0
    }
1721
0
  }
1722
0
  return realsize;
1723
0
}
1724
1725
size_t cmWriteToMemoryCallback(void* ptr, size_t size, size_t nmemb,
1726
                               void* data)
1727
0
{
1728
0
  int realsize = static_cast<int>(size * nmemb);
1729
0
  char const* chPtr = static_cast<char*>(ptr);
1730
0
  cm::append(*static_cast<cmFileCommandVectorOfChar*>(data), chPtr,
1731
0
             chPtr + realsize);
1732
0
  return realsize;
1733
0
}
1734
1735
int cmFileCommandCurlDebugCallback(CURL*, curl_infotype type, char* chPtr,
1736
                                   size_t size, void* data)
1737
0
{
1738
0
  cmFileCommandVectorOfChar& vec =
1739
0
    *static_cast<cmFileCommandVectorOfChar*>(data);
1740
0
  switch (type) {
1741
0
    case CURLINFO_TEXT:
1742
0
    case CURLINFO_HEADER_IN:
1743
0
    case CURLINFO_HEADER_OUT:
1744
0
      cm::append(vec, chPtr, chPtr + size);
1745
0
      break;
1746
0
    case CURLINFO_DATA_IN:
1747
0
    case CURLINFO_DATA_OUT:
1748
0
    case CURLINFO_SSL_DATA_IN:
1749
0
    case CURLINFO_SSL_DATA_OUT: {
1750
0
      char buf[128];
1751
0
      int n =
1752
0
        snprintf(buf, sizeof(buf), "[%" KWIML_INT_PRIu64 " bytes data]\n",
1753
0
                 static_cast<KWIML_INT_uint64_t>(size));
1754
0
      if (n > 0) {
1755
0
        cm::append(vec, buf, buf + n);
1756
0
      }
1757
0
    } break;
1758
0
    default:
1759
0
      break;
1760
0
  }
1761
0
  return 0;
1762
0
}
1763
1764
#  if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM >= 0x072000
1765
CURLoption const CM_CURLOPT_XFERINFOFUNCTION = CURLOPT_XFERINFOFUNCTION;
1766
using cm_curl_off_t = curl_off_t;
1767
#  else
1768
CURLoption const CM_CURLOPT_XFERINFOFUNCTION = CURLOPT_PROGRESSFUNCTION;
1769
using cm_curl_off_t = double;
1770
#  endif
1771
1772
class cURLProgressHelper
1773
{
1774
public:
1775
  cURLProgressHelper(cmMakefile* mf, char const* text)
1776
0
    : Makefile(mf)
1777
0
    , Text(text)
1778
0
  {
1779
0
  }
1780
1781
  bool UpdatePercentage(cm_curl_off_t value, cm_curl_off_t total,
1782
                        std::string& status)
1783
0
  {
1784
0
    long OldPercentage = this->CurrentPercentage;
1785
1786
0
    if (total > 0) {
1787
0
      this->CurrentPercentage = std::lround(
1788
0
        static_cast<double>(value) / static_cast<double>(total) * 100.0);
1789
0
      if (this->CurrentPercentage > 100) {
1790
        // Avoid extra progress reports for unexpected data beyond total.
1791
0
        this->CurrentPercentage = 100;
1792
0
      }
1793
0
    }
1794
1795
0
    bool updated = (OldPercentage != this->CurrentPercentage);
1796
1797
0
    if (updated) {
1798
0
      status =
1799
0
        cmStrCat('[', this->Text, ' ', this->CurrentPercentage, "% complete]");
1800
0
    }
1801
1802
0
    return updated;
1803
0
  }
1804
1805
0
  cmMakefile* GetMakefile() { return this->Makefile; }
1806
1807
private:
1808
  long CurrentPercentage = -1;
1809
  cmMakefile* Makefile;
1810
  std::string Text;
1811
};
1812
1813
int cmFileDownloadProgressCallback(void* clientp, cm_curl_off_t dltotal,
1814
                                   cm_curl_off_t dlnow, cm_curl_off_t ultotal,
1815
                                   cm_curl_off_t ulnow)
1816
0
{
1817
0
  cURLProgressHelper* helper = reinterpret_cast<cURLProgressHelper*>(clientp);
1818
1819
0
  static_cast<void>(ultotal);
1820
0
  static_cast<void>(ulnow);
1821
1822
0
  std::string status;
1823
0
  if (helper->UpdatePercentage(dlnow, dltotal, status)) {
1824
0
    cmMakefile* mf = helper->GetMakefile();
1825
0
    mf->DisplayStatus(status, -1);
1826
0
  }
1827
1828
0
  return 0;
1829
0
}
1830
1831
int cmFileUploadProgressCallback(void* clientp, cm_curl_off_t dltotal,
1832
                                 cm_curl_off_t dlnow, cm_curl_off_t ultotal,
1833
                                 cm_curl_off_t ulnow)
1834
0
{
1835
0
  cURLProgressHelper* helper = reinterpret_cast<cURLProgressHelper*>(clientp);
1836
1837
0
  static_cast<void>(dltotal);
1838
0
  static_cast<void>(dlnow);
1839
1840
0
  std::string status;
1841
0
  if (helper->UpdatePercentage(ulnow, ultotal, status)) {
1842
0
    cmMakefile* mf = helper->GetMakefile();
1843
0
    mf->DisplayStatus(status, -1);
1844
0
  }
1845
1846
0
  return 0;
1847
0
}
1848
1849
class cURLEasyGuard
1850
{
1851
public:
1852
  cURLEasyGuard(CURL* easy)
1853
0
    : Easy(easy)
1854
0
  {
1855
0
  }
1856
1857
  ~cURLEasyGuard()
1858
0
  {
1859
0
    if (this->Easy) {
1860
0
      ::curl_easy_cleanup(this->Easy);
1861
0
    }
1862
0
  }
1863
1864
  cURLEasyGuard(cURLEasyGuard const&) = delete;
1865
  cURLEasyGuard& operator=(cURLEasyGuard const&) = delete;
1866
1867
0
  void release() { this->Easy = nullptr; }
1868
1869
private:
1870
  ::CURL* Easy;
1871
};
1872
1873
#endif
1874
1875
#define check_curl_result(result, errstr)                                     \
1876
0
  do {                                                                        \
1877
0
    if (result != CURLE_OK) {                                                 \
1878
0
      std::string e(errstr);                                                  \
1879
0
      e += ::curl_easy_strerror(result);                                      \
1880
0
      status.SetError(e);                                                     \
1881
0
      return false;                                                           \
1882
0
    }                                                                         \
1883
0
  } while (false)
1884
1885
bool HandleDownloadCommand(std::vector<std::string> const& args,
1886
                           cmExecutionStatus& status)
1887
0
{
1888
0
#if !defined(CMAKE_BOOTSTRAP)
1889
0
  auto i = args.begin();
1890
0
  if (args.size() < 2) {
1891
0
    status.SetError("DOWNLOAD must be called with at least two arguments.");
1892
0
    return false;
1893
0
  }
1894
0
  ++i; // Get rid of subcommand
1895
0
  std::string url = *i;
1896
0
  ++i;
1897
0
  std::string file;
1898
1899
0
  long timeout = 0;
1900
0
  long inactivity_timeout = 0;
1901
0
  std::string logVar;
1902
0
  std::string statusVar;
1903
0
  cm::optional<std::string> tlsVersionOpt;
1904
0
  cm::optional<bool> tlsVerifyOpt;
1905
0
  cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
1906
0
  std::string netrc_level =
1907
0
    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC");
1908
0
  std::string netrc_file =
1909
0
    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC_FILE");
1910
0
  std::string expectedHash;
1911
0
  std::string hashMatchMSG;
1912
0
  std::unique_ptr<cmCryptoHash> hash;
1913
0
  bool showProgress = false;
1914
0
  std::string userpwd;
1915
1916
0
  std::vector<std::string> curl_headers;
1917
0
  std::vector<std::pair<std::string, cm::optional<std::string>>> curl_ranges;
1918
1919
0
  while (i != args.end()) {
1920
0
    if (*i == "TIMEOUT") {
1921
0
      ++i;
1922
0
      if (i != args.end()) {
1923
0
        timeout = atol(i->c_str());
1924
0
      } else {
1925
0
        status.SetError("DOWNLOAD missing time for TIMEOUT.");
1926
0
        return false;
1927
0
      }
1928
0
    } else if (*i == "INACTIVITY_TIMEOUT") {
1929
0
      ++i;
1930
0
      if (i != args.end()) {
1931
0
        inactivity_timeout = atol(i->c_str());
1932
0
      } else {
1933
0
        status.SetError("DOWNLOAD missing time for INACTIVITY_TIMEOUT.");
1934
0
        return false;
1935
0
      }
1936
0
    } else if (*i == "LOG") {
1937
0
      ++i;
1938
0
      if (i == args.end()) {
1939
0
        status.SetError("DOWNLOAD missing VAR for LOG.");
1940
0
        return false;
1941
0
      }
1942
0
      logVar = *i;
1943
0
    } else if (*i == "STATUS") {
1944
0
      ++i;
1945
0
      if (i == args.end()) {
1946
0
        status.SetError("DOWNLOAD missing VAR for STATUS.");
1947
0
        return false;
1948
0
      }
1949
0
      statusVar = *i;
1950
0
    } else if (*i == "TLS_VERSION") {
1951
0
      ++i;
1952
0
      if (i != args.end()) {
1953
0
        tlsVersionOpt = *i;
1954
0
      } else {
1955
0
        status.SetError("DOWNLOAD missing value for TLS_VERSION.");
1956
0
        return false;
1957
0
      }
1958
0
    } else if (*i == "TLS_VERIFY") {
1959
0
      ++i;
1960
0
      if (i != args.end()) {
1961
0
        tlsVerifyOpt = cmIsOn(*i);
1962
0
      } else {
1963
0
        status.SetError("DOWNLOAD missing bool value for TLS_VERIFY.");
1964
0
        return false;
1965
0
      }
1966
0
    } else if (*i == "TLS_CAINFO") {
1967
0
      ++i;
1968
0
      if (i != args.end()) {
1969
0
        cainfo = cmValue(*i);
1970
0
      } else {
1971
0
        status.SetError("DOWNLOAD missing file value for TLS_CAINFO.");
1972
0
        return false;
1973
0
      }
1974
0
    } else if (*i == "NETRC_FILE") {
1975
0
      ++i;
1976
0
      if (i != args.end()) {
1977
0
        netrc_file = *i;
1978
0
      } else {
1979
0
        status.SetError("DOWNLOAD missing file value for NETRC_FILE.");
1980
0
        return false;
1981
0
      }
1982
0
    } else if (*i == "NETRC") {
1983
0
      ++i;
1984
0
      if (i != args.end()) {
1985
0
        netrc_level = *i;
1986
0
      } else {
1987
0
        status.SetError("DOWNLOAD missing level value for NETRC.");
1988
0
        return false;
1989
0
      }
1990
0
    } else if (*i == "EXPECTED_MD5") {
1991
0
      ++i;
1992
0
      if (i == args.end()) {
1993
0
        status.SetError("DOWNLOAD missing sum value for EXPECTED_MD5.");
1994
0
        return false;
1995
0
      }
1996
0
      hash = cm::make_unique<cmCryptoHash>(cmCryptoHash::AlgoMD5);
1997
0
      hashMatchMSG = "MD5 sum";
1998
0
      expectedHash = cmSystemTools::LowerCase(*i);
1999
0
    } else if (*i == "SHOW_PROGRESS") {
2000
0
      showProgress = true;
2001
0
    } else if (*i == "EXPECTED_HASH") {
2002
0
      ++i;
2003
0
      if (i == args.end()) {
2004
0
        status.SetError("DOWNLOAD missing ALGO=value for EXPECTED_HASH.");
2005
0
        return false;
2006
0
      }
2007
0
      std::string::size_type pos = i->find("=");
2008
0
      if (pos == std::string::npos) {
2009
0
        std::string err =
2010
0
          cmStrCat("DOWNLOAD EXPECTED_HASH expects ALGO=value but got: ", *i);
2011
0
        status.SetError(err);
2012
0
        return false;
2013
0
      }
2014
0
      std::string algo = i->substr(0, pos);
2015
0
      expectedHash = cmSystemTools::LowerCase(i->substr(pos + 1));
2016
0
      hash = cmCryptoHash::New(algo);
2017
0
      if (!hash) {
2018
0
        std::string err =
2019
0
          cmStrCat("DOWNLOAD EXPECTED_HASH given unknown ALGO: ", algo);
2020
0
        status.SetError(err);
2021
0
        return false;
2022
0
      }
2023
0
      hashMatchMSG = algo + " hash";
2024
0
    } else if (*i == "USERPWD") {
2025
0
      ++i;
2026
0
      if (i == args.end()) {
2027
0
        status.SetError("DOWNLOAD missing string for USERPWD.");
2028
0
        return false;
2029
0
      }
2030
0
      userpwd = *i;
2031
0
    } else if (*i == "HTTPHEADER") {
2032
0
      ++i;
2033
0
      if (i == args.end()) {
2034
0
        status.SetError("DOWNLOAD missing string for HTTPHEADER.");
2035
0
        return false;
2036
0
      }
2037
0
      curl_headers.push_back(*i);
2038
0
    } else if (*i == "RANGE_START") {
2039
0
      ++i;
2040
0
      if (i == args.end()) {
2041
0
        status.SetError("DOWNLOAD missing value for RANGE_START.");
2042
0
        return false;
2043
0
      }
2044
0
      curl_ranges.emplace_back(*i, cm::nullopt);
2045
0
    } else if (*i == "RANGE_END") {
2046
0
      ++i;
2047
0
      if (curl_ranges.empty()) {
2048
0
        curl_ranges.emplace_back("0", *i);
2049
0
      } else {
2050
0
        auto& last_range = curl_ranges.back();
2051
0
        if (!last_range.second.has_value()) {
2052
0
          last_range.second = *i;
2053
0
        } else {
2054
0
          status.SetError("Multiple RANGE_END values is provided without "
2055
0
                          "the corresponding RANGE_START.");
2056
0
          return false;
2057
0
        }
2058
0
      }
2059
0
    } else if (file.empty()) {
2060
0
      file = *i;
2061
0
    } else {
2062
      // Do not return error for compatibility reason.
2063
0
      std::string err = cmStrCat("Unexpected argument: ", *i);
2064
0
      status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, err);
2065
0
    }
2066
0
    ++i;
2067
0
  }
2068
2069
0
  if (!tlsVerifyOpt.has_value()) {
2070
0
    if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERIFY")) {
2071
0
      tlsVerifyOpt = v.IsOn();
2072
0
    }
2073
0
  }
2074
0
  if (!tlsVerifyOpt.has_value()) {
2075
0
    if (cm::optional<std::string> v =
2076
0
          cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) {
2077
0
      tlsVerifyOpt = cmIsOn(*v);
2078
0
    }
2079
0
  }
2080
0
  bool tlsVerifyDefaulted = false;
2081
0
  if (!tlsVerifyOpt.has_value()) {
2082
0
    tlsVerifyOpt = TLS_VERIFY_DEFAULT;
2083
0
    tlsVerifyDefaulted = true;
2084
0
  }
2085
2086
0
  if (!tlsVersionOpt.has_value()) {
2087
0
    if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERSION")) {
2088
0
      tlsVersionOpt = *v;
2089
0
    }
2090
0
  }
2091
0
  if (!tlsVersionOpt.has_value()) {
2092
0
    if (cm::optional<std::string> v =
2093
0
          cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) {
2094
0
      tlsVersionOpt = std::move(v);
2095
0
    }
2096
0
  }
2097
0
  bool tlsVersionDefaulted = false;
2098
0
  if (!tlsVersionOpt.has_value()) {
2099
0
    tlsVersionOpt = TLS_VERSION_DEFAULT;
2100
0
    tlsVersionDefaulted = true;
2101
0
  }
2102
2103
  // Can't calculate hash if we don't save the file.
2104
  // TODO Incrementally calculate hash in the write callback as the file is
2105
  // being downloaded so this check can be relaxed.
2106
0
  if (file.empty() && hash) {
2107
0
    status.SetError("DOWNLOAD cannot calculate hash if file is not saved.");
2108
0
    return false;
2109
0
  }
2110
  // If file exists already, and caller specified an expected md5 or sha,
2111
  // and the existing file already has the expected hash, then simply
2112
  // return.
2113
  //
2114
0
  if (!file.empty() && cmSystemTools::FileExists(file) && hash.get()) {
2115
0
    std::string msg;
2116
0
    std::string actualHash = hash->HashFile(file);
2117
0
    if (actualHash == expectedHash) {
2118
0
      msg = cmStrCat("skipping download as file already exists with expected ",
2119
0
                     hashMatchMSG, '"');
2120
0
      if (!statusVar.empty()) {
2121
0
        status.GetMakefile().AddDefinition(statusVar, cmStrCat(0, ";\"", msg));
2122
0
      }
2123
0
      return true;
2124
0
    }
2125
0
  }
2126
  // Make sure parent directory exists so we can write to the file
2127
  // as we receive downloaded bits from curl...
2128
  //
2129
0
  if (!file.empty()) {
2130
0
    std::string dir = cmSystemTools::GetFilenamePath(file);
2131
0
    if (!dir.empty() && !cmSystemTools::FileExists(dir) &&
2132
0
        !cmSystemTools::MakeDirectory(dir)) {
2133
0
      std::string errstring = "DOWNLOAD error: cannot create directory '" +
2134
0
        dir +
2135
0
        "' - Specify file by full path name and verify that you "
2136
0
        "have directory creation and file write privileges.";
2137
0
      status.SetError(errstring);
2138
0
      return false;
2139
0
    }
2140
0
  }
2141
2142
0
  cmsys::ofstream fout;
2143
0
  if (!file.empty()) {
2144
0
    fout.open(file.c_str(), std::ios::binary);
2145
0
    if (!fout) {
2146
0
      status.SetError(cmStrCat("DOWNLOAD cannot open file for write\n"
2147
0
                               "  file: \"",
2148
0
                               file, '"'));
2149
0
      return false;
2150
0
    }
2151
0
  }
2152
2153
0
  url = CurlCompatFileURL(url);
2154
2155
0
  ::CURL* curl;
2156
0
  cm_curl_global_init(CURL_GLOBAL_DEFAULT);
2157
0
  curl = cm_curl_easy_init();
2158
0
  if (!curl) {
2159
0
    status.SetError("DOWNLOAD error initializing curl.");
2160
0
    return false;
2161
0
  }
2162
2163
0
  cURLEasyGuard g_curl(curl);
2164
0
  ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
2165
0
  check_curl_result(res, "DOWNLOAD cannot set url: ");
2166
2167
  // enable HTTP ERROR parsing
2168
0
  res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
2169
0
  check_curl_result(res, "DOWNLOAD cannot set http failure option: ");
2170
2171
0
  curl_version_info_data* cv = curl_version_info(CURLVERSION_FIRST);
2172
0
  res = ::curl_easy_setopt(
2173
0
    curl, CURLOPT_USERAGENT,
2174
0
    cmStrCat("curl/", cv ? cv->version : LIBCURL_VERSION).c_str());
2175
0
  check_curl_result(res, "DOWNLOAD cannot set user agent option: ");
2176
2177
0
  res = ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cmWriteToFileCallback);
2178
0
  check_curl_result(res, "DOWNLOAD cannot set write function: ");
2179
2180
0
  res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
2181
0
                           cmFileCommandCurlDebugCallback);
2182
0
  check_curl_result(res, "DOWNLOAD cannot set debug function: ");
2183
2184
0
  if (tlsVersionOpt.has_value()) {
2185
0
    if (cm::optional<int> v = cmCurlParseTLSVersion(*tlsVersionOpt)) {
2186
0
      res = ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, *v);
2187
0
      if (tlsVersionDefaulted && res == CURLE_NOT_BUILT_IN) {
2188
0
        res = CURLE_OK;
2189
0
      }
2190
0
      check_curl_result(res,
2191
0
                        cmStrCat("DOWNLOAD cannot set TLS/SSL version ",
2192
0
                                 *tlsVersionOpt, ": "));
2193
0
    } else {
2194
0
      status.SetError(
2195
0
        cmStrCat("DOWNLOAD given unknown TLS/SSL version ", *tlsVersionOpt));
2196
0
      return false;
2197
0
    }
2198
0
  }
2199
2200
  // check to see if TLS verification is requested
2201
0
  if (tlsVerifyOpt.has_value() && tlsVerifyOpt.value()) {
2202
0
    res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
2203
0
    check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify on: ");
2204
0
  } else {
2205
0
    res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
2206
0
    check_curl_result(res, "DOWNLOAD cannot set TLS/SSL Verify off: ");
2207
0
  }
2208
2209
0
  for (auto const& range : curl_ranges) {
2210
0
    std::string curl_range =
2211
0
      cmStrCat(range.first, '-',
2212
0
               (range.second.has_value() ? range.second.value() : ""));
2213
0
    res = ::curl_easy_setopt(curl, CURLOPT_RANGE, curl_range.c_str());
2214
0
    check_curl_result(res, "DOWNLOAD cannot set range: ");
2215
0
  }
2216
2217
  // check to see if a CAINFO file has been specified
2218
  // command arg comes first
2219
0
  std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo);
2220
0
  if (!cainfo_err.empty()) {
2221
0
    status.SetError(cainfo_err);
2222
0
    return false;
2223
0
  }
2224
2225
  // check to see if netrc parameters have been specified
2226
  // local command args takes precedence over CMAKE_NETRC*
2227
0
  netrc_level = cmSystemTools::UpperCase(netrc_level);
2228
0
  std::string const& netrc_option_err =
2229
0
    cmCurlSetNETRCOption(curl, netrc_level, netrc_file);
2230
0
  if (!netrc_option_err.empty()) {
2231
0
    status.SetError(netrc_option_err);
2232
0
    return false;
2233
0
  }
2234
2235
0
  cmFileCommandVectorOfChar chunkDebug;
2236
2237
0
  res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA,
2238
0
                           file.empty() ? nullptr : &fout);
2239
0
  check_curl_result(res, "DOWNLOAD cannot set write data: ");
2240
2241
0
  res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug);
2242
0
  check_curl_result(res, "DOWNLOAD cannot set debug data: ");
2243
2244
0
  res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
2245
0
  check_curl_result(res, "DOWNLOAD cannot set follow-redirect option: ");
2246
2247
0
  if (!logVar.empty()) {
2248
0
    res = ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
2249
0
    check_curl_result(res, "DOWNLOAD cannot set verbose: ");
2250
0
  }
2251
2252
0
  if (timeout > 0) {
2253
0
    res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
2254
0
    check_curl_result(res, "DOWNLOAD cannot set timeout: ");
2255
0
  }
2256
2257
0
  if (inactivity_timeout > 0) {
2258
    // Give up if there is no progress for a long time.
2259
0
    ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
2260
0
    ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, inactivity_timeout);
2261
0
  }
2262
2263
  // Need the progress helper's scope to last through the duration of
2264
  // the curl_easy_perform call... so this object is declared at function
2265
  // scope intentionally, rather than inside the "if(showProgress)"
2266
  // block...
2267
  //
2268
0
  cURLProgressHelper helper(&status.GetMakefile(), "download");
2269
2270
0
  if (showProgress) {
2271
0
    res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
2272
0
    check_curl_result(res, "DOWNLOAD cannot set noprogress value: ");
2273
2274
0
    res = ::curl_easy_setopt(curl, CM_CURLOPT_XFERINFOFUNCTION,
2275
0
                             cmFileDownloadProgressCallback);
2276
0
    check_curl_result(res, "DOWNLOAD cannot set progress function: ");
2277
2278
0
    res = ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA,
2279
0
                             reinterpret_cast<void*>(&helper));
2280
0
    check_curl_result(res, "DOWNLOAD cannot set progress data: ");
2281
0
  }
2282
2283
0
  if (!userpwd.empty()) {
2284
0
    res = ::curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd.c_str());
2285
0
    check_curl_result(res, "DOWNLOAD cannot set user password: ");
2286
0
  }
2287
2288
0
  struct curl_slist* headers = nullptr;
2289
0
  for (std::string const& h : curl_headers) {
2290
0
    headers = ::curl_slist_append(headers, h.c_str());
2291
0
  }
2292
0
  ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
2293
2294
0
  res = ::curl_easy_perform(curl);
2295
2296
0
  ::curl_slist_free_all(headers);
2297
2298
  /* always cleanup */
2299
0
  g_curl.release();
2300
0
  ::curl_easy_cleanup(curl);
2301
2302
  // Explicitly close the file so we can check for write errors.
2303
0
  if (!file.empty()) {
2304
0
    fout.close();
2305
0
    if (!fout) {
2306
0
      res = CURLE_WRITE_ERROR;
2307
0
    }
2308
0
  }
2309
2310
0
  if (!statusVar.empty()) {
2311
0
    std::string m = curl_easy_strerror(res);
2312
0
    if ((res == CURLE_SSL_CONNECT_ERROR ||
2313
0
         res == CURLE_PEER_FAILED_VERIFICATION) &&
2314
0
        tlsVerifyDefaulted) {
2315
0
      m = cmStrCat(
2316
0
        std::move(m),
2317
0
        ".  If this is due to https certificate verification failure, one may "
2318
0
        "set environment variable CMAKE_TLS_VERIFY=0 to suppress it.");
2319
0
    }
2320
0
    status.GetMakefile().AddDefinition(
2321
0
      statusVar, cmStrCat(static_cast<int>(res), ";\"", std::move(m), '"'));
2322
0
  }
2323
2324
0
  ::curl_global_cleanup();
2325
2326
  // Ensure requested curl logs are returned (especially in case of failure)
2327
  //
2328
0
  if (!logVar.empty()) {
2329
0
    chunkDebug.push_back(0);
2330
0
    status.GetMakefile().AddDefinition(logVar, chunkDebug.data());
2331
0
  }
2332
2333
  // Verify MD5 sum if requested:
2334
  //
2335
0
  if (hash) {
2336
0
    if (res != CURLE_OK) {
2337
0
      status.SetError(
2338
0
        cmStrCat("DOWNLOAD cannot compute hash on failed download\n"
2339
0
                 "  from url: \"",
2340
0
                 url, "\"\n  status: [", static_cast<int>(res), ";\"",
2341
0
                 ::curl_easy_strerror(res), "\"]"));
2342
0
      return false;
2343
0
    }
2344
2345
0
    std::string actualHash = hash->HashFile(file);
2346
0
    if (actualHash.empty()) {
2347
0
      status.SetError(cmStrCat("DOWNLOAD cannot compute hash on download\n"
2348
0
                               "  for file: \"",
2349
0
                               file, '"'));
2350
0
      return false;
2351
0
    }
2352
2353
0
    if (expectedHash != actualHash) {
2354
0
      if (!statusVar.empty() && res == 0) {
2355
0
        status.GetMakefile().AddDefinition(statusVar,
2356
0
                                           "1;HASH mismatch: "
2357
0
                                           "expected: " +
2358
0
                                             expectedHash +
2359
0
                                             " actual: " + actualHash);
2360
0
      }
2361
2362
0
      status.SetError(cmStrCat("DOWNLOAD HASH mismatch\n"
2363
0
                               "  for file: \"",
2364
0
                               file,
2365
0
                               "\"\n"
2366
0
                               "    expected hash: \"",
2367
0
                               expectedHash,
2368
0
                               "\"\n"
2369
0
                               "      actual hash: \"",
2370
0
                               actualHash, "\"\n"));
2371
0
      return false;
2372
0
    }
2373
0
  }
2374
2375
0
  return true;
2376
#else
2377
  status.SetError("DOWNLOAD not supported by bootstrap cmake.");
2378
  return false;
2379
#endif
2380
0
}
2381
2382
bool HandleUploadCommand(std::vector<std::string> const& args,
2383
                         cmExecutionStatus& status)
2384
0
{
2385
0
#if !defined(CMAKE_BOOTSTRAP)
2386
0
  if (args.size() < 3) {
2387
0
    status.SetError("UPLOAD must be called with at least three arguments.");
2388
0
    return false;
2389
0
  }
2390
0
  auto i = args.begin();
2391
0
  ++i;
2392
0
  std::string filename = *i;
2393
0
  ++i;
2394
0
  std::string url = *i;
2395
0
  ++i;
2396
2397
0
  long timeout = 0;
2398
0
  long inactivity_timeout = 0;
2399
0
  std::string logVar;
2400
0
  std::string statusVar;
2401
0
  bool showProgress = false;
2402
0
  cm::optional<std::string> tlsVersionOpt;
2403
0
  cm::optional<bool> tlsVerifyOpt;
2404
0
  cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
2405
0
  std::string userpwd;
2406
0
  std::string netrc_level =
2407
0
    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC");
2408
0
  std::string netrc_file =
2409
0
    status.GetMakefile().GetSafeDefinition("CMAKE_NETRC_FILE");
2410
2411
0
  std::vector<std::string> curl_headers;
2412
2413
0
  while (i != args.end()) {
2414
0
    if (*i == "TIMEOUT") {
2415
0
      ++i;
2416
0
      if (i != args.end()) {
2417
0
        timeout = atol(i->c_str());
2418
0
      } else {
2419
0
        status.SetError("UPLOAD missing time for TIMEOUT.");
2420
0
        return false;
2421
0
      }
2422
0
    } else if (*i == "INACTIVITY_TIMEOUT") {
2423
0
      ++i;
2424
0
      if (i != args.end()) {
2425
0
        inactivity_timeout = atol(i->c_str());
2426
0
      } else {
2427
0
        status.SetError("UPLOAD missing time for INACTIVITY_TIMEOUT.");
2428
0
        return false;
2429
0
      }
2430
0
    } else if (*i == "LOG") {
2431
0
      ++i;
2432
0
      if (i == args.end()) {
2433
0
        status.SetError("UPLOAD missing VAR for LOG.");
2434
0
        return false;
2435
0
      }
2436
0
      logVar = *i;
2437
0
    } else if (*i == "STATUS") {
2438
0
      ++i;
2439
0
      if (i == args.end()) {
2440
0
        status.SetError("UPLOAD missing VAR for STATUS.");
2441
0
        return false;
2442
0
      }
2443
0
      statusVar = *i;
2444
0
    } else if (*i == "SHOW_PROGRESS") {
2445
0
      showProgress = true;
2446
0
    } else if (*i == "TLS_VERSION") {
2447
0
      ++i;
2448
0
      if (i != args.end()) {
2449
0
        tlsVersionOpt = *i;
2450
0
      } else {
2451
0
        status.SetError("UPLOAD missing value for TLS_VERSION.");
2452
0
        return false;
2453
0
      }
2454
0
    } else if (*i == "TLS_VERIFY") {
2455
0
      ++i;
2456
0
      if (i != args.end()) {
2457
0
        tlsVerifyOpt = cmIsOn(*i);
2458
0
      } else {
2459
0
        status.SetError("UPLOAD missing bool value for TLS_VERIFY.");
2460
0
        return false;
2461
0
      }
2462
0
    } else if (*i == "TLS_CAINFO") {
2463
0
      ++i;
2464
0
      if (i != args.end()) {
2465
0
        cainfo = cmValue(*i);
2466
0
      } else {
2467
0
        status.SetError("UPLOAD missing file value for TLS_CAINFO.");
2468
0
        return false;
2469
0
      }
2470
0
    } else if (*i == "NETRC_FILE") {
2471
0
      ++i;
2472
0
      if (i != args.end()) {
2473
0
        netrc_file = *i;
2474
0
      } else {
2475
0
        status.SetError("UPLOAD missing file value for NETRC_FILE.");
2476
0
        return false;
2477
0
      }
2478
0
    } else if (*i == "NETRC") {
2479
0
      ++i;
2480
0
      if (i != args.end()) {
2481
0
        netrc_level = *i;
2482
0
      } else {
2483
0
        status.SetError("UPLOAD missing level value for NETRC.");
2484
0
        return false;
2485
0
      }
2486
0
    } else if (*i == "USERPWD") {
2487
0
      ++i;
2488
0
      if (i == args.end()) {
2489
0
        status.SetError("UPLOAD missing string for USERPWD.");
2490
0
        return false;
2491
0
      }
2492
0
      userpwd = *i;
2493
0
    } else if (*i == "HTTPHEADER") {
2494
0
      ++i;
2495
0
      if (i == args.end()) {
2496
0
        status.SetError("UPLOAD missing string for HTTPHEADER.");
2497
0
        return false;
2498
0
      }
2499
0
      curl_headers.push_back(*i);
2500
0
    } else {
2501
      // Do not return error for compatibility reason.
2502
0
      std::string err = cmStrCat("Unexpected argument: ", *i);
2503
0
      status.GetMakefile().IssueMessage(MessageType::AUTHOR_WARNING, err);
2504
0
    }
2505
2506
0
    ++i;
2507
0
  }
2508
2509
0
  if (!tlsVerifyOpt.has_value()) {
2510
0
    if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERIFY")) {
2511
0
      tlsVerifyOpt = v.IsOn();
2512
0
    }
2513
0
  }
2514
0
  if (!tlsVerifyOpt.has_value()) {
2515
0
    if (cm::optional<std::string> v =
2516
0
          cmSystemTools::GetEnvVar("CMAKE_TLS_VERIFY")) {
2517
0
      tlsVerifyOpt = cmIsOn(*v);
2518
0
    }
2519
0
  }
2520
0
  bool tlsVerifyDefaulted = false;
2521
0
  if (!tlsVerifyOpt.has_value()) {
2522
0
    tlsVerifyOpt = TLS_VERIFY_DEFAULT;
2523
0
    tlsVerifyDefaulted = true;
2524
0
  }
2525
2526
0
  if (!tlsVersionOpt.has_value()) {
2527
0
    if (cmValue v = status.GetMakefile().GetDefinition("CMAKE_TLS_VERSION")) {
2528
0
      tlsVersionOpt = *v;
2529
0
    }
2530
0
  }
2531
0
  if (!tlsVersionOpt.has_value()) {
2532
0
    if (cm::optional<std::string> v =
2533
0
          cmSystemTools::GetEnvVar("CMAKE_TLS_VERSION")) {
2534
0
      tlsVersionOpt = std::move(v);
2535
0
    }
2536
0
  }
2537
0
  bool tlsVersionDefaulted = false;
2538
0
  if (!tlsVersionOpt.has_value()) {
2539
0
    tlsVersionOpt = TLS_VERSION_DEFAULT;
2540
0
    tlsVersionDefaulted = true;
2541
0
  }
2542
2543
  // Open file for reading:
2544
  //
2545
0
  FILE* fin = cmsys::SystemTools::Fopen(filename, "rb");
2546
0
  if (!fin) {
2547
0
    std::string errStr =
2548
0
      cmStrCat("UPLOAD cannot open file '", filename, "' for reading.");
2549
0
    status.SetError(errStr);
2550
0
    return false;
2551
0
  }
2552
2553
0
  unsigned long file_size = cmsys::SystemTools::FileLength(filename);
2554
2555
0
  url = CurlCompatFileURL(url);
2556
2557
0
  ::CURL* curl;
2558
0
  cm_curl_global_init(CURL_GLOBAL_DEFAULT);
2559
0
  curl = cm_curl_easy_init();
2560
0
  if (!curl) {
2561
0
    status.SetError("UPLOAD error initializing curl.");
2562
0
    fclose(fin);
2563
0
    return false;
2564
0
  }
2565
2566
0
  cURLEasyGuard g_curl(curl);
2567
2568
  // enable HTTP ERROR parsing
2569
0
  ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
2570
0
  check_curl_result(res, "UPLOAD cannot set fail on error flag: ");
2571
2572
  // enable uploading
2573
0
  res = ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
2574
0
  check_curl_result(res, "UPLOAD cannot set upload flag: ");
2575
2576
0
  res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
2577
0
  check_curl_result(res, "UPLOAD cannot set url: ");
2578
2579
0
  res =
2580
0
    ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cmWriteToMemoryCallback);
2581
0
  check_curl_result(res, "UPLOAD cannot set write function: ");
2582
2583
0
  res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
2584
0
                           cmFileCommandCurlDebugCallback);
2585
0
  check_curl_result(res, "UPLOAD cannot set debug function: ");
2586
2587
0
  if (tlsVersionOpt.has_value()) {
2588
0
    if (cm::optional<int> v = cmCurlParseTLSVersion(*tlsVersionOpt)) {
2589
0
      res = ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, *v);
2590
0
      if (tlsVersionDefaulted && res == CURLE_NOT_BUILT_IN) {
2591
0
        res = CURLE_OK;
2592
0
      }
2593
0
      check_curl_result(
2594
0
        res,
2595
0
        cmStrCat("UPLOAD cannot set TLS/SSL version ", *tlsVersionOpt, ": "));
2596
0
    } else {
2597
0
      status.SetError(
2598
0
        cmStrCat("UPLOAD given unknown TLS/SSL version ", *tlsVersionOpt));
2599
0
      return false;
2600
0
    }
2601
0
  }
2602
2603
  // check to see if TLS verification is requested
2604
0
  if (tlsVerifyOpt.has_value() && tlsVerifyOpt.value()) {
2605
0
    res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
2606
0
    check_curl_result(res, "UPLOAD cannot set TLS/SSL Verify on: ");
2607
0
  } else {
2608
0
    res = ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
2609
0
    check_curl_result(res, "UPLOAD cannot set TLS/SSL Verify off: ");
2610
0
  }
2611
2612
  // check to see if a CAINFO file has been specified
2613
  // command arg comes first
2614
0
  std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo);
2615
0
  if (!cainfo_err.empty()) {
2616
0
    status.SetError(cainfo_err);
2617
0
    return false;
2618
0
  }
2619
2620
0
  cmFileCommandVectorOfChar chunkResponse;
2621
0
  cmFileCommandVectorOfChar chunkDebug;
2622
2623
0
  res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunkResponse);
2624
0
  check_curl_result(res, "UPLOAD cannot set write data: ");
2625
2626
0
  res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &chunkDebug);
2627
0
  check_curl_result(res, "UPLOAD cannot set debug data: ");
2628
2629
0
  res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
2630
0
  check_curl_result(res, "UPLOAD cannot set follow-redirect option: ");
2631
2632
0
  if (!logVar.empty()) {
2633
0
    res = ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
2634
0
    check_curl_result(res, "UPLOAD cannot set verbose: ");
2635
0
  }
2636
2637
0
  if (timeout > 0) {
2638
0
    res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
2639
0
    check_curl_result(res, "UPLOAD cannot set timeout: ");
2640
0
  }
2641
2642
0
  if (inactivity_timeout > 0) {
2643
    // Give up if there is no progress for a long time.
2644
0
    ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
2645
0
    ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, inactivity_timeout);
2646
0
  }
2647
2648
  // Need the progress helper's scope to last through the duration of
2649
  // the curl_easy_perform call... so this object is declared at function
2650
  // scope intentionally, rather than inside the "if(showProgress)"
2651
  // block...
2652
  //
2653
0
  cURLProgressHelper helper(&status.GetMakefile(), "upload");
2654
2655
0
  if (showProgress) {
2656
0
    res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
2657
0
    check_curl_result(res, "UPLOAD cannot set noprogress value: ");
2658
2659
0
    res = ::curl_easy_setopt(curl, CM_CURLOPT_XFERINFOFUNCTION,
2660
0
                             cmFileUploadProgressCallback);
2661
0
    check_curl_result(res, "UPLOAD cannot set progress function: ");
2662
2663
0
    res = ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA,
2664
0
                             reinterpret_cast<void*>(&helper));
2665
0
    check_curl_result(res, "UPLOAD cannot set progress data: ");
2666
0
  }
2667
2668
  // now specify which file to upload
2669
0
  res = ::curl_easy_setopt(curl, CURLOPT_INFILE, fin);
2670
0
  check_curl_result(res, "UPLOAD cannot set input file: ");
2671
2672
  // and give the size of the upload (optional)
2673
0
  res =
2674
0
    ::curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(file_size));
2675
0
  check_curl_result(res, "UPLOAD cannot set input file size: ");
2676
2677
0
  if (!userpwd.empty()) {
2678
0
    res = ::curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd.c_str());
2679
0
    check_curl_result(res, "UPLOAD cannot set user password: ");
2680
0
  }
2681
2682
  // check to see if netrc parameters have been specified
2683
  // local command args takes precedence over CMAKE_NETRC*
2684
0
  netrc_level = cmSystemTools::UpperCase(netrc_level);
2685
0
  std::string const& netrc_option_err =
2686
0
    cmCurlSetNETRCOption(curl, netrc_level, netrc_file);
2687
0
  if (!netrc_option_err.empty()) {
2688
0
    status.SetError(netrc_option_err);
2689
0
    return false;
2690
0
  }
2691
2692
0
  struct curl_slist* headers = nullptr;
2693
0
  for (std::string const& h : curl_headers) {
2694
0
    headers = ::curl_slist_append(headers, h.c_str());
2695
0
  }
2696
0
  ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
2697
2698
0
  res = ::curl_easy_perform(curl);
2699
2700
0
  ::curl_slist_free_all(headers);
2701
2702
  /* always cleanup */
2703
0
  g_curl.release();
2704
0
  ::curl_easy_cleanup(curl);
2705
2706
0
  if (!statusVar.empty()) {
2707
0
    std::string m = curl_easy_strerror(res);
2708
0
    if ((res == CURLE_SSL_CONNECT_ERROR ||
2709
0
         res == CURLE_PEER_FAILED_VERIFICATION) &&
2710
0
        tlsVerifyDefaulted) {
2711
0
      m = cmStrCat(
2712
0
        std::move(m),
2713
0
        ".  If this is due to https certificate verification failure, one may "
2714
0
        "set environment variable CMAKE_TLS_VERIFY=0 to suppress it.");
2715
0
    }
2716
0
    status.GetMakefile().AddDefinition(
2717
0
      statusVar, cmStrCat(static_cast<int>(res), ";\"", std::move(m), '"'));
2718
0
  }
2719
2720
0
  ::curl_global_cleanup();
2721
2722
0
  fclose(fin);
2723
0
  fin = nullptr;
2724
2725
0
  if (!logVar.empty()) {
2726
0
    std::string log;
2727
2728
0
    if (!chunkResponse.empty()) {
2729
0
      chunkResponse.push_back(0);
2730
0
      log += "Response:\n";
2731
0
      log += chunkResponse.data();
2732
0
      log += "\n";
2733
0
    }
2734
2735
0
    if (!chunkDebug.empty()) {
2736
0
      chunkDebug.push_back(0);
2737
0
      log += "Debug:\n";
2738
0
      log += chunkDebug.data();
2739
0
      log += "\n";
2740
0
    }
2741
2742
0
    status.GetMakefile().AddDefinition(logVar, log);
2743
0
  }
2744
2745
0
  return true;
2746
#else
2747
  status.SetError("UPLOAD not supported by bootstrap cmake.");
2748
  return false;
2749
#endif
2750
0
}
2751
2752
void AddEvaluationFile(std::string const& inputName,
2753
                       std::string const& targetName,
2754
                       std::string const& outputExpr,
2755
                       std::string const& condition, bool inputIsContent,
2756
                       std::string const& newLineCharacter, mode_t permissions,
2757
                       cmExecutionStatus& status)
2758
0
{
2759
0
  cmListFileBacktrace lfbt = status.GetMakefile().GetBacktrace();
2760
2761
0
  cmGeneratorExpression outputGe(*status.GetMakefile().GetCMakeInstance(),
2762
0
                                 lfbt);
2763
0
  std::unique_ptr<cmCompiledGeneratorExpression> outputCge =
2764
0
    outputGe.Parse(outputExpr);
2765
2766
0
  cmGeneratorExpression conditionGe(*status.GetMakefile().GetCMakeInstance(),
2767
0
                                    lfbt);
2768
0
  std::unique_ptr<cmCompiledGeneratorExpression> conditionCge =
2769
0
    conditionGe.Parse(condition);
2770
2771
0
  status.GetMakefile().AddEvaluationFile(
2772
0
    inputName, targetName, std::move(outputCge), std::move(conditionCge),
2773
0
    newLineCharacter, permissions, inputIsContent);
2774
0
}
2775
2776
bool HandleGenerateCommand(std::vector<std::string> const& args,
2777
                           cmExecutionStatus& status)
2778
0
{
2779
0
  if (args.size() < 5) {
2780
0
    status.SetError("Incorrect arguments to GENERATE subcommand.");
2781
0
    return false;
2782
0
  }
2783
2784
0
  struct Arguments : public ArgumentParser::ParseResult
2785
0
  {
2786
0
    cm::optional<std::string> Output;
2787
0
    cm::optional<std::string> Input;
2788
0
    cm::optional<std::string> Content;
2789
0
    cm::optional<std::string> Condition;
2790
0
    cm::optional<std::string> Target;
2791
0
    cm::optional<std::string> NewLineStyle;
2792
0
    bool NoSourcePermissions = false;
2793
0
    bool UseSourcePermissions = false;
2794
0
    ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
2795
0
    std::vector<cm::string_view> ParsedKeywords;
2796
0
  };
2797
2798
0
  static auto const parser =
2799
0
    cmArgumentParser<Arguments>{}
2800
0
      .Bind("OUTPUT"_s, &Arguments::Output)
2801
0
      .Bind("INPUT"_s, &Arguments::Input)
2802
0
      .Bind("CONTENT"_s, &Arguments::Content)
2803
0
      .Bind("CONDITION"_s, &Arguments::Condition)
2804
0
      .Bind("TARGET"_s, &Arguments::Target)
2805
0
      .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions)
2806
0
      .Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions)
2807
0
      .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions)
2808
0
      .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle)
2809
0
      .BindParsedKeywords(&Arguments::ParsedKeywords);
2810
2811
0
  std::vector<std::string> unparsedArguments;
2812
0
  Arguments const arguments =
2813
0
    parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
2814
2815
0
  if (arguments.MaybeReportError(status.GetMakefile())) {
2816
0
    return true;
2817
0
  }
2818
2819
0
  if (!unparsedArguments.empty()) {
2820
0
    status.SetError("Unknown argument to GENERATE subcommand.");
2821
0
    return false;
2822
0
  }
2823
2824
0
  if (!arguments.Output || arguments.ParsedKeywords[0] != "OUTPUT"_s) {
2825
0
    status.SetError("GENERATE requires OUTPUT as first option.");
2826
0
    return false;
2827
0
  }
2828
0
  std::string const& output = *arguments.Output;
2829
2830
0
  if (!arguments.Input && !arguments.Content) {
2831
0
    status.SetError("GENERATE requires INPUT or CONTENT option.");
2832
0
    return false;
2833
0
  }
2834
0
  bool const inputIsContent = arguments.ParsedKeywords[1] == "CONTENT"_s;
2835
0
  if (!inputIsContent && arguments.ParsedKeywords[1] != "INPUT") {
2836
0
    status.SetError("Unknown argument to GENERATE subcommand.");
2837
0
    return false;
2838
0
  }
2839
0
  std::string const& input =
2840
0
    inputIsContent ? *arguments.Content : *arguments.Input;
2841
2842
0
  if (arguments.Condition && arguments.Condition->empty()) {
2843
0
    status.SetError("CONDITION of sub-command GENERATE must not be empty "
2844
0
                    "if specified.");
2845
0
    return false;
2846
0
  }
2847
0
  std::string const& condition =
2848
0
    arguments.Condition ? *arguments.Condition : std::string();
2849
2850
0
  if (arguments.Target && arguments.Target->empty()) {
2851
0
    status.SetError("TARGET of sub-command GENERATE must not be empty "
2852
0
                    "if specified.");
2853
0
    return false;
2854
0
  }
2855
0
  std::string const& target =
2856
0
    arguments.Target ? *arguments.Target : std::string();
2857
2858
0
  cmNewLineStyle newLineStyle;
2859
0
  if (arguments.NewLineStyle) {
2860
0
    std::string errorMessage;
2861
0
    if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
2862
0
      status.SetError(cmStrCat("GENERATE ", errorMessage));
2863
0
      return false;
2864
0
    }
2865
0
  }
2866
2867
0
  if (arguments.NoSourcePermissions && arguments.UseSourcePermissions) {
2868
0
    status.SetError("given both NO_SOURCE_PERMISSIONS and "
2869
0
                    "USE_SOURCE_PERMISSIONS. Only one option allowed.");
2870
0
    return false;
2871
0
  }
2872
2873
0
  if (!arguments.FilePermissions.empty()) {
2874
0
    if (arguments.NoSourcePermissions) {
2875
0
      status.SetError("given both NO_SOURCE_PERMISSIONS and "
2876
0
                      "FILE_PERMISSIONS. Only one option allowed.");
2877
0
      return false;
2878
0
    }
2879
0
    if (arguments.UseSourcePermissions) {
2880
0
      status.SetError("given both USE_SOURCE_PERMISSIONS and "
2881
0
                      "FILE_PERMISSIONS. Only one option allowed.");
2882
0
      return false;
2883
0
    }
2884
0
  }
2885
2886
0
  if (arguments.UseSourcePermissions) {
2887
0
    if (inputIsContent) {
2888
0
      status.SetError("given USE_SOURCE_PERMISSIONS without a file INPUT.");
2889
0
      return false;
2890
0
    }
2891
0
  }
2892
2893
0
  mode_t permissions = 0;
2894
0
  if (arguments.NoSourcePermissions) {
2895
0
    permissions |= cmFSPermissions::mode_owner_read;
2896
0
    permissions |= cmFSPermissions::mode_owner_write;
2897
0
    permissions |= cmFSPermissions::mode_group_read;
2898
0
    permissions |= cmFSPermissions::mode_world_read;
2899
0
  }
2900
2901
0
  if (!arguments.FilePermissions.empty()) {
2902
0
    std::vector<std::string> invalidOptions;
2903
0
    for (auto const& e : arguments.FilePermissions) {
2904
0
      if (!cmFSPermissions::stringToModeT(e, permissions)) {
2905
0
        invalidOptions.push_back(e);
2906
0
      }
2907
0
    }
2908
0
    if (!invalidOptions.empty()) {
2909
0
      std::ostringstream oss;
2910
0
      oss << "given invalid permission ";
2911
0
      for (auto i = 0u; i < invalidOptions.size(); i++) {
2912
0
        if (i == 0u) {
2913
0
          oss << "\"" << invalidOptions[i] << "\"";
2914
0
        } else {
2915
0
          oss << ",\"" << invalidOptions[i] << "\"";
2916
0
        }
2917
0
      }
2918
0
      oss << ".";
2919
0
      status.SetError(oss.str());
2920
0
      return false;
2921
0
    }
2922
0
  }
2923
2924
0
  AddEvaluationFile(input, target, output, condition, inputIsContent,
2925
0
                    newLineStyle.GetCharacters(), permissions, status);
2926
0
  return true;
2927
0
}
2928
2929
bool HandleLockCommand(std::vector<std::string> const& args,
2930
                       cmExecutionStatus& status)
2931
0
{
2932
0
#if !defined(CMAKE_BOOTSTRAP)
2933
  // Default values
2934
0
  bool directory = false;
2935
0
  bool release = false;
2936
0
  enum Guard
2937
0
  {
2938
0
    GUARD_FUNCTION,
2939
0
    GUARD_FILE,
2940
0
    GUARD_PROCESS
2941
0
  };
2942
0
  Guard guard = GUARD_PROCESS;
2943
0
  std::string resultVariable;
2944
0
  unsigned long timeout = static_cast<unsigned long>(-1);
2945
2946
  // Parse arguments
2947
0
  if (args.size() < 2) {
2948
0
    status.GetMakefile().IssueMessage(
2949
0
      MessageType::FATAL_ERROR,
2950
0
      "sub-command LOCK requires at least two arguments.");
2951
0
    return false;
2952
0
  }
2953
2954
0
  std::string path = args[1];
2955
0
  for (unsigned i = 2; i < args.size(); ++i) {
2956
0
    if (args[i] == "DIRECTORY") {
2957
0
      directory = true;
2958
0
    } else if (args[i] == "RELEASE") {
2959
0
      release = true;
2960
0
    } else if (args[i] == "GUARD") {
2961
0
      ++i;
2962
0
      char const* merr = "expected FUNCTION, FILE or PROCESS after GUARD";
2963
0
      if (i >= args.size()) {
2964
0
        status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR, merr);
2965
0
        return false;
2966
0
      }
2967
0
      if (args[i] == "FUNCTION") {
2968
0
        guard = GUARD_FUNCTION;
2969
0
      } else if (args[i] == "FILE") {
2970
0
        guard = GUARD_FILE;
2971
0
      } else if (args[i] == "PROCESS") {
2972
0
        guard = GUARD_PROCESS;
2973
0
      } else {
2974
0
        status.GetMakefile().IssueMessage(
2975
0
          MessageType::FATAL_ERROR,
2976
0
          cmStrCat(merr, ", but got:\n  \"", args[i], "\"."));
2977
0
        return false;
2978
0
      }
2979
2980
0
    } else if (args[i] == "RESULT_VARIABLE") {
2981
0
      ++i;
2982
0
      if (i >= args.size()) {
2983
0
        status.GetMakefile().IssueMessage(
2984
0
          MessageType::FATAL_ERROR,
2985
0
          "expected variable name after RESULT_VARIABLE");
2986
0
        return false;
2987
0
      }
2988
0
      resultVariable = args[i];
2989
0
    } else if (args[i] == "TIMEOUT") {
2990
0
      ++i;
2991
0
      if (i >= args.size()) {
2992
0
        status.GetMakefile().IssueMessage(
2993
0
          MessageType::FATAL_ERROR, "expected timeout value after TIMEOUT");
2994
0
        return false;
2995
0
      }
2996
0
      long scanned;
2997
0
      if (!cmStrToLong(args[i], &scanned) || scanned < 0) {
2998
0
        status.GetMakefile().IssueMessage(
2999
0
          MessageType::FATAL_ERROR,
3000
0
          cmStrCat("TIMEOUT value \"", args[i],
3001
0
                   "\" is not an unsigned integer."));
3002
0
        return false;
3003
0
      }
3004
0
      timeout = static_cast<unsigned long>(scanned);
3005
0
    } else {
3006
0
      status.GetMakefile().IssueMessage(
3007
0
        MessageType::FATAL_ERROR,
3008
0
        cmStrCat("expected DIRECTORY, RELEASE, GUARD, RESULT_VARIABLE or "
3009
0
                 "TIMEOUT\nbut got: \"",
3010
0
                 args[i], "\"."));
3011
0
      return false;
3012
0
    }
3013
0
  }
3014
3015
0
  if (directory) {
3016
0
    path += "/cmake.lock";
3017
0
  }
3018
3019
  // Unify path (remove '//', '/../', ...)
3020
0
  path = cmSystemTools::CollapseFullPath(
3021
0
    path, status.GetMakefile().GetCurrentSourceDirectory());
3022
3023
  // Create file and directories if needed
3024
0
  std::string parentDir = cmSystemTools::GetParentDirectory(path);
3025
0
  if (!cmSystemTools::MakeDirectory(parentDir)) {
3026
0
    status.GetMakefile().IssueMessage(
3027
0
      MessageType::FATAL_ERROR,
3028
0
      cmStrCat("directory\n  \"", parentDir,
3029
0
               "\"\ncreation failed (check permissions)."));
3030
0
    cmSystemTools::SetFatalErrorOccurred();
3031
0
    return false;
3032
0
  }
3033
0
  FILE* file = cmsys::SystemTools::Fopen(path, "a");
3034
0
  if (!file) {
3035
0
    status.GetMakefile().IssueMessage(
3036
0
      MessageType::FATAL_ERROR,
3037
0
      cmStrCat("file\n  \"", path,
3038
0
               "\"\ncreation failed (check permissions)."));
3039
0
    cmSystemTools::SetFatalErrorOccurred();
3040
0
    return false;
3041
0
  }
3042
0
  fclose(file);
3043
3044
  // Actual lock/unlock
3045
0
  cmFileLockPool& lockPool =
3046
0
    status.GetMakefile().GetGlobalGenerator()->GetFileLockPool();
3047
3048
0
  cmFileLockResult fileLockResult(cmFileLockResult::MakeOk());
3049
0
  if (release) {
3050
0
    fileLockResult = lockPool.Release(path);
3051
0
  } else {
3052
0
    switch (guard) {
3053
0
      case GUARD_FUNCTION:
3054
0
        fileLockResult = lockPool.LockFunctionScope(path, timeout);
3055
0
        break;
3056
0
      case GUARD_FILE:
3057
0
        fileLockResult = lockPool.LockFileScope(path, timeout);
3058
0
        break;
3059
0
      case GUARD_PROCESS:
3060
0
        fileLockResult = lockPool.LockProcessScope(path, timeout);
3061
0
        break;
3062
0
      default:
3063
0
        cmSystemTools::SetFatalErrorOccurred();
3064
0
        return false;
3065
0
    }
3066
0
  }
3067
3068
0
  std::string const result = fileLockResult.GetOutputMessage();
3069
3070
0
  if (resultVariable.empty() && !fileLockResult.IsOk()) {
3071
0
    status.GetMakefile().IssueMessage(
3072
0
      MessageType::FATAL_ERROR,
3073
0
      cmStrCat("error locking file\n  \"", path, "\"\n", result, '.'));
3074
0
    cmSystemTools::SetFatalErrorOccurred();
3075
0
    return false;
3076
0
  }
3077
3078
0
  if (!resultVariable.empty()) {
3079
0
    status.GetMakefile().AddDefinition(resultVariable, result);
3080
0
  }
3081
3082
0
  return true;
3083
#else
3084
  static_cast<void>(args);
3085
  status.SetError("sub-command LOCK not implemented in bootstrap cmake");
3086
  return false;
3087
#endif
3088
0
}
3089
3090
bool HandleTimestampCommand(std::vector<std::string> const& args,
3091
                            cmExecutionStatus& status)
3092
0
{
3093
0
  if (args.size() < 3) {
3094
0
    status.SetError("sub-command TIMESTAMP requires at least two arguments.");
3095
0
    return false;
3096
0
  }
3097
0
  if (args.size() > 5) {
3098
0
    status.SetError("sub-command TIMESTAMP takes at most four arguments.");
3099
0
    return false;
3100
0
  }
3101
3102
0
  unsigned int argsIndex = 1;
3103
3104
0
  std::string filename = args[argsIndex++];
3105
0
  if (!cmsys::SystemTools::FileIsFullPath(filename)) {
3106
0
    filename = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/',
3107
0
                        filename);
3108
0
  }
3109
3110
0
  std::string const& outputVariable = args[argsIndex++];
3111
3112
0
  std::string formatString;
3113
0
  if (args.size() > argsIndex && args[argsIndex] != "UTC") {
3114
0
    formatString = args[argsIndex++];
3115
0
  }
3116
3117
0
  bool utcFlag = false;
3118
0
  if (args.size() > argsIndex) {
3119
0
    if (args[argsIndex] == "UTC") {
3120
0
      utcFlag = true;
3121
0
    } else {
3122
0
      std::string e =
3123
0
        cmStrCat(" TIMESTAMP sub-command does not recognize option ",
3124
0
                 args[argsIndex], '.');
3125
0
      status.SetError(e);
3126
0
      return false;
3127
0
    }
3128
0
  }
3129
3130
0
  cmTimestamp timestamp;
3131
0
  std::string result =
3132
0
    timestamp.FileModificationTime(filename.c_str(), formatString, utcFlag);
3133
0
  status.GetMakefile().AddDefinition(outputVariable, result);
3134
3135
0
  return true;
3136
0
}
3137
3138
bool HandleSizeCommand(std::vector<std::string> const& args,
3139
                       cmExecutionStatus& status)
3140
0
{
3141
0
  if (args.size() != 3) {
3142
0
    status.SetError(
3143
0
      cmStrCat(args[0], " requires a file name and output variable"));
3144
0
    return false;
3145
0
  }
3146
3147
0
  unsigned int argsIndex = 1;
3148
3149
0
  std::string const& filename = args[argsIndex++];
3150
3151
0
  std::string const& outputVariable = args[argsIndex++];
3152
3153
0
  if (!cmSystemTools::FileExists(filename, true)) {
3154
0
    status.SetError(
3155
0
      cmStrCat("SIZE requested of path that is not readable:\n  ", filename));
3156
0
    return false;
3157
0
  }
3158
3159
0
  status.GetMakefile().AddDefinition(
3160
0
    outputVariable, std::to_string(cmSystemTools::FileLength(filename)));
3161
3162
0
  return true;
3163
0
}
3164
3165
bool HandleReadSymlinkCommand(std::vector<std::string> const& args,
3166
                              cmExecutionStatus& status)
3167
0
{
3168
0
  if (args.size() != 3) {
3169
0
    status.SetError(
3170
0
      cmStrCat(args[0], " requires a file name and output variable"));
3171
0
    return false;
3172
0
  }
3173
3174
0
  std::string const& filename = args[1];
3175
0
  std::string const& outputVariable = args[2];
3176
3177
0
  std::string result;
3178
0
  if (!cmSystemTools::ReadSymlink(filename, result)) {
3179
0
    status.SetError(cmStrCat(
3180
0
      "READ_SYMLINK requested of path that is not a symlink:\n  ", filename));
3181
0
    return false;
3182
0
  }
3183
3184
0
  status.GetMakefile().AddDefinition(outputVariable, result);
3185
3186
0
  return true;
3187
0
}
3188
3189
bool HandleCreateLinkCommand(std::vector<std::string> const& args,
3190
                             cmExecutionStatus& status)
3191
0
{
3192
0
  if (args.size() < 3) {
3193
0
    status.SetError("CREATE_LINK must be called with at least two additional "
3194
0
                    "arguments");
3195
0
    return false;
3196
0
  }
3197
3198
0
  std::string const& fileName = args[1];
3199
0
  std::string const& newFileName = args[2];
3200
3201
0
  struct Arguments
3202
0
  {
3203
0
    std::string Result;
3204
0
    bool CopyOnError = false;
3205
0
    bool Symbolic = false;
3206
0
  };
3207
3208
0
  static auto const parser =
3209
0
    cmArgumentParser<Arguments>{}
3210
0
      .Bind("RESULT"_s, &Arguments::Result)
3211
0
      .Bind("COPY_ON_ERROR"_s, &Arguments::CopyOnError)
3212
0
      .Bind("SYMBOLIC"_s, &Arguments::Symbolic);
3213
3214
0
  std::vector<std::string> unconsumedArgs;
3215
0
  Arguments const arguments =
3216
0
    parser.Parse(cmMakeRange(args).advance(3), &unconsumedArgs);
3217
3218
0
  if (!unconsumedArgs.empty()) {
3219
0
    status.SetError(cmStrCat("unknown argument: \"",
3220
0
                             std::move(unconsumedArgs.front()), '\"'));
3221
0
    return false;
3222
0
  }
3223
3224
  // The system error message generated in the operation.
3225
0
  std::string result;
3226
3227
  // Check if the paths are distinct.
3228
0
  if (fileName == newFileName) {
3229
0
    result = "CREATE_LINK cannot use same file and newfile";
3230
0
    if (!arguments.Result.empty()) {
3231
0
      status.GetMakefile().AddDefinition(arguments.Result, result);
3232
0
      return true;
3233
0
    }
3234
0
    status.SetError(result);
3235
0
    return false;
3236
0
  }
3237
3238
0
  cmPolicies::PolicyStatus const cmp0205 =
3239
0
    status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0205);
3240
3241
  // Hard link requires original file to exist.
3242
0
  if (!arguments.Symbolic &&
3243
0
      (!cmSystemTools::PathExists(fileName) ||
3244
0
       (cmp0205 != cmPolicies::NEW && !cmSystemTools::FileExists(fileName)))) {
3245
0
    result =
3246
0
      cmStrCat("Cannot hard link \'", fileName, "\' as it does not exist.");
3247
0
    if (!arguments.Result.empty()) {
3248
0
      status.GetMakefile().AddDefinition(arguments.Result, result);
3249
0
      return true;
3250
0
    }
3251
0
    status.SetError(result);
3252
0
    return false;
3253
0
  }
3254
3255
  // Check if the new file already exists and remove it.
3256
0
  if (cmSystemTools::PathExists(newFileName)) {
3257
0
    cmsys::Status rmStatus;
3258
0
    if (cmp0205 == cmPolicies::NEW &&
3259
0
        cmSystemTools::FileIsDirectory(newFileName)) {
3260
0
      rmStatus = cmSystemTools::RepeatedRemoveDirectory(newFileName);
3261
0
    } else {
3262
0
      rmStatus = cmSystemTools::RemoveFile(newFileName);
3263
0
    }
3264
0
    if (!rmStatus) {
3265
0
      std::string err = cmStrCat("Failed to create link '", newFileName,
3266
0
                                 "' because existing path cannot be removed: ",
3267
0
                                 rmStatus.GetString(), '\n');
3268
3269
0
      if (!arguments.Result.empty()) {
3270
0
        status.GetMakefile().AddDefinition(arguments.Result, err);
3271
0
        return true;
3272
0
      }
3273
0
      status.SetError(err);
3274
0
      return false;
3275
0
    }
3276
0
  }
3277
3278
0
  bool const sourceIsDirectory = cmSystemTools::FileIsDirectory(fileName);
3279
3280
  // Whether the operation completed successfully.
3281
0
  bool completed = false;
3282
3283
  // Check if the command requires a symbolic link.
3284
0
  if (arguments.Symbolic) {
3285
0
    cmsys::Status linked =
3286
0
      cmSystemTools::CreateSymlinkQuietly(fileName, newFileName);
3287
0
    if (linked) {
3288
0
      completed = true;
3289
0
    } else {
3290
0
      result = cmStrCat("failed to create symbolic link '", newFileName,
3291
0
                        "': ", linked.GetString());
3292
0
    }
3293
0
  } else {
3294
0
    bool needToTry = true;
3295
0
    if (sourceIsDirectory) {
3296
0
      if (cmp0205 == cmPolicies::NEW) {
3297
0
        needToTry = false;
3298
0
      } else if (cmp0205 == cmPolicies::WARN) {
3299
0
        status.GetMakefile().IssueMessage(
3300
0
          MessageType::AUTHOR_WARNING,
3301
0
          cmStrCat("Path\n  ", fileName,
3302
0
                   "\nis directory. Hardlinks creation is not supported for "
3303
0
                   "directories.\n",
3304
0
                   cmPolicies::GetPolicyWarning(cmPolicies::CMP0205)));
3305
0
      }
3306
0
    }
3307
3308
0
    if (needToTry) {
3309
0
      cmsys::Status linked =
3310
0
        cmSystemTools::CreateLinkQuietly(fileName, newFileName);
3311
0
      if (linked) {
3312
0
        completed = true;
3313
0
      } else {
3314
0
        result = cmStrCat("failed to create link '", newFileName,
3315
0
                          "': ", linked.GetString());
3316
0
      }
3317
0
    } else {
3318
0
      result =
3319
0
        cmStrCat("failed to create link '", newFileName, "': not supported");
3320
0
    }
3321
0
  }
3322
3323
0
  if (arguments.CopyOnError && cmp0205 == cmPolicies::WARN &&
3324
0
      sourceIsDirectory) {
3325
0
    status.GetMakefile().IssueMessage(
3326
0
      MessageType::AUTHOR_WARNING,
3327
0
      cmStrCat("Path\n  ", fileName,
3328
0
               "\nis directory. It will be copied recursively when NEW policy "
3329
0
               "behavior applies for CMP0205.\n",
3330
0
               cmPolicies::GetPolicyWarning(cmPolicies::CMP0205)));
3331
0
  }
3332
3333
  // Check if copy-on-error is enabled in the arguments.
3334
0
  if (!completed && arguments.CopyOnError) {
3335
0
    cmsys::Status copied;
3336
0
    if (cmp0205 == cmPolicies::NEW && sourceIsDirectory) {
3337
0
      copied = cmsys::SystemTools::CopyADirectory(fileName, newFileName);
3338
0
    } else {
3339
0
      copied = cmsys::SystemTools::CopyFileAlways(fileName, newFileName);
3340
0
    }
3341
3342
0
    if (copied) {
3343
0
      completed = true;
3344
0
    } else {
3345
0
      result = "Copy failed: " + copied.GetString();
3346
0
    }
3347
0
  }
3348
3349
  // Check if the operation was successful.
3350
0
  if (completed) {
3351
0
    result = "0";
3352
0
  } else if (arguments.Result.empty()) {
3353
    // The operation failed and the result is not reported in a variable.
3354
0
    status.SetError(result);
3355
0
    return false;
3356
0
  }
3357
3358
0
  if (!arguments.Result.empty()) {
3359
0
    status.GetMakefile().AddDefinition(arguments.Result, result);
3360
0
  }
3361
3362
0
  return true;
3363
0
}
3364
3365
bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
3366
                                         cmExecutionStatus& status)
3367
0
{
3368
0
  std::string platform =
3369
0
    status.GetMakefile().GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
3370
0
  if (!cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
3371
0
        platform)) {
3372
0
    status.SetError(
3373
0
      cmStrCat("GET_RUNTIME_DEPENDENCIES is not supported on system \"",
3374
0
               platform, '"'));
3375
0
    cmSystemTools::SetFatalErrorOccurred();
3376
0
    return false;
3377
0
  }
3378
3379
0
  if (status.GetMakefile().GetState()->GetRole() == cmState::Role::Project) {
3380
0
    status.GetMakefile().IssueMessage(
3381
0
      MessageType::AUTHOR_WARNING,
3382
0
      "You have used file(GET_RUNTIME_DEPENDENCIES)"
3383
0
      " in project mode. This is probably not what "
3384
0
      "you intended to do. Instead, please consider"
3385
0
      " using it in an install(CODE) or "
3386
0
      "install(SCRIPT) command. For example:"
3387
0
      "\n  install(CODE [["
3388
0
      "\n    file(GET_RUNTIME_DEPENDENCIES"
3389
0
      "\n      # ..."
3390
0
      "\n      )"
3391
0
      "\n    ]])");
3392
0
  }
3393
3394
0
  struct Arguments : public ArgumentParser::ParseResult
3395
0
  {
3396
0
    std::string ResolvedDependenciesVar;
3397
0
    std::string UnresolvedDependenciesVar;
3398
0
    std::string ConflictingDependenciesPrefix;
3399
0
    std::string RPathPrefix;
3400
0
    std::string BundleExecutable;
3401
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Executables;
3402
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Libraries;
3403
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
3404
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Modules;
3405
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes;
3406
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes;
3407
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes;
3408
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes;
3409
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles;
3410
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles;
3411
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>>
3412
0
      PostExcludeFilesStrict;
3413
0
  };
3414
3415
0
  static auto const parser =
3416
0
    cmArgumentParser<Arguments>{}
3417
0
      .Bind("RESOLVED_DEPENDENCIES_VAR"_s, &Arguments::ResolvedDependenciesVar)
3418
0
      .Bind("UNRESOLVED_DEPENDENCIES_VAR"_s,
3419
0
            &Arguments::UnresolvedDependenciesVar)
3420
0
      .Bind("CONFLICTING_DEPENDENCIES_PREFIX"_s,
3421
0
            &Arguments::ConflictingDependenciesPrefix)
3422
0
      .Bind("RPATH_PREFIX"_s, &Arguments::RPathPrefix)
3423
0
      .Bind("BUNDLE_EXECUTABLE"_s, &Arguments::BundleExecutable)
3424
0
      .Bind("EXECUTABLES"_s, &Arguments::Executables)
3425
0
      .Bind("LIBRARIES"_s, &Arguments::Libraries)
3426
0
      .Bind("MODULES"_s, &Arguments::Modules)
3427
0
      .Bind("DIRECTORIES"_s, &Arguments::Directories)
3428
0
      .Bind("PRE_INCLUDE_REGEXES"_s, &Arguments::PreIncludeRegexes)
3429
0
      .Bind("PRE_EXCLUDE_REGEXES"_s, &Arguments::PreExcludeRegexes)
3430
0
      .Bind("POST_INCLUDE_REGEXES"_s, &Arguments::PostIncludeRegexes)
3431
0
      .Bind("POST_EXCLUDE_REGEXES"_s, &Arguments::PostExcludeRegexes)
3432
0
      .Bind("POST_INCLUDE_FILES"_s, &Arguments::PostIncludeFiles)
3433
0
      .Bind("POST_EXCLUDE_FILES"_s, &Arguments::PostExcludeFiles)
3434
0
      .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict);
3435
3436
0
  std::vector<std::string> unrecognizedArguments;
3437
0
  auto parsedArgs =
3438
0
    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
3439
0
  auto argIt = unrecognizedArguments.begin();
3440
0
  if (argIt != unrecognizedArguments.end()) {
3441
0
    status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, '"'));
3442
0
    cmSystemTools::SetFatalErrorOccurred();
3443
0
    return false;
3444
0
  }
3445
3446
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
3447
0
    cmSystemTools::SetFatalErrorOccurred();
3448
0
    return true;
3449
0
  }
3450
3451
0
  cmRuntimeDependencyArchive archive(
3452
0
    status, parsedArgs.Directories, parsedArgs.BundleExecutable,
3453
0
    parsedArgs.PreIncludeRegexes, parsedArgs.PreExcludeRegexes,
3454
0
    parsedArgs.PostIncludeRegexes, parsedArgs.PostExcludeRegexes,
3455
0
    std::move(parsedArgs.PostIncludeFiles),
3456
0
    std::move(parsedArgs.PostExcludeFiles),
3457
0
    std::move(parsedArgs.PostExcludeFilesStrict));
3458
0
  if (!archive.Prepare()) {
3459
0
    cmSystemTools::SetFatalErrorOccurred();
3460
0
    return false;
3461
0
  }
3462
3463
0
  if (!archive.GetRuntimeDependencies(
3464
0
        parsedArgs.Executables, parsedArgs.Libraries, parsedArgs.Modules)) {
3465
0
    cmSystemTools::SetFatalErrorOccurred();
3466
0
    return false;
3467
0
  }
3468
3469
0
  std::vector<std::string> deps;
3470
0
  std::vector<std::string> unresolvedDeps;
3471
0
  std::vector<std::string> conflictingDeps;
3472
0
  for (auto const& val : archive.GetResolvedPaths()) {
3473
0
    bool unique = true;
3474
0
    auto it = val.second.begin();
3475
0
    assert(it != val.second.end());
3476
0
    auto const& firstPath = *it;
3477
0
    while (++it != val.second.end()) {
3478
0
      if (!cmSystemTools::SameFile(firstPath, *it)) {
3479
0
        unique = false;
3480
0
        break;
3481
0
      }
3482
0
    }
3483
3484
0
    if (unique) {
3485
0
      deps.push_back(firstPath);
3486
0
      if (!parsedArgs.RPathPrefix.empty()) {
3487
0
        status.GetMakefile().AddDefinition(
3488
0
          cmStrCat(parsedArgs.RPathPrefix, '_', firstPath),
3489
0
          cmList::to_string(archive.GetRPaths().at(firstPath)));
3490
0
      }
3491
0
    } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
3492
0
      conflictingDeps.push_back(val.first);
3493
0
      std::vector<std::string> paths;
3494
0
      paths.insert(paths.begin(), val.second.begin(), val.second.end());
3495
0
      std::string varName =
3496
0
        cmStrCat(parsedArgs.ConflictingDependenciesPrefix, '_', val.first);
3497
0
      std::string pathsStr = cmList::to_string(paths);
3498
0
      status.GetMakefile().AddDefinition(varName, pathsStr);
3499
0
    } else {
3500
0
      std::ostringstream e;
3501
0
      e << "Multiple conflicting paths found for " << val.first << ":";
3502
0
      for (auto const& path : val.second) {
3503
0
        e << "\n  " << path;
3504
0
      }
3505
0
      status.SetError(e.str());
3506
0
      cmSystemTools::SetFatalErrorOccurred();
3507
0
      return false;
3508
0
    }
3509
0
  }
3510
0
  if (!archive.GetUnresolvedPaths().empty()) {
3511
0
    if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
3512
0
      unresolvedDeps.insert(unresolvedDeps.begin(),
3513
0
                            archive.GetUnresolvedPaths().begin(),
3514
0
                            archive.GetUnresolvedPaths().end());
3515
0
    } else {
3516
0
      std::ostringstream e;
3517
0
      e << "Could not resolve runtime dependencies:";
3518
0
      for (auto const& path : archive.GetUnresolvedPaths()) {
3519
0
        e << "\n  " << path;
3520
0
      }
3521
0
      status.SetError(e.str());
3522
0
      cmSystemTools::SetFatalErrorOccurred();
3523
0
      return false;
3524
0
    }
3525
0
  }
3526
3527
0
  if (!parsedArgs.ResolvedDependenciesVar.empty()) {
3528
0
    std::string val = cmList::to_string(deps);
3529
0
    status.GetMakefile().AddDefinition(parsedArgs.ResolvedDependenciesVar,
3530
0
                                       val);
3531
0
  }
3532
0
  if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
3533
0
    std::string val = cmList::to_string(unresolvedDeps);
3534
0
    status.GetMakefile().AddDefinition(parsedArgs.UnresolvedDependenciesVar,
3535
0
                                       val);
3536
0
  }
3537
0
  if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
3538
0
    std::string val = cmList::to_string(conflictingDeps);
3539
0
    status.GetMakefile().AddDefinition(
3540
0
      parsedArgs.ConflictingDependenciesPrefix + "_FILENAMES", val);
3541
0
  }
3542
0
  return true;
3543
0
}
3544
3545
bool HandleConfigureCommand(std::vector<std::string> const& args,
3546
                            cmExecutionStatus& status)
3547
0
{
3548
0
  struct Arguments : public ArgumentParser::ParseResult
3549
0
  {
3550
0
    cm::optional<std::string> Output;
3551
0
    cm::optional<std::string> Content;
3552
0
    bool EscapeQuotes = false;
3553
0
    bool AtOnly = false;
3554
    // "NEWLINE_STYLE" requires one value, but we use a custom check below.
3555
0
    ArgumentParser::Maybe<std::string> NewlineStyle;
3556
0
  };
3557
3558
0
  static auto const parser =
3559
0
    cmArgumentParser<Arguments>{}
3560
0
      .Bind("OUTPUT"_s, &Arguments::Output)
3561
0
      .Bind("CONTENT"_s, &Arguments::Content)
3562
0
      .Bind("ESCAPE_QUOTES"_s, &Arguments::EscapeQuotes)
3563
0
      .Bind("@ONLY"_s, &Arguments::AtOnly)
3564
0
      .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle);
3565
3566
0
  std::vector<std::string> unrecognizedArguments;
3567
0
  auto parsedArgs =
3568
0
    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
3569
3570
0
  auto argIt = unrecognizedArguments.begin();
3571
0
  if (argIt != unrecognizedArguments.end()) {
3572
0
    status.SetError(
3573
0
      cmStrCat("CONFIGURE Unrecognized argument: \"", *argIt, '"'));
3574
0
    cmSystemTools::SetFatalErrorOccurred();
3575
0
    return false;
3576
0
  }
3577
3578
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
3579
0
    cmSystemTools::SetFatalErrorOccurred();
3580
0
    return true;
3581
0
  }
3582
3583
0
  if (!parsedArgs.Output) {
3584
0
    status.SetError("CONFIGURE OUTPUT option is mandatory.");
3585
0
    cmSystemTools::SetFatalErrorOccurred();
3586
0
    return false;
3587
0
  }
3588
0
  if (!parsedArgs.Content) {
3589
0
    status.SetError("CONFIGURE CONTENT option is mandatory.");
3590
0
    cmSystemTools::SetFatalErrorOccurred();
3591
0
    return false;
3592
0
  }
3593
3594
0
  std::string errorMessage;
3595
0
  cmNewLineStyle newLineStyle;
3596
0
  if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
3597
0
    status.SetError(cmStrCat("CONFIGURE ", errorMessage));
3598
0
    return false;
3599
0
  }
3600
3601
  // Check for generator expressions
3602
0
  std::string outputFile = cmSystemTools::CollapseFullPath(
3603
0
    *parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory());
3604
3605
0
  std::string::size_type pos = outputFile.find_first_of("<>");
3606
0
  if (pos != std::string::npos) {
3607
0
    status.SetError(cmStrCat("CONFIGURE called with OUTPUT containing a \"",
3608
0
                             outputFile[pos],
3609
0
                             "\".  This character is not allowed."));
3610
0
    return false;
3611
0
  }
3612
3613
0
  cmMakefile& makeFile = status.GetMakefile();
3614
0
  if (!makeFile.CanIWriteThisFile(outputFile)) {
3615
0
    cmSystemTools::Error("Attempt to write file: " + outputFile +
3616
0
                         " into a source directory.");
3617
0
    return false;
3618
0
  }
3619
3620
0
  cmSystemTools::ConvertToUnixSlashes(outputFile);
3621
3622
  // Re-generate if non-temporary outputs are missing.
3623
  // when we finalize the configuration we will remove all
3624
  // output files that now don't exist.
3625
0
  makeFile.AddCMakeOutputFile(outputFile);
3626
3627
  // Create output directory
3628
0
  std::string::size_type const slashPos = outputFile.rfind('/');
3629
0
  if (slashPos != std::string::npos) {
3630
0
    std::string const path = outputFile.substr(0, slashPos);
3631
0
    cmSystemTools::MakeDirectory(path);
3632
0
  }
3633
3634
0
  std::string newLineCharacters = "\n";
3635
0
  bool open_with_binary_flag = false;
3636
0
  if (newLineStyle.IsValid()) {
3637
0
    newLineCharacters = newLineStyle.GetCharacters();
3638
0
    open_with_binary_flag = true;
3639
0
  }
3640
3641
0
  cmGeneratedFileStream fout;
3642
0
  fout.Open(outputFile, false, open_with_binary_flag);
3643
0
  if (!fout) {
3644
0
    cmSystemTools::Error("Could not open file for write in copy operation " +
3645
0
                         outputFile);
3646
0
    cmSystemTools::ReportLastSystemError("");
3647
0
    return false;
3648
0
  }
3649
0
  fout.SetCopyIfDifferent(true);
3650
3651
  // copy input to output and expand variables from input at the same time
3652
0
  std::stringstream sin(*parsedArgs.Content, std::ios::in);
3653
0
  std::string inLine;
3654
0
  std::string outLine;
3655
0
  bool hasNewLine = false;
3656
0
  while (cmSystemTools::GetLineFromStream(sin, inLine, &hasNewLine)) {
3657
0
    outLine.clear();
3658
0
    makeFile.ConfigureString(inLine, outLine, parsedArgs.AtOnly,
3659
0
                             parsedArgs.EscapeQuotes);
3660
0
    fout << outLine;
3661
0
    if (hasNewLine || newLineStyle.IsValid()) {
3662
0
      fout << newLineCharacters;
3663
0
    }
3664
0
  }
3665
3666
  // close file before attempting to copy
3667
0
  fout.close();
3668
3669
0
  return true;
3670
0
}
3671
3672
bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
3673
                                cmExecutionStatus& status)
3674
0
{
3675
0
  struct Arguments : public ArgumentParser::ParseResult
3676
0
  {
3677
0
    std::string Output;
3678
0
    std::string Format;
3679
0
    std::string Compression;
3680
0
    std::string CompressionLevel;
3681
    // "MTIME" should require one value, but it has long been accidentally
3682
    // accepted without one and treated as if an empty value were given.
3683
    // Fixing this would require a policy.
3684
0
    ArgumentParser::Maybe<std::string> MTime;
3685
0
    std::string Threads;
3686
0
    std::string WorkingDirectory;
3687
0
    bool Verbose = false;
3688
    // "PATHS" requires at least one value, but use a custom check below.
3689
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths;
3690
0
  };
3691
3692
0
  static auto const parser =
3693
0
    cmArgumentParser<Arguments>{}
3694
0
      .Bind("OUTPUT"_s, &Arguments::Output)
3695
0
      .Bind("FORMAT"_s, &Arguments::Format)
3696
0
      .Bind("COMPRESSION"_s, &Arguments::Compression)
3697
0
      .Bind("COMPRESSION_LEVEL"_s, &Arguments::CompressionLevel)
3698
0
      .Bind("MTIME"_s, &Arguments::MTime)
3699
0
      .Bind("THREADS"_s, &Arguments::Threads)
3700
0
      .Bind("WORKING_DIRECTORY"_s, &Arguments::WorkingDirectory)
3701
0
      .Bind("VERBOSE"_s, &Arguments::Verbose)
3702
0
      .Bind("PATHS"_s, &Arguments::Paths);
3703
3704
0
  std::vector<std::string> unrecognizedArguments;
3705
0
  auto parsedArgs =
3706
0
    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
3707
0
  auto argIt = unrecognizedArguments.begin();
3708
0
  if (argIt != unrecognizedArguments.end()) {
3709
0
    status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, '"'));
3710
0
    cmSystemTools::SetFatalErrorOccurred();
3711
0
    return false;
3712
0
  }
3713
3714
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
3715
0
    cmSystemTools::SetFatalErrorOccurred();
3716
0
    return true;
3717
0
  }
3718
3719
0
  char const* knownFormats[] = {
3720
0
    "7zip", "gnutar", "pax", "paxr", "raw", "zip"
3721
0
  };
3722
3723
0
  if (!parsedArgs.Format.empty() &&
3724
0
      !cm::contains(knownFormats, parsedArgs.Format)) {
3725
0
    status.SetError(
3726
0
      cmStrCat("archive format ", parsedArgs.Format, " not supported"));
3727
0
    cmSystemTools::SetFatalErrorOccurred();
3728
0
    return false;
3729
0
  }
3730
3731
0
  static std::map<std::string, cmSystemTools::cmTarCompression>
3732
0
    compressionTypeMap = { { "None", cmSystemTools::TarCompressNone },
3733
0
                           { "BZip2", cmSystemTools::TarCompressBZip2 },
3734
0
                           { "Deflate", cmSystemTools::TarCompressGZip },
3735
0
                           { "GZip", cmSystemTools::TarCompressGZip },
3736
0
                           { "LZMA", cmSystemTools::TarCompressLZMA },
3737
0
                           { "LZMA2", cmSystemTools::TarCompressXZ },
3738
0
                           { "PPMd", cmSystemTools::TarCompressPPMd },
3739
0
                           { "XZ", cmSystemTools::TarCompressXZ },
3740
0
                           { "Zstd", cmSystemTools::TarCompressZstd } };
3741
3742
0
  cmSystemTools::cmTarCompression compress = cmSystemTools::TarCompressAuto;
3743
0
  auto typeIt = compressionTypeMap.find(parsedArgs.Compression);
3744
0
  if (typeIt != compressionTypeMap.end()) {
3745
0
    compress = typeIt->second;
3746
0
  } else if (!parsedArgs.Compression.empty()) {
3747
0
    status.SetError(cmStrCat("compression type ", parsedArgs.Compression,
3748
0
                             " is not supported"));
3749
0
    cmSystemTools::SetFatalErrorOccurred();
3750
0
    return false;
3751
0
  }
3752
3753
0
  if (compress == cmSystemTools::TarCompressPPMd &&
3754
0
      parsedArgs.Format != "7zip") {
3755
0
    status.SetError(cmStrCat("PPMd compression is not supported for ",
3756
0
                             parsedArgs.Format, " format"));
3757
0
    cmSystemTools::SetFatalErrorOccurred();
3758
0
    return false;
3759
0
  }
3760
3761
0
  int compressionLevel = 0;
3762
0
  constexpr int minCompressionLevel = 0;
3763
0
  int maxCompressionLevel = 9;
3764
0
  if (compress == cmSystemTools::TarCompressZstd &&
3765
0
      parsedArgs.Format != "zip") {
3766
0
    maxCompressionLevel = 19;
3767
0
  }
3768
3769
0
  if (!parsedArgs.CompressionLevel.empty()) {
3770
0
    if (parsedArgs.CompressionLevel.size() != 1 &&
3771
0
        !cmsysString_isdigit(parsedArgs.CompressionLevel[0])) {
3772
0
      status.SetError(
3773
0
        cmStrCat("compression level ", parsedArgs.CompressionLevel, " for ",
3774
0
                 parsedArgs.Compression, " should be in range ",
3775
0
                 minCompressionLevel, " to ", maxCompressionLevel));
3776
0
      cmSystemTools::SetFatalErrorOccurred();
3777
0
      return false;
3778
0
    }
3779
0
    compressionLevel = std::stoi(parsedArgs.CompressionLevel);
3780
0
    if (compressionLevel < minCompressionLevel ||
3781
0
        compressionLevel > maxCompressionLevel) {
3782
0
      status.SetError(
3783
0
        cmStrCat("compression level ", parsedArgs.CompressionLevel, " for ",
3784
0
                 parsedArgs.Compression, " should be in range ",
3785
0
                 minCompressionLevel, " to ", maxCompressionLevel));
3786
0
      cmSystemTools::SetFatalErrorOccurred();
3787
0
      return false;
3788
0
    }
3789
0
    if (compress == cmSystemTools::TarCompressNone) {
3790
0
      status.SetError(
3791
0
        cmStrCat("compression level is not supported for "
3792
0
                 "compression \"None\". Provided compression level: ",
3793
0
                 parsedArgs.CompressionLevel));
3794
0
      cmSystemTools::SetFatalErrorOccurred();
3795
0
      return false;
3796
0
    }
3797
0
  }
3798
3799
  // Use the single thread by default for backward compatibility
3800
0
  int threads = 1;
3801
0
  constexpr int minThreads = 0;
3802
0
  if (!parsedArgs.Threads.empty()) {
3803
0
    if (parsedArgs.Threads.size() != 1 &&
3804
0
        !cmsysString_isdigit(parsedArgs.Threads[0])) {
3805
0
      status.SetError(cmStrCat("number of threads ", parsedArgs.Threads,
3806
0
                               " should be at least ", minThreads));
3807
0
      cmSystemTools::SetFatalErrorOccurred();
3808
0
      return false;
3809
0
    }
3810
0
    threads = std::stoi(parsedArgs.Threads);
3811
0
    if (threads < minThreads) {
3812
0
      status.SetError(cmStrCat("number of threads ", parsedArgs.Threads,
3813
0
                               " should be at least ", minThreads));
3814
0
      cmSystemTools::SetFatalErrorOccurred();
3815
0
      return false;
3816
0
    }
3817
0
  }
3818
3819
0
  if (parsedArgs.Paths.empty()) {
3820
0
    status.SetError("ARCHIVE_CREATE requires a non-empty list of PATHS");
3821
0
    cmSystemTools::SetFatalErrorOccurred();
3822
0
    return false;
3823
0
  }
3824
3825
0
  if (!cmSystemTools::CreateTar(
3826
0
        parsedArgs.Output, parsedArgs.Paths, parsedArgs.WorkingDirectory,
3827
0
        compress, parsedArgs.Verbose, parsedArgs.MTime, parsedArgs.Format,
3828
0
        compressionLevel, threads)) {
3829
0
    status.SetError(cmStrCat("failed to compress: ", parsedArgs.Output));
3830
0
    cmSystemTools::SetFatalErrorOccurred();
3831
0
    return false;
3832
0
  }
3833
3834
0
  return true;
3835
0
}
3836
3837
bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
3838
                                 cmExecutionStatus& status)
3839
0
{
3840
0
  struct Arguments : public ArgumentParser::ParseResult
3841
0
  {
3842
0
    std::string Input;
3843
0
    bool Verbose = false;
3844
0
    bool ListOnly = false;
3845
0
    std::string Destination;
3846
0
    ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns;
3847
0
    bool Touch = false;
3848
0
  };
3849
3850
0
  static auto const parser = cmArgumentParser<Arguments>{}
3851
0
                               .Bind("INPUT"_s, &Arguments::Input)
3852
0
                               .Bind("VERBOSE"_s, &Arguments::Verbose)
3853
0
                               .Bind("LIST_ONLY"_s, &Arguments::ListOnly)
3854
0
                               .Bind("DESTINATION"_s, &Arguments::Destination)
3855
0
                               .Bind("PATTERNS"_s, &Arguments::Patterns)
3856
0
                               .Bind("TOUCH"_s, &Arguments::Touch);
3857
3858
0
  std::vector<std::string> unrecognizedArguments;
3859
0
  auto parsedArgs =
3860
0
    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
3861
0
  auto argIt = unrecognizedArguments.begin();
3862
0
  if (argIt != unrecognizedArguments.end()) {
3863
0
    status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, '"'));
3864
0
    cmSystemTools::SetFatalErrorOccurred();
3865
0
    return false;
3866
0
  }
3867
3868
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
3869
0
    cmSystemTools::SetFatalErrorOccurred();
3870
0
    return true;
3871
0
  }
3872
3873
0
  std::string inFile = parsedArgs.Input;
3874
3875
0
  if (parsedArgs.ListOnly) {
3876
0
    if (!cmSystemTools::ListTar(inFile, parsedArgs.Patterns,
3877
0
                                parsedArgs.Verbose)) {
3878
0
      status.SetError(cmStrCat("failed to list: ", inFile));
3879
0
      cmSystemTools::SetFatalErrorOccurred();
3880
0
      return false;
3881
0
    }
3882
0
  } else {
3883
0
    std::string destDir = status.GetMakefile().GetCurrentBinaryDirectory();
3884
0
    if (!parsedArgs.Destination.empty()) {
3885
0
      if (cmSystemTools::FileIsFullPath(parsedArgs.Destination)) {
3886
0
        destDir = parsedArgs.Destination;
3887
0
      } else {
3888
0
        destDir = cmStrCat(destDir, '/', parsedArgs.Destination);
3889
0
      }
3890
3891
0
      if (!cmSystemTools::MakeDirectory(destDir)) {
3892
0
        status.SetError(cmStrCat("failed to create directory: ", destDir));
3893
0
        cmSystemTools::SetFatalErrorOccurred();
3894
0
        return false;
3895
0
      }
3896
3897
0
      if (!cmSystemTools::FileIsFullPath(inFile)) {
3898
0
        inFile =
3899
0
          cmStrCat(cmSystemTools::GetLogicalWorkingDirectory(), '/', inFile);
3900
0
      }
3901
0
    }
3902
3903
0
    cmWorkingDirectory workdir(destDir);
3904
0
    if (workdir.Failed()) {
3905
0
      status.SetError(workdir.GetError());
3906
0
      cmSystemTools::SetFatalErrorOccurred();
3907
0
      return false;
3908
0
    }
3909
3910
0
    if (!cmSystemTools::ExtractTar(
3911
0
          inFile, parsedArgs.Patterns,
3912
0
          parsedArgs.Touch ? cmSystemTools::cmTarExtractTimestamps::No
3913
0
                           : cmSystemTools::cmTarExtractTimestamps::Yes,
3914
0
          parsedArgs.Verbose)) {
3915
0
      status.SetError(cmStrCat("failed to extract:\n  ", inFile));
3916
0
      cmSystemTools::SetFatalErrorOccurred();
3917
0
      return false;
3918
0
    }
3919
0
  }
3920
3921
0
  return true;
3922
0
}
3923
3924
bool ValidateAndConvertPermissions(
3925
  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> const&
3926
    permissions,
3927
  mode_t& perms, cmExecutionStatus& status)
3928
0
{
3929
0
  if (!permissions) {
3930
0
    return true;
3931
0
  }
3932
0
  for (auto const& i : *permissions) {
3933
0
    if (!cmFSPermissions::stringToModeT(i, perms)) {
3934
0
      status.SetError(i + " is an invalid permission specifier");
3935
0
      cmSystemTools::SetFatalErrorOccurred();
3936
0
      return false;
3937
0
    }
3938
0
  }
3939
0
  return true;
3940
0
}
3941
3942
bool SetPermissions(std::string const& filename, mode_t perms,
3943
                    cmExecutionStatus& status)
3944
0
{
3945
0
  if (!cmSystemTools::SetPermissions(filename, perms)) {
3946
0
    status.SetError("Failed to set permissions for " + filename);
3947
0
    cmSystemTools::SetFatalErrorOccurred();
3948
0
    return false;
3949
0
  }
3950
0
  return true;
3951
0
}
3952
3953
bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
3954
                            cmExecutionStatus& status)
3955
0
{
3956
0
  mode_t perms = 0;
3957
0
  mode_t fperms = 0;
3958
0
  mode_t dperms = 0;
3959
0
  cmsys::Glob globber;
3960
3961
0
  globber.SetRecurse(recurse);
3962
0
  globber.SetRecurseListDirs(recurse);
3963
3964
0
  struct Arguments : public ArgumentParser::ParseResult
3965
0
  {
3966
0
    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
3967
0
      Permissions;
3968
0
    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
3969
0
      FilePermissions;
3970
0
    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
3971
0
      DirectoryPermissions;
3972
0
  };
3973
3974
0
  static auto const parser =
3975
0
    cmArgumentParser<Arguments>{}
3976
0
      .Bind("PERMISSIONS"_s, &Arguments::Permissions)
3977
0
      .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions)
3978
0
      .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions);
3979
3980
0
  std::vector<std::string> pathEntries;
3981
0
  Arguments parsedArgs =
3982
0
    parser.Parse(cmMakeRange(args).advance(1), &pathEntries);
3983
3984
  // check validity of arguments
3985
0
  if (!parsedArgs.Permissions && !parsedArgs.FilePermissions &&
3986
0
      !parsedArgs.DirectoryPermissions) // no permissions given
3987
0
  {
3988
0
    status.SetError("No permissions given");
3989
0
    cmSystemTools::SetFatalErrorOccurred();
3990
0
    return false;
3991
0
  }
3992
3993
0
  if (parsedArgs.Permissions && parsedArgs.FilePermissions &&
3994
0
      parsedArgs.DirectoryPermissions) // all keywords are used
3995
0
  {
3996
0
    status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or "
3997
0
                    "DIRECTORY_PERMISSIONS from the invocation");
3998
0
    cmSystemTools::SetFatalErrorOccurred();
3999
0
    return false;
4000
0
  }
4001
4002
0
  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
4003
0
    cmSystemTools::SetFatalErrorOccurred();
4004
0
    return true;
4005
0
  }
4006
4007
  // validate permissions
4008
0
  bool validatePermissions =
4009
0
    ValidateAndConvertPermissions(parsedArgs.Permissions, perms, status) &&
4010
0
    ValidateAndConvertPermissions(parsedArgs.FilePermissions, fperms,
4011
0
                                  status) &&
4012
0
    ValidateAndConvertPermissions(parsedArgs.DirectoryPermissions, dperms,
4013
0
                                  status);
4014
0
  if (!validatePermissions) {
4015
0
    return false;
4016
0
  }
4017
4018
0
  std::vector<std::string> allPathEntries;
4019
4020
0
  if (recurse) {
4021
0
    std::vector<std::string> tempPathEntries;
4022
0
    for (auto const& i : pathEntries) {
4023
0
      if (cmSystemTools::FileIsDirectory(i)) {
4024
0
        globber.FindFiles(i + "/*");
4025
0
        tempPathEntries = globber.GetFiles();
4026
0
        allPathEntries.insert(allPathEntries.end(), tempPathEntries.begin(),
4027
0
                              tempPathEntries.end());
4028
0
        allPathEntries.emplace_back(i);
4029
0
      } else {
4030
0
        allPathEntries.emplace_back(i); // We validate path entries below
4031
0
      }
4032
0
    }
4033
0
  } else {
4034
0
    allPathEntries = std::move(pathEntries);
4035
0
  }
4036
4037
  // chmod
4038
0
  for (auto const& i : allPathEntries) {
4039
0
    if (!(cmSystemTools::FileExists(i) || cmSystemTools::FileIsDirectory(i))) {
4040
0
      status.SetError(cmStrCat("does not exist:\n  ", i));
4041
0
      cmSystemTools::SetFatalErrorOccurred();
4042
0
      return false;
4043
0
    }
4044
4045
0
    if (cmSystemTools::FileExists(i, true)) {
4046
0
      bool success = true;
4047
0
      mode_t filePermissions = parsedArgs.FilePermissions ? fperms : perms;
4048
0
      if (filePermissions) {
4049
0
        success = SetPermissions(i, filePermissions, status);
4050
0
      }
4051
0
      if (!success) {
4052
0
        return false;
4053
0
      }
4054
0
    }
4055
4056
0
    else if (cmSystemTools::FileIsDirectory(i)) {
4057
0
      bool success = true;
4058
0
      mode_t directoryPermissions =
4059
0
        parsedArgs.DirectoryPermissions ? dperms : perms;
4060
0
      if (directoryPermissions) {
4061
0
        success = SetPermissions(i, directoryPermissions, status);
4062
0
      }
4063
0
      if (!success) {
4064
0
        return false;
4065
0
      }
4066
0
    }
4067
0
  }
4068
4069
0
  return true;
4070
0
}
4071
4072
bool HandleChmodCommand(std::vector<std::string> const& args,
4073
                        cmExecutionStatus& status)
4074
0
{
4075
0
  return HandleChmodCommandImpl(args, false, status);
4076
0
}
4077
4078
bool HandleChmodRecurseCommand(std::vector<std::string> const& args,
4079
                               cmExecutionStatus& status)
4080
0
{
4081
0
  return HandleChmodCommandImpl(args, true, status);
4082
0
}
4083
4084
} // namespace
4085
4086
bool cmFileCommand(std::vector<std::string> const& args,
4087
                   cmExecutionStatus& status)
4088
0
{
4089
0
  if (args.empty()) {
4090
0
    status.SetError(
4091
0
      "given no arguments, but it requires at least a sub-command.");
4092
0
    return false;
4093
0
  }
4094
4095
0
  static cmSubcommandTable const subcommand{
4096
0
    { "WRITE"_s, HandleWriteCommand },
4097
0
    { "APPEND"_s, HandleAppendCommand },
4098
0
    { "DOWNLOAD"_s, HandleDownloadCommand },
4099
0
    { "UPLOAD"_s, HandleUploadCommand },
4100
0
    { "READ"_s, HandleReadCommand },
4101
0
    { "MD5"_s, HandleHashCommand },
4102
0
    { "SHA1"_s, HandleHashCommand },
4103
0
    { "SHA224"_s, HandleHashCommand },
4104
0
    { "SHA256"_s, HandleHashCommand },
4105
0
    { "SHA384"_s, HandleHashCommand },
4106
0
    { "SHA512"_s, HandleHashCommand },
4107
0
    { "SHA3_224"_s, HandleHashCommand },
4108
0
    { "SHA3_256"_s, HandleHashCommand },
4109
0
    { "SHA3_384"_s, HandleHashCommand },
4110
0
    { "SHA3_512"_s, HandleHashCommand },
4111
0
    { "STRINGS"_s, HandleStringsCommand },
4112
0
    { "GLOB"_s, HandleGlobCommand },
4113
0
    { "GLOB_RECURSE"_s, HandleGlobRecurseCommand },
4114
0
    { "MAKE_DIRECTORY"_s, HandleMakeDirectoryCommand },
4115
0
    { "RENAME"_s, HandleRename },
4116
0
    { "COPY_FILE"_s, HandleCopyFile },
4117
0
    { "REMOVE"_s, HandleRemove },
4118
0
    { "REMOVE_RECURSE"_s, HandleRemoveRecurse },
4119
0
    { "COPY"_s, HandleCopyCommand },
4120
0
    { "INSTALL"_s, HandleInstallCommand },
4121
0
    { "DIFFERENT"_s, HandleDifferentCommand },
4122
0
    { "RPATH_CHANGE"_s, HandleRPathChangeCommand },
4123
0
    { "CHRPATH"_s, HandleRPathChangeCommand },
4124
0
    { "RPATH_SET"_s, HandleRPathSetCommand },
4125
0
    { "RPATH_CHECK"_s, HandleRPathCheckCommand },
4126
0
    { "RPATH_REMOVE"_s, HandleRPathRemoveCommand },
4127
0
    { "READ_ELF"_s, HandleReadElfCommand },
4128
0
    { "READ_MACHO"_s, HandleReadMachoCommand },
4129
0
    { "REAL_PATH"_s, HandleRealPathCommand },
4130
0
    { "RELATIVE_PATH"_s, HandleRelativePathCommand },
4131
0
    { "TO_CMAKE_PATH"_s, HandleCMakePathCommand },
4132
0
    { "TO_NATIVE_PATH"_s, HandleNativePathCommand },
4133
0
    { "TOUCH"_s, HandleTouchCommand },
4134
0
    { "TOUCH_NOCREATE"_s, HandleTouchNocreateCommand },
4135
0
    { "TIMESTAMP"_s, HandleTimestampCommand },
4136
0
    { "GENERATE"_s, HandleGenerateCommand },
4137
0
    { "LOCK"_s, HandleLockCommand },
4138
0
    { "SIZE"_s, HandleSizeCommand },
4139
0
    { "READ_SYMLINK"_s, HandleReadSymlinkCommand },
4140
0
    { "CREATE_LINK"_s, HandleCreateLinkCommand },
4141
0
    { "GET_RUNTIME_DEPENDENCIES"_s, HandleGetRuntimeDependenciesCommand },
4142
0
    { "CONFIGURE"_s, HandleConfigureCommand },
4143
0
    { "ARCHIVE_CREATE"_s, HandleArchiveCreateCommand },
4144
0
    { "ARCHIVE_EXTRACT"_s, HandleArchiveExtractCommand },
4145
0
    { "CHMOD"_s, HandleChmodCommand },
4146
0
    { "CHMOD_RECURSE"_s, HandleChmodRecurseCommand },
4147
0
  };
4148
4149
0
  return subcommand(args[0], args, status);
4150
0
}