Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmSystemTools.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
4
#if !defined(_WIN32) && !defined(__sun) && !defined(__OpenBSD__)
5
// POSIX APIs are needed
6
// NOLINTNEXTLINE(bugprone-reserved-identifier)
7
#  define _POSIX_C_SOURCE 200809L
8
#endif
9
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) ||  \
10
  defined(__QNX__)
11
// For isascii
12
// NOLINTNEXTLINE(bugprone-reserved-identifier)
13
#  define _XOPEN_SOURCE 700
14
#endif
15
#if defined(__APPLE__)
16
// Restore Darwin APIs removed by _POSIX_C_SOURCE.
17
// NOLINTNEXTLINE(bugprone-reserved-identifier)
18
#  define _DARWIN_C_SOURCE
19
#endif
20
21
#ifndef __has_feature
22
#  define __has_feature(x) 0
23
#endif
24
25
#if !defined(__clang__) || __has_feature(cxx_thread_local)
26
#  define CM_HAVE_THREAD_LOCAL
27
#endif
28
29
#include "cmSystemTools.h"
30
31
#include <iterator>
32
33
#if defined(_WIN32) || defined(__APPLE__)
34
#  include <unordered_map>
35
#endif
36
37
#include <cm/optional>
38
#include <cmext/algorithm>
39
#include <cmext/string_view>
40
41
#include <cm3p/uv.h>
42
43
#include "cmDuration.h"
44
#include "cmELF.h"
45
#include "cmMessageMetadata.h"
46
#include "cmPathResolver.h"
47
#include "cmProcessOutput.h"
48
#include "cmRange.h"
49
#include "cmStringAlgorithms.h"
50
#include "cmUVHandlePtr.h"
51
#include "cmUVProcessChain.h"
52
#include "cmUVStream.h"
53
#include "cmValue.h"
54
#include "cmWorkingDirectory.h"
55
56
#if !defined(CMAKE_BOOTSTRAP)
57
#  include <cm3p/archive.h>
58
#  include <cm3p/archive_entry.h>
59
60
#  include "cmArchiveWrite.h"
61
#  ifndef __LA_INT64_T
62
#    define __LA_INT64_T la_int64_t
63
#  endif
64
#  ifndef __LA_SSIZE_T
65
#    define __LA_SSIZE_T la_ssize_t
66
#  endif
67
#endif
68
69
#if defined(CMake_USE_MACH_PARSER)
70
#  include "cmMachO.h"
71
#endif
72
73
#if defined(CMake_USE_XCOFF_PARSER)
74
#  include "cmXCOFF.h"
75
#endif
76
77
#include <algorithm>
78
#include <cassert>
79
#include <cerrno>
80
#include <cstdint>
81
#include <cstdio>
82
#include <cstdlib>
83
#include <cstring>
84
#include <ctime>
85
#include <functional>
86
#include <iostream>
87
#include <memory>
88
#include <random>
89
#include <utility>
90
#include <vector>
91
92
#ifndef CM_HAVE_THREAD_LOCAL
93
#  include <mutex>
94
#endif
95
96
#include <fcntl.h>
97
98
#include "cmsys/Directory.hxx"
99
#ifdef _WIN32
100
#  include "cmsys/Encoding.hxx"
101
#endif
102
#include "cmsys/FStream.hxx"
103
#include "cmsys/RegularExpression.hxx"
104
#include "cmsys/String.h"
105
#include "cmsys/System.h"
106
107
#if defined(_WIN32)
108
#  include <windows.h>
109
110
#  include <knownfolders.h>
111
#  include <shlobj.h>
112
// include wincrypt.h after windows.h
113
#  include <wincrypt.h>
114
#else
115
#  include <unistd.h>
116
117
#  include <sys/time.h>
118
#endif
119
120
#if defined(_WIN32) &&                                                        \
121
  (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__MINGW32__))
122
#  include <io.h>
123
#endif
124
125
#if defined(__APPLE__)
126
#  include <mach-o/dyld.h>
127
#endif
128
129
#ifdef __QNX__
130
#  include <malloc.h> /* for malloc/free on QNX */
131
#endif
132
133
#ifdef __linux__
134
#  include <linux/fs.h>
135
136
#  include <sys/ioctl.h>
137
#endif
138
139
#if !defined(_WIN32) && !defined(__ANDROID__)
140
#  include <sys/utsname.h>
141
#endif
142
143
#if defined(CMAKE_BOOTSTRAP) && defined(__sun) && defined(__i386)
144
#  define CMAKE_NO_MKDTEMP
145
#endif
146
147
#ifdef CMAKE_NO_MKDTEMP
148
#  include <dlfcn.h>
149
#endif
150
151
#ifndef CMAKE_NO_GETPWNAM
152
#  if defined(_WIN32)
153
#    define CMAKE_NO_GETPWNAM
154
#  endif
155
#endif
156
#ifndef CMAKE_NO_GETPWNAM
157
#  include <pwd.h>
158
#endif
159
160
#if defined(_MSC_VER) && _MSC_VER >= 1800
161
#  define CM_WINDOWS_DEPRECATED_GetVersionEx
162
#endif
163
164
namespace {
165
166
cmSystemTools::InterruptCallback s_InterruptCallback;
167
cmSystemTools::MessageCallback s_MessageCallback;
168
cmSystemTools::OutputCallback s_StderrCallback;
169
cmSystemTools::OutputCallback s_StdoutCallback;
170
171
std::string ResolveTildePath(std::string p)
172
19
{
173
19
  if (!p.empty() && p[0] == '~') {
174
0
    cm::optional<std::string> home;
175
0
    std::string::size_type last = p.find_first_of("/\\");
176
0
    if (last == std::string::npos) {
177
0
      last = p.size();
178
0
    }
179
0
    if (last == 1) {
180
#if defined(_WIN32) && !defined(__CYGWIN__)
181
      home = cmSystemTools::GetEnvVar("USERPROFILE");
182
      if (!home)
183
#endif
184
0
        home = cmSystemTools::GetEnvVar("HOME");
185
0
#ifndef CMAKE_NO_GETPWNAM
186
0
    } else if (last > 1) {
187
0
      std::string user = p.substr(1, last - 1);
188
0
      if (passwd* pw = getpwnam(user.c_str())) {
189
0
        home = std::string(pw->pw_dir);
190
0
      }
191
0
#endif
192
0
    }
193
0
    if (home) {
194
0
      p.replace(0, last, *home);
195
0
    }
196
0
  }
197
19
  return p;
198
19
}
199
200
#ifdef _WIN32
201
std::string GetDosDriveWorkingDirectory(char letter)
202
{
203
  // The Windows command processor tracks a per-drive working
204
  // directory for compatibility with MS-DOS by using special
205
  // environment variables named "=C:".
206
  // https://web.archive.org/web/20100522040616/
207
  // https://blogs.msdn.com/oldnewthing/archive/2010/05/06/10008132.aspx
208
  return cmSystemTools::GetEnvVar(cmStrCat('=', letter, ':'))
209
    .value_or(std::string());
210
}
211
212
cmsys::Status ReadNameOnDisk(std::string const& path, std::string& name)
213
{
214
  std::wstring wp = cmsys::Encoding::ToWide(path);
215
  HANDLE h = CreateFileW(
216
    wp.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
217
    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
218
  if (h == INVALID_HANDLE_VALUE) {
219
    return cmsys::Status::Windows_GetLastError();
220
  }
221
222
  WCHAR local_fni[((sizeof(FILE_NAME_INFO) - 1) / sizeof(WCHAR)) + 1024];
223
  size_t fni_size = sizeof(local_fni);
224
  auto* fni = reinterpret_cast<FILE_NAME_INFO*>(local_fni);
225
  if (!GetFileInformationByHandleEx(h, FileNameInfo, fni, fni_size)) {
226
    DWORD e = GetLastError();
227
    if (e != ERROR_MORE_DATA) {
228
      CloseHandle(h);
229
      return cmsys::Status::Windows(e);
230
    }
231
    fni_size = fni->FileNameLength;
232
    fni = static_cast<FILE_NAME_INFO*>(malloc(fni_size));
233
    if (!fni) {
234
      e = ERROR_NOT_ENOUGH_MEMORY;
235
      CloseHandle(h);
236
      return cmsys::Status::Windows(e);
237
    }
238
    if (!GetFileInformationByHandleEx(h, FileNameInfo, fni, fni_size)) {
239
      e = GetLastError();
240
      free(fni);
241
      CloseHandle(h);
242
      return cmsys::Status::Windows(e);
243
    }
244
  }
245
246
  std::wstring wn{ fni->FileName, fni->FileNameLength / sizeof(WCHAR) };
247
  std::string nn = cmsys::Encoding::ToNarrow(wn);
248
  std::string::size_type last_slash = nn.find_last_of("/\\");
249
  if (last_slash != std::string::npos) {
250
    name = nn.substr(last_slash + 1);
251
  }
252
  if (fni != reinterpret_cast<FILE_NAME_INFO*>(local_fni)) {
253
    free(fni);
254
  }
255
  CloseHandle(h);
256
  return cmsys::Status::Success();
257
}
258
#elif defined(__APPLE__)
259
cmsys::Status ReadNameOnDiskIterateDir(std::string const& path,
260
                                       std::string& name)
261
{
262
  // Read contents of the parent directory to find the
263
  // entry matching the given path.
264
  std::string const bn = cmSystemTools::GetFilenameName(path);
265
  std::string const dn = cmSystemTools::GetFilenamePath(path);
266
  DIR* d = opendir(dn.c_str());
267
  while (struct dirent* dr = readdir(d)) {
268
    if (strcasecmp(dr->d_name, bn.c_str()) == 0) {
269
      name = dr->d_name;
270
      closedir(d);
271
      return cmsys::Status::Success();
272
    }
273
  }
274
  closedir(d);
275
  return cmsys::Status::POSIX(ENOENT);
276
}
277
278
cmsys::Status ReadNameOnDiskFcntlGetPath(std::string const& path,
279
                                         std::string& name)
280
{
281
  // macOS (and *BSD) offer a syscall to get an on-disk path to
282
  // a descriptor's file.
283
  int fd = open(path.c_str(), O_SYMLINK | O_RDONLY);
284
  if (fd == -1) {
285
    return cmsys::Status::POSIX(errno);
286
  }
287
  char out[MAXPATHLEN + 1];
288
  if (fcntl(fd, F_GETPATH, out) == -1) {
289
    int e = errno;
290
    close(fd);
291
    return cmsys::Status::POSIX(e);
292
  }
293
  close(fd);
294
  name = cmSystemTools::GetFilenameName(out);
295
  return cmsys::Status::Success();
296
}
297
298
cmsys::Status ReadNameOnDisk(std::string const& path, std::string& name)
299
{
300
  struct stat stat_path;
301
  if (lstat(path.c_str(), &stat_path) != 0) {
302
    return cmsys::Status::POSIX(errno);
303
  }
304
  // macOS (and *BSD) use namei(9) to cache file paths.  Use it unless
305
  // the inode has multiple hardlinks: if it is opened through multiple
306
  // paths, the results may be unpredictable.
307
  if (S_ISDIR(stat_path.st_mode) || stat_path.st_nlink < 2) {
308
    return ReadNameOnDiskFcntlGetPath(path, name);
309
  }
310
  // Fall back to reading the parent directory.
311
  return ReadNameOnDiskIterateDir(path, name);
312
}
313
#endif
314
315
class RealSystem : public cm::PathResolver::System
316
{
317
public:
318
  ~RealSystem() override = default;
319
  cmsys::Status ReadSymlink(std::string const& path,
320
                            std::string& link) override
321
0
  {
322
0
    return cmSystemTools::ReadSymlink(path, link);
323
0
  }
324
  bool PathExists(std::string const& path) override
325
0
  {
326
0
    return cmSystemTools::PathExists(path);
327
0
  }
328
  std::string GetWorkingDirectory() override
329
0
  {
330
0
    return cmSystemTools::GetLogicalWorkingDirectory();
331
0
  }
332
#ifdef _WIN32
333
  std::string GetWorkingDirectoryOnDrive(char letter) override
334
  {
335
    return GetDosDriveWorkingDirectory(letter);
336
  }
337
#endif
338
339
#if defined(_WIN32) || defined(__APPLE__)
340
  struct NameOnDisk
341
  {
342
    cmsys::Status Status;
343
    std::string Name;
344
  };
345
  using NameOnDiskMap = std::unordered_map<std::string, NameOnDisk>;
346
  NameOnDiskMap CachedNameOnDisk;
347
348
  cmsys::Status ReadName(std::string const& path, std::string& name) override
349
  {
350
    // Cache results to avoid repeated filesystem access.
351
    // We assume any files created by our own process keep their case.
352
    // Index the cache by lower-case paths to make it case-insensitive.
353
    std::string path_lower = cmSystemTools::LowerCase(path);
354
    auto i = this->CachedNameOnDisk.find(path_lower);
355
    if (i == this->CachedNameOnDisk.end()) {
356
      i = this->CachedNameOnDisk.emplace(path_lower, NameOnDisk()).first;
357
      i->second.Status = ReadNameOnDisk(path, i->second.Name);
358
    }
359
    name = i->second.Name;
360
    return i->second.Status;
361
  }
362
#endif
363
};
364
365
RealSystem RealOS;
366
367
} // namespace
368
369
#if defined(_WIN32) || defined(__APPLE__)
370
cmsys::Status cmSystemTools::ReadNameOnDisk(std::string const& path,
371
                                            std::string& name)
372
{
373
  return ::ReadNameOnDisk(path, name);
374
}
375
#endif
376
377
#if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
378
// For GetEnvironmentVariables
379
#  if defined(_WIN32)
380
extern __declspec(dllimport) char** environ;
381
#  else
382
extern char** environ; // NOLINT(readability-redundant-declaration)
383
#  endif
384
#endif
385
386
#if !defined(CMAKE_BOOTSTRAP)
387
// Get path that was read from the archive.
388
static char const* cm_archive_entry_pathname(struct archive_entry* entry)
389
25.3k
{
390
#  ifdef _WIN32
391
  // libarchive converts the archive's encoding to our UTF-8 encoding.
392
  return archive_entry_pathname_utf8(entry);
393
#  else
394
  // libarchive converts the archive's encoding to our locale's encoding.
395
25.3k
  return archive_entry_pathname(entry);
396
25.3k
#  endif
397
25.3k
}
398
399
// Open archive file for reading.
400
static int cm_archive_read_open_filename(struct archive* a, char const* file,
401
                                         int block_size)
402
45.2k
{
403
#  ifdef _WIN32
404
  std::wstring wfile = cmsys::Encoding::ToWide(file);
405
  return archive_read_open_filename_w(a, wfile.c_str(), block_size);
406
#  else
407
45.2k
  return archive_read_open_filename(a, file, block_size);
408
45.2k
#  endif
409
45.2k
}
410
#endif
411
412
#ifdef _WIN32
413
#elif defined(__APPLE__)
414
#  include <crt_externs.h>
415
416
#  define environ (*_NSGetEnviron())
417
#endif
418
419
bool cmSystemTools::s_RunCommandHideConsole = false;
420
bool cmSystemTools::s_DisableRunCommandOutput = false;
421
bool cmSystemTools::s_ErrorOccurred = false;
422
bool cmSystemTools::s_FatalErrorOccurred = false;
423
bool cmSystemTools::s_ForceUnixPaths = false;
424
425
// replace replace with with as many times as it shows up in source.
426
// write the result into source.
427
#if defined(_WIN32) && !defined(__CYGWIN__)
428
void cmSystemTools::ExpandRegistryValues(std::string& source, KeyWOW64 view)
429
{
430
  // Regular expression to match anything inside [...] that begins in HKEY.
431
  // Note that there is a special rule for regular expressions to match a
432
  // close square-bracket inside a list delimited by square brackets.
433
  // The "[^]]" part of this expression will match any character except
434
  // a close square-bracket.  The ']' character must be the first in the
435
  // list of characters inside the [^...] block of the expression.
436
  cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
437
438
  // check for black line or comment
439
  while (regEntry.find(source)) {
440
    // the arguments are the second match
441
    std::string key = regEntry.match(1);
442
    std::string val;
443
    if (ReadRegistryValue(key.c_str(), val, view)) {
444
      std::string reg = cmStrCat('[', key, ']');
445
      cmSystemTools::ReplaceString(source, reg.c_str(), val.c_str());
446
    } else {
447
      std::string reg = cmStrCat('[', key, ']');
448
      cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
449
    }
450
  }
451
}
452
#else
453
void cmSystemTools::ExpandRegistryValues(std::string& source,
454
                                         KeyWOW64 /*unused*/)
455
0
{
456
0
  cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
457
0
  while (regEntry.find(source)) {
458
    // the arguments are the second match
459
0
    std::string key = regEntry.match(1);
460
0
    std::string reg = cmStrCat('[', key, ']');
461
0
    cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
462
0
  }
463
0
}
464
#endif
465
466
std::string cmSystemTools::HelpFileName(cm::string_view str)
467
0
{
468
0
  std::string name(str);
469
0
  cmSystemTools::ReplaceString(name, "<", "");
470
0
  cmSystemTools::ReplaceString(name, ">", "");
471
0
  return name;
472
0
}
473
474
void cmSystemTools::Error(std::string const& m)
475
38.8k
{
476
38.8k
  std::string message = "CMake Error: " + m;
477
38.8k
  cmSystemTools::s_ErrorOccurred = true;
478
38.8k
  cmSystemTools::Message(message, "Error");
479
38.8k
}
480
481
void cmSystemTools::SetInterruptCallback(InterruptCallback f)
482
0
{
483
0
  s_InterruptCallback = std::move(f);
484
0
}
485
486
bool cmSystemTools::GetInterruptFlag()
487
1.41M
{
488
1.41M
  if (s_InterruptCallback) {
489
0
    return s_InterruptCallback();
490
0
  }
491
1.41M
  return false;
492
1.41M
}
493
494
void cmSystemTools::SetMessageCallback(MessageCallback f)
495
8
{
496
8
  s_MessageCallback = std::move(f);
497
8
}
498
499
void cmSystemTools::SetStdoutCallback(OutputCallback f)
500
8
{
501
8
  s_StdoutCallback = std::move(f);
502
8
}
503
504
void cmSystemTools::SetStderrCallback(OutputCallback f)
505
8
{
506
8
  s_StderrCallback = std::move(f);
507
8
}
508
509
void cmSystemTools::Stderr(std::string const& s)
510
0
{
511
0
  if (s_StderrCallback) {
512
0
    s_StderrCallback(s);
513
0
  } else {
514
0
    std::cerr << s << std::flush;
515
0
  }
516
0
}
517
518
void cmSystemTools::Stdout(std::string const& s)
519
48.4k
{
520
48.4k
  if (s_StdoutCallback) {
521
0
    s_StdoutCallback(s);
522
48.4k
  } else {
523
48.4k
    std::cout << s << std::flush;
524
48.4k
  }
525
48.4k
}
526
527
void cmSystemTools::Message(std::string const& m, char const* title)
528
38.8k
{
529
38.8k
  cmMessageMetadata md;
530
38.8k
  md.title = title;
531
38.8k
  Message(m, md);
532
38.8k
}
533
534
void cmSystemTools::Message(std::string const& m, cmMessageMetadata const& md)
535
76.1k
{
536
76.1k
  if (s_MessageCallback) {
537
37.3k
    s_MessageCallback(m, md);
538
38.8k
  } else {
539
38.8k
    std::cerr << m << std::endl;
540
38.8k
  }
541
76.1k
}
542
543
void cmSystemTools::ReportLastSystemError(char const* msg)
544
0
{
545
0
  std::string m =
546
0
    cmStrCat(msg, ": System Error: ", Superclass::GetLastSystemError());
547
0
  cmSystemTools::Error(m);
548
0
}
549
550
void cmSystemTools::ParseWindowsCommandLine(char const* command,
551
                                            std::vector<std::string>& args)
552
0
{
553
  // See the MSDN document "Parsing C Command-Line Arguments" at
554
  // http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx for rules
555
  // of parsing the windows command line.
556
557
0
  bool in_argument = false;
558
0
  bool in_quotes = false;
559
0
  int backslashes = 0;
560
0
  std::string arg;
561
0
  for (char const* c = command; *c; ++c) {
562
0
    if (*c == '\\') {
563
0
      ++backslashes;
564
0
      in_argument = true;
565
0
    } else if (*c == '"') {
566
0
      int backslash_pairs = backslashes >> 1;
567
0
      int backslash_escaped = backslashes & 1;
568
0
      arg.append(backslash_pairs, '\\');
569
0
      backslashes = 0;
570
0
      if (backslash_escaped) {
571
        /* An odd number of backslashes precede this quote.
572
           It is escaped.  */
573
0
        arg.append(1, '"');
574
0
      } else {
575
        /* An even number of backslashes precede this quote.
576
           It is not escaped.  */
577
0
        in_quotes = !in_quotes;
578
0
      }
579
0
      in_argument = true;
580
0
    } else {
581
0
      arg.append(backslashes, '\\');
582
0
      backslashes = 0;
583
0
      if (cmsysString_isspace(*c)) {
584
0
        if (in_quotes) {
585
0
          arg.append(1, *c);
586
0
        } else if (in_argument) {
587
0
          args.push_back(arg);
588
0
          arg.clear();
589
0
          in_argument = false;
590
0
        }
591
0
      } else {
592
0
        in_argument = true;
593
0
        arg.append(1, *c);
594
0
      }
595
0
    }
596
0
  }
597
0
  arg.append(backslashes, '\\');
598
0
  if (in_argument) {
599
0
    args.push_back(arg);
600
0
  }
601
0
}
602
603
class cmSystemToolsArgV
604
{
605
  char** ArgV;
606
607
public:
608
  cmSystemToolsArgV(char** argv)
609
0
    : ArgV(argv)
610
0
  {
611
0
  }
612
  ~cmSystemToolsArgV()
613
0
  {
614
0
    for (char** arg = this->ArgV; arg && *arg; ++arg) {
615
0
      free(*arg);
616
0
    }
617
0
    free(this->ArgV);
618
0
  }
619
  cmSystemToolsArgV(cmSystemToolsArgV const&) = delete;
620
  cmSystemToolsArgV& operator=(cmSystemToolsArgV const&) = delete;
621
  void Store(std::vector<std::string>& args) const
622
0
  {
623
0
    for (char** arg = this->ArgV; arg && *arg; ++arg) {
624
0
      args.emplace_back(*arg);
625
0
    }
626
0
  }
627
};
628
629
void cmSystemTools::ParseUnixCommandLine(char const* command,
630
                                         std::vector<std::string>& args)
631
0
{
632
  // Invoke the underlying parser.
633
0
  cmSystemToolsArgV argv(cmsysSystem_Parse_CommandForUnix(command, 0));
634
0
  argv.Store(args);
635
0
}
636
637
std::vector<std::string> cmSystemTools::HandleResponseFile(
638
  std::vector<std::string>::const_iterator argBeg,
639
  std::vector<std::string>::const_iterator argEnd)
640
0
{
641
0
  std::vector<std::string> arg_full;
642
0
  for (std::string const& arg : cmMakeRange(argBeg, argEnd)) {
643
0
    if (cmHasPrefix(arg, '@')) {
644
0
      cmsys::ifstream responseFile(arg.substr(1).c_str(), std::ios::in);
645
0
      if (!responseFile) {
646
0
        std::string error = cmStrCat("failed to open for reading (",
647
0
                                     cmSystemTools::GetLastSystemError(),
648
0
                                     "):\n  ", cm::string_view(arg).substr(1));
649
0
        cmSystemTools::Error(error);
650
0
      } else {
651
0
        std::string line;
652
0
        cmSystemTools::GetLineFromStream(responseFile, line);
653
0
        std::vector<std::string> args2;
654
#ifdef _WIN32
655
        cmSystemTools::ParseWindowsCommandLine(line.c_str(), args2);
656
#else
657
0
        cmSystemTools::ParseUnixCommandLine(line.c_str(), args2);
658
0
#endif
659
0
        cm::append(arg_full, args2);
660
0
      }
661
0
    } else {
662
0
      arg_full.push_back(arg);
663
0
    }
664
0
  }
665
0
  return arg_full;
666
0
}
667
668
std::vector<std::string> cmSystemTools::ParseArguments(std::string const& cmd)
669
0
{
670
0
  std::vector<std::string> args;
671
0
  std::string arg;
672
673
0
  bool win_path = false;
674
675
0
  char const* command = cmd.c_str();
676
0
  if (command[0] && command[1] &&
677
0
      ((command[0] != '/' && command[1] == ':' && command[2] == '\\') ||
678
0
       (command[0] == '\"' && command[1] != '/' && command[2] == ':' &&
679
0
        command[3] == '\\') ||
680
0
       (command[0] == '\'' && command[1] != '/' && command[2] == ':' &&
681
0
        command[3] == '\\') ||
682
0
       (command[0] == '\\' && command[1] == '\\'))) {
683
0
    win_path = true;
684
0
  }
685
  // Split the command into an argv array.
686
0
  for (char const* c = command; *c;) {
687
    // Skip over whitespace.
688
0
    while (*c == ' ' || *c == '\t') {
689
0
      ++c;
690
0
    }
691
0
    arg.clear();
692
0
    if (*c == '"') {
693
      // Parse a quoted argument.
694
0
      ++c;
695
0
      while (*c && *c != '"') {
696
0
        arg.append(1, *c);
697
0
        ++c;
698
0
      }
699
0
      if (*c) {
700
0
        ++c;
701
0
      }
702
0
      args.push_back(arg);
703
0
    } else if (*c == '\'') {
704
      // Parse a quoted argument.
705
0
      ++c;
706
0
      while (*c && *c != '\'') {
707
0
        arg.append(1, *c);
708
0
        ++c;
709
0
      }
710
0
      if (*c) {
711
0
        ++c;
712
0
      }
713
0
      args.push_back(arg);
714
0
    } else if (*c) {
715
      // Parse an unquoted argument.
716
0
      while (*c && *c != ' ' && *c != '\t') {
717
0
        if (*c == '\\' && !win_path) {
718
0
          ++c;
719
0
          if (*c) {
720
0
            arg.append(1, *c);
721
0
            ++c;
722
0
          }
723
0
        } else {
724
0
          arg.append(1, *c);
725
0
          ++c;
726
0
        }
727
0
      }
728
0
      args.push_back(arg);
729
0
    }
730
0
  }
731
732
0
  return args;
733
0
}
734
735
bool cmSystemTools::SplitProgramFromArgs(std::string const& command,
736
                                         std::string& program,
737
                                         std::string& args)
738
0
{
739
0
  char const* c = command.c_str();
740
741
  // Skip leading whitespace.
742
0
  while (cmsysString_isspace(*c)) {
743
0
    ++c;
744
0
  }
745
746
  // Parse one command-line element up to an unquoted space.
747
0
  bool in_escape = false;
748
0
  bool in_double = false;
749
0
  bool in_single = false;
750
0
  for (; *c; ++c) {
751
0
    if (in_single) {
752
0
      if (*c == '\'') {
753
0
        in_single = false;
754
0
      } else {
755
0
        program += *c;
756
0
      }
757
0
    } else if (in_escape) {
758
0
      in_escape = false;
759
0
      program += *c;
760
0
    } else if (*c == '\\') {
761
0
      in_escape = true;
762
0
    } else if (in_double) {
763
0
      if (*c == '"') {
764
0
        in_double = false;
765
0
      } else {
766
0
        program += *c;
767
0
      }
768
0
    } else if (*c == '"') {
769
0
      in_double = true;
770
0
    } else if (*c == '\'') {
771
0
      in_single = true;
772
0
    } else if (cmsysString_isspace(*c)) {
773
0
      break;
774
0
    } else {
775
0
      program += *c;
776
0
    }
777
0
  }
778
779
  // The remainder of the command line holds unparsed arguments.
780
0
  args = c;
781
782
0
  return !in_single && !in_escape && !in_double;
783
0
}
784
785
std::size_t cmSystemTools::CalculateCommandLineLengthLimit()
786
0
{
787
0
  size_t sz =
788
#ifdef _WIN32
789
    // There's a maximum of 65536 bytes and thus 32768 WCHARs on Windows
790
    // However, cmd.exe itself can only handle 8191 WCHARs and Ninja for
791
    // example uses it to spawn processes.
792
    size_t(8191);
793
#elif defined(__linux)
794
    // MAX_ARG_STRLEN is the maximum length of a string permissible for
795
    // the execve() syscall on Linux. It's defined as (PAGE_SIZE * 32)
796
    // in Linux's binfmts.h
797
0
    static_cast<size_t>(sysconf(_SC_PAGESIZE) * 32);
798
#else
799
    size_t(0);
800
#endif
801
802
0
#if defined(_SC_ARG_MAX)
803
  // ARG_MAX is the maximum size of the command and environment
804
  // that can be passed to the exec functions on UNIX.
805
  // The value in climits does not need to be present and may
806
  // depend upon runtime memory constraints, hence sysconf()
807
  // should be used to query it.
808
0
  long szArgMax = sysconf(_SC_ARG_MAX);
809
  // A return value of -1 signifies an undetermined limit, but
810
  // it does not imply an infinite limit, and thus is ignored.
811
0
  if (szArgMax != -1) {
812
    // We estimate the size of the environment block to be 1000.
813
    // This isn't accurate at all, but leaves some headroom.
814
0
    szArgMax = szArgMax < 1000 ? 0 : szArgMax - 1000;
815
0
#  if defined(_WIN32) || defined(__linux)
816
0
    sz = std::min(sz, static_cast<size_t>(szArgMax));
817
#  else
818
    sz = static_cast<size_t>(szArgMax);
819
#  endif
820
0
  }
821
0
#endif
822
0
  return sz;
823
0
}
824
825
void cmSystemTools::MaybePrependCmdExe(std::vector<std::string>& cmdLine)
826
0
{
827
#if defined(_WIN32) && !defined(__CYGWIN__)
828
  if (!cmdLine.empty()) {
829
    std::string& applicationName = cmdLine.at(0);
830
    static cmsys::RegularExpression const winCmdRegex(
831
      "\\.([Bb][Aa][Tt]|[Cc][Mm][Dd])$");
832
    cmsys::RegularExpressionMatch winCmdMatch;
833
    if (winCmdRegex.find(applicationName.c_str(), winCmdMatch)) {
834
      // Wrap `.bat` and `.cmd` commands with `cmd /c call`.
835
      std::vector<std::string> output;
836
      output.reserve(cmdLine.size() + 3);
837
      output.emplace_back(cmSystemTools::GetComspec());
838
      output.emplace_back("/c");
839
      output.emplace_back("call");
840
      // Convert the batch file path to use backslashes for cmd.exe to parse.
841
      std::replace(applicationName.begin(), applicationName.end(), '/', '\\');
842
      output.emplace_back(applicationName);
843
      std::move(cmdLine.begin() + 1, cmdLine.end(),
844
                std::back_inserter(output));
845
      cmdLine = std::move(output);
846
    }
847
  }
848
#else
849
0
  static_cast<void>(cmdLine);
850
0
#endif
851
0
}
852
853
bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
854
                                     std::string* captureStdOut,
855
                                     std::string* captureStdErr, int* retVal,
856
                                     char const* dir, OutputOption outputflag,
857
                                     cmDuration timeout, Encoding encoding,
858
                                     std::vector<std::string> env)
859
0
{
860
0
  cmUVProcessChainBuilder builder;
861
0
  builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin)
862
0
    .SetEnvironment(std::move(env))
863
0
    .AddCommand(command);
864
0
  if (dir) {
865
0
    builder.SetWorkingDirectory(dir);
866
0
  }
867
868
0
  if (outputflag == OUTPUT_PASSTHROUGH) {
869
0
    captureStdOut = nullptr;
870
0
    captureStdErr = nullptr;
871
0
    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
872
0
      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
873
0
  } else if (outputflag == OUTPUT_MERGE ||
874
0
             (captureStdErr && captureStdErr == captureStdOut)) {
875
0
    builder.SetMergedBuiltinStreams();
876
0
    captureStdErr = nullptr;
877
0
  } else {
878
0
    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
879
0
      .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
880
0
  }
881
0
  assert(!captureStdErr || captureStdErr != captureStdOut);
882
883
0
  auto chain = builder.Start();
884
0
  bool timedOut = false;
885
0
  cm::uv_timer_ptr timer;
886
0
  if (timeout.count()) {
887
0
    timer.init(chain.GetLoop(), &timedOut);
888
0
    timer.start(
889
0
      [](uv_timer_t* t) {
890
0
        auto* timedOutPtr = static_cast<bool*>(t->data);
891
0
        *timedOutPtr = true;
892
0
      },
893
0
      static_cast<uint64_t>(timeout.count() * 1000.0), 0,
894
0
      cm::uv_update_time::yes);
895
0
  }
896
897
0
  std::vector<char> tempStdOut;
898
0
  std::vector<char> tempStdErr;
899
0
  bool outFinished = true;
900
0
  bool errFinished = true;
901
0
  cmProcessOutput processOutput(encoding);
902
0
  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
903
0
  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
904
0
  if (outputflag != OUTPUT_PASSTHROUGH &&
905
0
      (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
906
0
    auto startRead =
907
0
      [&outputflag, &processOutput](
908
0
        uv_stream_t* stream, std::string* captureStd,
909
0
        std::vector<char>& tempStd, int id,
910
0
        void (*outputFunc)(std::string const&),
911
0
        bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
912
0
      finished = false;
913
0
      return cmUVStreamRead(
914
0
        stream,
915
0
        [outputflag, &processOutput, captureStd, &tempStd, id,
916
0
         outputFunc](std::vector<char> data) {
917
          // Translate NULL characters in the output into valid text.
918
0
          for (auto& c : data) {
919
0
            if (c == '\0') {
920
0
              c = ' ';
921
0
            }
922
0
          }
923
924
0
          if (outputflag != OUTPUT_NONE) {
925
0
            std::string strdata;
926
0
            processOutput.DecodeText(data.data(), data.size(), strdata, id);
927
0
            outputFunc(strdata);
928
0
          }
929
0
          if (captureStd) {
930
0
            cm::append(tempStd, data.data(), data.data() + data.size());
931
0
          }
932
0
        },
933
0
        [&finished, outputflag, &processOutput, id, outputFunc]() {
934
0
          finished = true;
935
0
          if (outputflag != OUTPUT_NONE) {
936
0
            std::string strdata;
937
0
            processOutput.DecodeText(std::string(), strdata, id);
938
0
            if (!strdata.empty()) {
939
0
              outputFunc(strdata);
940
0
            }
941
0
          }
942
0
        });
943
0
    };
944
945
0
    outputHandle = startRead(chain.OutputStream(), captureStdOut, tempStdOut,
946
0
                             1, cmSystemTools::Stdout, outFinished);
947
0
    if (chain.ErrorStream()) {
948
0
      errorHandle = startRead(chain.ErrorStream(), captureStdErr, tempStdErr,
949
0
                              2, cmSystemTools::Stderr, errFinished);
950
0
    }
951
0
  }
952
953
0
  while (!timedOut && !(chain.Finished() && outFinished && errFinished)) {
954
0
    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
955
0
  }
956
957
0
  if (captureStdOut) {
958
0
    captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
959
0
    processOutput.DecodeText(*captureStdOut, *captureStdOut);
960
0
  }
961
0
  if (captureStdErr) {
962
0
    captureStdErr->assign(tempStdErr.begin(), tempStdErr.end());
963
0
    processOutput.DecodeText(*captureStdErr, *captureStdErr);
964
0
  }
965
966
0
  bool result = true;
967
0
  if (timedOut) {
968
0
    chain.Terminate();
969
0
    char const* error_str = "Process terminated due to timeout\n";
970
0
    if (outputflag != OUTPUT_NONE) {
971
0
      std::cerr << error_str << std::endl;
972
0
    }
973
0
    if (captureStdErr) {
974
0
      captureStdErr->append(error_str, strlen(error_str));
975
0
    }
976
0
    result = false;
977
0
  } else {
978
0
    auto const& status = chain.GetStatus(0);
979
0
    auto exception = status.GetException();
980
981
0
    switch (exception.first) {
982
0
      case cmUVProcessChain::ExceptionCode::None:
983
0
        if (retVal) {
984
0
          *retVal = static_cast<int>(status.ExitStatus);
985
0
        } else {
986
0
          if (status.ExitStatus != 0) {
987
0
            result = false;
988
0
          }
989
0
        }
990
0
        break;
991
0
      default: {
992
0
        if (outputflag != OUTPUT_NONE) {
993
0
          std::cerr << exception.second << std::endl;
994
0
        }
995
0
        if (captureStdErr) {
996
0
          captureStdErr->append(exception.second);
997
0
        } else if (captureStdOut) {
998
0
          captureStdOut->append(exception.second);
999
0
        }
1000
0
        result = false;
1001
0
      } break;
1002
0
    }
1003
0
  }
1004
1005
0
  return result;
1006
0
}
1007
1008
bool cmSystemTools::RunSingleCommand(std::string const& command,
1009
                                     std::string* captureStdOut,
1010
                                     std::string* captureStdErr, int* retVal,
1011
                                     char const* dir, OutputOption outputflag,
1012
                                     cmDuration timeout)
1013
0
{
1014
0
  if (s_DisableRunCommandOutput) {
1015
0
    outputflag = OUTPUT_NONE;
1016
0
  }
1017
1018
0
  std::vector<std::string> args = cmSystemTools::ParseArguments(command);
1019
1020
0
  if (args.empty()) {
1021
0
    return false;
1022
0
  }
1023
0
  return cmSystemTools::RunSingleCommand(args, captureStdOut, captureStdErr,
1024
0
                                         retVal, dir, outputflag, timeout);
1025
0
}
1026
1027
std::string cmSystemTools::PrintSingleCommand(
1028
  std::vector<std::string> const& command)
1029
0
{
1030
0
  if (command.empty()) {
1031
0
    return std::string();
1032
0
  }
1033
1034
0
  return cmWrap('"', command, '"', " ");
1035
0
}
1036
1037
bool cmSystemTools::DoesFileExistWithExtensions(
1038
  std::string const& name, std::vector<std::string> const& headerExts)
1039
0
{
1040
0
  std::string hname;
1041
1042
0
  for (std::string const& headerExt : headerExts) {
1043
0
    hname = cmStrCat(name, '.', headerExt);
1044
0
    if (cmSystemTools::FileExists(hname)) {
1045
0
      return true;
1046
0
    }
1047
0
  }
1048
0
  return false;
1049
0
}
1050
1051
std::string cmSystemTools::FileExistsInParentDirectories(
1052
  std::string const& fname, std::string const& directory,
1053
  std::string const& toplevel)
1054
0
{
1055
0
  std::string file = fname;
1056
0
  cmSystemTools::ConvertToUnixSlashes(file);
1057
0
  std::string dir = directory;
1058
0
  cmSystemTools::ConvertToUnixSlashes(dir);
1059
0
  std::string prevDir;
1060
0
  while (dir != prevDir) {
1061
0
    std::string path = cmStrCat(dir, '/', file);
1062
0
    if (cmSystemTools::FileExists(path)) {
1063
0
      return path;
1064
0
    }
1065
0
    if (dir.size() < toplevel.size()) {
1066
0
      break;
1067
0
    }
1068
0
    prevDir = dir;
1069
0
    dir = cmSystemTools::GetParentDirectory(dir);
1070
0
  }
1071
0
  return "";
1072
0
}
1073
1074
#ifdef _WIN32
1075
namespace {
1076
1077
/* Helper class to save and restore the specified file (or directory)
1078
   attribute bits. Instantiate this class as an automatic variable on the
1079
   stack. Its constructor saves a copy of the file attributes, and then its
1080
   destructor restores the original attribute settings.  */
1081
class SaveRestoreFileAttributes
1082
{
1083
public:
1084
  SaveRestoreFileAttributes(std::wstring const& path,
1085
                            uint32_t file_attrs_to_set);
1086
  ~SaveRestoreFileAttributes();
1087
1088
  SaveRestoreFileAttributes(SaveRestoreFileAttributes const&) = delete;
1089
  SaveRestoreFileAttributes& operator=(SaveRestoreFileAttributes const&) =
1090
    delete;
1091
1092
  void SetPath(std::wstring const& path) { path_ = path; }
1093
1094
private:
1095
  std::wstring path_;
1096
  uint32_t original_attr_bits_;
1097
};
1098
1099
SaveRestoreFileAttributes::SaveRestoreFileAttributes(
1100
  std::wstring const& path, uint32_t file_attrs_to_set)
1101
  : path_(path)
1102
  , original_attr_bits_(0)
1103
{
1104
  // Set the specified attributes for the source file/directory.
1105
  original_attr_bits_ = GetFileAttributesW(path_.c_str());
1106
  if ((INVALID_FILE_ATTRIBUTES != original_attr_bits_) &&
1107
      ((file_attrs_to_set & original_attr_bits_) != file_attrs_to_set)) {
1108
    SetFileAttributesW(path_.c_str(), original_attr_bits_ | file_attrs_to_set);
1109
  }
1110
}
1111
1112
// We set attribute bits.  Now we need to restore their original state.
1113
SaveRestoreFileAttributes::~SaveRestoreFileAttributes()
1114
{
1115
  DWORD last_error = GetLastError();
1116
  // Verify or restore the original attributes.
1117
  const DWORD source_attr_bits = GetFileAttributesW(path_.c_str());
1118
  if (INVALID_FILE_ATTRIBUTES != source_attr_bits) {
1119
    if (original_attr_bits_ != source_attr_bits) {
1120
      // The file still exists, and its attributes aren't our saved values.
1121
      // Time to restore them.
1122
      SetFileAttributesW(path_.c_str(), original_attr_bits_);
1123
    }
1124
  }
1125
  SetLastError(last_error);
1126
}
1127
1128
struct WindowsFileRetryInit
1129
{
1130
  cmSystemTools::WindowsFileRetry Retry;
1131
  bool Explicit;
1132
};
1133
1134
WindowsFileRetryInit InitWindowsFileRetry(wchar_t const* const values[2],
1135
                                          unsigned int const defaults[2])
1136
{
1137
  unsigned int data[2] = { 0, 0 };
1138
  HKEY const keys[2] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE };
1139
  for (int k = 0; k < 2; ++k) {
1140
    HKEY hKey;
1141
    if (RegOpenKeyExW(keys[k], L"Software\\Kitware\\CMake\\Config", 0,
1142
                      KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
1143
      for (int v = 0; v < 2; ++v) {
1144
        DWORD dwData, dwType, dwSize = 4;
1145
        if (!data[v] &&
1146
            RegQueryValueExW(hKey, values[v], 0, &dwType, (BYTE*)&dwData,
1147
                             &dwSize) == ERROR_SUCCESS &&
1148
            dwType == REG_DWORD && dwSize == 4) {
1149
          data[v] = static_cast<unsigned int>(dwData);
1150
        }
1151
      }
1152
      RegCloseKey(hKey);
1153
    }
1154
  }
1155
  WindowsFileRetryInit init;
1156
  init.Explicit = data[0] || data[1];
1157
  init.Retry.Count = data[0] ? data[0] : defaults[0];
1158
  init.Retry.Delay = data[1] ? data[1] : defaults[1];
1159
  return init;
1160
}
1161
1162
WindowsFileRetryInit InitWindowsFileRetry()
1163
{
1164
  static wchar_t const* const values[2] = { L"FilesystemRetryCount",
1165
                                            L"FilesystemRetryDelay" };
1166
  static unsigned int const defaults[2] = { 5, 500 };
1167
  return InitWindowsFileRetry(values, defaults);
1168
}
1169
1170
WindowsFileRetryInit InitWindowsDirectoryRetry()
1171
{
1172
  static wchar_t const* const values[2] = { L"FilesystemDirectoryRetryCount",
1173
                                            L"FilesystemDirectoryRetryDelay" };
1174
  static unsigned int const defaults[2] = { 120, 500 };
1175
  WindowsFileRetryInit dirInit = InitWindowsFileRetry(values, defaults);
1176
  if (dirInit.Explicit) {
1177
    return dirInit;
1178
  }
1179
  WindowsFileRetryInit fileInit = InitWindowsFileRetry();
1180
  if (fileInit.Explicit) {
1181
    return fileInit;
1182
  }
1183
  return dirInit;
1184
}
1185
1186
cmSystemTools::WindowsFileRetry GetWindowsRetry(std::wstring const& path)
1187
{
1188
  // If we are performing a directory operation, then try and get the
1189
  // appropriate timing info.
1190
  DWORD const attrs = GetFileAttributesW(path.c_str());
1191
  if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
1192
    return cmSystemTools::GetWindowsDirectoryRetry();
1193
  }
1194
  return cmSystemTools::GetWindowsFileRetry();
1195
}
1196
1197
} // end of anonymous namespace
1198
1199
cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry()
1200
{
1201
  static WindowsFileRetry retry = InitWindowsFileRetry().Retry;
1202
  return retry;
1203
}
1204
1205
cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsDirectoryRetry()
1206
{
1207
  static cmSystemTools::WindowsFileRetry retry =
1208
    InitWindowsDirectoryRetry().Retry;
1209
  return retry;
1210
}
1211
1212
cmSystemTools::WindowsVersion cmSystemTools::GetWindowsVersion()
1213
{
1214
  /* Windows version number data.  */
1215
  OSVERSIONINFOEXW osviex;
1216
  ZeroMemory(&osviex, sizeof(osviex));
1217
  osviex.dwOSVersionInfoSize = sizeof(osviex);
1218
1219
#  ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
1220
#    pragma warning(push)
1221
#    ifdef __INTEL_COMPILER
1222
#      pragma warning(disable : 1478)
1223
#    elif defined __clang__
1224
#      pragma clang diagnostic push
1225
#      pragma clang diagnostic ignored "-Wdeprecated-declarations"
1226
#    else
1227
#      pragma warning(disable : 4996)
1228
#    endif
1229
#  endif
1230
  GetVersionExW((OSVERSIONINFOW*)&osviex);
1231
#  ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
1232
#    ifdef __clang__
1233
#      pragma clang diagnostic pop
1234
#    else
1235
#      pragma warning(pop)
1236
#    endif
1237
#  endif
1238
1239
  WindowsVersion result;
1240
  result.dwMajorVersion = osviex.dwMajorVersion;
1241
  result.dwMinorVersion = osviex.dwMinorVersion;
1242
  result.dwBuildNumber = osviex.dwBuildNumber;
1243
  return result;
1244
}
1245
1246
std::string cmSystemTools::GetComspec()
1247
{
1248
  std::string comspec;
1249
  if (!cmSystemTools::GetEnv("COMSPEC", comspec) ||
1250
      !cmSystemTools::FileIsFullPath(comspec)) {
1251
    comspec = "cmd.exe";
1252
  }
1253
  return comspec;
1254
}
1255
1256
#endif
1257
1258
// File changes involve removing SETUID/SETGID bits when a file is modified.
1259
// This behavior is consistent across most Unix-like operating systems.
1260
class FileModeGuard
1261
{
1262
public:
1263
  FileModeGuard(std::string const& file_path, std::string* emsg);
1264
  bool Restore(std::string* emsg);
1265
  bool HasErrors() const;
1266
1267
private:
1268
#ifndef _WIN32
1269
  mode_t mode_;
1270
#endif
1271
  std::string filepath_;
1272
};
1273
1274
FileModeGuard::FileModeGuard(std::string const& file_path, std::string* emsg)
1275
0
{
1276
0
#ifndef _WIN32
1277
0
  struct stat file_stat;
1278
0
  if (stat(file_path.c_str(), &file_stat) != 0) {
1279
0
    if (emsg) {
1280
0
      *emsg = cmStrCat("Cannot get file stat: ", strerror(errno));
1281
0
    }
1282
0
    return;
1283
0
  }
1284
1285
0
  mode_ = file_stat.st_mode;
1286
#else
1287
  static_cast<void>(emsg);
1288
#endif
1289
0
  filepath_ = file_path;
1290
0
}
1291
1292
bool FileModeGuard::Restore(std::string* emsg)
1293
0
{
1294
0
  assert(filepath_.empty() == false);
1295
1296
0
#ifndef _WIN32
1297
0
  struct stat file_stat;
1298
0
  if (stat(filepath_.c_str(), &file_stat) != 0) {
1299
0
    if (emsg) {
1300
0
      *emsg = cmStrCat("Cannot get file stat: ", strerror(errno));
1301
0
    }
1302
0
    return false;
1303
0
  }
1304
1305
  // Nothing changed; everything is in the expected state
1306
0
  if (file_stat.st_mode == mode_) {
1307
0
    return true;
1308
0
  }
1309
1310
0
  if (chmod(filepath_.c_str(), mode_) != 0) {
1311
0
    if (emsg) {
1312
0
      *emsg = cmStrCat("Cannot restore the file mode: ", strerror(errno));
1313
0
    }
1314
0
    return false;
1315
0
  }
1316
#else
1317
  static_cast<void>(emsg);
1318
#endif
1319
1320
0
  return true;
1321
0
}
1322
1323
bool FileModeGuard::HasErrors() const
1324
0
{
1325
0
  return filepath_.empty();
1326
0
}
1327
1328
std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
1329
  std::string const& path, std::string* errorMessage)
1330
0
{
1331
#ifdef _WIN32
1332
  // uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found
1333
  std::string resolved_path;
1334
  uv_fs_t req;
1335
  int err = uv_fs_realpath(nullptr, &req, path.c_str(), nullptr);
1336
  if (!err) {
1337
    resolved_path = std::string((char*)req.ptr);
1338
    cmSystemTools::ConvertToUnixSlashes(resolved_path);
1339
  } else if (err == UV_ENOSYS) {
1340
    resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage);
1341
  } else if (errorMessage) {
1342
    cmsys::Status status =
1343
      cmsys::Status::Windows(uv_fs_get_system_error(&req));
1344
    *errorMessage = status.GetString();
1345
    resolved_path.clear();
1346
  } else {
1347
    resolved_path = path;
1348
  }
1349
  // Normalize to upper-case drive letter as cm::PathResolver does.
1350
  if (resolved_path.size() > 1 && resolved_path[1] == ':') {
1351
    resolved_path[0] = cmsysString_toupper(resolved_path[0]);
1352
  }
1353
  return resolved_path;
1354
#else
1355
0
  return cmsys::SystemTools::GetRealPath(path, errorMessage);
1356
0
#endif
1357
0
}
1358
1359
std::string cmSystemTools::GetRealPath(std::string const& path,
1360
                                       std::string* errorMessage)
1361
350
{
1362
#ifdef _WIN32
1363
  std::string resolved_path =
1364
    cmSystemTools::GetRealPathResolvingWindowsSubst(path, errorMessage);
1365
1366
  // If the original path used a subst drive and the real path starts
1367
  // with the substitution, restore the subst drive prefix.  This may
1368
  // incorrectly restore a subst drive if the underlying drive was
1369
  // encountered via an absolute symlink, but this is an acceptable
1370
  // limitation to otherwise preserve susbt drives.
1371
  if (resolved_path.size() >= 2 && resolved_path[1] == ':' &&
1372
      path.size() >= 2 && path[1] == ':' &&
1373
      cmsysString_toupper(resolved_path[0]) != cmsysString_toupper(path[0])) {
1374
    // FIXME: Add thread_local or mutex if we use threads.
1375
    static std::map<char, std::string> substMap;
1376
    char const drive = static_cast<char>(cmsysString_toupper(path[0]));
1377
    std::string maybe_subst = cmStrCat(drive, ":/");
1378
    auto smi = substMap.find(drive);
1379
    if (smi == substMap.end()) {
1380
      smi = substMap
1381
              .emplace(
1382
                drive,
1383
                cmSystemTools::GetRealPathResolvingWindowsSubst(maybe_subst))
1384
              .first;
1385
    }
1386
    std::string const& resolved_subst = smi->second;
1387
    std::string::size_type const ns = resolved_subst.size();
1388
    if (ns > 0) {
1389
      std::string::size_type const np = resolved_path.size();
1390
      if (ns == np && resolved_path == resolved_subst) {
1391
        resolved_path = maybe_subst;
1392
      } else if (ns > 0 && ns < np && resolved_path[ns] == '/' &&
1393
                 resolved_path.compare(0, ns, resolved_subst) == 0) {
1394
        resolved_path.replace(0, ns + 1, maybe_subst);
1395
      }
1396
    }
1397
  }
1398
1399
  return resolved_path;
1400
#else
1401
350
  return cmsys::SystemTools::GetRealPath(path, errorMessage);
1402
350
#endif
1403
350
}
1404
1405
#if defined(_WIN32)
1406
#  include <random>
1407
1408
#  include <wctype.h>
1409
#  ifdef _MSC_VER
1410
using mode_t = cmSystemTools::SystemTools::mode_t;
1411
#  endif
1412
#else
1413
#  include <sys/stat.h>
1414
#endif
1415
1416
inline int Mkdir(char const* dir, mode_t const* mode)
1417
0
{
1418
#if defined(_WIN32)
1419
  int ret = _wmkdir(cmSystemTools::ConvertToWindowsExtendedPath(dir).c_str());
1420
  if (ret == 0 && mode)
1421
    cmSystemTools::SystemTools::SetPermissions(dir, *mode);
1422
  return ret;
1423
#else
1424
0
  return mkdir(dir, mode ? *mode : 0777);
1425
0
#endif
1426
0
}
1427
1428
#ifdef CMAKE_NO_MKDTEMP
1429
namespace {
1430
char* cm_mkdtemp_fallback(char* template_)
1431
{
1432
  if (mktemp(template_) == nullptr || mkdir(template_, 0700) != 0) {
1433
    return nullptr;
1434
  }
1435
  return template_;
1436
}
1437
using cm_mkdtemp_t = char* (*)(char*);
1438
cm_mkdtemp_t const cm_mkdtemp = []() -> cm_mkdtemp_t {
1439
  cm_mkdtemp_t f = (cm_mkdtemp_t)dlsym(RTLD_DEFAULT, "mkdtemp");
1440
  dlerror(); // Ignore/cleanup dlsym errors.
1441
  if (!f) {
1442
    f = cm_mkdtemp_fallback;
1443
  }
1444
  return f;
1445
}();
1446
}
1447
#else
1448
0
#  define cm_mkdtemp mkdtemp
1449
#endif
1450
1451
cmsys::Status cmSystemTools::MakeTempDirectory(std::string& path,
1452
                                               mode_t const* mode)
1453
0
{
1454
0
  if (path.empty()) {
1455
0
    return cmsys::Status::POSIX(EINVAL);
1456
0
  }
1457
0
  return cmSystemTools::MakeTempDirectory(&path.front(), mode);
1458
0
}
1459
1460
cmsys::Status cmSystemTools::MakeTempDirectory(char* path, mode_t const* mode)
1461
0
{
1462
0
  if (!path) {
1463
0
    return cmsys::Status::POSIX(EINVAL);
1464
0
  }
1465
1466
  // verify that path ends with "XXXXXX"
1467
0
  auto const l = std::strlen(path);
1468
0
  if (!cmHasLiteralSuffix(cm::string_view{ path, l }, "XXXXXX")) {
1469
0
    return cmsys::Status::POSIX(EINVAL);
1470
0
  }
1471
1472
  // create parent directories
1473
0
  auto* sep = path;
1474
0
  while ((sep = strchr(sep, '/'))) {
1475
    // all underlying functions use C strings,
1476
    // so temporarily end the string here
1477
0
    *sep = '\0';
1478
0
    Mkdir(path, mode);
1479
1480
0
    *sep = '/';
1481
0
    ++sep;
1482
0
  }
1483
1484
#ifdef _WIN32
1485
  int const nchars = 36;
1486
  char const chars[nchars + 1] = "abcdefghijklmnopqrstuvwxyz0123456789";
1487
1488
  std::random_device rd;
1489
  std::mt19937 rg{ rd() };
1490
  std::uniform_int_distribution<int> dist{ 0, nchars - 1 };
1491
1492
  for (auto tries = 100; tries; --tries) {
1493
    for (auto n = l - 6; n < l; ++n) {
1494
      path[n] = chars[dist(rg)];
1495
    }
1496
    if (Mkdir(path, mode) == 0) {
1497
      return cmsys::Status::Success();
1498
    } else if (errno != EEXIST) {
1499
      return cmsys::Status::POSIX_errno();
1500
    }
1501
  }
1502
  return cmsys::Status::POSIX(EAGAIN);
1503
#else
1504
0
  if (cm_mkdtemp(path)) {
1505
0
    if (mode) {
1506
0
      chmod(path, *mode);
1507
0
    }
1508
0
  } else {
1509
0
    return cmsys::Status::POSIX_errno();
1510
0
  }
1511
0
  return cmsys::Status::Success();
1512
0
#endif
1513
0
}
1514
1515
#ifdef _WIN32
1516
namespace {
1517
bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname,
1518
                cmSystemTools::Replace replace)
1519
{
1520
  // Not only ignore any previous error, but clear any memory of it.
1521
  SetLastError(0);
1522
1523
  DWORD flags = 0;
1524
  if (replace == cmSystemTools::Replace::Yes) {
1525
    // Use MOVEFILE_REPLACE_EXISTING to replace an existing destination file.
1526
    flags = flags | MOVEFILE_REPLACE_EXISTING;
1527
  }
1528
1529
  return MoveFileExW(oldname.c_str(), newname.c_str(), flags);
1530
}
1531
}
1532
#endif
1533
1534
cmSystemTools::CopyResult cmSystemTools::CopySingleFile(
1535
  std::string const& oldname, std::string const& newname, CopyWhen when,
1536
  CopyInputRecent inputRecent, std::string* err)
1537
0
{
1538
0
  switch (when) {
1539
0
    case CopyWhen::Unconditional:
1540
0
      break;
1541
0
    case CopyWhen::OnlyIfDifferent:
1542
0
      if (!FilesDiffer(oldname, newname)) {
1543
0
        return CopyResult::Success;
1544
0
      }
1545
0
      break;
1546
0
    case CopyWhen::OnlyIfNewer: {
1547
0
      if (!SystemTools::FileExists(newname)) {
1548
0
        break;
1549
0
      }
1550
0
      int timeResult = 0;
1551
0
      cmsys::Status timeStatus =
1552
0
        cmsys::SystemTools::FileTimeCompare(oldname, newname, &timeResult);
1553
0
      if (timeStatus.IsSuccess() && timeResult <= 0) {
1554
0
        return CopyResult::Success;
1555
0
      }
1556
0
      break;
1557
0
    }
1558
0
  }
1559
1560
0
  mode_t perm = 0;
1561
0
  cmsys::Status perms = SystemTools::GetPermissions(oldname, perm);
1562
1563
  // If files are the same do not copy
1564
0
  if (SystemTools::SameFile(oldname, newname)) {
1565
0
    return CopyResult::Success;
1566
0
  }
1567
1568
0
  cmsys::SystemTools::CopyStatus status;
1569
0
  status = cmsys::SystemTools::CloneFileContent(oldname, newname);
1570
0
  if (!status) {
1571
    // if cloning did not succeed, fall back to blockwise copy
1572
#ifdef _WIN32
1573
    if (inputRecent == CopyInputRecent::Yes) {
1574
      // Windows sometimes locks a file immediately after creation.
1575
      // Retry a few times.
1576
      WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry();
1577
      while ((status =
1578
                cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname),
1579
              status.Path == cmsys::SystemTools::CopyStatus::SourcePath &&
1580
                status.GetPOSIX() == EACCES && --retry.Count)) {
1581
        cmSystemTools::Delay(retry.Delay);
1582
      }
1583
    } else {
1584
      status = cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname);
1585
    }
1586
#else
1587
0
    static_cast<void>(inputRecent);
1588
0
    status = cmsys::SystemTools::CopyFileContentBlockwise(oldname, newname);
1589
0
#endif
1590
0
  }
1591
0
  if (!status) {
1592
0
    if (err) {
1593
0
      *err = status.GetString();
1594
0
      switch (status.Path) {
1595
0
        case cmsys::SystemTools::CopyStatus::SourcePath:
1596
0
          *err = cmStrCat(*err, " (input)");
1597
0
          break;
1598
0
        case cmsys::SystemTools::CopyStatus::DestPath:
1599
0
          *err = cmStrCat(*err, " (output)");
1600
0
          break;
1601
0
        default:
1602
0
          break;
1603
0
      }
1604
0
    }
1605
0
    return CopyResult::Failure;
1606
0
  }
1607
0
  if (perms) {
1608
0
    perms = SystemTools::SetPermissions(newname, perm);
1609
0
    if (!perms) {
1610
0
      if (err) {
1611
0
        *err = cmStrCat(perms.GetString(), " (output)");
1612
0
      }
1613
0
      return CopyResult::Failure;
1614
0
    }
1615
0
  }
1616
0
  return CopyResult::Success;
1617
0
}
1618
1619
bool cmSystemTools::RenameFile(std::string const& oldname,
1620
                               std::string const& newname)
1621
0
{
1622
0
  return cmSystemTools::RenameFile(oldname, newname, Replace::Yes) ==
1623
0
    RenameResult::Success;
1624
0
}
1625
1626
cmSystemTools::RenameResult cmSystemTools::RenameFile(
1627
  std::string const& oldname, std::string const& newname, Replace replace,
1628
  std::string* err)
1629
0
{
1630
#ifdef _WIN32
1631
#  ifndef INVALID_FILE_ATTRIBUTES
1632
#    define INVALID_FILE_ATTRIBUTES ((DWORD) - 1)
1633
#  endif
1634
  std::wstring const oldname_wstr =
1635
    SystemTools::ConvertToWindowsExtendedPath(oldname);
1636
  std::wstring const newname_wstr =
1637
    SystemTools::ConvertToWindowsExtendedPath(newname);
1638
1639
  /* Windows MoveFileEx may not replace read-only or in-use files.  If it
1640
     fails then remove the read-only attribute from any existing destination.
1641
     Try multiple times since we may be racing against another process
1642
     creating/opening the destination file just before our MoveFileEx.  */
1643
  WindowsFileRetry retry = GetWindowsRetry(oldname_wstr);
1644
1645
  // Use RAII to set the attribute bit blocking Microsoft Search Indexing,
1646
  // and restore the previous value upon return.
1647
  SaveRestoreFileAttributes save_restore_file_attributes(
1648
    oldname_wstr, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
1649
1650
  DWORD move_last_error = 0;
1651
  while (!cmMoveFile(oldname_wstr, newname_wstr, replace) && --retry.Count) {
1652
    move_last_error = GetLastError();
1653
1654
    // There was no error ==> the operation is not yet complete.
1655
    if (move_last_error == NO_ERROR) {
1656
      break;
1657
    }
1658
1659
    // Try again only if failure was due to access/sharing permissions.
1660
    // Most often ERROR_ACCESS_DENIED (a.k.a. I/O error) for a directory, and
1661
    // ERROR_SHARING_VIOLATION for a file, are caused by one of the following:
1662
    // 1) Anti-Virus Software
1663
    // 2) Windows Search Indexer
1664
    // 3) Windows Explorer has an associated directory already opened.
1665
    if (move_last_error != ERROR_ACCESS_DENIED &&
1666
        move_last_error != ERROR_SHARING_VIOLATION) {
1667
      if (replace == Replace::No && move_last_error == ERROR_ALREADY_EXISTS) {
1668
        return RenameResult::NoReplace;
1669
      }
1670
      if (err) {
1671
        *err = cmsys::Status::Windows(move_last_error).GetString();
1672
      }
1673
      return RenameResult::Failure;
1674
    }
1675
1676
    DWORD const attrs = GetFileAttributesW(newname_wstr.c_str());
1677
    if ((attrs != INVALID_FILE_ATTRIBUTES) &&
1678
        (attrs & FILE_ATTRIBUTE_READONLY) &&
1679
        // FILE_ATTRIBUTE_READONLY is not honored on directories.
1680
        !(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
1681
      // Remove the read-only attribute from the destination file.
1682
      SetFileAttributesW(newname_wstr.c_str(),
1683
                         attrs & ~FILE_ATTRIBUTE_READONLY);
1684
    } else {
1685
      // The file may be temporarily in use so wait a bit.
1686
      cmSystemTools::Delay(retry.Delay);
1687
    }
1688
  }
1689
1690
  // If we were successful, then there was no error.
1691
  if (retry.Count > 0) {
1692
    move_last_error = 0;
1693
    // Restore the attributes on the new name.
1694
    save_restore_file_attributes.SetPath(newname_wstr);
1695
  }
1696
  SetLastError(move_last_error);
1697
  if (retry.Count > 0) {
1698
    return RenameResult::Success;
1699
  }
1700
  if (replace == Replace::No && GetLastError() == ERROR_ALREADY_EXISTS) {
1701
    return RenameResult::NoReplace;
1702
  }
1703
  if (err) {
1704
    *err = cmsys::Status::Windows_GetLastError().GetString();
1705
  }
1706
  return RenameResult::Failure;
1707
#else
1708
  // On UNIX we have OS-provided calls to create 'newname' atomically.
1709
0
  if (replace == Replace::No) {
1710
0
    if (link(oldname.c_str(), newname.c_str()) == 0) {
1711
0
      if (unlink(oldname.c_str()) == 0) {
1712
0
        return RenameResult::Success;
1713
0
      }
1714
0
      if (err) {
1715
0
        *err = cmsys::Status::POSIX_errno().GetString();
1716
0
      }
1717
0
      return RenameResult::Failure;
1718
0
    }
1719
0
    if (errno == EEXIST) {
1720
0
      return RenameResult::NoReplace;
1721
0
    }
1722
0
    if (err) {
1723
0
      *err = cmsys::Status::POSIX_errno().GetString();
1724
0
    }
1725
0
    return RenameResult::Failure;
1726
0
  }
1727
0
  if (rename(oldname.c_str(), newname.c_str()) == 0) {
1728
0
    return RenameResult::Success;
1729
0
  }
1730
0
  if (err) {
1731
0
    *err = cmsys::Status::POSIX_errno().GetString();
1732
0
  }
1733
0
  return RenameResult::Failure;
1734
0
#endif
1735
0
}
1736
1737
cmsys::Status cmSystemTools::MoveFileIfDifferent(
1738
  std::string const& source, std::string const& destination)
1739
0
{
1740
0
  cmsys::Status res = {};
1741
0
  if (FilesDiffer(source, destination)) {
1742
0
    if (RenameFile(source, destination)) {
1743
0
      return res;
1744
0
    }
1745
0
    res = CopyFileAlways(source, destination);
1746
0
  }
1747
0
  RemoveFile(source);
1748
0
  return res;
1749
0
}
1750
1751
bool cmSystemTools::FileHasContent(std::string const& path,
1752
                                   cm::string_view content)
1753
0
{
1754
  // Compare sizes first to avoid opening the file when they differ.
1755
  // A missing file has length zero and is caught by opening it below.
1756
0
  if (FileLength(path) != content.size()) {
1757
0
    return false;
1758
0
  }
1759
0
  cmsys::ifstream fin(path.c_str(), std::ios::in | std::ios::binary);
1760
0
  if (!fin) {
1761
0
    return false;
1762
0
  }
1763
0
  char buffer[4096];
1764
0
  while (!content.empty()) {
1765
0
    std::size_t const n = std::min(content.size(), sizeof(buffer));
1766
0
    if (!fin.read(buffer, static_cast<std::streamsize>(n)) ||
1767
0
        content.compare(0, n, cm::string_view(buffer, n)) != 0) {
1768
0
      return false;
1769
0
    }
1770
0
    content.remove_prefix(n);
1771
0
  }
1772
0
  return true;
1773
0
}
1774
1775
void cmSystemTools::Glob(std::string const& directory,
1776
                         std::string const& regexp,
1777
                         std::vector<std::string>& files)
1778
0
{
1779
0
  cmsys::Directory d;
1780
0
  cmsys::RegularExpression reg(regexp.c_str());
1781
1782
0
  if (d.Load(directory)) {
1783
0
    size_t numf;
1784
0
    unsigned int i;
1785
0
    numf = d.GetNumberOfFiles();
1786
0
    for (i = 0; i < numf; i++) {
1787
0
      std::string const& fname = d.GetFileName(i);
1788
0
      if (reg.find(fname)) {
1789
0
        files.push_back(fname);
1790
0
      }
1791
0
    }
1792
0
  }
1793
0
}
1794
1795
void cmSystemTools::GlobDirs(std::string const& path,
1796
                             std::vector<std::string>& files)
1797
0
{
1798
0
  std::string::size_type pos = path.find("/*");
1799
0
  if (pos == std::string::npos) {
1800
0
    files.push_back(path);
1801
0
    return;
1802
0
  }
1803
0
  std::string startPath = path.substr(0, pos);
1804
0
  std::string finishPath = path.substr(pos + 2);
1805
1806
0
  cmsys::Directory d;
1807
0
  if (d.Load(startPath)) {
1808
0
    for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
1809
0
      std::string const& f = d.GetFileName(i);
1810
0
      if (f != "." && f != "..") {
1811
0
        std::string fname = cmStrCat(startPath, '/', f);
1812
0
        if (cmSystemTools::FileIsDirectory(fname)) {
1813
0
          fname += finishPath;
1814
0
          cmSystemTools::GlobDirs(fname, files);
1815
0
        }
1816
0
      }
1817
0
    }
1818
0
  }
1819
0
}
1820
1821
bool cmSystemTools::SimpleGlob(std::string const& glob,
1822
                               std::vector<std::string>& files,
1823
                               int type /* = 0 */)
1824
0
{
1825
0
  files.clear();
1826
0
  if (glob.back() != '*') {
1827
0
    return false;
1828
0
  }
1829
0
  std::string path = cmSystemTools::GetFilenamePath(glob);
1830
0
  std::string ppath = cmSystemTools::GetFilenameName(glob);
1831
0
  ppath.pop_back();
1832
0
  if (path.empty()) {
1833
0
    path = "/";
1834
0
  }
1835
1836
0
  bool res = false;
1837
0
  cmsys::Directory d;
1838
0
  if (d.Load(path)) {
1839
0
    for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i) {
1840
0
      std::string const& sfname = d.GetFileName(i);
1841
0
      if (sfname != "." && sfname != "..") {
1842
0
        std::string fname = path;
1843
0
        if (path.back() != '/') {
1844
0
          fname += "/";
1845
0
        }
1846
0
        fname += sfname;
1847
0
        if (type > 0 && cmSystemTools::FileIsDirectory(fname)) {
1848
0
          continue;
1849
0
        }
1850
0
        if (type < 0 && !cmSystemTools::FileIsDirectory(fname)) {
1851
0
          continue;
1852
0
        }
1853
0
        if (cmHasPrefix(sfname, ppath)) {
1854
0
          files.push_back(fname);
1855
0
          res = true;
1856
0
        }
1857
0
      }
1858
0
    }
1859
0
  }
1860
0
  return res;
1861
0
}
1862
1863
std::string cmSystemTools::ConvertToOutputPath(std::string const& path)
1864
0
{
1865
#if defined(_WIN32) && !defined(__CYGWIN__)
1866
  if (s_ForceUnixPaths) {
1867
    return cmSystemTools::ConvertToUnixOutputPath(path);
1868
  }
1869
  return cmSystemTools::ConvertToWindowsOutputPath(path);
1870
#else
1871
0
  return cmSystemTools::ConvertToUnixOutputPath(path);
1872
0
#endif
1873
0
}
1874
1875
void cmSystemTools::ConvertToOutputSlashes(std::string& path)
1876
0
{
1877
#if defined(_WIN32) && !defined(__CYGWIN__)
1878
  if (!s_ForceUnixPaths) {
1879
    // Convert to windows slashes.
1880
    std::string::size_type pos = 0;
1881
    while ((pos = path.find('/', pos)) != std::string::npos) {
1882
      path[pos++] = '\\';
1883
    }
1884
  }
1885
#else
1886
0
  static_cast<void>(path);
1887
0
#endif
1888
0
}
1889
1890
void cmSystemTools::ConvertToLongPath(std::string& path)
1891
220k
{
1892
#if defined(_WIN32) && !defined(__CYGWIN__)
1893
  // Try to convert path to a long path only if the path contains character '~'
1894
  if (path.find('~') == std::string::npos) {
1895
    return;
1896
  }
1897
1898
  std::wstring wPath = cmsys::Encoding::ToWide(path);
1899
  DWORD ret = GetLongPathNameW(wPath.c_str(), nullptr, 0);
1900
  std::vector<wchar_t> buffer(ret);
1901
  if (ret != 0) {
1902
    ret = GetLongPathNameW(wPath.c_str(), buffer.data(),
1903
                           static_cast<DWORD>(buffer.size()));
1904
  }
1905
1906
  if (ret != 0) {
1907
    path = cmsys::Encoding::ToNarrow(buffer.data());
1908
  }
1909
#else
1910
220k
  static_cast<void>(path);
1911
220k
#endif
1912
220k
}
1913
1914
std::string cmSystemTools::ConvertToRunCommandPath(std::string const& path)
1915
0
{
1916
#if defined(_WIN32) && !defined(__CYGWIN__)
1917
  return cmSystemTools::ConvertToWindowsOutputPath(path);
1918
#else
1919
0
  return cmSystemTools::ConvertToUnixOutputPath(path);
1920
0
#endif
1921
0
}
1922
1923
// compute the relative path from here to there
1924
std::string cmSystemTools::RelativePath(std::string const& local,
1925
                                        std::string const& remote)
1926
0
{
1927
0
  if (!cmSystemTools::FileIsFullPath(local)) {
1928
0
    cmSystemTools::Error("RelativePath must be passed a full path to local: " +
1929
0
                         local);
1930
0
  }
1931
0
  if (!cmSystemTools::FileIsFullPath(remote)) {
1932
0
    cmSystemTools::Error(
1933
0
      "RelativePath must be passed a full path to remote: " + remote);
1934
0
  }
1935
0
  return cmsys::SystemTools::RelativePath(local, remote);
1936
0
}
1937
1938
std::string cmSystemTools::ForceToRelativePath(std::string const& local_path,
1939
                                               std::string const& remote_path)
1940
0
{
1941
  // The paths should never be quoted.
1942
0
  assert(local_path.front() != '\"');
1943
0
  assert(remote_path.front() != '\"');
1944
1945
  // The local path should never have a trailing slash except if it is just the
1946
  // bare root directory
1947
0
  assert(local_path.empty() || local_path.back() != '/' ||
1948
0
         local_path.size() == 1 ||
1949
0
         (local_path.size() == 3 && local_path[1] == ':' &&
1950
0
          ((local_path[0] >= 'A' && local_path[0] <= 'Z') ||
1951
0
           (local_path[0] >= 'a' && local_path[0] <= 'z'))));
1952
1953
  // If the path is already relative then just return the path.
1954
0
  if (!cmSystemTools::FileIsFullPath(remote_path)) {
1955
0
    return remote_path;
1956
0
  }
1957
1958
  // Identify the longest shared path component between the remote
1959
  // path and the local path.
1960
0
  std::vector<std::string> local;
1961
0
  cmSystemTools::SplitPath(local_path, local);
1962
0
  std::vector<std::string> remote;
1963
0
  cmSystemTools::SplitPath(remote_path, remote);
1964
0
  unsigned int common = 0;
1965
0
  while (common < remote.size() && common < local.size() &&
1966
0
         cmSystemTools::ComparePath(remote[common], local[common])) {
1967
0
    ++common;
1968
0
  }
1969
1970
  // If no part of the path is in common then return the full path.
1971
0
  if (common == 0) {
1972
0
    return remote_path;
1973
0
  }
1974
1975
  // If the entire path is in common then just return a ".".
1976
0
  if (common == remote.size() && common == local.size()) {
1977
0
    return ".";
1978
0
  }
1979
1980
  // If the entire path is in common except for a trailing slash then
1981
  // just return a "./".
1982
0
  if (common + 1 == remote.size() && remote[common].empty() &&
1983
0
      common == local.size()) {
1984
0
    return "./";
1985
0
  }
1986
1987
  // Construct the relative path.
1988
0
  std::string relative;
1989
1990
  // First add enough ../ to get up to the level of the shared portion
1991
  // of the path.  Leave off the trailing slash.  Note that the last
1992
  // component of local will never be empty because local should never
1993
  // have a trailing slash.
1994
0
  for (unsigned int i = common; i < local.size(); ++i) {
1995
0
    relative += "..";
1996
0
    if (i < local.size() - 1) {
1997
0
      relative += "/";
1998
0
    }
1999
0
  }
2000
2001
  // Now add the portion of the destination path that is not included
2002
  // in the shared portion of the path.  Add a slash the first time
2003
  // only if there was already something in the path.  If there was a
2004
  // trailing slash in the input then the last iteration of the loop
2005
  // will add a slash followed by an empty string which will preserve
2006
  // the trailing slash in the output.
2007
2008
0
  if (!relative.empty() && !remote.empty()) {
2009
0
    relative += "/";
2010
0
  }
2011
0
  relative += cmJoin(cmMakeRange(remote).advance(common), "/");
2012
2013
  // Finally return the path.
2014
0
  return relative;
2015
0
}
2016
2017
std::string cmSystemTools::RelativeIfUnder(std::string const& top,
2018
                                           std::string const& in)
2019
9.41k
{
2020
9.41k
  std::string out;
2021
9.41k
  if (in == top) {
2022
0
    out = ".";
2023
9.41k
  } else if (cmSystemTools::IsSubDirectory(in, top)) {
2024
9.41k
    out = in.substr(top.size() + (top.back() == '/' ? 0 : 1));
2025
9.41k
  } else {
2026
1
    out = in;
2027
1
  }
2028
9.41k
  return out;
2029
9.41k
}
2030
2031
std::string cmSystemTools::GetActualCaseForPath(
2032
  std::string const& p, std::unordered_map<std::string, std::string>* cache)
2033
0
{
2034
#ifdef _WIN32
2035
  std::string casePath;
2036
2037
  // First check if the file is relative. We don't fix relative paths since the
2038
  // real case depends on the root directory and the given path fragment may
2039
  // have meaning elsewhere in the project.
2040
  if (!cmsys::SystemTools::FileIsFullPath(p)) {
2041
    // This looks unnecessary, but it allows for the return value optimization
2042
    // since all return paths return the same local variable.
2043
    casePath = p;
2044
    return casePath;
2045
  }
2046
2047
  std::vector<std::string> path_components;
2048
  cmsys::SystemTools::SplitPath(p, path_components);
2049
2050
  // Start with root component.
2051
  std::vector<std::string>::size_type idx = 0;
2052
  casePath = path_components[idx++];
2053
  // make sure drive letter is always upper case
2054
  if (casePath.size() > 1 && casePath[1] == ':') {
2055
    casePath[0] = cmsysString_toupper(casePath[0]);
2056
  }
2057
  char const* sep = "";
2058
2059
  // If network path, fill casePath with server/share so FindFirstFile
2060
  // will work after that.  Maybe someday call other APIs to get
2061
  // actual case of servers and shares.
2062
  if (path_components.size() > 2 && path_components[0] == "//") {
2063
    casePath += path_components[idx++];
2064
    casePath += '/';
2065
    casePath += path_components[idx++];
2066
    sep = "/";
2067
  }
2068
2069
  // Convert case of all components that exist.
2070
  bool converting = true;
2071
  for (; idx < path_components.size(); idx++) {
2072
    casePath += sep;
2073
    sep = "/";
2074
2075
    if (converting) {
2076
      // If path component contains wildcards, we skip matching
2077
      // because these filenames are not allowed on windows,
2078
      // and we do not want to match a different file.
2079
      if (path_components[idx].find('*') != std::string::npos ||
2080
          path_components[idx].find('?') != std::string::npos) {
2081
        converting = false;
2082
      } else {
2083
        std::string test_str = casePath;
2084
        test_str += path_components[idx];
2085
2086
        // Only components found to exist are cached, so that a path
2087
        // created later is still converted.
2088
        std::string const* cached = nullptr;
2089
        if (cache) {
2090
          auto it = cache->find(test_str);
2091
          if (it != cache->end()) {
2092
            cached = &it->second;
2093
          }
2094
        }
2095
        if (cached) {
2096
          path_components[idx] = *cached;
2097
        } else {
2098
          WIN32_FIND_DATAW findData;
2099
          HANDLE hFind = ::FindFirstFileW(
2100
            cmsys::Encoding::ToWide(test_str).c_str(), &findData);
2101
          if (INVALID_HANDLE_VALUE != hFind) {
2102
            auto case_file_name =
2103
              cmsys::Encoding::ToNarrow(findData.cFileName);
2104
            ::FindClose(hFind);
2105
            if (cache) {
2106
              cache->emplace(std::move(test_str), case_file_name);
2107
            }
2108
            path_components[idx] = std::move(case_file_name);
2109
          } else {
2110
            converting = false;
2111
          }
2112
        }
2113
      }
2114
    }
2115
2116
    casePath += path_components[idx];
2117
  }
2118
  return casePath;
2119
#else
2120
0
  static_cast<void>(cache);
2121
0
  return p;
2122
0
#endif
2123
0
}
2124
2125
cm::optional<std::string> cmSystemTools::GetEnvVar(std::string const& var)
2126
555k
{
2127
555k
  cm::optional<std::string> result;
2128
555k
  {
2129
555k
    std::string value;
2130
555k
    if (cmSystemTools::GetEnv(var, value)) {
2131
48.3k
      result = std::move(value);
2132
48.3k
    }
2133
555k
  }
2134
555k
  return result;
2135
555k
}
2136
2137
std::vector<std::string> cmSystemTools::GetEnvPathNormalized(
2138
  std::string const& var)
2139
0
{
2140
0
  std::vector<std::string> result;
2141
0
  if (cm::optional<std::string> env = cmSystemTools::GetEnvVar(var)) {
2142
0
    std::vector<std::string> p = cmSystemTools::SplitEnvPathNormalized(*env);
2143
0
    std::move(p.begin(), p.end(), std::back_inserter(result));
2144
0
  }
2145
0
  return result;
2146
0
}
2147
2148
std::vector<std::string> cmSystemTools::SplitEnvPath(cm::string_view in)
2149
0
{
2150
#if defined(_WIN32) && !defined(__CYGWIN__)
2151
  static cm::string_view sep = ";"_s;
2152
#else
2153
0
  static cm::string_view sep = ":"_s;
2154
0
#endif
2155
0
  std::vector<std::string> paths;
2156
0
  cm::string_view::size_type e = 0;
2157
0
  for (;;) {
2158
0
    cm::string_view::size_type b = in.find_first_not_of(sep, e);
2159
0
    if (b == cm::string_view::npos) {
2160
0
      break;
2161
0
    }
2162
0
    e = in.find_first_of(sep, b);
2163
0
    if (e == cm::string_view::npos) {
2164
0
      paths.emplace_back(in.substr(b));
2165
0
      break;
2166
0
    }
2167
0
    paths.emplace_back(in.substr(b, e - b));
2168
0
  }
2169
0
  return paths;
2170
0
}
2171
2172
std::vector<std::string> cmSystemTools::SplitEnvPathNormalized(
2173
  cm::string_view in)
2174
0
{
2175
0
  std::vector<std::string> paths = cmSystemTools::SplitEnvPath(in);
2176
0
  std::transform(paths.begin(), paths.end(), paths.begin(),
2177
0
                 cmSystemTools::ToNormalizedPathOnDisk);
2178
0
  return paths;
2179
0
}
2180
2181
std::string cmSystemTools::ToNormalizedPathOnDisk(std::string p)
2182
19
{
2183
19
  p = ResolveTildePath(p);
2184
19
  using namespace cm::PathResolver;
2185
#ifdef _WIN32
2186
  // IWYU pragma: no_forward_declare cm::PathResolver::Policies::CasePath
2187
  static Resolver<Policies::CasePath> const resolver(RealOS);
2188
#else
2189
  // IWYU pragma: no_forward_declare cm::PathResolver::Policies::LogicalPath
2190
19
  static Resolver<Policies::LogicalPath> const resolver(RealOS);
2191
19
#endif
2192
19
  resolver.Resolve(std::move(p), p);
2193
#ifdef __clang_analyzer__ /* cplusplus.Move */
2194
  p.clear();
2195
#endif
2196
19
  return p;
2197
19
}
2198
2199
#ifndef CMAKE_BOOTSTRAP
2200
bool cmSystemTools::UnsetEnv(char const* value)
2201
0
{
2202
#  if !defined(HAVE_UNSETENV)
2203
  return cmSystemTools::UnPutEnv(value);
2204
#  else
2205
0
  unsetenv(value);
2206
0
  return true;
2207
0
#  endif
2208
0
}
2209
2210
std::vector<std::string> cmSystemTools::GetEnvironmentVariables()
2211
0
{
2212
0
  std::vector<std::string> env;
2213
#  ifdef _WIN32
2214
  struct EnvDeleter
2215
  {
2216
    void operator()(wchar_t* p) const { FreeEnvironmentStringsW(p); }
2217
  };
2218
2219
  auto block = std::unique_ptr<wchar_t, EnvDeleter>(GetEnvironmentStringsW());
2220
  if (!block) {
2221
    return env;
2222
  }
2223
2224
  for (wchar_t const* p = block.get(); *p; p += wcslen(p) + 1) {
2225
    if (p[0] != L'=') {
2226
      env.emplace_back(cmsys::Encoding::ToNarrow(p));
2227
    }
2228
  }
2229
#  else
2230
0
  for (int cc = 0; environ[cc]; ++cc) {
2231
0
    env.emplace_back(environ[cc]);
2232
0
  }
2233
0
#  endif
2234
0
  return env;
2235
0
}
2236
2237
cmSystemTools::SaveRestoreEnvironment::SaveRestoreEnvironment()
2238
0
{
2239
0
  this->Env = cmSystemTools::GetEnvironmentVariables();
2240
0
}
2241
2242
cmSystemTools::SaveRestoreEnvironment::~SaveRestoreEnvironment()
2243
0
{
2244
  // First clear everything in the current environment:
2245
0
  std::vector<std::string> currentEnv = GetEnvironmentVariables();
2246
0
  for (std::string var : currentEnv) {
2247
0
    std::string::size_type pos = var.find('=');
2248
0
    if (pos != std::string::npos) {
2249
0
      var.resize(pos);
2250
0
    }
2251
2252
0
    cmSystemTools::UnsetEnv(var.c_str());
2253
0
  }
2254
2255
  // Then put back each entry from the original environment:
2256
0
  for (std::string const& var : this->Env) {
2257
0
    cmSystemTools::PutEnv(var);
2258
0
  }
2259
0
}
2260
#endif
2261
2262
cmSystemTools::ScopedEnv::ScopedEnv(cm::string_view var)
2263
0
{
2264
0
  std::string::size_type pos = var.find('=');
2265
0
  if (pos != std::string::npos) {
2266
0
    this->Key = std::string{ var.substr(0, pos) };
2267
0
    this->Original = cmSystemTools::GetEnvVar(this->Key);
2268
2269
0
    cm::string_view value = var.substr(pos + 1);
2270
2271
0
    if (!this->Original && value.empty()) {
2272
      // nothing to do if the environment variable wasn't already set and the
2273
      // new value is also empty. clear the Key member so the destructor also
2274
      // does nothing.
2275
0
      this->Key.clear();
2276
0
    } else {
2277
0
      if (value.empty()) {
2278
0
        cmSystemTools::UnPutEnv(this->Key);
2279
0
      } else {
2280
0
        cmSystemTools::PutEnv(cmStrCat(this->Key, '=', value));
2281
0
      }
2282
0
    }
2283
0
  }
2284
0
}
2285
2286
cmSystemTools::ScopedEnv::~ScopedEnv()
2287
0
{
2288
0
  if (!this->Key.empty()) {
2289
0
    if (this->Original) {
2290
0
      cmSystemTools::PutEnv(cmStrCat(this->Key, '=', *this->Original));
2291
0
    } else {
2292
0
      cmSystemTools::UnPutEnv(Key);
2293
0
    }
2294
0
  }
2295
0
}
2296
2297
void cmSystemTools::EnableVSConsoleOutput()
2298
19.9k
{
2299
#ifdef _WIN32
2300
  // Visual Studio tools like devenv may not
2301
  // display output to the console unless this environment variable is
2302
  // set.  We need it to capture the output of these build tools.
2303
  // Note for future work that one could pass "/out \\.\pipe\NAME" to
2304
  // either of these executables where NAME is created with
2305
  // CreateNamedPipe.  This would bypass the internal buffering of the
2306
  // output and allow it to be captured on the fly.
2307
  cmSystemTools::PutEnv("vsconsoleoutput=1");
2308
2309
#  ifndef CMAKE_BOOTSTRAP
2310
  // VS sets an environment variable to tell MS tools like "cl" to report
2311
  // output through a backdoor pipe instead of stdout/stderr.  Unset the
2312
  // environment variable to close this backdoor for any path of process
2313
  // invocations that passes through CMake so we can capture the output.
2314
  cmSystemTools::UnsetEnv("VS_UNICODE_OUTPUT");
2315
#  endif
2316
#endif
2317
19.9k
}
2318
2319
bool cmSystemTools::IsPathToFramework(std::string const& path)
2320
0
{
2321
0
  return (cmSystemTools::FileIsFullPath(path) &&
2322
0
          cmHasLiteralSuffix(path, ".framework"));
2323
0
}
2324
2325
bool cmSystemTools::IsPathToXcFramework(std::string const& path)
2326
0
{
2327
0
  return (cmSystemTools::FileIsFullPath(path) &&
2328
0
          cmHasLiteralSuffix(path, ".xcframework"));
2329
0
}
2330
2331
bool cmSystemTools::IsPathToMacOSSharedLibrary(std::string const& path)
2332
0
{
2333
0
  return (cmSystemTools::FileIsFullPath(path) &&
2334
0
          cmHasLiteralSuffix(path, ".dylib"));
2335
0
}
2336
2337
bool cmSystemTools::CreateTar(
2338
  std::string const& arFileName, std::vector<std::string> const& files,
2339
  std::vector<std::string> const& excludeFiles,
2340
  std::string const& workingDirectory, cmTarCompression compressType,
2341
  std::string const& encoding, bool verbose, std::string const& mtime,
2342
  std::string const& format, int compressionLevel, int numThreads)
2343
0
{
2344
0
#if !defined(CMAKE_BOOTSTRAP)
2345
0
  cmWorkingDirectory workdir(cmSystemTools::GetLogicalWorkingDirectory());
2346
0
  if (!workingDirectory.empty()) {
2347
0
    workdir.SetDirectory(workingDirectory);
2348
0
  }
2349
2350
0
  std::string const cwd = cmSystemTools::GetLogicalWorkingDirectory();
2351
0
  cmsys::ofstream fout(arFileName.c_str(), std::ios::out | std::ios::binary);
2352
0
  if (!fout) {
2353
0
    std::string e = cmStrCat("Cannot open output file \"", arFileName,
2354
0
                             "\": ", cmSystemTools::GetLastSystemError());
2355
0
    cmSystemTools::Error(e);
2356
0
    return false;
2357
0
  }
2358
0
  cmArchiveWrite::Compress compress = cmArchiveWrite::CompressNone;
2359
0
  switch (compressType) {
2360
0
    case TarCompressGZip:
2361
0
      compress = cmArchiveWrite::CompressGZip;
2362
0
      break;
2363
0
    case TarCompressBZip2:
2364
0
      compress = cmArchiveWrite::CompressBZip2;
2365
0
      break;
2366
0
    case TarCompressXZ:
2367
0
      compress = cmArchiveWrite::CompressXZ;
2368
0
      break;
2369
0
    case TarCompressZstd:
2370
0
      compress = cmArchiveWrite::CompressZstd;
2371
0
      break;
2372
0
    case TarCompressLZMA:
2373
0
      compress = cmArchiveWrite::CompressLZMA;
2374
0
      break;
2375
0
    case TarCompressPPMd:
2376
0
      compress = cmArchiveWrite::CompressPPMd;
2377
0
      break;
2378
0
    case TarCompressAuto:
2379
      // Kept for backwards compatibility with pre-4.3 versions of CMake
2380
0
      if (format == "zip") {
2381
0
        compress = cmArchiveWrite::CompressGZip;
2382
0
      } else if (format == "7zip") {
2383
0
        compress = cmArchiveWrite::CompressLZMA;
2384
0
      } else {
2385
0
        compress = cmArchiveWrite::CompressNone;
2386
0
      }
2387
0
      break;
2388
0
    case TarCompressNone:
2389
0
      compress = cmArchiveWrite::CompressNone;
2390
0
      break;
2391
0
  }
2392
2393
0
  cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format, encoding,
2394
0
                   compressionLevel, numThreads);
2395
2396
0
  if (!a.Open()) {
2397
0
    cmSystemTools::Error(a.GetError());
2398
0
    return false;
2399
0
  }
2400
0
  a.SetMTime(mtime);
2401
0
  a.SetVerbose(verbose);
2402
0
  if (!a.SetExcludePatterns(excludeFiles)) {
2403
0
    cmSystemTools::Error(a.GetError());
2404
0
    return false;
2405
0
  }
2406
0
  bool tarCreatedSuccessfully = true;
2407
0
  for (auto path : files) {
2408
0
    if (cmSystemTools::FileIsFullPath(path)) {
2409
      // Get the relative path to the file.
2410
0
      path = cmSystemTools::RelativePath(cwd, path);
2411
0
    }
2412
0
    if (!a.Add(path)) {
2413
0
      cmSystemTools::Error(a.GetError());
2414
0
      tarCreatedSuccessfully = false;
2415
0
    }
2416
0
  }
2417
0
  return tarCreatedSuccessfully;
2418
#else
2419
  (void)arFileName;
2420
  (void)files;
2421
  (void)excludeFiles;
2422
  (void)encoding;
2423
  (void)verbose;
2424
  return false;
2425
#endif
2426
0
}
2427
2428
#if !defined(CMAKE_BOOTSTRAP)
2429
namespace {
2430
0
#  define BSDTAR_FILESIZE_PRINTF "%lu"
2431
#  define BSDTAR_FILESIZE_TYPE unsigned long
2432
void list_item_verbose(FILE* out, struct archive_entry* entry)
2433
0
{
2434
0
  char tmp[100];
2435
0
  size_t w;
2436
0
  char const* p;
2437
0
  char const* fmt;
2438
0
  time_t tim;
2439
0
  static time_t now;
2440
0
  size_t u_width = 6;
2441
0
  size_t gs_width = 13;
2442
2443
  /*
2444
   * We avoid collecting the entire list in memory at once by
2445
   * listing things as we see them.  However, that also means we can't
2446
   * just pre-compute the field widths.  Instead, we start with guesses
2447
   * and just widen them as necessary.  These numbers are completely
2448
   * arbitrary.
2449
   */
2450
0
  if (!now) {
2451
0
    time(&now);
2452
0
  }
2453
0
  fprintf(out, "%s %u ", archive_entry_strmode(entry),
2454
0
          archive_entry_nlink(entry));
2455
2456
  /* Use uname if it's present, else uid. */
2457
0
  p = archive_entry_uname(entry);
2458
0
  if (!p || (*p == '\0')) {
2459
0
    snprintf(tmp, sizeof(tmp), "%lu ",
2460
0
             static_cast<unsigned long>(archive_entry_uid(entry)));
2461
0
    p = tmp;
2462
0
  }
2463
0
  w = strlen(p);
2464
0
  if (w > u_width) {
2465
0
    u_width = w;
2466
0
  }
2467
0
  fprintf(out, "%-*s ", static_cast<int>(u_width), p);
2468
  /* Use gname if it's present, else gid. */
2469
0
  p = archive_entry_gname(entry);
2470
0
  if (p && p[0] != '\0') {
2471
0
    fprintf(out, "%s", p);
2472
0
    w = strlen(p);
2473
0
  } else {
2474
0
    snprintf(tmp, sizeof(tmp), "%lu",
2475
0
             static_cast<unsigned long>(archive_entry_gid(entry)));
2476
0
    w = strlen(tmp);
2477
0
    fprintf(out, "%s", tmp);
2478
0
  }
2479
2480
  /*
2481
   * Print device number or file size, right-aligned so as to make
2482
   * total width of group and devnum/filesize fields be gs_width.
2483
   * If gs_width is too small, grow it.
2484
   */
2485
0
  if (archive_entry_filetype(entry) == AE_IFCHR ||
2486
0
      archive_entry_filetype(entry) == AE_IFBLK) {
2487
0
    unsigned long rdevmajor = archive_entry_rdevmajor(entry);
2488
0
    unsigned long rdevminor = archive_entry_rdevminor(entry);
2489
0
    snprintf(tmp, sizeof(tmp), "%lu,%lu", rdevmajor, rdevminor);
2490
0
  } else {
2491
    /*
2492
     * Note the use of platform-dependent macros to format
2493
     * the filesize here.  We need the format string and the
2494
     * corresponding type for the cast.
2495
     */
2496
0
    snprintf(tmp, sizeof(tmp), BSDTAR_FILESIZE_PRINTF,
2497
0
             static_cast<BSDTAR_FILESIZE_TYPE>(archive_entry_size(entry)));
2498
0
  }
2499
0
  if (w + strlen(tmp) >= gs_width) {
2500
0
    gs_width = w + strlen(tmp) + 1;
2501
0
  }
2502
0
  fprintf(out, "%*s", static_cast<int>(gs_width - w), tmp);
2503
2504
  /* Format the time using 'ls -l' conventions. */
2505
0
  tim = archive_entry_mtime(entry);
2506
0
#  define HALF_YEAR ((time_t)365 * 86400 / 2)
2507
#  if defined(_WIN32) && !defined(__CYGWIN__)
2508
/* Windows' strftime function does not support %e format. */
2509
#    define DAY_FMT "%d"
2510
#  else
2511
0
#    define DAY_FMT "%e" /* Day number without leading zeros */
2512
0
#  endif
2513
0
  if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) {
2514
0
    fmt = DAY_FMT " %b  %Y";
2515
0
  } else {
2516
0
    fmt = DAY_FMT " %b %H:%M";
2517
0
  }
2518
0
  strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
2519
0
  fprintf(out, " %s ", tmp);
2520
0
  fprintf(out, "%s", cm_archive_entry_pathname(entry));
2521
2522
  /* Extra information for links. */
2523
0
  if (archive_entry_hardlink(entry)) /* Hard link */
2524
0
  {
2525
0
    fprintf(out, " link to %s", archive_entry_hardlink(entry));
2526
0
  } else if (archive_entry_symlink(entry)) /* Symbolic link */
2527
0
  {
2528
0
    fprintf(out, " -> %s", archive_entry_symlink(entry));
2529
0
  }
2530
0
  fflush(out);
2531
0
}
2532
2533
void ArchiveError(char const* m1, struct archive* a)
2534
37.7k
{
2535
37.7k
  std::string message(m1);
2536
37.7k
  char const* m2 = archive_error_string(a);
2537
37.7k
  if (m2) {
2538
35.2k
    message += m2;
2539
35.2k
  }
2540
37.7k
  cmSystemTools::Error(message);
2541
37.7k
}
2542
2543
// Return 'true' if the return value 'r' from a libarchive function indicates
2544
// success or a warning that can be ignored.  Return 'false' if it indicates an
2545
// error
2546
bool la_diagnostic(struct archive* ar, __LA_SSIZE_T r)
2547
65.5k
{
2548
  // See archive.h definition of ARCHIVE_OK for return values.
2549
2550
65.5k
  if (r >= ARCHIVE_OK) {
2551
58.4k
    return true;
2552
58.4k
  }
2553
2554
7.11k
  if (r >= ARCHIVE_WARN) {
2555
1.18k
    if (archive_errno(ar) == ENOSPC) {
2556
      // If we fall through to the generic error handling, the error message
2557
      // will be "Write failed". Explicit handling for better diagnostics
2558
0
      std::cerr << "cmake -E tar: error: No space left on device\n";
2559
0
      return false;
2560
0
    }
2561
1.18k
    char const* warn = archive_error_string(ar);
2562
1.18k
    if (!warn) {
2563
0
      warn = "unknown warning";
2564
0
    }
2565
1.18k
    std::cerr << "cmake -E tar: warning: " << warn << '\n';
2566
1.18k
    return true;
2567
1.18k
  }
2568
2569
  // Error.
2570
5.93k
  char const* err = archive_error_string(ar);
2571
5.93k
  if (!err) {
2572
201
    err = "unknown error";
2573
201
  }
2574
5.93k
  std::cerr << "cmake -E tar: error: " << err << '\n';
2575
5.93k
  return false;
2576
7.11k
}
2577
2578
// Return 'true' on success
2579
bool copy_data(struct archive* ar, struct archive* aw)
2580
47.3k
{
2581
47.3k
  long r;
2582
47.3k
  void const* buff;
2583
47.3k
  size_t size;
2584
47.3k
#  if defined(ARCHIVE_VERSION_NUMBER) && ARCHIVE_VERSION_NUMBER >= 3000000
2585
47.3k
  __LA_INT64_T offset;
2586
#  else
2587
  off_t offset;
2588
#  endif
2589
2590
77.1k
  for (;;) {
2591
    // See archive.h definition of ARCHIVE_OK for return values.
2592
77.1k
    r = archive_read_data_block(ar, &buff, &size, &offset);
2593
77.1k
    if (r == ARCHIVE_EOF) {
2594
41.4k
      return true;
2595
41.4k
    }
2596
35.7k
    if (!la_diagnostic(ar, r)) {
2597
5.93k
      return false;
2598
5.93k
    }
2599
    // See archive.h definition of ARCHIVE_OK for return values.
2600
35.7k
    __LA_SSIZE_T const w = archive_write_data_block(aw, buff, size, offset);
2601
29.8k
    if (!la_diagnostic(aw, w)) {
2602
0
      return false;
2603
0
    }
2604
29.8k
  }
2605
#  if !defined(__clang__) && !defined(__NVCOMPILER) && !defined(__HP_aCC)
2606
  return false; /* this should not happen but it quiets some compilers */
2607
#  endif
2608
47.3k
}
2609
2610
struct ArchiveReadDeleter
2611
{
2612
45.2k
  void operator()(struct archive* a) const { archive_read_free(a); }
2613
};
2614
2615
struct ArchiveWriteDeleter
2616
{
2617
45.2k
  void operator()(struct archive* a) const { archive_write_free(a); }
2618
};
2619
2620
struct ArchiveMatchDeleter
2621
{
2622
45.2k
  void operator()(struct archive* a) const { archive_match_free(a); }
2623
};
2624
2625
bool extract_tar(std::string const& arFileName,
2626
                 std::vector<std::string> const& files,
2627
                 std::vector<std::string> const& excludeFiles,
2628
                 std::string const& encoding, bool verbose,
2629
                 cmSystemTools::cmTarExtractTimestamps extractTimestamps,
2630
                 bool extract)
2631
45.2k
{
2632
45.2k
  std::unique_ptr<struct archive, ArchiveReadDeleter> a_owner(
2633
45.2k
    archive_read_new());
2634
45.2k
  std::unique_ptr<struct archive, ArchiveWriteDeleter> ext_owner(
2635
45.2k
    archive_write_disk_new());
2636
45.2k
  struct archive* a = a_owner.get();
2637
45.2k
  struct archive* ext = ext_owner.get();
2638
45.2k
  if (extract) {
2639
45.2k
    int flags =
2640
45.2k
      ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS;
2641
45.2k
    if (extractTimestamps == cmSystemTools::cmTarExtractTimestamps::Yes) {
2642
22.6k
      flags |= ARCHIVE_EXTRACT_TIME;
2643
22.6k
    }
2644
45.2k
    if (archive_write_disk_set_options(ext, flags) != ARCHIVE_OK) {
2645
0
      ArchiveError("Problem with archive_write_disk_set_options(): ", ext);
2646
0
      return false;
2647
0
    }
2648
45.2k
  }
2649
45.2k
  archive_read_support_filter_all(a);
2650
45.2k
  archive_read_support_format_all(a);
2651
2652
45.2k
  if (encoding != "OEM") {
2653
45.2k
    if (archive_read_set_options(
2654
45.2k
          a, cmStrCat("hdrcharset=", encoding).c_str()) != ARCHIVE_OK) {
2655
0
      cmSystemTools::Error(
2656
0
        cmStrCat("Cannot set archive encoding: ", encoding));
2657
0
      return false;
2658
0
    }
2659
45.2k
  }
2660
45.2k
  struct archive_entry* entry;
2661
2662
45.2k
  std::unique_ptr<struct archive, ArchiveMatchDeleter> matching_owner(
2663
45.2k
    archive_match_new());
2664
45.2k
  struct archive* matching = matching_owner.get();
2665
45.2k
  if (!matching) {
2666
0
    cmSystemTools::Error("Out of memory");
2667
0
    return false;
2668
0
  }
2669
2670
45.2k
  for (auto const& filename : files) {
2671
0
    if (archive_match_include_pattern(matching, filename.c_str()) !=
2672
0
        ARCHIVE_OK) {
2673
0
      cmSystemTools::Error("Failed to add to inclusion list: " + filename);
2674
0
      return false;
2675
0
    }
2676
0
  }
2677
2678
45.2k
  for (auto const& filename : excludeFiles) {
2679
0
    if (archive_match_exclude_pattern(matching, filename.c_str()) !=
2680
0
        ARCHIVE_OK) {
2681
0
      cmSystemTools::Error("Failed to add to exclusion list: " + filename);
2682
0
      return false;
2683
0
    }
2684
0
  }
2685
2686
45.2k
  int r = cm_archive_read_open_filename(a, arFileName.c_str(), 10240);
2687
45.2k
  if (r) {
2688
21.8k
    ArchiveError("Problem with archive_read_open_filename(): ", a);
2689
21.8k
    return false;
2690
21.8k
  }
2691
64.8k
  for (;;) {
2692
64.8k
    r = archive_read_next_header(a, &entry);
2693
64.8k
    if (r == ARCHIVE_EOF) {
2694
1.57k
      break;
2695
1.57k
    }
2696
63.2k
    if (r != ARCHIVE_OK) {
2697
14.7k
      ArchiveError("Problem with archive_read_next_header(): ", a);
2698
14.7k
      break;
2699
14.7k
    }
2700
2701
48.4k
    if (archive_match_excluded(matching, entry)) {
2702
0
      continue;
2703
0
    }
2704
2705
48.4k
    if (verbose) {
2706
24.2k
      if (extract) {
2707
24.2k
        cmSystemTools::Stdout(
2708
24.2k
          cmStrCat("x ", cm_archive_entry_pathname(entry)));
2709
24.2k
      } else {
2710
0
        list_item_verbose(stdout, entry);
2711
0
      }
2712
24.2k
      cmSystemTools::Stdout("\n");
2713
24.2k
    } else if (!extract) {
2714
0
      cmSystemTools::Stdout(cmStrCat(cm_archive_entry_pathname(entry), '\n'));
2715
0
    }
2716
48.4k
    if (extract) {
2717
48.4k
      r = archive_write_header(ext, entry);
2718
48.4k
      if (r == ARCHIVE_OK) {
2719
47.3k
        if (!copy_data(a, ext)) {
2720
5.93k
          r = ARCHIVE_FAILED;
2721
5.93k
          break;
2722
5.93k
        }
2723
41.4k
        r = archive_write_finish_entry(ext);
2724
41.4k
        if (r != ARCHIVE_OK) {
2725
2
          ArchiveError("Problem with archive_write_finish_entry(): ", ext);
2726
2
          break;
2727
2
        }
2728
41.4k
      }
2729
#  ifdef _WIN32
2730
      else if (char const* linktext = archive_entry_symlink(entry)) {
2731
        std::cerr << "cmake -E tar: warning: skipping symbolic link \""
2732
                  << cm_archive_entry_pathname(entry) << "\" -> \"" << linktext
2733
                  << "\"." << std::endl;
2734
      }
2735
#  endif
2736
1.14k
      else {
2737
1.14k
        ArchiveError("Problem with archive_write_header(): ", ext);
2738
1.14k
        cmSystemTools::Error(
2739
1.14k
          cmStrCat("Current file:\n  ", cm_archive_entry_pathname(entry)));
2740
1.14k
        break;
2741
1.14k
      }
2742
48.4k
    }
2743
48.4k
  }
2744
2745
23.4k
  bool error_occurred = false;
2746
23.4k
  if (matching) {
2747
23.4k
    char const* p;
2748
23.4k
    int ar;
2749
2750
23.4k
    while ((ar = archive_match_path_unmatched_inclusions_next(matching, &p)) ==
2751
23.4k
           ARCHIVE_OK) {
2752
0
      cmSystemTools::Error(cmStrCat("tar: ", p, ": Not found in archive"));
2753
0
      error_occurred = true;
2754
0
    }
2755
23.4k
    if (error_occurred) {
2756
0
      return false;
2757
0
    }
2758
23.4k
    if (ar == ARCHIVE_FATAL) {
2759
0
      cmSystemTools::Error("tar: Out of memory");
2760
0
      return false;
2761
0
    }
2762
23.4k
  }
2763
23.4k
  return r == ARCHIVE_EOF || r == ARCHIVE_OK;
2764
23.4k
}
2765
}
2766
#endif
2767
2768
bool cmSystemTools::ExtractTar(std::string const& arFileName,
2769
                               std::vector<std::string> const& files,
2770
                               std::vector<std::string> const& excludeFiles,
2771
                               cmTarExtractTimestamps extractTimestamps,
2772
                               std::string const& encoding, bool verbose)
2773
45.2k
{
2774
45.2k
#if !defined(CMAKE_BOOTSTRAP)
2775
45.2k
  return extract_tar(arFileName, files, excludeFiles, encoding, verbose,
2776
45.2k
                     extractTimestamps, true);
2777
#else
2778
  (void)arFileName;
2779
  (void)files;
2780
  (void)excludeFiles;
2781
  (void)extractTimestamps;
2782
  (void)encoding;
2783
  (void)verbose;
2784
  return false;
2785
#endif
2786
45.2k
}
2787
2788
bool cmSystemTools::ListTar(std::string const& arFileName,
2789
                            std::vector<std::string> const& files,
2790
                            std::vector<std::string> const& excludeFiles,
2791
                            std::string const& encoding, bool verbose)
2792
0
{
2793
0
#if !defined(CMAKE_BOOTSTRAP)
2794
0
  return extract_tar(arFileName, files, excludeFiles, encoding, verbose,
2795
0
                     cmTarExtractTimestamps::Yes, false);
2796
#else
2797
  (void)arFileName;
2798
  (void)files;
2799
  (void)excludeFiles;
2800
  (void)encoding;
2801
  (void)verbose;
2802
  return false;
2803
#endif
2804
0
}
2805
2806
cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
2807
  uv_loop_t* loop, uv_stream_t* outPipe, uv_stream_t* errPipe,
2808
  std::string& line, std::vector<char>& out, std::vector<char>& err)
2809
0
{
2810
0
  line.clear();
2811
0
  auto outiter = out.begin();
2812
0
  auto erriter = err.begin();
2813
0
  cmProcessOutput processOutput;
2814
0
  std::string strdata;
2815
0
  while (true) {
2816
    // Check for a newline in stdout.
2817
0
    for (; outiter != out.end(); ++outiter) {
2818
0
      if ((*outiter == '\r') && ((outiter + 1) == out.end())) {
2819
0
        break;
2820
0
      }
2821
0
      if (*outiter == '\n' || *outiter == '\0') {
2822
0
        std::vector<char>::size_type length = outiter - out.begin();
2823
0
        if (length > 1 && *(outiter - 1) == '\r') {
2824
0
          --length;
2825
0
        }
2826
0
        if (length > 0) {
2827
0
          line.append(out.data(), length);
2828
0
        }
2829
0
        out.erase(out.begin(), outiter + 1);
2830
0
        return WaitForLineResult::STDOUT;
2831
0
      }
2832
0
    }
2833
2834
    // Check for a newline in stderr.
2835
0
    for (; erriter != err.end(); ++erriter) {
2836
0
      if ((*erriter == '\r') && ((erriter + 1) == err.end())) {
2837
0
        break;
2838
0
      }
2839
0
      if (*erriter == '\n' || *erriter == '\0') {
2840
0
        std::vector<char>::size_type length = erriter - err.begin();
2841
0
        if (length > 1 && *(erriter - 1) == '\r') {
2842
0
          --length;
2843
0
        }
2844
0
        if (length > 0) {
2845
0
          line.append(err.data(), length);
2846
0
        }
2847
0
        err.erase(err.begin(), erriter + 1);
2848
0
        return WaitForLineResult::STDERR;
2849
0
      }
2850
0
    }
2851
2852
    // No newlines found.  Wait for more data from the process.
2853
0
    struct ReadData
2854
0
    {
2855
0
      uv_stream_t* Stream;
2856
0
      std::vector<char> Buffer;
2857
0
      bool Read = false;
2858
0
      bool Finished = false;
2859
0
    };
2860
0
    auto startRead =
2861
0
      [](uv_stream_t* stream,
2862
0
         ReadData& data) -> std::unique_ptr<cmUVStreamReadHandle> {
2863
0
      data.Stream = stream;
2864
0
      return cmUVStreamRead(
2865
0
        stream,
2866
0
        [&data](std::vector<char> buf) {
2867
0
          data.Buffer = std::move(buf);
2868
0
          data.Read = true;
2869
0
          uv_read_stop(data.Stream);
2870
0
        },
2871
0
        [&data]() { data.Finished = true; });
2872
0
    };
2873
0
    ReadData outData;
2874
0
    auto outHandle = startRead(outPipe, outData);
2875
0
    ReadData errData;
2876
0
    auto errHandle = startRead(errPipe, errData);
2877
2878
0
    uv_run(loop, UV_RUN_ONCE);
2879
0
    if (outData.Read) {
2880
0
      processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(),
2881
0
                               strdata, 1);
2882
      // Append to the stdout buffer.
2883
0
      std::vector<char>::size_type size = out.size();
2884
0
      cm::append(out, strdata);
2885
0
      outiter = out.begin() + size;
2886
0
    } else if (errData.Read) {
2887
0
      processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(),
2888
0
                               strdata, 2);
2889
      // Append to the stderr buffer.
2890
0
      std::vector<char>::size_type size = err.size();
2891
0
      cm::append(err, strdata);
2892
0
      erriter = err.begin() + size;
2893
0
    } else if (outData.Finished && errData.Finished) {
2894
      // Both stdout and stderr pipes have broken.  Return leftover data.
2895
0
      processOutput.DecodeText(std::string(), strdata, 1);
2896
0
      if (!strdata.empty()) {
2897
0
        std::vector<char>::size_type size = out.size();
2898
0
        cm::append(out, strdata);
2899
0
        outiter = out.begin() + size;
2900
0
      }
2901
0
      processOutput.DecodeText(std::string(), strdata, 2);
2902
0
      if (!strdata.empty()) {
2903
0
        std::vector<char>::size_type size = err.size();
2904
0
        cm::append(err, strdata);
2905
0
        erriter = err.begin() + size;
2906
0
      }
2907
0
      if (!out.empty()) {
2908
0
        line.append(out.data(), outiter - out.begin());
2909
0
        out.erase(out.begin(), out.end());
2910
0
        return WaitForLineResult::STDOUT;
2911
0
      }
2912
0
      if (!err.empty()) {
2913
0
        line.append(err.data(), erriter - err.begin());
2914
0
        err.erase(err.begin(), err.end());
2915
0
        return WaitForLineResult::STDERR;
2916
0
      }
2917
0
      return WaitForLineResult::None;
2918
0
    }
2919
0
    if (!outData.Finished) {
2920
0
      uv_read_stop(outPipe);
2921
0
    }
2922
0
    if (!errData.Finished) {
2923
0
      uv_read_stop(errPipe);
2924
0
    }
2925
0
  }
2926
0
}
2927
2928
#ifdef _WIN32
2929
#  ifndef CRYPT_SILENT
2930
#    define CRYPT_SILENT 0x40 /* Not defined by VS 6 version of header.  */
2931
#  endif
2932
static int WinCryptRandom(void* data, size_t size)
2933
{
2934
  int result = 0;
2935
  HCRYPTPROV hProvider = 0;
2936
  if (CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL,
2937
                           CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
2938
    result = CryptGenRandom(hProvider, (DWORD)size, (BYTE*)data) ? 1 : 0;
2939
    CryptReleaseContext(hProvider, 0);
2940
  }
2941
  return result;
2942
}
2943
#endif
2944
2945
unsigned int cmSystemTools::RandomSeed()
2946
0
{
2947
#if defined(_WIN32) && !defined(__CYGWIN__)
2948
  unsigned int seed = 0;
2949
2950
  // Try using a real random source.
2951
  if (WinCryptRandom(&seed, sizeof(seed))) {
2952
    return seed;
2953
  }
2954
2955
  // Fall back to the time and pid.
2956
  FILETIME ft;
2957
  GetSystemTimeAsFileTime(&ft);
2958
  unsigned int t1 = static_cast<unsigned int>(ft.dwHighDateTime);
2959
  unsigned int t2 = static_cast<unsigned int>(ft.dwLowDateTime);
2960
  unsigned int pid = static_cast<unsigned int>(GetCurrentProcessId());
2961
  return t1 ^ t2 ^ pid;
2962
#else
2963
0
  union
2964
0
  {
2965
0
    unsigned int integer;
2966
0
    char bytes[sizeof(unsigned int)];
2967
0
  } seed;
2968
2969
  // Try using a real random source.
2970
0
  cmsys::ifstream fin;
2971
0
  fin.rdbuf()->pubsetbuf(nullptr, 0); // Unbuffered read.
2972
0
  fin.open("/dev/urandom");
2973
0
  if (fin.good() && fin.read(seed.bytes, sizeof(seed)) &&
2974
0
      fin.gcount() == sizeof(seed)) {
2975
0
    return seed.integer;
2976
0
  }
2977
2978
  // Fall back to the time and pid.
2979
0
  struct timeval t;
2980
0
  gettimeofday(&t, nullptr);
2981
0
  unsigned int pid = static_cast<unsigned int>(getpid());
2982
0
  unsigned int tv_sec = static_cast<unsigned int>(t.tv_sec);
2983
0
  unsigned int tv_usec = static_cast<unsigned int>(t.tv_usec);
2984
  // Since tv_usec never fills more than 11 bits we shift it to fill
2985
  // in the slow-changing high-order bits of tv_sec.
2986
0
  return tv_sec ^ (tv_usec << 21) ^ pid;
2987
0
#endif
2988
0
}
2989
2990
unsigned int cmSystemTools::RandomNumber()
2991
0
{
2992
#ifndef CM_HAVE_THREAD_LOCAL
2993
  static std::mutex gen_mutex;
2994
  std::lock_guard<std::mutex> gen_mutex_lock(gen_mutex);
2995
#else
2996
0
  thread_local
2997
0
#endif
2998
0
  static std::mt19937 gen{ cmSystemTools::RandomSeed() };
2999
0
  return static_cast<unsigned int>(gen());
3000
0
}
3001
3002
std::string cmSystemTools::FindProgram(std::string const& name,
3003
                                       std::vector<std::string> const& path)
3004
0
{
3005
0
  std::string exe = cmsys::SystemTools::FindProgram(name, path);
3006
0
  if (!exe.empty()) {
3007
0
    exe = cmSystemTools::ToNormalizedPathOnDisk(std::move(exe));
3008
0
  }
3009
0
  return exe;
3010
0
}
3011
3012
namespace {
3013
std::string InitLogicalWorkingDirectory()
3014
18
{
3015
18
  std::string cwd = cmsys::SystemTools::GetCurrentWorkingDirectory();
3016
18
  std::string pwd;
3017
18
  if (cmSystemTools::GetEnv("PWD", pwd) &&
3018
18
      cmSystemTools::FileIsFullPath(pwd)) {
3019
18
    std::string const pwd_real = cmSystemTools::GetRealPath(pwd);
3020
18
    if (pwd_real == cwd) {
3021
18
      cwd = cmSystemTools::ToNormalizedPathOnDisk(std::move(pwd));
3022
18
    }
3023
18
  }
3024
18
  return cwd;
3025
18
}
3026
3027
bool cmSystemToolsCMakeInBuildTree = false;
3028
3029
std::string cmSystemToolsLogicalWorkingDirectory =
3030
  InitLogicalWorkingDirectory();
3031
3032
std::string cmSystemToolsCMakeCommand;
3033
std::string cmSystemToolsCTestCommand;
3034
std::string cmSystemToolsCPackCommand;
3035
std::string cmSystemToolsCMakeCursesCommand;
3036
std::string cmSystemToolsCMakeGUICommand;
3037
std::string cmSystemToolsCMClDepsCommand;
3038
std::string cmSystemToolsCMakeRoot;
3039
std::string cmSystemToolsHTMLDoc;
3040
3041
#if defined(__APPLE__)
3042
bool IsCMakeAppBundleExe(std::string const& exe)
3043
{
3044
  return cmHasLiteralSuffix(cmSystemTools::LowerCase(exe), "/macos/cmake");
3045
}
3046
#endif
3047
3048
std::string FindOwnExecutable(char const* argv0)
3049
0
{
3050
#if defined(_WIN32) && !defined(__CYGWIN__)
3051
  static_cast<void>(argv0);
3052
  wchar_t modulepath[_MAX_PATH];
3053
  ::GetModuleFileNameW(nullptr, modulepath, sizeof(modulepath));
3054
  std::string exe = cmsys::Encoding::ToNarrow(modulepath);
3055
#elif defined(__APPLE__)
3056
  static_cast<void>(argv0);
3057
#  define CM_EXE_PATH_LOCAL_SIZE 16384
3058
  char exe_path_local[CM_EXE_PATH_LOCAL_SIZE];
3059
#  if defined(MAC_OS_X_VERSION_10_3) && !defined(MAC_OS_X_VERSION_10_4)
3060
  unsigned long exe_path_size = CM_EXE_PATH_LOCAL_SIZE;
3061
#  else
3062
  uint32_t exe_path_size = CM_EXE_PATH_LOCAL_SIZE;
3063
#  endif
3064
#  undef CM_EXE_PATH_LOCAL_SIZE
3065
  char* exe_path = exe_path_local;
3066
  if (_NSGetExecutablePath(exe_path, &exe_path_size) < 0) {
3067
    exe_path = static_cast<char*>(malloc(exe_path_size));
3068
    _NSGetExecutablePath(exe_path, &exe_path_size);
3069
  }
3070
  std::string exe = exe_path;
3071
  if (exe_path != exe_path_local) {
3072
    free(exe_path);
3073
  }
3074
  if (IsCMakeAppBundleExe(exe)) {
3075
    // The executable is inside an application bundle.
3076
    // The install tree has "..<CMAKE_BIN_DIR>/cmake-gui".
3077
    // The build tree has '../../../cmake-gui".
3078
    std::string dir = cmSystemTools::GetFilenamePath(exe);
3079
    dir = cmSystemTools::GetFilenamePath(dir);
3080
    exe = cmStrCat(dir, CMAKE_BIN_DIR "/cmake-gui");
3081
    if (!cmSystemTools::PathExists(exe)) {
3082
      dir = cmSystemTools::GetFilenamePath(dir);
3083
      dir = cmSystemTools::GetFilenamePath(dir);
3084
      exe = cmStrCat(dir, "/cmake-gui");
3085
    }
3086
  }
3087
#else
3088
0
  std::string exe = cmsys::SystemTools::FindProgram(argv0);
3089
0
#endif
3090
0
  exe = cmSystemTools::ToNormalizedPathOnDisk(std::move(exe));
3091
0
  return exe;
3092
0
}
3093
3094
#ifndef CMAKE_BOOTSTRAP
3095
bool ResolveSymlinkToOwnExecutable(std::string& exe, std::string& exe_dir)
3096
0
{
3097
0
  std::string linked_exe;
3098
0
  if (!cmSystemTools::ReadSymlink(exe, linked_exe)) {
3099
0
    return false;
3100
0
  }
3101
#  if defined(__APPLE__)
3102
  // Ignore "cmake-gui -> ../MacOS/CMake".
3103
  if (IsCMakeAppBundleExe(linked_exe)) {
3104
    return false;
3105
  }
3106
#  endif
3107
0
  if (cmSystemTools::FileIsFullPath(linked_exe)) {
3108
0
    exe = std::move(linked_exe);
3109
0
  } else {
3110
0
    exe = cmStrCat(exe_dir, '/', std::move(linked_exe));
3111
0
  }
3112
0
  exe = cmSystemTools::ToNormalizedPathOnDisk(std::move(exe));
3113
0
  exe_dir = cmSystemTools::GetFilenamePath(exe);
3114
0
  return true;
3115
0
}
3116
3117
bool FindCMakeResourcesInInstallTree(std::string const& exe_dir)
3118
0
{
3119
  // Install tree has
3120
  // - "<prefix><CMAKE_BIN_DIR>/cmake"
3121
  // - "<prefix><CMAKE_DATA_DIR>"
3122
  // - "<prefix><CMAKE_DOC_DIR>"
3123
0
  if (cmHasLiteralSuffix(exe_dir, CMAKE_BIN_DIR)) {
3124
0
    std::string const prefix =
3125
0
      exe_dir.substr(0, exe_dir.size() - cmStrLen(CMAKE_BIN_DIR));
3126
    // Set cmSystemToolsCMakeRoot set to the location expected in an
3127
    // install tree, even if it does not exist, so that
3128
    // cmake::AddCMakePaths can print the location in its error message.
3129
0
    cmSystemToolsCMakeRoot = cmStrCat(prefix, CMAKE_DATA_DIR);
3130
0
    if (cmSystemTools::FileExists(
3131
0
          cmStrCat(cmSystemToolsCMakeRoot, "/Modules/CMake.cmake"))) {
3132
0
      if (cmSystemTools::FileExists(
3133
0
            cmStrCat(prefix, CMAKE_DOC_DIR "/html/index.html"))) {
3134
0
        cmSystemToolsHTMLDoc = cmStrCat(prefix, CMAKE_DOC_DIR "/html");
3135
0
      }
3136
0
      return true;
3137
0
    }
3138
0
  }
3139
0
  return false;
3140
0
}
3141
3142
void FindCMakeResourcesInBuildTree(std::string const& exe_dir)
3143
0
{
3144
  // Build tree has "<build>/bin[/<config>]/cmake" and
3145
  // "<build>/CMakeFiles/CMakeSourceDir.txt".
3146
0
  std::string dir = cmSystemTools::GetFilenamePath(exe_dir);
3147
0
  std::string src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt");
3148
0
  cmsys::ifstream fin(src_dir_txt.c_str());
3149
0
  std::string src_dir;
3150
0
  if (fin && cmSystemTools::GetLineFromStream(fin, src_dir) &&
3151
0
      cmSystemTools::FileIsDirectory(src_dir)) {
3152
0
    cmSystemToolsCMakeRoot = src_dir;
3153
0
    cmSystemToolsCMakeInBuildTree = true;
3154
0
  } else {
3155
0
    dir = cmSystemTools::GetFilenamePath(dir);
3156
0
    src_dir_txt = cmStrCat(dir, "/CMakeFiles/CMakeSourceDir.txt");
3157
0
    cmsys::ifstream fin2(src_dir_txt.c_str());
3158
0
    if (fin2 && cmSystemTools::GetLineFromStream(fin2, src_dir) &&
3159
0
        cmSystemTools::FileIsDirectory(src_dir)) {
3160
0
      cmSystemToolsCMakeRoot = src_dir;
3161
0
      cmSystemToolsCMakeInBuildTree = true;
3162
0
    }
3163
0
  }
3164
0
  if (!cmSystemToolsCMakeRoot.empty() && cmSystemToolsHTMLDoc.empty() &&
3165
0
      cmSystemTools::FileExists(
3166
0
        cmStrCat(dir, "/Utilities/Sphinx/html/index.html"))) {
3167
0
    cmSystemToolsHTMLDoc = cmStrCat(dir, "/Utilities/Sphinx/html");
3168
0
  }
3169
0
}
3170
#endif
3171
}
3172
3173
void cmSystemTools::InitializeCMake(char const* argv0)
3174
0
{
3175
#if defined(_WIN32)
3176
  // Perform libuv one-time initialization now, and then un-do its
3177
  // global _fmode setting so that using libuv does not change the
3178
  // default file text/binary mode.  See libuv issue 840.
3179
  if (uv_loop_t* loop = uv_default_loop()) {
3180
    uv_loop_close(loop);
3181
  }
3182
#  ifdef _MSC_VER
3183
  _set_fmode(_O_TEXT);
3184
#  else
3185
  _fmode = _O_TEXT;
3186
#  endif
3187
  // Replace libuv's report handler with our own to suppress popups.
3188
  cmSystemTools::EnableMSVCDebugHook();
3189
#endif
3190
3191
0
  std::string exe = FindOwnExecutable(argv0);
3192
#ifdef CMAKE_BOOTSTRAP
3193
  // The bootstrap cmake knows its resource locations.
3194
  cmSystemToolsCMakeRoot = CMAKE_BOOTSTRAP_SOURCE_DIR;
3195
  cmSystemToolsCMakeInBuildTree = true;
3196
  cmSystemToolsCMakeCommand = exe;
3197
  // The bootstrap cmake does not provide the other tools,
3198
  // so use the directory where they are about to be built.
3199
  std::string exe_dir = CMAKE_BOOTSTRAP_BINARY_DIR "/bin";
3200
#else
3201
  // Find resources relative to our own executable.
3202
0
  std::string exe_dir = cmSystemTools::GetFilenamePath(exe);
3203
0
  bool found = false;
3204
  // When running through a symlink to our own executable,
3205
  // preserve symlinks in directory components if possible.
3206
0
  do {
3207
0
    found = FindCMakeResourcesInInstallTree(exe_dir);
3208
0
  } while (!found && ResolveSymlinkToOwnExecutable(exe, exe_dir));
3209
  // If we have not yet found the resources, the above loop will
3210
  // have left 'exe' referring to a real file, not a symlink, so
3211
  // all our binaries should exist under 'exe_dir'.  However, the
3212
  // resources may be discoverable only in the real path.
3213
0
  if (!found) {
3214
0
    found =
3215
0
      FindCMakeResourcesInInstallTree(cmSystemTools::GetRealPath(exe_dir));
3216
0
  }
3217
0
  if (!found) {
3218
0
    FindCMakeResourcesInBuildTree(exe_dir);
3219
0
  }
3220
0
  cmSystemToolsCMakeCommand =
3221
0
    cmStrCat(exe_dir, "/cmake", cmSystemTools::GetExecutableExtension());
3222
0
#endif
3223
0
  cmSystemToolsCTestCommand =
3224
0
    cmStrCat(exe_dir, "/ctest", cmSystemTools::GetExecutableExtension());
3225
0
  cmSystemToolsCPackCommand =
3226
0
    cmStrCat(exe_dir, "/cpack", cmSystemTools::GetExecutableExtension());
3227
0
  cmSystemToolsCMakeGUICommand =
3228
0
    cmStrCat(exe_dir, "/cmake-gui", cmSystemTools::GetExecutableExtension());
3229
0
  if (!cmSystemTools::FileExists(cmSystemToolsCMakeGUICommand)) {
3230
0
    cmSystemToolsCMakeGUICommand.clear();
3231
0
  }
3232
0
  cmSystemToolsCMakeCursesCommand =
3233
0
    cmStrCat(exe_dir, "/ccmake", cmSystemTools::GetExecutableExtension());
3234
0
  if (!cmSystemTools::FileExists(cmSystemToolsCMakeCursesCommand)) {
3235
0
    cmSystemToolsCMakeCursesCommand.clear();
3236
0
  }
3237
0
  cmSystemToolsCMClDepsCommand =
3238
0
    cmStrCat(exe_dir, "/cmcldeps", cmSystemTools::GetExecutableExtension());
3239
0
  if (!cmSystemTools::FileExists(cmSystemToolsCMClDepsCommand)) {
3240
0
    cmSystemToolsCMClDepsCommand.clear();
3241
0
  }
3242
0
}
3243
3244
std::string const& cmSystemTools::GetCMakeCommand()
3245
1
{
3246
1
  return cmSystemToolsCMakeCommand;
3247
1
}
3248
3249
std::string const& cmSystemTools::GetCTestCommand()
3250
1
{
3251
1
  return cmSystemToolsCTestCommand;
3252
1
}
3253
3254
std::string const& cmSystemTools::GetCPackCommand()
3255
1
{
3256
1
  return cmSystemToolsCPackCommand;
3257
1
}
3258
3259
std::string const& cmSystemTools::GetCMakeCursesCommand()
3260
0
{
3261
0
  return cmSystemToolsCMakeCursesCommand;
3262
0
}
3263
3264
std::string const& cmSystemTools::GetCMakeGUICommand()
3265
0
{
3266
0
  return cmSystemToolsCMakeGUICommand;
3267
0
}
3268
3269
std::string const& cmSystemTools::GetCMClDepsCommand()
3270
0
{
3271
0
  return cmSystemToolsCMClDepsCommand;
3272
0
}
3273
3274
std::string const& cmSystemTools::GetCMakeRoot()
3275
2
{
3276
2
  return cmSystemToolsCMakeRoot;
3277
2
}
3278
3279
bool cmSystemTools::GetCMakeInBuildTree()
3280
0
{
3281
0
  return cmSystemToolsCMakeInBuildTree;
3282
0
}
3283
3284
std::string const& cmSystemTools::GetHTMLDoc()
3285
0
{
3286
0
  return cmSystemToolsHTMLDoc;
3287
0
}
3288
3289
cm::optional<std::string> cmSystemTools::GetSystemConfigDirectory()
3290
0
{
3291
#if defined(_WIN32)
3292
  // Prefer %LOCALAPPDATA%, which Windows sets from the same known folder at
3293
  // logon.  SHGetKnownFolderPath loads shell32 and its dependencies, which
3294
  // costs several milliseconds in every process that looks up this directory.
3295
  if (cm::optional<std::string> env =
3296
        cmSystemTools::GetEnvVar("LOCALAPPDATA")) {
3297
    if (cmSystemTools::FileIsFullPath(*env)) {
3298
      cmSystemTools::ConvertToUnixSlashes(*env);
3299
      return env;
3300
    }
3301
  }
3302
  LPWSTR lpwstr;
3303
  if (FAILED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &lpwstr))) {
3304
    return cm::nullopt;
3305
  }
3306
  std::wstring wstr = std::wstring(lpwstr);
3307
  CoTaskMemFree(lpwstr);
3308
  std::string config = cmsys::Encoding::ToNarrow(wstr);
3309
  cmSystemTools::ConvertToUnixSlashes(config);
3310
  return config;
3311
#else
3312
0
  auto config = cmSystemTools::GetEnvVar("XDG_CONFIG_HOME");
3313
0
  if (!config.has_value()) {
3314
0
    config = cmSystemTools::GetEnvVar("HOME");
3315
0
    if (config.has_value()) {
3316
#  if defined(__APPLE__)
3317
      config = cmStrCat(config.value(), "/Library/Application Support");
3318
#  else
3319
0
      config = cmStrCat(config.value(), "/.config");
3320
0
#  endif
3321
0
    }
3322
0
  }
3323
0
  return config;
3324
0
#endif
3325
0
}
3326
3327
cm::optional<std::string> cmSystemTools::GetCMakeConfigDirectory()
3328
0
{
3329
0
  auto config = cmSystemTools::GetEnvVar("CMAKE_CONFIG_DIR");
3330
0
  if (!config.has_value()) {
3331
0
    config = cmSystemTools::GetSystemConfigDirectory();
3332
0
    if (config.has_value()) {
3333
#if defined(_WIN32) || defined(__APPLE__)
3334
      config = cmStrCat(config.value(), "/CMake");
3335
#else
3336
0
      config = cmStrCat(config.value(), "/cmake");
3337
0
#endif
3338
0
    }
3339
0
  }
3340
0
  return config;
3341
0
}
3342
3343
std::string const& cmSystemTools::GetLogicalWorkingDirectory()
3344
19.9k
{
3345
19.9k
  return cmSystemToolsLogicalWorkingDirectory;
3346
19.9k
}
3347
3348
cmsys::Status cmSystemTools::SetLogicalWorkingDirectory(std::string const& lwd)
3349
0
{
3350
0
  cmsys::Status status = cmSystemTools::ChangeDirectory(lwd);
3351
0
  if (status) {
3352
0
    cmSystemToolsLogicalWorkingDirectory = lwd;
3353
0
  }
3354
0
  return status;
3355
0
}
3356
3357
bool cmSystemTools::GuessLibrarySOName(std::string const& fullPath,
3358
                                       std::string& soname)
3359
0
{
3360
  // For ELF shared libraries use a real parser to get the correct
3361
  // soname.
3362
0
  cmELF elf(fullPath.c_str());
3363
0
  if (elf) {
3364
0
    return elf.GetSOName(soname);
3365
0
  }
3366
3367
  // If the file is not a symlink we have no guess for its soname.
3368
0
  if (!cmSystemTools::FileIsSymlink(fullPath)) {
3369
0
    return false;
3370
0
  }
3371
0
  if (!cmSystemTools::ReadSymlink(fullPath, soname)) {
3372
0
    return false;
3373
0
  }
3374
3375
  // If the symlink has a path component we have no guess for the soname.
3376
0
  if (!cmSystemTools::GetFilenamePath(soname).empty()) {
3377
0
    return false;
3378
0
  }
3379
3380
  // If the symlink points at an extended version of the same name
3381
  // assume it is the soname.
3382
0
  std::string name = cmSystemTools::GetFilenameName(fullPath);
3383
0
  return soname.length() > name.length() &&
3384
0
    soname.compare(0, name.length(), name) == 0;
3385
0
}
3386
3387
bool cmSystemTools::GuessLibraryInstallName(std::string const& fullPath,
3388
                                            std::string& soname)
3389
0
{
3390
#if defined(CMake_USE_MACH_PARSER)
3391
  cmMachO macho(fullPath.c_str());
3392
  if (macho) {
3393
    return macho.GetInstallName(soname);
3394
  }
3395
#else
3396
0
  (void)fullPath;
3397
0
  (void)soname;
3398
0
#endif
3399
3400
0
  return false;
3401
0
}
3402
3403
static std::string::size_type cmSystemToolsFindRPath(cm::string_view have,
3404
                                                     cm::string_view want)
3405
0
{
3406
0
  std::string::size_type pos = 0;
3407
0
  while (pos < have.size()) {
3408
    // Look for an occurrence of the string.
3409
0
    std::string::size_type const beg = have.find(want, pos);
3410
0
    if (beg == std::string::npos) {
3411
0
      return std::string::npos;
3412
0
    }
3413
3414
    // Make sure it is separated from preceding entries.
3415
0
    if (beg > 0 && have[beg - 1] != ':') {
3416
0
      pos = beg + 1;
3417
0
      continue;
3418
0
    }
3419
3420
    // Make sure it is separated from following entries.
3421
0
    std::string::size_type const end = beg + want.size();
3422
0
    if (end < have.size() && have[end] != ':') {
3423
0
      pos = beg + 1;
3424
0
      continue;
3425
0
    }
3426
3427
    // Return the position of the path portion.
3428
0
    return beg;
3429
0
  }
3430
3431
  // The desired rpath was not found.
3432
0
  return std::string::npos;
3433
0
}
3434
3435
namespace {
3436
struct cmSystemToolsRPathInfo
3437
{
3438
  unsigned long Position;
3439
  unsigned long Size;
3440
  std::string Name;
3441
  std::string Value;
3442
};
3443
3444
using EmptyCallback = std::function<bool(std::string*, cmELF const&)>;
3445
using AdjustCallback = std::function<bool(
3446
  cm::optional<std::string>&, std::string const&, char const*, std::string*)>;
3447
3448
cm::optional<bool> AdjustRPathELF(std::string const& file,
3449
                                  EmptyCallback const& emptyCallback,
3450
                                  AdjustCallback const& adjustCallback,
3451
                                  std::string* emsg, bool* changed)
3452
0
{
3453
0
  if (changed) {
3454
0
    *changed = false;
3455
0
  }
3456
0
  int rp_count = 0;
3457
0
  bool remove_rpath = true;
3458
0
  cmSystemToolsRPathInfo rp[2];
3459
0
  {
3460
    // Parse the ELF binary.
3461
0
    cmELF elf(file.c_str());
3462
0
    if (!elf) {
3463
0
      return cm::nullopt; // Not a valid ELF file.
3464
0
    }
3465
3466
0
    if (!elf.HasDynamicSection()) {
3467
0
      return true; // No dynamic section to update.
3468
0
    }
3469
3470
    // Get the RPATH and RUNPATH entries from it.
3471
0
    int se_count = 0;
3472
0
    cmELF::StringEntry const* se[2] = { nullptr, nullptr };
3473
0
    char const* se_name[2] = { nullptr, nullptr };
3474
0
    if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
3475
0
      se[se_count] = se_rpath;
3476
0
      se_name[se_count] = "RPATH";
3477
0
      ++se_count;
3478
0
    }
3479
0
    if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
3480
0
      se[se_count] = se_runpath;
3481
0
      se_name[se_count] = "RUNPATH";
3482
0
      ++se_count;
3483
0
    }
3484
0
    if (se_count == 0) {
3485
0
      return emptyCallback(emsg, elf);
3486
0
    }
3487
3488
0
    for (int i = 0; i < se_count; ++i) {
3489
      // If both RPATH and RUNPATH refer to the same string literal it
3490
      // needs to be changed only once.
3491
0
      if (rp_count && rp[0].Position == se[i]->Position) {
3492
0
        continue;
3493
0
      }
3494
3495
      // Store information about the entry in the file.
3496
0
      rp[rp_count].Position = se[i]->Position;
3497
0
      rp[rp_count].Size = se[i]->Size;
3498
0
      rp[rp_count].Name = se_name[i];
3499
3500
      // Adjust the rpath.
3501
0
      cm::optional<std::string> outRPath;
3502
0
      if (!adjustCallback(outRPath, se[i]->Value, se_name[i], emsg)) {
3503
0
        return false;
3504
0
      }
3505
3506
0
      if (outRPath) {
3507
0
        if (!outRPath->empty()) {
3508
0
          remove_rpath = false;
3509
0
        }
3510
3511
        // Make sure there is enough room to store the new rpath and at
3512
        // least one null terminator.
3513
0
        if (rp[rp_count].Size < outRPath->length() + 1) {
3514
0
          if (emsg) {
3515
0
            *emsg = cmStrCat("The replacement path is too long for the ",
3516
0
                             se_name[i], " entry.");
3517
0
          }
3518
0
          return false;
3519
0
        }
3520
3521
        // This entry is ready for update.
3522
0
        rp[rp_count].Value = std::move(*outRPath);
3523
0
        ++rp_count;
3524
0
      } else {
3525
0
        remove_rpath = false;
3526
0
      }
3527
0
    }
3528
0
  }
3529
3530
  // If no runtime path needs to be changed, we are done.
3531
0
  if (rp_count == 0) {
3532
0
    return true;
3533
0
  }
3534
3535
  // If the resulting rpath is empty, just remove the entire entry instead.
3536
0
  if (remove_rpath) {
3537
0
    return cmSystemTools::RemoveRPath(file, emsg, changed);
3538
0
  }
3539
3540
0
  FileModeGuard file_mode_guard(file, emsg);
3541
0
  if (file_mode_guard.HasErrors()) {
3542
0
    return false;
3543
0
  }
3544
3545
0
  {
3546
    // Open the file for update.
3547
0
    cmsys::ofstream f(file.c_str(),
3548
0
                      std::ios::in | std::ios::out | std::ios::binary);
3549
0
    if (!f) {
3550
0
      if (emsg) {
3551
0
        *emsg = "Error opening file for update.";
3552
0
      }
3553
0
      return false;
3554
0
    }
3555
3556
    // Store the new RPATH and RUNPATH strings.
3557
0
    for (int i = 0; i < rp_count; ++i) {
3558
      // Seek to the RPATH position.
3559
0
      if (!f.seekp(rp[i].Position)) {
3560
0
        if (emsg) {
3561
0
          *emsg = cmStrCat("Error seeking to ", rp[i].Name, " position.");
3562
0
        }
3563
0
        return false;
3564
0
      }
3565
3566
      // Write the new rpath.  Follow it with enough null terminators to
3567
      // fill the string table entry.
3568
0
      f << rp[i].Value;
3569
0
      for (unsigned long j = rp[i].Value.length(); j < rp[i].Size; ++j) {
3570
0
        f << '\0';
3571
0
      }
3572
3573
      // Make sure it wrote correctly.
3574
0
      if (!f) {
3575
0
        if (emsg) {
3576
0
          *emsg = cmStrCat("Error writing the new ", rp[i].Name,
3577
0
                           " string to the file.");
3578
0
        }
3579
0
        return false;
3580
0
      }
3581
0
    }
3582
0
  }
3583
3584
0
  if (!file_mode_guard.Restore(emsg)) {
3585
0
    return false;
3586
0
  }
3587
3588
  // Everything was updated successfully.
3589
0
  if (changed) {
3590
0
    *changed = true;
3591
0
  }
3592
0
  return true;
3593
0
}
3594
3595
std::function<bool(std::string*, cmELF const&)> MakeEmptyCallback(
3596
  std::string const& newRPath)
3597
0
{
3598
0
  return [newRPath](std::string* emsg, cmELF const& elf) -> bool {
3599
0
    if (newRPath.empty()) {
3600
      // The new rpath is empty and there is no rpath anyway so it is
3601
      // okay.
3602
0
      return true;
3603
0
    }
3604
0
    if (emsg) {
3605
0
      *emsg =
3606
0
        cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ",
3607
0
                 elf.GetErrorMessage());
3608
0
    }
3609
0
    return false;
3610
0
  };
3611
0
}
3612
}
3613
3614
static cm::optional<bool> ChangeRPathELF(std::string const& file,
3615
                                         std::string const& oldRPath,
3616
                                         std::string const& newRPath,
3617
                                         bool removeEnvironmentRPath,
3618
                                         std::string* emsg, bool* changed)
3619
0
{
3620
0
  auto adjustCallback = [oldRPath, newRPath, removeEnvironmentRPath](
3621
0
                          cm::optional<std::string>& outRPath,
3622
0
                          std::string const& inRPath, char const* se_name,
3623
0
                          std::string* emsg2) -> bool {
3624
    // Make sure the current rpath contains the old rpath.
3625
0
    std::string::size_type pos = cmSystemToolsFindRPath(inRPath, oldRPath);
3626
0
    if (pos == std::string::npos) {
3627
      // If it contains the new rpath instead then it is okay.
3628
0
      if (cmSystemToolsFindRPath(inRPath, newRPath) != std::string::npos) {
3629
0
        return true;
3630
0
      }
3631
0
      if (emsg2) {
3632
0
        *emsg2 = cmStrCat("The current ", se_name,
3633
0
                          " is:\n"
3634
0
                          "  ",
3635
0
                          inRPath,
3636
0
                          "\n"
3637
0
                          "which does not contain:\n"
3638
0
                          "  ",
3639
0
                          oldRPath,
3640
0
                          "\n"
3641
0
                          "as was expected.");
3642
0
      }
3643
0
      return false;
3644
0
    }
3645
3646
0
    std::string::size_type prefix_len = pos;
3647
3648
    // If oldRPath was at the end of the file's RPath, and newRPath is empty,
3649
    // we should remove the unnecessary ':' at the end.
3650
0
    if (newRPath.empty() && pos > 0 && inRPath[pos - 1] == ':' &&
3651
0
        pos + oldRPath.length() == inRPath.length()) {
3652
0
      prefix_len--;
3653
0
    }
3654
3655
    // Construct the new value which preserves the part of the path
3656
    // not being changed.
3657
0
    outRPath.emplace();
3658
0
    if (!removeEnvironmentRPath) {
3659
0
      *outRPath += inRPath.substr(0, prefix_len);
3660
0
    }
3661
0
    *outRPath += newRPath;
3662
0
    *outRPath += inRPath.substr(pos + oldRPath.length());
3663
3664
0
    return true;
3665
0
  };
3666
3667
0
  return AdjustRPathELF(file, MakeEmptyCallback(newRPath), adjustCallback,
3668
0
                        emsg, changed);
3669
0
}
3670
3671
static cm::optional<bool> SetRPathELF(std::string const& file,
3672
                                      std::string const& newRPath,
3673
                                      std::string* emsg, bool* changed)
3674
0
{
3675
0
  auto adjustCallback = [newRPath](cm::optional<std::string>& outRPath,
3676
0
                                   std::string const& inRPath,
3677
0
                                   char const* /*se_name*/, std::string*
3678
0
                                   /*emsg*/) -> bool {
3679
0
    if (inRPath != newRPath) {
3680
0
      outRPath = newRPath;
3681
0
    }
3682
0
    return true;
3683
0
  };
3684
3685
0
  return AdjustRPathELF(file, MakeEmptyCallback(newRPath), adjustCallback,
3686
0
                        emsg, changed);
3687
0
}
3688
static cm::optional<bool> ChangeRPathXCOFF(std::string const& file,
3689
                                           std::string const& oldRPath,
3690
                                           std::string const& newRPath,
3691
                                           bool removeEnvironmentRPath,
3692
                                           std::string* emsg, bool* changed)
3693
0
{
3694
0
  if (changed) {
3695
0
    *changed = false;
3696
0
  }
3697
0
#if !defined(CMake_USE_XCOFF_PARSER)
3698
0
  (void)file;
3699
0
  (void)oldRPath;
3700
0
  (void)newRPath;
3701
0
  (void)removeEnvironmentRPath;
3702
0
  (void)emsg;
3703
0
  return cm::nullopt;
3704
#else
3705
  bool chg = false;
3706
  cmXCOFF xcoff(file.c_str(), cmXCOFF::Mode::ReadWrite);
3707
  if (!xcoff) {
3708
    return cm::nullopt; // Not a valid XCOFF file
3709
  }
3710
  if (cm::optional<cm::string_view> maybeLibPath = xcoff.GetLibPath()) {
3711
    cm::string_view libPath = *maybeLibPath;
3712
    // Make sure the current rpath contains the old rpath.
3713
    std::string::size_type pos = cmSystemToolsFindRPath(libPath, oldRPath);
3714
    if (pos == std::string::npos) {
3715
      // If it contains the new rpath instead then it is okay.
3716
      if (cmSystemToolsFindRPath(libPath, newRPath) != std::string::npos) {
3717
        return true;
3718
      }
3719
      if (emsg) {
3720
        *emsg = cmStrCat("The current RPATH is:\n"
3721
                         "  ",
3722
                         libPath,
3723
                         "\n"
3724
                         "which does not contain:\n"
3725
                         "  ",
3726
                         oldRPath,
3727
                         "\n"
3728
                         "as was expected.");
3729
      }
3730
      return false;
3731
    }
3732
3733
    // The prefix is either empty or ends in a ':'.
3734
    cm::string_view prefix = libPath.substr(0, pos);
3735
    if (newRPath.empty() && !prefix.empty()) {
3736
      prefix.remove_suffix(1);
3737
    }
3738
3739
    // The suffix is either empty or starts in a ':'.
3740
    cm::string_view suffix = libPath.substr(pos + oldRPath.length());
3741
3742
    // Construct the new value which preserves the part of the path
3743
    // not being changed.
3744
    std::string newLibPath;
3745
    if (!removeEnvironmentRPath) {
3746
      newLibPath = std::string(prefix);
3747
    }
3748
    newLibPath += newRPath;
3749
    newLibPath += suffix;
3750
3751
    chg = xcoff.SetLibPath(newLibPath);
3752
  }
3753
  if (!xcoff) {
3754
    if (emsg) {
3755
      *emsg = xcoff.GetErrorMessage();
3756
    }
3757
    return false;
3758
  }
3759
3760
  // Everything was updated successfully.
3761
  if (changed) {
3762
    *changed = chg;
3763
  }
3764
  return true;
3765
#endif
3766
0
}
3767
3768
static cm::optional<bool> SetRPathXCOFF(std::string const& /*file*/,
3769
                                        std::string const& /*newRPath*/,
3770
                                        std::string* /*emsg*/,
3771
                                        bool* /*changed*/)
3772
0
{
3773
0
  return cm::nullopt; // Not implemented.
3774
0
}
3775
3776
bool cmSystemTools::ChangeRPath(std::string const& file,
3777
                                std::string const& oldRPath,
3778
                                std::string const& newRPath,
3779
                                bool removeEnvironmentRPath, std::string* emsg,
3780
                                bool* changed)
3781
0
{
3782
0
  if (cm::optional<bool> result = ChangeRPathELF(
3783
0
        file, oldRPath, newRPath, removeEnvironmentRPath, emsg, changed)) {
3784
0
    return result.value();
3785
0
  }
3786
0
  if (cm::optional<bool> result = ChangeRPathXCOFF(
3787
0
        file, oldRPath, newRPath, removeEnvironmentRPath, emsg, changed)) {
3788
0
    return result.value();
3789
0
  }
3790
  // The file format is not recognized.  Assume it has no RPATH.
3791
0
  if (newRPath.empty()) {
3792
    // The caller wanted no RPATH anyway.
3793
0
    return true;
3794
0
  }
3795
0
  if (emsg) {
3796
0
    *emsg = "The file format is not recognized.";
3797
0
  }
3798
0
  return false;
3799
0
}
3800
3801
bool cmSystemTools::SetRPath(std::string const& file,
3802
                             std::string const& newRPath, std::string* emsg,
3803
                             bool* changed)
3804
0
{
3805
0
  if (cm::optional<bool> result = SetRPathELF(file, newRPath, emsg, changed)) {
3806
0
    return result.value();
3807
0
  }
3808
0
  if (cm::optional<bool> result =
3809
0
        SetRPathXCOFF(file, newRPath, emsg, changed)) {
3810
0
    return result.value();
3811
0
  }
3812
  // The file format is not recognized.  Assume it has no RPATH.
3813
0
  if (newRPath.empty()) {
3814
    // The caller wanted no RPATH anyway.
3815
0
    return true;
3816
0
  }
3817
0
  if (emsg) {
3818
0
    *emsg = "The file format is not recognized.";
3819
0
  }
3820
0
  return false;
3821
0
}
3822
3823
namespace {
3824
bool VersionCompare(cmSystemTools::CompareOp op, char const* lhss,
3825
                    char const* rhss)
3826
22.4k
{
3827
22.4k
  char const* endl = lhss;
3828
22.4k
  char const* endr = rhss;
3829
3830
62.5k
  while (((*endl >= '0') && (*endl <= '9')) ||
3831
59.1k
         ((*endr >= '0') && (*endr <= '9'))) {
3832
    // Do component-wise comparison, ignoring leading zeros
3833
    // (components are treated as integers, not as mantissas)
3834
106k
    while (*endl == '0') {
3835
47.4k
      endl++;
3836
47.4k
    }
3837
86.9k
    while (*endr == '0') {
3838
27.7k
      endr++;
3839
27.7k
    }
3840
3841
59.1k
    char const* beginl = endl;
3842
59.1k
    char const* beginr = endr;
3843
3844
    // count significant digits
3845
165k
    while ((*endl >= '0') && (*endl <= '9')) {
3846
105k
      endl++;
3847
105k
    }
3848
117k
    while ((*endr >= '0') && (*endr <= '9')) {
3849
58.7k
      endr++;
3850
58.7k
    }
3851
3852
    // compare number of digits first
3853
59.1k
    ptrdiff_t r = ((endl - beginl) - (endr - beginr));
3854
59.1k
    if (r == 0) {
3855
      // compare the digits if number of digits is equal
3856
56.8k
      r = strncmp(beginl, beginr, endl - beginl);
3857
56.8k
    }
3858
3859
59.1k
    if (r < 0) {
3860
      // lhs < rhs, so true if operation is LESS
3861
10.7k
      return (op & cmSystemTools::OP_LESS) != 0;
3862
10.7k
    }
3863
48.3k
    if (r > 0) {
3864
      // lhs > rhs, so true if operation is GREATER
3865
8.20k
      return (op & cmSystemTools::OP_GREATER) != 0;
3866
8.20k
    }
3867
3868
40.1k
    if (*endr == '.') {
3869
23.4k
      endr++;
3870
23.4k
    }
3871
3872
40.1k
    if (*endl == '.') {
3873
34.3k
      endl++;
3874
34.3k
    }
3875
40.1k
  }
3876
  // lhs == rhs, so true if operation is EQUAL
3877
3.42k
  return (op & cmSystemTools::OP_EQUAL) != 0;
3878
22.4k
}
3879
}
3880
3881
bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
3882
                                   std::string const& lhs,
3883
                                   std::string const& rhs)
3884
1.96k
{
3885
1.96k
  return ::VersionCompare(op, lhs.c_str(), rhs.c_str());
3886
1.96k
}
3887
bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
3888
                                   std::string const& lhs, char const rhs[])
3889
20.4k
{
3890
20.4k
  return ::VersionCompare(op, lhs.c_str(), rhs);
3891
20.4k
}
3892
3893
bool cmSystemTools::VersionCompareEqual(std::string const& lhs,
3894
                                        std::string const& rhs)
3895
0
{
3896
0
  return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, lhs, rhs);
3897
0
}
3898
3899
bool cmSystemTools::VersionCompareGreater(std::string const& lhs,
3900
                                          std::string const& rhs)
3901
338
{
3902
338
  return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, lhs, rhs);
3903
338
}
3904
3905
bool cmSystemTools::VersionCompareGreaterEq(std::string const& lhs,
3906
                                            std::string const& rhs)
3907
338
{
3908
338
  return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, lhs,
3909
338
                                       rhs);
3910
338
}
3911
3912
static size_t cm_strverscmp_find_first_difference_or_end(char const* lhs,
3913
                                                         char const* rhs)
3914
0
{
3915
0
  size_t i = 0;
3916
  /* Step forward until we find a difference or both strings end together.
3917
     The difference may lie on the null-terminator of one string.  */
3918
0
  while (lhs[i] == rhs[i] && lhs[i] != 0) {
3919
0
    ++i;
3920
0
  }
3921
0
  return i;
3922
0
}
3923
3924
static size_t cm_strverscmp_find_digits_begin(char const* s, size_t i)
3925
0
{
3926
  /* Step back until we are not preceded by a digit.  */
3927
0
  while (i > 0 && cmsysString_isdigit(s[i - 1])) {
3928
0
    --i;
3929
0
  }
3930
0
  return i;
3931
0
}
3932
3933
static size_t cm_strverscmp_find_digits_end(char const* s, size_t i)
3934
0
{
3935
  /* Step forward over digits.  */
3936
0
  while (cmsysString_isdigit(s[i])) {
3937
0
    ++i;
3938
0
  }
3939
0
  return i;
3940
0
}
3941
3942
static size_t cm_strverscmp_count_leading_zeros(char const* s, size_t b)
3943
0
{
3944
0
  size_t i = b;
3945
  /* Step forward over zeros that are followed by another digit.  */
3946
0
  while (s[i] == '0' && cmsysString_isdigit(s[i + 1])) {
3947
0
    ++i;
3948
0
  }
3949
0
  return i - b;
3950
0
}
3951
3952
static int cm_strverscmp(char const* lhs, char const* rhs)
3953
0
{
3954
0
  size_t const i = cm_strverscmp_find_first_difference_or_end(lhs, rhs);
3955
0
  if (lhs[i] != rhs[i]) {
3956
    /* The strings differ starting at 'i'.  Check for a digit sequence.  */
3957
0
    size_t const b = cm_strverscmp_find_digits_begin(lhs, i);
3958
0
    if (b != i ||
3959
0
        (cmsysString_isdigit(lhs[i]) && cmsysString_isdigit(rhs[i]))) {
3960
      /* A digit sequence starts at 'b', preceding or at 'i'.  */
3961
3962
      /* Look for leading zeros, implying a leading decimal point.  */
3963
0
      size_t const lhs_zeros = cm_strverscmp_count_leading_zeros(lhs, b);
3964
0
      size_t const rhs_zeros = cm_strverscmp_count_leading_zeros(rhs, b);
3965
0
      if (lhs_zeros != rhs_zeros) {
3966
        /* The side with more leading zeros orders first.  */
3967
0
        return rhs_zeros > lhs_zeros ? 1 : -1;
3968
0
      }
3969
0
      if (lhs_zeros == 0) {
3970
        /* No leading zeros; compare digit sequence lengths.  */
3971
0
        size_t const lhs_end = cm_strverscmp_find_digits_end(lhs, i);
3972
0
        size_t const rhs_end = cm_strverscmp_find_digits_end(rhs, i);
3973
0
        if (lhs_end != rhs_end) {
3974
          /* The side with fewer digits orders first.  */
3975
0
          return lhs_end > rhs_end ? 1 : -1;
3976
0
        }
3977
0
      }
3978
0
    }
3979
0
  }
3980
3981
  /* Ordering was not decided by digit sequence lengths; compare bytes.  */
3982
0
  return lhs[i] - rhs[i];
3983
0
}
3984
3985
int cmSystemTools::strverscmp(std::string const& lhs, std::string const& rhs)
3986
0
{
3987
0
  return cm_strverscmp(lhs.c_str(), rhs.c_str());
3988
0
}
3989
3990
static cm::optional<bool> RemoveRPathELF(std::string const& file,
3991
                                         std::string* emsg, bool* removed)
3992
0
{
3993
0
  if (removed) {
3994
0
    *removed = false;
3995
0
  }
3996
0
  int zeroCount = 0;
3997
0
  unsigned long zeroPosition[2] = { 0, 0 };
3998
0
  unsigned long zeroSize[2] = { 0, 0 };
3999
0
  unsigned long bytesBegin = 0;
4000
0
  std::vector<char> bytes;
4001
0
  {
4002
    // Parse the ELF binary.
4003
0
    cmELF elf(file.c_str());
4004
0
    if (!elf) {
4005
0
      return cm::nullopt; // Not a valid ELF file.
4006
0
    }
4007
4008
    // Get the RPATH and RUNPATH entries from it and sort them by index
4009
    // in the dynamic section header.
4010
0
    int se_count = 0;
4011
0
    cmELF::StringEntry const* se[2] = { nullptr, nullptr };
4012
0
    if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
4013
0
      se[se_count++] = se_rpath;
4014
0
    }
4015
0
    if (cmELF::StringEntry const* se_runpath = elf.GetRunPath()) {
4016
0
      se[se_count++] = se_runpath;
4017
0
    }
4018
0
    if (se_count == 0) {
4019
      // There is no RPATH or RUNPATH anyway.
4020
0
      return true;
4021
0
    }
4022
0
    if (se_count == 2 && se[0] && se[1] &&
4023
0
        se[1]->IndexInSection < se[0]->IndexInSection) {
4024
0
      std::swap(se[0], se[1]);
4025
0
    }
4026
4027
    // Obtain a copy of the dynamic entries
4028
0
    cmELF::DynamicEntryList dentries = elf.GetDynamicEntries();
4029
0
    if (dentries.empty()) {
4030
      // This should happen only for invalid ELF files where a DT_NULL
4031
      // appears before the end of the table.
4032
0
      if (emsg) {
4033
0
        *emsg = "DYNAMIC section contains a DT_NULL before the end.";
4034
0
      }
4035
0
      return false;
4036
0
    }
4037
4038
    // Save information about the string entries to be zeroed.
4039
0
    zeroCount = se_count;
4040
0
    for (int i = 0; i < se_count; ++i) {
4041
0
      zeroPosition[i] = se[i]->Position;
4042
0
      zeroSize[i] = se[i]->Size;
4043
0
    }
4044
4045
    // Get size of one DYNAMIC entry
4046
0
    unsigned long const sizeof_dentry =
4047
0
      elf.GetDynamicEntryPosition(1) - elf.GetDynamicEntryPosition(0);
4048
4049
    // Adjust the entry list as necessary to remove the run path
4050
0
    unsigned long entriesErased = 0;
4051
0
    for (auto it = dentries.begin(); it != dentries.end();) {
4052
0
      if (it->first == cmELF::TagRPath || it->first == cmELF::TagRunPath) {
4053
0
        it = dentries.erase(it);
4054
0
        entriesErased++;
4055
0
        continue;
4056
0
      }
4057
0
      if (it->first == cmELF::TagMipsRldMapRel && elf.IsMIPS()) {
4058
        // Background: debuggers need to know the "linker map" which contains
4059
        // the addresses each dynamic object is loaded at. Most arches use
4060
        // the DT_DEBUG tag which the dynamic linker writes to (directly) and
4061
        // contain the location of the linker map, however on MIPS the
4062
        // .dynamic section is always read-only so this is not possible. MIPS
4063
        // objects instead contain a DT_MIPS_RLD_MAP tag which contains the
4064
        // address where the dynamic linker will write to (an indirect
4065
        // version of DT_DEBUG). Since this doesn't work when using PIE, a
4066
        // relative equivalent was created - DT_MIPS_RLD_MAP_REL. Since this
4067
        // version contains a relative offset, moving it changes the
4068
        // calculated address. This may cause the dynamic linker to write
4069
        // into memory it should not be changing.
4070
        //
4071
        // To fix this, we adjust the value of DT_MIPS_RLD_MAP_REL here. If
4072
        // we move it up by n bytes, we add n bytes to the value of this tag.
4073
0
        it->second += entriesErased * sizeof_dentry;
4074
0
      }
4075
4076
0
      it++;
4077
0
    }
4078
4079
    // Encode new entries list
4080
0
    bytes = elf.EncodeDynamicEntries(dentries);
4081
0
    bytesBegin = elf.GetDynamicEntryPosition(0);
4082
0
  }
4083
4084
0
  FileModeGuard file_mode_guard(file, emsg);
4085
0
  if (file_mode_guard.HasErrors()) {
4086
0
    return false;
4087
0
  }
4088
4089
  // Open the file for update.
4090
0
  cmsys::ofstream f(file.c_str(),
4091
0
                    std::ios::in | std::ios::out | std::ios::binary);
4092
0
  if (!f) {
4093
0
    if (emsg) {
4094
0
      *emsg = "Error opening file for update.";
4095
0
    }
4096
0
    return false;
4097
0
  }
4098
4099
  // Write the new DYNAMIC table header.
4100
0
  if (!f.seekp(bytesBegin)) {
4101
0
    if (emsg) {
4102
0
      *emsg = "Error seeking to DYNAMIC table header for RPATH.";
4103
0
    }
4104
0
    return false;
4105
0
  }
4106
0
  if (!f.write(bytes.data(), bytes.size())) {
4107
0
    if (emsg) {
4108
0
      *emsg = "Error replacing DYNAMIC table header.";
4109
0
    }
4110
0
    return false;
4111
0
  }
4112
4113
  // Fill the RPATH and RUNPATH strings with zero bytes.
4114
0
  for (int i = 0; i < zeroCount; ++i) {
4115
0
    if (!f.seekp(zeroPosition[i])) {
4116
0
      if (emsg) {
4117
0
        *emsg = "Error seeking to RPATH position.";
4118
0
      }
4119
0
      return false;
4120
0
    }
4121
0
    for (unsigned long j = 0; j < zeroSize[i]; ++j) {
4122
0
      f << '\0';
4123
0
    }
4124
0
    if (!f) {
4125
0
      if (emsg) {
4126
0
        *emsg = "Error writing the empty rpath string to the file.";
4127
0
      }
4128
0
      return false;
4129
0
    }
4130
0
  }
4131
4132
  // Close the handle to allow further operations on the file
4133
0
  f.close();
4134
4135
0
  if (!file_mode_guard.Restore(emsg)) {
4136
0
    return false;
4137
0
  }
4138
4139
  // Everything was updated successfully.
4140
0
  if (removed) {
4141
0
    *removed = true;
4142
0
  }
4143
0
  return true;
4144
0
}
4145
4146
static cm::optional<bool> RemoveRPathXCOFF(std::string const& file,
4147
                                           std::string* emsg, bool* removed)
4148
0
{
4149
0
  if (removed) {
4150
0
    *removed = false;
4151
0
  }
4152
0
#if !defined(CMake_USE_XCOFF_PARSER)
4153
0
  (void)file;
4154
0
  (void)emsg;
4155
0
  return cm::nullopt; // Cannot handle XCOFF files.
4156
#else
4157
  bool rm = false;
4158
4159
  FileModeGuard file_mode_guard(file, emsg);
4160
  if (file_mode_guard.HasErrors()) {
4161
    return false;
4162
  }
4163
4164
  {
4165
    cmXCOFF xcoff(file.c_str(), cmXCOFF::Mode::ReadWrite);
4166
    if (!xcoff) {
4167
      return cm::nullopt; // Not a valid XCOFF file.
4168
    }
4169
    rm = xcoff.RemoveLibPath();
4170
    if (!xcoff) {
4171
      if (emsg) {
4172
        *emsg = xcoff.GetErrorMessage();
4173
      }
4174
      return false;
4175
    }
4176
  }
4177
4178
  if (!file_mode_guard.Restore(emsg)) {
4179
    return false;
4180
  }
4181
4182
  if (removed) {
4183
    *removed = rm;
4184
  }
4185
  return true;
4186
#endif
4187
0
}
4188
bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg,
4189
                                bool* removed)
4190
0
{
4191
0
  if (cm::optional<bool> result = RemoveRPathELF(file, emsg, removed)) {
4192
0
    return result.value();
4193
0
  }
4194
0
  if (cm::optional<bool> result = RemoveRPathXCOFF(file, emsg, removed)) {
4195
0
    return result.value();
4196
0
  }
4197
  // The file format is not recognized.  Assume it has no RPATH.
4198
0
  return true;
4199
0
}
4200
4201
bool cmSystemTools::CheckRPath(std::string const& file,
4202
                               std::string const& newRPath)
4203
0
{
4204
  // Parse the ELF binary.
4205
0
  cmELF elf(file.c_str());
4206
0
  if (elf) {
4207
    // Get the RPATH or RUNPATH entry from it.
4208
0
    cmELF::StringEntry const* se = elf.GetRPath();
4209
0
    if (!se) {
4210
0
      se = elf.GetRunPath();
4211
0
    }
4212
4213
    // Make sure the current rpath contains the new rpath.
4214
0
    if (newRPath.empty()) {
4215
0
      if (!se) {
4216
0
        return true;
4217
0
      }
4218
0
    } else {
4219
0
      if (se &&
4220
0
          cmSystemToolsFindRPath(se->Value, newRPath) != std::string::npos) {
4221
0
        return true;
4222
0
      }
4223
0
    }
4224
0
    return false;
4225
0
  }
4226
#if defined(CMake_USE_XCOFF_PARSER)
4227
  // Parse the XCOFF binary.
4228
  cmXCOFF xcoff(file.c_str());
4229
  if (xcoff) {
4230
    if (cm::optional<cm::string_view> libPath = xcoff.GetLibPath()) {
4231
      if (cmSystemToolsFindRPath(*libPath, newRPath) != std::string::npos) {
4232
        return true;
4233
      }
4234
    }
4235
    return false;
4236
  }
4237
#endif
4238
  // The file format is not recognized.  Assume it has no RPATH.
4239
  // Therefore we succeed if the new rpath is empty anyway.
4240
0
  return newRPath.empty();
4241
0
}
4242
4243
cmsys::Status cmSystemTools::RepeatedRemoveDirectory(std::string const& dir)
4244
0
{
4245
#ifdef _WIN32
4246
  // Windows sometimes locks files temporarily so try a few times.
4247
  WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry();
4248
4249
  cmsys::Status status;
4250
  unsigned int tries = 0;
4251
  while (!(status = cmSystemTools::RemoveADirectory(dir)) &&
4252
         ++tries < retry.Count) {
4253
    cmSystemTools::Delay(retry.Delay);
4254
  }
4255
  return status;
4256
#else
4257
0
  return cmSystemTools::RemoveADirectory(dir);
4258
0
#endif
4259
0
}
4260
4261
std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes)
4262
0
{
4263
0
  std::string out;
4264
0
  for (char c : in) {
4265
0
    char hexCh[4] = { 0, 0, 0, 0 };
4266
0
    hexCh[0] = c;
4267
0
    switch (c) {
4268
0
      case '+':
4269
0
      case '?':
4270
0
      case '\\':
4271
0
      case '&':
4272
0
      case ' ':
4273
0
      case '=':
4274
0
      case '%':
4275
0
        snprintf(hexCh, sizeof(hexCh), "%%%02X", static_cast<unsigned int>(c));
4276
0
        break;
4277
0
      case '/':
4278
0
        if (escapeSlashes) {
4279
0
          strcpy(hexCh, "%2F");
4280
0
        }
4281
0
        break;
4282
0
      default:
4283
0
        break;
4284
0
    }
4285
0
    out.append(hexCh);
4286
0
  }
4287
0
  return out;
4288
0
}
4289
4290
cm::optional<cmSystemTools::DirCase> cmSystemTools::GetDirCase(
4291
  std::string const& dir)
4292
0
{
4293
0
  if (!cmSystemTools::FileIsDirectory(dir)) {
4294
0
    return cm::nullopt;
4295
0
  }
4296
#if defined(_WIN32) || defined(__APPLE__)
4297
  return DirCase::Insensitive;
4298
#elif defined(__linux__)
4299
0
  int fd = open(dir.c_str(), O_RDONLY);
4300
0
  if (fd == -1) {
4301
    // cannot open dir but it exists, assume dir is case sensitive.
4302
0
    return DirCase::Sensitive;
4303
0
  }
4304
0
  int attr = 0;
4305
0
  int ioctl_res = ioctl(fd, FS_IOC_GETFLAGS, &attr);
4306
0
  close(fd);
4307
4308
0
  if (ioctl_res == -1) {
4309
0
    return DirCase::Sensitive;
4310
0
  }
4311
4312
  // FS_CASEFOLD_FD from linux/fs.h, in Linux libc-dev 5.4+
4313
  // For compat with old libc-dev, define it here.
4314
0
  int const CMAKE_FS_CASEFOLD_FL = 0x40000000;
4315
0
  return (attr & CMAKE_FS_CASEFOLD_FL) != 0 ? DirCase::Insensitive
4316
0
                                            : DirCase::Sensitive;
4317
#else
4318
  return DirCase::Sensitive;
4319
#endif
4320
0
}
4321
4322
cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
4323
                                           std::string const& newName)
4324
0
{
4325
0
  cmsys::Status status =
4326
0
    cmSystemTools::CreateSymlinkQuietly(origName, newName);
4327
0
  if (!status) {
4328
0
    cmSystemTools::Error(cmStrCat("failed to create symbolic link '", newName,
4329
0
                                  "': ", status.GetString()));
4330
0
  }
4331
0
  return status;
4332
0
}
4333
4334
cmsys::Status cmSystemTools::CreateSymlinkQuietly(std::string const& origName,
4335
                                                  std::string const& newName)
4336
0
{
4337
0
  uv_fs_t req;
4338
0
  int flags = 0;
4339
#if defined(_WIN32)
4340
  if (cmsys::SystemTools::FileIsDirectory(origName)) {
4341
    flags |= UV_FS_SYMLINK_DIR;
4342
  }
4343
#endif
4344
0
  int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
4345
0
                          flags, nullptr);
4346
0
  cmsys::Status status;
4347
0
  if (err) {
4348
#if defined(_WIN32)
4349
    status = cmsys::Status::Windows(uv_fs_get_system_error(&req));
4350
#elif UV_VERSION_MAJOR > 1 || (UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 38)
4351
    status = cmsys::Status::POSIX(uv_fs_get_system_error(&req));
4352
#else
4353
    status = cmsys::Status::POSIX(-err);
4354
#endif
4355
0
  }
4356
0
  return status;
4357
0
}
4358
4359
cmsys::Status cmSystemTools::CreateLink(std::string const& origName,
4360
                                        std::string const& newName)
4361
0
{
4362
0
  cmsys::Status status = cmSystemTools::CreateLinkQuietly(origName, newName);
4363
0
  if (!status) {
4364
0
    cmSystemTools::Error(
4365
0
      cmStrCat("failed to create link '", newName, "': ", status.GetString()));
4366
0
  }
4367
0
  return status;
4368
0
}
4369
4370
cmsys::Status cmSystemTools::CreateLinkQuietly(std::string const& origName,
4371
                                               std::string const& newName)
4372
0
{
4373
0
  uv_fs_t req;
4374
0
  int err =
4375
0
    uv_fs_link(nullptr, &req, origName.c_str(), newName.c_str(), nullptr);
4376
0
  cmsys::Status status;
4377
0
  if (err) {
4378
#if defined(_WIN32)
4379
    status = cmsys::Status::Windows(uv_fs_get_system_error(&req));
4380
#elif UV_VERSION_MAJOR > 1 || (UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR >= 38)
4381
    status = cmsys::Status::POSIX(uv_fs_get_system_error(&req));
4382
#else
4383
    status = cmsys::Status::POSIX(-err);
4384
#endif
4385
0
  }
4386
0
  return status;
4387
0
}
4388
4389
cm::string_view cmSystemTools::GetSystemName()
4390
9.84k
{
4391
#if defined(_WIN32)
4392
  return "Windows";
4393
#elif defined(__MSYS__)
4394
  return "MSYS";
4395
#elif defined(__CYGWIN__)
4396
  return "CYGWIN";
4397
#elif defined(__ANDROID__)
4398
  return "Android";
4399
#else
4400
9.84k
  static struct utsname uts_name;
4401
9.84k
  static bool initialized = false;
4402
9.84k
  static cm::string_view systemName;
4403
9.84k
  if (initialized) {
4404
9.84k
    return systemName;
4405
9.84k
  }
4406
4
  if (uname(&uts_name) >= 0) {
4407
4
    initialized = true;
4408
4
    systemName = uts_name.sysname;
4409
4410
4
    if (cmIsOff(systemName)) {
4411
0
      systemName = "UnknownOS";
4412
0
    }
4413
4414
    // fix for BSD/OS, remove the /
4415
4
    static cmsys::RegularExpression const bsdOsRegex("BSD.OS");
4416
4
    cmsys::RegularExpressionMatch match;
4417
4
    if (bsdOsRegex.find(uts_name.sysname, match)) {
4418
0
      systemName = "BSDOS";
4419
0
    }
4420
4421
4
    return systemName;
4422
4
  }
4423
0
  return "";
4424
4
#endif
4425
4
}
4426
4427
char cmSystemTools::GetSystemPathlistSeparator()
4428
2.91k
{
4429
#if defined(_WIN32)
4430
  return ';';
4431
#else
4432
2.91k
  return ':';
4433
2.91k
#endif
4434
2.91k
}
4435
4436
cm::string_view cmSystemTools::GetFilenameNameView(cm::string_view filename)
4437
59.3k
{
4438
// implementation mostly taken from cmsys::SystemTools
4439
#if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
4440
  cm::static_string_view separators = "/\\"_s;
4441
#else
4442
59.3k
  char separators = '/';
4443
59.3k
#endif
4444
59.3k
  std::string::size_type slash_pos = filename.find_last_of(separators);
4445
59.3k
  if (slash_pos == std::string::npos) {
4446
44.6k
    return filename;
4447
44.6k
  }
4448
14.7k
  return filename.substr(slash_pos + 1);
4449
59.3k
}
4450
4451
cm::string_view cmSystemTools::GetFilenameLastExtensionView(
4452
  cm::string_view filename)
4453
59.3k
{
4454
59.3k
  cm::string_view name = cmSystemTools::GetFilenameNameView(filename);
4455
59.3k
  cm::string_view::size_type dot_pos = name.rfind('.');
4456
59.3k
  if (dot_pos == std::string::npos) {
4457
8.33k
    return cm::string_view();
4458
8.33k
  }
4459
51.0k
  return name.substr(dot_pos);
4460
59.3k
}