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