cmSystemTools.cxx 80.4 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
#include "cmSystemTools.h"
4

5
#include "cmAlgorithms.h"
6
#include "cmDuration.h"
7
#include "cmProcessOutput.h"
8
#include "cmRange.h"
9
#include "cmStringAlgorithms.h"
10
#include "cm_uv.h"
11

12
#if !defined(CMAKE_BOOTSTRAP)
13 14 15 16 17 18
#  include "cmArchiveWrite.h"
#  include "cmLocale.h"
#  include "cm_libarchive.h"
#  ifndef __LA_INT64_T
#    define __LA_INT64_T la_int64_t
#  endif
19 20 21
#  ifndef __LA_SSIZE_T
#    define __LA_SSIZE_T la_ssize_t
#  endif
22
#endif
23

24
#if !defined(CMAKE_BOOTSTRAP)
25
#  include "cmCryptoHash.h"
26 27 28
#endif

#if defined(CMAKE_USE_ELF_PARSER)
29
#  include "cmELF.h"
30 31 32
#endif

#if defined(CMAKE_USE_MACH_PARSER)
33
#  include "cmMachO.h"
34 35
#endif

36 37 38 39 40 41
#include "cmsys/Directory.hxx"
#include "cmsys/Encoding.hxx"
#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
#include "cmsys/System.h"
#include "cmsys/Terminal.h"
42 43 44 45
#include <algorithm>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
46
#include <fcntl.h>
47 48 49 50 51 52
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
53
#include <utility>
54
#include <vector>
55

56
#if defined(_WIN32)
57
#  include <windows.h>
58
// include wincrypt.h after windows.h
59
#  include <wincrypt.h>
60
#else
61 62
#  include <sys/time.h>
#  include <unistd.h>
63 64
#endif

65 66
#if defined(_WIN32) &&                                                        \
  (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__MINGW32__))
67
#  include <io.h>
68 69
#endif

70
#if defined(__APPLE__)
71
#  include <mach-o/dyld.h>
Brad King's avatar
Brad King committed
72 73
#endif

74
#ifdef __QNX__
75
#  include <malloc.h> /* for malloc/free on QNX */
76 77
#endif

78 79 80 81 82 83 84 85 86
namespace {

cmSystemTools::InterruptCallback s_InterruptCallback;
cmSystemTools::MessageCallback s_MessageCallback;
cmSystemTools::OutputCallback s_StderrCallback;
cmSystemTools::OutputCallback s_StdoutCallback;

} // namespace

Andy Cedilnik's avatar
Andy Cedilnik committed
87
#if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
88
// For GetEnvironmentVariables
89
#  if defined(_WIN32)
90
extern __declspec(dllimport) char** environ;
91
#  else
92
extern char** environ;
93
#  endif
94
#endif
95

96
#if !defined(CMAKE_BOOTSTRAP)
97
static std::string cm_archive_entry_pathname(struct archive_entry* entry)
98
{
99
#  if cmsys_STL_HAS_WSTRING
100
  return cmsys::Encoding::ToNarrow(archive_entry_pathname_w(entry));
101
#  else
102
  return archive_entry_pathname(entry);
103
#  endif
104 105
}

106
static int cm_archive_read_open_file(struct archive* a, const char* file,
107 108
                                     int block_size)
{
109
#  if cmsys_STL_HAS_WSTRING
110 111
  std::wstring wfile = cmsys::Encoding::ToWide(file);
  return archive_read_open_filename_w(a, wfile.c_str(), block_size);
112
#  else
113
  return archive_read_open_filename(a, file, block_size);
114
#  endif
115 116 117
}
#endif

118
#ifdef _WIN32
119
#elif defined(__APPLE__)
120
#  include <crt_externs.h>
121

122
#  define environ (*_NSGetEnviron())
123 124
#endif

125
bool cmSystemTools::s_RunCommandHideConsole = false;
126
bool cmSystemTools::s_DisableRunCommandOutput = false;
127
bool cmSystemTools::s_ErrorOccured = false;
128
bool cmSystemTools::s_FatalErrorOccured = false;
129
bool cmSystemTools::s_ForceUnixPaths = false;
130

131 132
// replace replace with with as many times as it shows up in source.
// write the result into source.
133
#if defined(_WIN32) && !defined(__CYGWIN__)
134
void cmSystemTools::ExpandRegistryValues(std::string& source, KeyWOW64 view)
135 136 137 138 139 140 141
{
  // Regular expression to match anything inside [...] that begins in HKEY.
  // Note that there is a special rule for regular expressions to match a
  // close square-bracket inside a list delimited by square brackets.
  // The "[^]]" part of this expression will match any character except
  // a close square-bracket.  The ']' character must be the first in the
  // list of characters inside the [^...] block of the expression.
142
  cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
143

144
  // check for black line or comment
145
  while (regEntry.find(source)) {
146 147 148
    // the arguments are the second match
    std::string key = regEntry.match(1);
    std::string val;
149
    if (ReadRegistryValue(key.c_str(), val, view)) {
150 151 152
      std::string reg = "[";
      reg += key + "]";
      cmSystemTools::ReplaceString(source, reg.c_str(), val.c_str());
153
    } else {
154 155 156
      std::string reg = "[";
      reg += key + "]";
      cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
157
    }
158
  }
159
}
160
#else
161 162
void cmSystemTools::ExpandRegistryValues(std::string& source,
                                         KeyWOW64 /*unused*/)
163
{
164
  cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
165
  while (regEntry.find(source)) {
166 167 168 169 170
    // the arguments are the second match
    std::string key = regEntry.match(1);
    std::string reg = "[";
    reg += key + "]";
    cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
171
  }
172
}
173
#endif
174

175
std::string cmSystemTools::HelpFileName(cm::string_view str)
176
{
177
  std::string name(str);
178 179 180 181 182
  cmSystemTools::ReplaceString(name, "<", "");
  cmSystemTools::ReplaceString(name, ">", "");
  return name;
}

183 184 185 186
void cmSystemTools::Error(const std::string& m)
{
  std::string message = "CMake Error: " + m;
  cmSystemTools::s_ErrorOccured = true;
187
  cmSystemTools::Message(message, "Error");
188 189
}

190
void cmSystemTools::SetInterruptCallback(InterruptCallback f)
191
{
192
  s_InterruptCallback = std::move(f);
193 194 195 196
}

bool cmSystemTools::GetInterruptFlag()
{
197
  if (s_InterruptCallback) {
198
    return s_InterruptCallback();
199
  }
200 201
  return false;
}
202

203
void cmSystemTools::SetMessageCallback(MessageCallback f)
204
{
205
  s_MessageCallback = std::move(f);
206 207
}

208
void cmSystemTools::SetStdoutCallback(OutputCallback f)
209
{
210
  s_StdoutCallback = std::move(f);
211 212
}

213
void cmSystemTools::SetStderrCallback(OutputCallback f)
214
{
215
  s_StderrCallback = std::move(f);
216 217
}

218
void cmSystemTools::Stderr(const std::string& s)
219
{
220
  if (s_StderrCallback) {
221
    s_StderrCallback(s);
222
  } else {
223
    std::cerr << s << std::flush;
224
  }
225 226
}

227
void cmSystemTools::Stdout(const std::string& s)
228
{
229
  if (s_StdoutCallback) {
230
    s_StdoutCallback(s);
231
  } else {
232
    std::cout << s << std::flush;
233
  }
234 235
}

236
void cmSystemTools::Message(const std::string& m, const char* title)
237
{
238
  if (s_MessageCallback) {
239
    s_MessageCallback(m, title);
240 241
  } else {
    std::cerr << m << std::endl;
242
  }
243
}
244

245
void cmSystemTools::ReportLastSystemError(const char* msg)
246
{
247 248
  std::string m = msg;
  m += ": System Error: ";
249
  m += Superclass::GetLastSystemError();
250
  cmSystemTools::Error(m);
251 252
}

253 254 255 256 257 258 259 260 261 262 263
void cmSystemTools::ParseWindowsCommandLine(const char* command,
                                            std::vector<std::string>& args)
{
  // See the MSDN document "Parsing C Command-Line Arguments" at
  // http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx for rules
  // of parsing the windows command line.

  bool in_argument = false;
  bool in_quotes = false;
  int backslashes = 0;
  std::string arg;
264 265
  for (const char* c = command; *c; ++c) {
    if (*c == '\\') {
266 267
      ++backslashes;
      in_argument = true;
268 269
    } else if (*c == '"') {
      int backslash_pairs = backslashes >> 1;
270 271 272
      int backslash_escaped = backslashes & 1;
      arg.append(backslash_pairs, '\\');
      backslashes = 0;
273
      if (backslash_escaped) {
274 275 276
        /* An odd number of backslashes precede this quote.
           It is escaped.  */
        arg.append(1, '"');
277
      } else {
278 279 280 281
        /* An even number of backslashes precede this quote.
           It is not escaped.  */
        in_quotes = !in_quotes;
      }
282 283
      in_argument = true;
    } else {
284 285
      arg.append(backslashes, '\\');
      backslashes = 0;
286
      if (cmIsSpace(*c)) {
287
        if (in_quotes) {
288
          arg.append(1, *c);
289
        } else if (in_argument) {
290
          args.push_back(arg);
291
          arg.clear();
292 293
          in_argument = false;
        }
294
      } else {
295 296 297 298
        in_argument = true;
        arg.append(1, *c);
      }
    }
299
  }
300
  arg.append(backslashes, '\\');
301
  if (in_argument) {
302
    args.push_back(arg);
303
  }
304 305
}

306 307 308
class cmSystemToolsArgV
{
  char** ArgV;
309

310
public:
311 312 313 314
  cmSystemToolsArgV(char** argv)
    : ArgV(argv)
  {
  }
315
  ~cmSystemToolsArgV()
316 317
  {
    for (char** arg = this->ArgV; arg && *arg; ++arg) {
318 319
      free(*arg);
    }
320 321
    free(this->ArgV);
  }
322 323
  cmSystemToolsArgV(const cmSystemToolsArgV&) = delete;
  cmSystemToolsArgV& operator=(const cmSystemToolsArgV&) = delete;
324
  void Store(std::vector<std::string>& args) const
325 326
  {
    for (char** arg = this->ArgV; arg && *arg; ++arg) {
wahikihiki's avatar
wahikihiki committed
327
      args.emplace_back(*arg);
328
    }
329
  }
330 331 332 333 334 335
};

void cmSystemTools::ParseUnixCommandLine(const char* command,
                                         std::vector<std::string>& args)
{
  // Invoke the underlying parser.
336
  cmSystemToolsArgV argv(cmsysSystem_Parse_CommandForUnix(command, 0));
337 338 339
  argv.Store(args);
}

340 341 342 343 344
std::vector<std::string> cmSystemTools::HandleResponseFile(
  std::vector<std::string>::const_iterator argBeg,
  std::vector<std::string>::const_iterator argEnd)
{
  std::vector<std::string> arg_full;
345
  for (std::string const& arg : cmMakeRange(argBeg, argEnd)) {
346 347 348 349 350 351 352
    if (cmHasLiteralPrefix(arg, "@")) {
      cmsys::ifstream responseFile(arg.substr(1).c_str(), std::ios::in);
      if (!responseFile) {
        std::string error = "failed to open for reading (";
        error += cmSystemTools::GetLastSystemError();
        error += "):\n  ";
        error += arg.substr(1);
353
        cmSystemTools::Error(error);
354 355 356 357 358 359 360 361 362
      } else {
        std::string line;
        cmSystemTools::GetLineFromStream(responseFile, line);
        std::vector<std::string> args2;
#ifdef _WIN32
        cmSystemTools::ParseWindowsCommandLine(line.c_str(), args2);
#else
        cmSystemTools::ParseUnixCommandLine(line.c_str(), args2);
#endif
363
        cmAppend(arg_full, args2);
364 365 366 367 368 369 370 371
      }
    } else {
      arg_full.push_back(arg);
    }
  }
  return arg_full;
}

372
std::vector<std::string> cmSystemTools::ParseArguments(const std::string& cmd)
373
{
374
  std::vector<std::string> args;
375
  std::string arg;
376

377 378
  bool win_path = false;

379
  const char* command = cmd.c_str();
380 381 382 383 384 385 386
  if (command[0] && command[1] &&
      ((command[0] != '/' && command[1] == ':' && command[2] == '\\') ||
       (command[0] == '\"' && command[1] != '/' && command[2] == ':' &&
        command[3] == '\\') ||
       (command[0] == '\'' && command[1] != '/' && command[2] == ':' &&
        command[3] == '\\') ||
       (command[0] == '\\' && command[1] == '\\'))) {
387
    win_path = true;
388
  }
389
  // Split the command into an argv array.
390
  for (const char* c = command; *c;) {
391
    // Skip over whitespace.
392
    while (*c == ' ' || *c == '\t') {
393
      ++c;
394
    }
395
    arg.clear();
396
    if (*c == '"') {
397 398
      // Parse a quoted argument.
      ++c;
399
      while (*c && *c != '"') {
400 401
        arg.append(1, *c);
        ++c;
402 403
      }
      if (*c) {
404 405
        ++c;
      }
406 407
      args.push_back(arg);
    } else if (*c == '\'') {
408 409
      // Parse a quoted argument.
      ++c;
410
      while (*c && *c != '\'') {
411 412
        arg.append(1, *c);
        ++c;
413 414
      }
      if (*c) {
415 416
        ++c;
      }
417 418
      args.push_back(arg);
    } else if (*c) {
419
      // Parse an unquoted argument.
420 421
      while (*c && *c != ' ' && *c != '\t') {
        if (*c == '\\' && !win_path) {
422
          ++c;
423
          if (*c) {
424 425 426
            arg.append(1, *c);
            ++c;
          }
427
        } else {
428 429
          arg.append(1, *c);
          ++c;
430 431
        }
      }
432
      args.push_back(arg);
433
    }
434
  }
435

436 437 438
  return args;
}

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
bool cmSystemTools::SplitProgramFromArgs(std::string const& command,
                                         std::string& program,
                                         std::string& args)
{
  const char* c = command.c_str();

  // Skip leading whitespace.
  while (isspace(static_cast<unsigned char>(*c))) {
    ++c;
  }

  // Parse one command-line element up to an unquoted space.
  bool in_escape = false;
  bool in_double = false;
  bool in_single = false;
  for (; *c; ++c) {
    if (in_single) {
      if (*c == '\'') {
        in_single = false;
      } else {
        program += *c;
      }
    } else if (in_escape) {
      in_escape = false;
      program += *c;
    } else if (*c == '\\') {
      in_escape = true;
    } else if (in_double) {
      if (*c == '"') {
        in_double = false;
      } else {
        program += *c;
      }
    } else if (*c == '"') {
      in_double = true;
    } else if (*c == '\'') {
      in_single = true;
    } else if (isspace(static_cast<unsigned char>(*c))) {
      break;
    } else {
      program += *c;
    }
  }

  // The remainder of the command line holds unparsed arguments.
  args = c;

  return !in_single && !in_escape && !in_double;
}

489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
size_t cmSystemTools::CalculateCommandLineLengthLimit()
{
  size_t sz =
#ifdef _WIN32
    // There's a maximum of 65536 bytes and thus 32768 WCHARs on Windows
    // However, cmd.exe itself can only handle 8191 WCHARs and Ninja for
    // example uses it to spawn processes.
    size_t(8191);
#elif defined(__linux)
    // MAX_ARG_STRLEN is the maximum length of a string permissible for
    // the execve() syscall on Linux. It's defined as (PAGE_SIZE * 32)
    // in Linux's binfmts.h
    static_cast<size_t>(sysconf(_SC_PAGESIZE) * 32);
#else
    size_t(0);
#endif

#if defined(_SC_ARG_MAX)
  // ARG_MAX is the maximum size of the command and environment
  // that can be passed to the exec functions on UNIX.
  // The value in limits.h does not need to be present and may
  // depend upon runtime memory constraints, hence sysconf()
  // should be used to query it.
  long szArgMax = sysconf(_SC_ARG_MAX);
  // A return value of -1 signifies an undetermined limit, but
  // it does not imply an infinite limit, and thus is ignored.
  if (szArgMax != -1) {
    // We estimate the size of the environment block to be 1000.
    // This isn't accurate at all, but leaves some headroom.
    szArgMax = szArgMax < 1000 ? 0 : szArgMax - 1000;
519
#  if defined(_WIN32) || defined(__linux)
520
    sz = std::min(sz, static_cast<size_t>(szArgMax));
521
#  else
522
    sz = static_cast<size_t>(szArgMax);
523
#  endif
524 525 526 527 528
  }
#endif
  return sz;
}

529
bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
530
                                     std::string* captureStdOut,
531 532
                                     std::string* captureStdErr, int* retVal,
                                     const char* dir, OutputOption outputflag,
533
                                     cmDuration timeout, Encoding encoding)
534
{
535
  std::vector<const char*> argv;
536
  argv.reserve(command.size() + 1);
537 538
  for (std::string const& cmd : command) {
    argv.push_back(cmd.c_str());
539
  }
Daniel Pfeifer's avatar
Daniel Pfeifer committed
540
  argv.push_back(nullptr);
541

542
  cmsysProcess* cp = cmsysProcess_New();
543
  cmsysProcess_SetCommand(cp, argv.data());
544
  cmsysProcess_SetWorkingDirectory(cp, dir);
545
  if (cmSystemTools::GetRunCommandHideConsole()) {
546
    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
547
  }
548

549
  if (outputflag == OUTPUT_PASSTHROUGH) {
550 551
    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
552 553
    captureStdOut = nullptr;
    captureStdErr = nullptr;
554 555
  } else if (outputflag == OUTPUT_MERGE ||
             (captureStdErr && captureStdErr == captureStdOut)) {
556
    cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
Daniel Pfeifer's avatar
Daniel Pfeifer committed
557
    captureStdErr = nullptr;
558
  }
559
  assert(!captureStdErr || captureStdErr != captureStdOut);
560

561
  cmsysProcess_SetTimeout(cp, timeout.count());
562
  cmsysProcess_Execute(cp);
563

564 565
  std::vector<char> tempStdOut;
  std::vector<char> tempStdErr;
566 567
  char* data;
  int length;
568
  int pipe;
569
  cmProcessOutput processOutput(encoding);
570
  std::string strdata;
571 572
  if (outputflag != OUTPUT_PASSTHROUGH &&
      (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
Daniel Pfeifer's avatar
Daniel Pfeifer committed
573
    while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) >
Daniel Pfeifer's avatar
Daniel Pfeifer committed
574
           0) {
575
      // Translate NULL characters in the output into valid text.
576 577
      for (int i = 0; i < length; ++i) {
        if (data[i] == '\0') {
578
          data[i] = ' ';
579
        }
580
      }
581

582 583
      if (pipe == cmsysProcess_Pipe_STDOUT) {
        if (outputflag != OUTPUT_NONE) {
584
          processOutput.DecodeText(data, length, strdata, 1);
585
          cmSystemTools::Stdout(strdata);
586
        }
587
        if (captureStdOut) {
588
          cmAppend(tempStdOut, data, data + length);
589 590 591
        }
      } else if (pipe == cmsysProcess_Pipe_STDERR) {
        if (outputflag != OUTPUT_NONE) {
592
          processOutput.DecodeText(data, length, strdata, 2);
593
          cmSystemTools::Stderr(strdata);
594 595
        }
        if (captureStdErr) {
596
          cmAppend(tempStdErr, data, data + length);
597
        }
598
      }
599
    }
600 601 602 603

    if (outputflag != OUTPUT_NONE) {
      processOutput.DecodeText(std::string(), strdata, 1);
      if (!strdata.empty()) {
604
        cmSystemTools::Stdout(strdata);
605 606 607
      }
      processOutput.DecodeText(std::string(), strdata, 2);
      if (!strdata.empty()) {
608
        cmSystemTools::Stderr(strdata);
609 610
      }
    }
611
  }
612

Daniel Pfeifer's avatar
Daniel Pfeifer committed
613
  cmsysProcess_WaitForExit(cp, nullptr);
614

615
  if (captureStdOut) {
616
    captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
617
    processOutput.DecodeText(*captureStdOut, *captureStdOut);
618 619
  }
  if (captureStdErr) {
620
    captureStdErr->assign(tempStdErr.begin(), tempStdErr.end());
621
    processOutput.DecodeText(*captureStdErr, *captureStdErr);
622
  }
623

624
  bool result = true;
625 626
  if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
    if (retVal) {
627
      *retVal = cmsysProcess_GetExitValue(cp);
628 629
    } else {
      if (cmsysProcess_GetExitValue(cp) != 0) {
630 631 632
        result = false;
      }
    }
633
  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
634
    const char* exception_str = cmsysProcess_GetExceptionString(cp);
635
    if (outputflag != OUTPUT_NONE) {
636
      std::cerr << exception_str << std::endl;
637 638
    }
    if (captureStdErr) {
639
      captureStdErr->append(exception_str, strlen(exception_str));
640 641
    } else if (captureStdOut) {
      captureStdOut->append(exception_str, strlen(exception_str));
642
    }
643 644
    result = false;
  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
645
    const char* error_str = cmsysProcess_GetErrorString(cp);
646
    if (outputflag != OUTPUT_NONE) {
647
      std::cerr << error_str << std::endl;
648 649
    }
    if (captureStdErr) {
650
      captureStdErr->append(error_str, strlen(error_str));
651 652
    } else if (captureStdOut) {
      captureStdOut->append(error_str, strlen(error_str));
653
    }
654 655
    result = false;
  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
656
    const char* error_str = "Process terminated due to timeout\n";
657
    if (outputflag != OUTPUT_NONE) {
658
      std::cerr << error_str << std::endl;
659 660
    }
    if (captureStdErr) {
661
      captureStdErr->append(error_str, strlen(error_str));
662
    }
663 664
    result = false;
  }
665

666 667 668
  cmsysProcess_Delete(cp);
  return result;
}
669

670
bool cmSystemTools::RunSingleCommand(const std::string& command,
671 672 673
                                     std::string* captureStdOut,
                                     std::string* captureStdErr, int* retVal,
                                     const char* dir, OutputOption outputflag,
674
                                     cmDuration timeout)
675
{
676
  if (s_DisableRunCommandOutput) {
677
    outputflag = OUTPUT_NONE;
678
  }
679

680
  std::vector<std::string> args = cmSystemTools::ParseArguments(command);
681

682
  if (args.empty()) {
683
    return false;
684
  }
685 686
  return cmSystemTools::RunSingleCommand(args, captureStdOut, captureStdErr,
                                         retVal, dir, outputflag, timeout);
687
}
688

689 690
std::string cmSystemTools::PrintSingleCommand(
  std::vector<std::string> const& command)
691
{
692
  if (command.empty()) {
693
    return std::string();
694
  }
695

696
  return cmWrap('"', command, '"', " ");
697 698
}

699
bool cmSystemTools::DoesFileExistWithExtensions(
700
  const std::string& name, const std::vector<std::string>& headerExts)
701
{
702 703
  std::string hname;

704
  for (std::string const& headerExt : headerExts) {
705 706
    hname = name;
    hname += ".";
707
    hname += headerExt;
708
    if (cmSystemTools::FileExists(hname)) {
709
      return true;
710
    }
711
  }
712
  return false;
713 714
}

715 716 717
std::string cmSystemTools::FileExistsInParentDirectories(
  const std::string& fname, const std::string& directory,
  const std::string& toplevel)
718 719
{
  std::string file = fname;
720
  cmSystemTools::ConvertToUnixSlashes(file);
721
  std::string dir = directory;
722
  cmSystemTools::ConvertToUnixSlashes(dir);
723
  std::string prevDir;
724
  while (dir != prevDir) {
725
    std::string path = cmStrCat(dir, "/", file);
726
    if (cmSystemTools::FileExists(path)) {
727
      return path;
728
    }
729
    if (dir.size() < toplevel.size()) {
730
      break;
731
    }
732
    prevDir = dir;
733
    dir = cmSystemTools::GetParentDirectory(dir);
734
  }
735 736 737
  return "";
}

738 739 740
#ifdef _WIN32
cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry()
{
741 742 743 744 745 746 747
  static WindowsFileRetry retry = { 0, 0 };
  if (!retry.Count) {
    unsigned int data[2] = { 0, 0 };
    HKEY const keys[2] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE };
    wchar_t const* const values[2] = { L"FilesystemRetryCount",
                                       L"FilesystemRetryDelay" };
    for (int k = 0; k < 2; ++k) {
748
      HKEY hKey;
749 750 751
      if (RegOpenKeyExW(keys[k], L"Software\\Kitware\\CMake\\Config", 0,
                        KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
        for (int v = 0; v < 2; ++v) {
752
          DWORD dwData, dwType, dwSize = 4;
753 754 755 756
          if (!data[v] &&
              RegQueryValueExW(hKey, values[v], 0, &dwType, (BYTE*)&dwData,
                               &dwSize) == ERROR_SUCCESS &&
              dwType == REG_DWORD && dwSize == 4) {
757 758 759
            data[v] = static_cast<unsigned int>(dwData);
          }
        }
760
        RegCloseKey(hKey);
761 762
      }
    }
763 764 765
    retry.Count = data[0] ? data[0] : 5;
    retry.Delay = data[1] ? data[1] : 500;
  }
766 767
  return retry;
}
768
#endif
769

770 771
std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
  const std::string& path, std::string* errorMessage)
772
{
773
#ifdef _WIN32
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
  // uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found
  std::string resolved_path;
  uv_fs_t req;
  int err = uv_fs_realpath(NULL, &req, path.c_str(), NULL);
  if (!err) {
    resolved_path = std::string((char*)req.ptr);
    cmSystemTools::ConvertToUnixSlashes(resolved_path);
    // Normalize to upper-case drive letter as GetActualCaseForPath does.
    if (resolved_path.size() > 1 && resolved_path[1] == ':') {
      resolved_path[0] = toupper(resolved_path[0]);
    }
  } else if (err == UV_ENOSYS) {
    resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage);
  } else if (errorMessage) {
    LPSTR message = NULL;
    DWORD size = FormatMessageA(
      FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
      NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&message, 0,
      NULL);
    *errorMessage = std::string(message, size);
    LocalFree(message);

    resolved_path = "";
  } else {
    resolved_path = path;
  }
  return resolved_path;
802 803
#else
  return cmsys::SystemTools::GetRealPath(path, errorMessage);
804
#endif
805
}
806

807 808 809 810 811 812 813
void cmSystemTools::InitializeLibUV()
{
#if defined(_WIN32)
  // Perform libuv one-time initialization now, and then un-do its
  // global _fmode setting so that using libuv does not change the
  // default file text/binary mode.  See libuv issue 840.
  uv_loop_close(uv_default_loop());
814
#  ifdef _MSC_VER
815
  _set_fmode(_O_TEXT);
816
#  else
817
  _fmode = _O_TEXT;
818
#  endif
819 820 821
#endif
}

822 823
bool cmSystemTools::RenameFile(const std::string& oldname,
                               const std::string& newname)
824 825
{
#ifdef _WIN32
826 827 828
#  ifndef INVALID_FILE_ATTRIBUTES
#    define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#  endif
829 830 831 832
  /* Windows MoveFileEx may not replace read-only or in-use files.  If it
     fails then remove the read-only attribute from any existing destination.
     Try multiple times since we may be racing against another process
     creating/opening the destination file just before our MoveFileEx.  */
833
  WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry();
834 835 836 837 838
  while (
    !MoveFileExW(SystemTools::ConvertToWindowsExtendedPath(oldname).c_str(),
                 SystemTools::ConvertToWindowsExtendedPath(newname).c_str(),
                 MOVEFILE_REPLACE_EXISTING) &&
    --retry.Count) {
839 840
    DWORD last_error = GetLastError();
    // Try again only if failure was due to access/sharing permissions.
841 842
    if (last_error != ERROR_ACCESS_DENIED &&
        last_error != ERROR_SHARING_VIOLATION) {
843
      return false;
844 845 846 847 848
    }
    DWORD attrs = GetFileAttributesW(
      SystemTools::ConvertToWindowsExtendedPath(newname).c_str());
    if ((attrs != INVALID_FILE_ATTRIBUTES) &&
        (attrs & FILE_ATTRIBUTE_READONLY)) {
849
      // Remove the read-only attribute from the destination file.
850 851
      SetFileAttributesW(
        SystemTools::ConvertToWindowsExtendedPath(newname).c_str(),
852 853
        attrs & ~FILE_ATTRIBUTE_READONLY);
    } else {
854
      // The file may be temporarily in use so wait a bit.
855
      cmSystemTools::Delay(retry.Delay);
856
    }
857
  }
858
  return retry.Count > 0;
859 860
#else
  /* On UNIX we have an OS-provided call to do this atomically.  */
861
  return rename(oldname.c_str(), newname.c_str()) == 0;
862 863 864
#endif
}

865 866
std::string cmSystemTools::ComputeFileHash(const std::string& source,
                                           cmCryptoHash::Algo algo)
867
{
868
#if !defined(CMAKE_BOOTSTRAP)
869 870
  cmCryptoHash hash(algo);
  return hash.HashFile(source);
871 872
#else
  (void)source;
873
  cmSystemTools::Message("hashsum not supported in bootstrapping mode",
874
                         "Error");
875
  return std::string();
876
#endif
877 878
}

879
std::string cmSystemTools::ComputeStringMD5(const std::string& input)
880
{
881
#if !defined(CMAKE_BOOTSTRAP)
882 883
  cmCryptoHash md5(cmCryptoHash::AlgoMD5);
  return md5.HashString(input);
884 885
#else
  (void)input;
886 887
  cmSystemTools::Message("md5sum not supported in bootstrapping mode",
                         "Error");
888
  return "";
889
#endif
890 891
}

892 893 894 895 896
std::string cmSystemTools::ComputeCertificateThumbprint(
  const std::string& source)
{
  std::string thumbprint;

897
#if !defined(CMAKE_BOOTSTRAP) && defined(_WIN32)
898 899 900 901 902
  BYTE* certData = NULL;
  CRYPT_INTEGER_BLOB cryptBlob;
  HCERTSTORE certStore = NULL;
  PCCERT_CONTEXT certContext = NULL;

903 904 905 906 907
  HANDLE certFile = CreateFileW(
    cmsys::Encoding::ToWide(source.c_str()).c_str(), GENERIC_READ,
    FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  if (certFile != INVALID_HANDLE_VALUE && certFile != NULL) {
908
    DWORD fileSize = GetFileSize(certFile, NULL);
909
    if (fileSize != INVALID_FILE_SIZE) {
910
      certData = new BYTE[fileSize];
911
      if (certData != NULL) {
912
        DWORD dwRead = 0;
913
        if (ReadFile(certFile, certData, fileSize, &dwRead, NULL)) {
914 915 916 917
          cryptBlob.cbData = fileSize;
          cryptBlob.pbData = certData;

          // Verify that this is a valid cert
918
          if (PFXIsPFXBlob(&cryptBlob)) {
919
            // Open the certificate as a store
920 921
            certStore = PFXImportCertStore(&cryptBlob, NULL, CRYPT_EXPORTABLE);
            if (certStore != NULL) {
922
              // There should only be 1 cert.
923 924 925
              certContext =
                CertEnumCertificatesInStore(certStore, certContext);
              if (certContext != NULL) {
926 927 928 929 930 931 932
                // The hash is 20 bytes
                BYTE hashData[20];
                DWORD hashLength = 20;

                // Buffer to print the hash. Each byte takes 2 chars +
                // terminating character
                char hashPrint[41];
933
                char* pHashPrint = hashPrint;
934
                // Get the hash property from the certificate
935 936 937
                if (CertGetCertificateContextProperty(
                      certContext, CERT_HASH_PROP_ID, hashData, &hashLength)) {
                  for (DWORD i = 0; i < hashLength; i++) {
938 939 940
                    // Convert each byte to hexadecimal
                    sprintf(pHashPrint, "%02X", hashData[i]);
                    pHashPrint += 2;
941
                  }
942 943 944
                  *pHashPrint = '\0';
                  thumbprint = hashPrint;
                }
945
                CertFreeCertificateContext(certContext);
946
              }
947
              CertCloseStore(certStore, 0);
948 949 950
            }
          }
        }
951
        delete[] certData;
952 953
      }
    }
954 955
    CloseHandle(certFile);
  }
956 957 958
#else
  (void)source;
  cmSystemTools::Message("ComputeCertificateThumbprint is not implemented",
959
                         "Error");
960 961 962 963 964
#endif

  return thumbprint;
}

965 966
void cmSystemTools::Glob(const std::string& directory,
                         const std::string& regexp,
Ken Martin's avatar
Ken Martin committed
967 968
                         std::vector<std::string>& files)
{
969
  cmsys::Directory d;
970
  cmsys::RegularExpression reg(regexp.c_str());
971

972
  if (d.Load(directory)) {
973
    size_t numf;
974
    unsigned int i;
Ken Martin's avatar
Ken Martin committed
975
    numf = d.GetNumberOfFiles();
976
    for (i = 0; i < numf; i++) {
Ken Martin's avatar
Ken Martin committed
977
      std::string fname = d.GetFile(i);
978
      if (reg.find(fname)) {
979
        files.push_back(std::move(fname));
Ken Martin's avatar
Ken Martin committed
980 981
      }
    }
982
  }
Ken Martin's avatar
Ken Martin committed
983 984
}

985
void cmSystemTools::GlobDirs(const std::string& path,
986 987
                             std::vector<std::string>& files)
{
Bill Hoffman's avatar
Bill Hoffman committed
988
  std::string::size_type pos = path.find("/*");
989
  if (pos == std::string::npos) {
990
    files.push_back(path);
991
    return;
992
  }
993
  std::string startPath = path.substr(0, pos);
994
  std::string finishPath = path.substr(pos + 2);
995

996
  cmsys::Directory d;
997 998 999 1000
  if (d.Load(startPath)) {
    for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
      if ((std::string(d.GetFile(i)) != ".") &&
          (std::string(d.GetFile(i)) != "..")) {
1001
        std::string fname = startPath;
1002
        fname += "/";
1003
        fname += d.GetFile(i);
1004
        if (cmSystemTools::FileIsDirectory(fname)) {
1005
          fname += finishPath;
1006
          cmSystemTools::GlobDirs(fname, files);
1007 1008 1009
        }
      }
    }
1010
  }
1011
}
1012

1013 1014
bool cmSystemTools::SimpleGlob(const std::string& glob,
                               std::vector<std::string>& files,
1015 1016
                               int type /* = 0 */)
{
1017
  files.clear();
1018
  if (glob.back() != '*') {
1019
    return false;
1020
  }
1021 1022
  std::string path = cmSystemTools::GetFilenamePath(glob);
  std::string ppath = cmSystemTools::GetFilenameName(glob);
1023 1024
  ppath = ppath.substr(0, ppath.size() - 1);
  if (path.empty()) {
1025
    path = "/";
1026
  }
1027

Andy Cedilnik's avatar
Andy Cedilnik committed
1028
  bool res = false;
1029
  cmsys::Directory d;
1030 1031 1032 1033
  if (d.Load(path)) {
    for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
      if ((std::string(d.GetFile(i)) != ".") &&
          (std::string(d.GetFile(i)) != "..")) {
Andy Cedilnik's avatar