Coverage Report

Created: 2026-02-09 06:05

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