Coverage Report

Created: 2026-09-14 06:43

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