Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/kwsys/SystemTools.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
3
#ifdef __osf__
4
#  define _OSF_SOURCE
5
#  define _POSIX_C_SOURCE 199506L
6
#  define _XOPEN_SOURCE_EXTENDED
7
#endif
8
9
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
10
#  define KWSYS_WINDOWS_DIRS
11
#else
12
#  if defined(__SUNPRO_CC)
13
#    include <fcntl.h>
14
#  endif
15
#endif
16
17
#if defined(_WIN32) && !defined(_WIN32_WINNT)
18
#  define _WIN32_WINNT _WIN32_WINNT_VISTA
19
#endif
20
21
#include "kwsysPrivate.h"
22
#include KWSYS_HEADER(RegularExpression.hxx)
23
#include KWSYS_HEADER(SystemTools.hxx)
24
#include KWSYS_HEADER(Directory.hxx)
25
#include KWSYS_HEADER(FStream.hxx)
26
#include KWSYS_HEADER(Encoding.h)
27
#include KWSYS_HEADER(Encoding.hxx)
28
29
#include <algorithm>
30
#include <cctype>
31
#include <fstream>
32
#include <iostream>
33
#include <set>
34
#include <sstream>
35
#include <utility>
36
#include <vector>
37
38
#ifdef _WIN32
39
#  include <cwchar>
40
#  include <unordered_map>
41
#endif
42
43
// Work-around CMake dependency scanning limitation.  This must
44
// duplicate the above list of headers.
45
#if 0
46
#  include "Directory.hxx.in"
47
#  include "Encoding.hxx.in"
48
#  include "FStream.hxx.in"
49
#  include "RegularExpression.hxx.in"
50
#  include "SystemTools.hxx.in"
51
#endif
52
53
#ifdef _MSC_VER
54
#  pragma warning(disable : 4786)
55
#endif
56
57
#if defined(__sgi) && !defined(__GNUC__)
58
#  pragma set woff 1375 /* base class destructor not virtual */
59
#endif
60
61
#include <cctype>
62
#include <cerrno>
63
#include <cstdio>
64
#include <cstdlib>
65
#include <cstring>
66
#include <ctime>
67
68
#if defined(_WIN32) && !defined(_MSC_VER) && defined(__GNUC__)
69
#  include <strings.h> /* for strcasecmp */
70
#endif
71
72
#ifdef _MSC_VER
73
#  define umask _umask
74
#endif
75
76
// support for realpath call
77
#ifndef _WIN32
78
#  include <climits>
79
#  include <pwd.h>
80
#  include <sys/ioctl.h>
81
#  include <sys/time.h>
82
#  include <sys/wait.h>
83
#  include <unistd.h>
84
#  include <utime.h>
85
#  ifndef __VMS
86
#    include <sys/param.h>
87
#    include <termios.h>
88
#  endif
89
#  include <csignal> /* sigprocmask */
90
#endif
91
92
#ifdef __linux
93
#  include <linux/fs.h>
94
#endif
95
96
#if defined(__APPLE__) &&                                                     \
97
  (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - 0 >= 101200)
98
#  define KWSYS_SYSTEMTOOLS_HAVE_MACOS_COPYFILE_CLONE
99
#  include <copyfile.h>
100
#  include <sys/stat.h>
101
#endif
102
103
// Windows API.
104
#if defined(_WIN32)
105
#  include <windows.h>
106
#  include <winioctl.h>
107
#  ifndef INVALID_FILE_ATTRIBUTES
108
#    define INVALID_FILE_ATTRIBUTES ((DWORD) - 1)
109
#  endif
110
#  ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
111
#    define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
112
#  endif
113
#  if defined(_MSC_VER) && _MSC_VER >= 1800
114
#    define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
115
#  endif
116
#  ifndef IO_REPARSE_TAG_APPEXECLINK
117
#    define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL)
118
#  endif
119
// from ntifs.h, which can only be used by drivers
120
typedef struct _REPARSE_DATA_BUFFER
121
{
122
  ULONG ReparseTag;
123
  USHORT ReparseDataLength;
124
  USHORT Reserved;
125
  union
126
  {
127
    struct
128
    {
129
      USHORT SubstituteNameOffset;
130
      USHORT SubstituteNameLength;
131
      USHORT PrintNameOffset;
132
      USHORT PrintNameLength;
133
      ULONG Flags;
134
      WCHAR PathBuffer[1];
135
    } SymbolicLinkReparseBuffer;
136
    struct
137
    {
138
      USHORT SubstituteNameOffset;
139
      USHORT SubstituteNameLength;
140
      USHORT PrintNameOffset;
141
      USHORT PrintNameLength;
142
      WCHAR PathBuffer[1];
143
    } MountPointReparseBuffer;
144
    struct
145
    {
146
      UCHAR DataBuffer[1];
147
    } GenericReparseBuffer;
148
    struct
149
    {
150
      ULONG Version;
151
      WCHAR StringList[1];
152
      // In version 3, there are 4 NUL-terminated strings:
153
      // * Package ID
154
      // * Entry Point
155
      // * Executable Path
156
      // * Application Type
157
    } AppExecLinkReparseBuffer;
158
  } DUMMYUNIONNAME;
159
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
160
161
namespace {
162
WCHAR* GetAppExecLink(PREPARSE_DATA_BUFFER data, size_t& len)
163
{
164
  // We only know the layout of version 3.
165
  if (data->AppExecLinkReparseBuffer.Version != 3) {
166
    return nullptr;
167
  }
168
169
  WCHAR* pstr = data->AppExecLinkReparseBuffer.StringList;
170
171
  // Skip the package id and entry point strings.
172
  for (int i = 0; i < 2; ++i) {
173
    len = std::wcslen(pstr);
174
    if (len == 0) {
175
      return nullptr;
176
    }
177
    pstr += len + 1;
178
  }
179
180
  // The third string is the executable path.
181
  len = std::wcslen(pstr);
182
  if (len == 0) {
183
    return nullptr;
184
  }
185
  return pstr;
186
}
187
}
188
#endif
189
190
#if !KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H
191
extern char** environ;
192
#endif
193
194
// getpwnam doesn't exist on Windows and Cray Xt3/Catamount
195
// same for TIOCGWINSZ
196
#if defined(_WIN32) || defined(__LIBCATAMOUNT__) ||                           \
197
  (defined(HAVE_GETPWNAM) && HAVE_GETPWNAM == 0)
198
#  undef HAVE_GETPWNAM
199
#  undef HAVE_TTY_INFO
200
#else
201
#  define HAVE_GETPWNAM 1
202
#  define HAVE_TTY_INFO 1
203
#endif
204
205
0
#define VTK_URL_PROTOCOL_REGEX "([a-zA-Z0-9]*)://(.*)"
206
#define VTK_URL_REGEX                                                         \
207
0
  "([a-zA-Z0-9]*)://(([A-Za-z0-9]+)(:([^:@]+))?@)?([^:@/]*)(:([0-9]+))?/"     \
208
0
  "(.+)?"
209
0
#define VTK_URL_BYTE_REGEX "%[0-9a-fA-F][0-9a-fA-F]"
210
#ifdef _MSC_VER
211
#  include <sys/utime.h>
212
#else
213
#  include <utime.h>
214
#endif
215
216
// This is a hack to prevent warnings about these functions being
217
// declared but not referenced.
218
#if defined(__sgi) && !defined(__GNUC__)
219
#  include <sys/termios.h>
220
namespace KWSYS_NAMESPACE {
221
class SystemToolsHack
222
{
223
public:
224
  enum
225
  {
226
    Ref1 = sizeof(cfgetospeed(0)),
227
    Ref2 = sizeof(cfgetispeed(0)),
228
    Ref3 = sizeof(tcgetattr(0, 0)),
229
    Ref4 = sizeof(tcsetattr(0, 0, 0)),
230
    Ref5 = sizeof(cfsetospeed(0, 0)),
231
    Ref6 = sizeof(cfsetispeed(0, 0))
232
  };
233
};
234
}
235
#endif
236
237
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
238
#  include <direct.h>
239
#  include <io.h>
240
#  define _unlink unlink
241
#endif
242
243
/* The maximum length of a file name.  */
244
#if defined(PATH_MAX)
245
0
#  define KWSYS_SYSTEMTOOLS_MAXPATH PATH_MAX
246
#elif defined(MAXPATHLEN)
247
#  define KWSYS_SYSTEMTOOLS_MAXPATH MAXPATHLEN
248
#else
249
#  define KWSYS_SYSTEMTOOLS_MAXPATH 16384
250
#endif
251
252
#if defined(__BEOS__) && !defined(__ZETA__)
253
#  include <be/kernel/OS.h>
254
#  include <be/storage/Path.h>
255
256
// BeOS 5 doesn't have usleep(), but it has snooze(), which is identical.
257
static inline void usleep(unsigned int msec)
258
{
259
  ::snooze(msec);
260
}
261
262
// BeOS 5 also doesn't have realpath(), but its C++ API offers something close.
263
static inline char* realpath(char const* path, char* resolved_path)
264
{
265
  size_t const maxlen = KWSYS_SYSTEMTOOLS_MAXPATH;
266
  snprintf(resolved_path, maxlen, "%s", path);
267
  BPath normalized(resolved_path, nullptr, true);
268
  char const* resolved = normalized.Path();
269
  if (resolved) // nullptr == No such file.
270
  {
271
    if (snprintf(resolved_path, maxlen, "%s", resolved) < maxlen) {
272
      return resolved_path;
273
    }
274
  }
275
  return nullptr; // something went wrong.
276
}
277
#endif
278
279
#ifdef _WIN32
280
static time_t windows_filetime_to_posix_time(const FILETIME& ft)
281
{
282
  LARGE_INTEGER date;
283
  date.HighPart = ft.dwHighDateTime;
284
  date.LowPart = ft.dwLowDateTime;
285
286
  // removes the diff between 1970 and 1601
287
  date.QuadPart -= ((LONGLONG)(369 * 365 + 89) * 24 * 3600 * 10000000);
288
289
  // converts back from 100-nanoseconds to seconds
290
  return date.QuadPart / 10000000;
291
}
292
#endif
293
294
#ifdef KWSYS_WINDOWS_DIRS
295
#  include <wctype.h>
296
#  ifdef _MSC_VER
297
typedef KWSYS_NAMESPACE::SystemTools::mode_t mode_t;
298
#  endif
299
300
inline int Mkdir(std::string const& dir, mode_t const* mode)
301
{
302
  int ret =
303
    _wmkdir(KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str());
304
  if (ret == 0 && mode)
305
    KWSYS_NAMESPACE::SystemTools::SetPermissions(dir, *mode);
306
  return ret;
307
}
308
inline int Rmdir(std::string const& dir)
309
{
310
  return _wrmdir(
311
    KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str());
312
}
313
inline char const* Getcwd(char* buf, unsigned int len)
314
{
315
  std::vector<wchar_t> w_buf(len);
316
  if (_wgetcwd(&w_buf[0], len)) {
317
    size_t nlen = kwsysEncoding_wcstombs(buf, &w_buf[0], len);
318
    if (nlen == static_cast<size_t>(-1)) {
319
      return 0;
320
    }
321
    if (nlen < len) {
322
      // make sure the drive letter is capital
323
      if (nlen > 1 && buf[1] == ':') {
324
        buf[0] =
325
          static_cast<char>(toupper(static_cast<unsigned char>(buf[0])));
326
      }
327
      return buf;
328
    }
329
  }
330
  return 0;
331
}
332
inline int Chdir(std::string const& dir)
333
{
334
  // We cannot use ToWindowsExtendedPath here because that causes a
335
  // UNC path to be recorded as the process working directory, and
336
  // can break child processes.
337
  return _wchdir(KWSYS_NAMESPACE::Encoding::ToWide(dir).c_str());
338
}
339
inline void Realpath(std::string const& path, std::string& resolved_path,
340
                     std::string* errorMessage = nullptr)
341
{
342
  std::wstring tmp = KWSYS_NAMESPACE::Encoding::ToWide(path);
343
  wchar_t fullpath[MAX_PATH];
344
  DWORD bufferLen = GetFullPathNameW(
345
    tmp.c_str(), sizeof(fullpath) / sizeof(fullpath[0]), fullpath, nullptr);
346
  if (bufferLen < sizeof(fullpath) / sizeof(fullpath[0])) {
347
    resolved_path = KWSYS_NAMESPACE::Encoding::ToNarrow(fullpath);
348
    KWSYS_NAMESPACE::SystemTools::ConvertToUnixSlashes(resolved_path);
349
  } else if (errorMessage) {
350
    if (bufferLen) {
351
      *errorMessage = "Destination path buffer size too small.";
352
    } else if (unsigned int errorId = GetLastError()) {
353
      LPSTR message = nullptr;
354
      DWORD size = FormatMessageA(
355
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
356
          FORMAT_MESSAGE_IGNORE_INSERTS,
357
        nullptr, errorId, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
358
        (LPSTR)&message, 0, nullptr);
359
      *errorMessage = std::string(message, size);
360
      LocalFree(message);
361
    } else {
362
      *errorMessage = "Unknown error.";
363
    }
364
365
    resolved_path = "";
366
  } else {
367
    resolved_path = path;
368
  }
369
}
370
#else
371
#  include <sys/types.h>
372
373
#  include <fcntl.h>
374
#  include <unistd.h>
375
inline int Mkdir(std::string const& dir, mode_t const* mode)
376
42.4k
{
377
42.4k
  return mkdir(dir.c_str(), mode ? *mode : 00777);
378
42.4k
}
379
inline int Rmdir(std::string const& dir)
380
10.7k
{
381
10.7k
  return rmdir(dir.c_str());
382
10.7k
}
383
inline char const* Getcwd(char* buf, unsigned int len)
384
16.0k
{
385
16.0k
  return getcwd(buf, len);
386
16.0k
}
387
388
inline int Chdir(std::string const& dir)
389
37.9k
{
390
37.9k
  return chdir(dir.c_str());
391
37.9k
}
392
inline void Realpath(std::string const& path, std::string& resolved_path,
393
                     std::string* errorMessage = nullptr)
394
10
{
395
10
  char resolved_name[KWSYS_SYSTEMTOOLS_MAXPATH];
396
397
10
  errno = 0;
398
10
  char* ret = realpath(path.c_str(), resolved_name);
399
10
  if (ret) {
400
10
    resolved_path = ret;
401
10
  } else if (errorMessage) {
402
0
    if (errno) {
403
0
      *errorMessage = strerror(errno);
404
0
    } else {
405
0
      *errorMessage = "Unknown error.";
406
0
    }
407
408
0
    resolved_path = "";
409
0
  } else {
410
    // if path resolution fails, return what was passed in
411
0
    resolved_path = path;
412
0
  }
413
10
}
414
#endif
415
416
namespace KWSYS_NAMESPACE {
417
418
double SystemTools::GetTime()
419
0
{
420
#if defined(_WIN32) && !defined(__CYGWIN__)
421
  FILETIME ft;
422
  GetSystemTimeAsFileTime(&ft);
423
  return (429.4967296 * ft.dwHighDateTime + 0.0000001 * ft.dwLowDateTime -
424
          11644473600.0);
425
#else
426
0
  struct timeval t;
427
0
  gettimeofday(&t, nullptr);
428
0
  return 1.0 * double(t.tv_sec) + 0.000001 * double(t.tv_usec);
429
0
#endif
430
0
}
431
432
/* Type of character storing the environment.  */
433
#if defined(_WIN32)
434
typedef wchar_t envchar;
435
#else
436
using envchar = char;
437
#endif
438
439
/* Order by environment key only (VAR from VAR=VALUE).  */
440
struct kwsysEnvCompare
441
{
442
  bool operator()(envchar const* l, envchar const* r) const
443
0
  {
444
0
#if defined(_WIN32)
445
0
    wchar_t const* leq = wcschr(l, L'=');
446
0
    wchar_t const* req = wcschr(r, L'=');
447
0
    size_t llen = leq ? (leq - l) : wcslen(l);
448
0
    size_t rlen = req ? (req - r) : wcslen(r);
449
0
    if (llen == rlen) {
450
0
      return wcsncmp(l, r, llen) < 0;
451
0
    } else {
452
0
      return wcscmp(l, r) < 0;
453
0
    }
454
0
#else
455
0
    char const* leq = strchr(l, '=');
456
0
    char const* req = strchr(r, '=');
457
0
    size_t llen = leq ? static_cast<size_t>(leq - l) : strlen(l);
458
0
    size_t rlen = req ? static_cast<size_t>(req - r) : strlen(r);
459
0
    if (llen == rlen) {
460
0
      return strncmp(l, r, llen) < 0;
461
0
    } else {
462
0
      return strcmp(l, r) < 0;
463
0
    }
464
0
#endif
465
0
  }
466
};
467
468
class kwsysEnvSet : public std::set<envchar const*, kwsysEnvCompare>
469
{
470
public:
471
  class Free
472
  {
473
    envchar const* Env;
474
475
  public:
476
    Free(envchar const* env)
477
      : Env(env)
478
0
    {
479
0
    }
480
0
    ~Free() { free(const_cast<envchar*>(this->Env)); }
481
482
    Free(Free const&) = delete;
483
    Free& operator=(Free const&) = delete;
484
  };
485
486
  envchar const* Release(envchar const* env)
487
0
  {
488
0
    envchar const* old = nullptr;
489
0
    auto i = this->find(env);
490
0
    if (i != this->end()) {
491
0
      old = *i;
492
0
      this->erase(i);
493
0
    }
494
0
    return old;
495
0
  }
496
};
497
498
/**
499
 * SystemTools static variables singleton class.
500
 */
501
class SystemToolsStatic
502
{
503
public:
504
#ifdef _WIN32
505
  static std::string GetCasePathName(std::string const& pathIn);
506
  static char const* GetEnvBuffered(char const* key);
507
  std::map<std::string, std::string> EnvMap;
508
#endif
509
510
  /**
511
   * Actual implementation of ReplaceString.
512
   */
513
  static void ReplaceString(std::string& source, char const* replace,
514
                            size_t replaceSize, std::string const& with);
515
516
  /**
517
   * Actual implementation of FileIsFullPath.
518
   */
519
  static bool FileIsFullPath(char const*, size_t);
520
521
  /**
522
   * Find a filename (file or directory) in the system PATH, with
523
   * optional extra paths.
524
   */
525
  static std::string FindName(
526
    std::string const& name,
527
    std::vector<std::string> const& userPaths = std::vector<std::string>(),
528
    bool no_system_path = false);
529
};
530
531
// Do NOT initialize.  Default initialization to zero is necessary.
532
static SystemToolsStatic* SystemToolsStatics;
533
534
#ifdef _WIN32
535
std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
536
{
537
  std::string casePath;
538
539
  // First check if the file is relative. We don't fix relative paths since the
540
  // real case depends on the root directory and the given path fragment may
541
  // have meaning elsewhere in the project.
542
  if (!SystemTools::FileIsFullPath(pathIn)) {
543
    // This looks unnecessary, but it allows for the return value optimization
544
    // since all return paths return the same local variable.
545
    casePath = pathIn;
546
    return casePath;
547
  }
548
549
  std::vector<std::string> path_components;
550
  SystemTools::SplitPath(pathIn, path_components);
551
552
  // Start with root component.
553
  std::vector<std::string>::size_type idx = 0;
554
  casePath = path_components[idx++];
555
  // make sure drive letter is always upper case
556
  if (casePath.size() > 1 && casePath[1] == ':') {
557
    casePath[0] = static_cast<std::string::value_type>(
558
      toupper(static_cast<unsigned char>(casePath[0])));
559
  }
560
  char const* sep = "";
561
562
  // If network path, fill casePath with server/share so FindFirstFile
563
  // will work after that.  Maybe someday call other APIs to get
564
  // actual case of servers and shares.
565
  if (path_components.size() > 2 && path_components[0] == "//") {
566
    casePath += path_components[idx++];
567
    casePath += "/";
568
    casePath += path_components[idx++];
569
    sep = "/";
570
  }
571
572
  // Convert case of all components that exist.
573
  bool converting = true;
574
  for (; idx < path_components.size(); idx++) {
575
    casePath += sep;
576
    sep = "/";
577
578
    if (converting) {
579
      // If path component contains wildcards, we skip matching
580
      // because these filenames are not allowed on windows,
581
      // and we do not want to match a different file.
582
      if (path_components[idx].find('*') != std::string::npos ||
583
          path_components[idx].find('?') != std::string::npos) {
584
        converting = false;
585
      } else {
586
        std::string test_str = casePath;
587
        test_str += path_components[idx];
588
589
        WIN32_FIND_DATAW findData;
590
        HANDLE hFind =
591
          ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData);
592
        if (INVALID_HANDLE_VALUE != hFind) {
593
          auto case_file_name = Encoding::ToNarrow(findData.cFileName);
594
          path_components[idx] = std::move(case_file_name);
595
          ::FindClose(hFind);
596
        } else {
597
          converting = false;
598
        }
599
      }
600
    }
601
602
    casePath += path_components[idx];
603
  }
604
  return casePath;
605
}
606
#endif
607
608
// adds the elements of the env variable path to the arg passed in
609
void SystemTools::GetPath(std::vector<std::string>& path, char const* env)
610
0
{
611
0
  size_t const old_size = path.size();
612
#if defined(_WIN32) && !defined(__CYGWIN__)
613
  char const pathSep = ';';
614
#else
615
0
  char const pathSep = ':';
616
0
#endif
617
0
  if (!env) {
618
0
    env = "PATH";
619
0
  }
620
0
  std::string pathEnv;
621
0
  if (!SystemTools::GetEnv(env, pathEnv)) {
622
0
    return;
623
0
  }
624
625
  // A hack to make the below algorithm work.
626
0
  if (!pathEnv.empty() && pathEnv.back() != pathSep) {
627
0
    pathEnv += pathSep;
628
0
  }
629
0
  std::string::size_type start = 0;
630
0
  bool done = false;
631
0
  while (!done) {
632
0
    std::string::size_type endpos = pathEnv.find(pathSep, start);
633
0
    if (endpos != std::string::npos) {
634
0
      path.push_back(pathEnv.substr(start, endpos - start));
635
0
      start = endpos + 1;
636
0
    } else {
637
0
      done = true;
638
0
    }
639
0
  }
640
0
  for (auto i = path.begin() + old_size; i != path.end(); ++i) {
641
0
    SystemTools::ConvertToUnixSlashes(*i);
642
0
  }
643
0
}
644
645
#if defined(_WIN32)
646
char const* SystemToolsStatic::GetEnvBuffered(char const* key)
647
{
648
  std::string env;
649
  if (SystemTools::GetEnv(key, env)) {
650
    std::string& menv = SystemToolsStatics->EnvMap[key];
651
    if (menv != env) {
652
      menv = std::move(env);
653
    }
654
    return menv.c_str();
655
  }
656
  return nullptr;
657
}
658
#endif
659
660
char const* SystemTools::GetEnv(char const* key)
661
0
{
662
#if defined(_WIN32)
663
  return SystemToolsStatic::GetEnvBuffered(key);
664
#else
665
0
  return getenv(key);
666
0
#endif
667
0
}
668
669
char const* SystemTools::GetEnv(std::string const& key)
670
0
{
671
#if defined(_WIN32)
672
  return SystemToolsStatic::GetEnvBuffered(key.c_str());
673
#else
674
0
  return getenv(key.c_str());
675
0
#endif
676
0
}
677
678
bool SystemTools::GetEnv(char const* key, std::string& result)
679
37.3k
{
680
#if defined(_WIN32)
681
  std::wstring const wKey = Encoding::ToWide(key);
682
  std::vector<wchar_t> heapBuf;
683
  wchar_t stackBuf[256];
684
  DWORD bufSz = static_cast<DWORD>(sizeof(stackBuf) / sizeof(stackBuf[0]));
685
  wchar_t* buf = stackBuf;
686
  DWORD r;
687
  while ((r = GetEnvironmentVariableW(wKey.c_str(), buf, bufSz)) >= bufSz) {
688
    heapBuf.resize(r);
689
    bufSz = r;
690
    buf = &heapBuf[0];
691
  }
692
  if (r == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
693
    return false;
694
  }
695
  result = Encoding::ToNarrow(buf);
696
  return true;
697
#else
698
37.3k
  char const* v = getenv(key);
699
37.3k
  if (v) {
700
37.2k
    result = v;
701
37.2k
    return true;
702
37.2k
  }
703
17
#endif
704
17
  return false;
705
37.3k
}
706
707
bool SystemTools::GetEnv(std::string const& key, std::string& result)
708
17
{
709
17
  return SystemTools::GetEnv(key.c_str(), result);
710
17
}
711
712
bool SystemTools::HasEnv(char const* key)
713
0
{
714
#if defined(_WIN32)
715
  std::wstring const wKey = Encoding::ToWide(key);
716
  DWORD r = GetEnvironmentVariableW(wKey.c_str(), nullptr, 0);
717
  return !(r == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND);
718
#else
719
0
  return getenv(key) != nullptr;
720
0
#endif
721
0
}
722
723
bool SystemTools::HasEnv(std::string const& key)
724
0
{
725
0
  return SystemTools::HasEnv(key.c_str());
726
0
}
727
728
#if KWSYS_CXX_HAS_UNSETENV
729
/* unsetenv("A") removes A from the environment.
730
   On older platforms it returns void instead of int.  */
731
static int kwsysUnPutEnv(std::string const& env)
732
0
{
733
0
  size_t pos = env.find('=');
734
0
  if (pos != std::string::npos) {
735
0
    std::string name = env.substr(0, pos);
736
0
    unsetenv(name.c_str());
737
0
  } else {
738
0
    unsetenv(env.c_str());
739
0
  }
740
0
  return 0;
741
0
}
742
743
#elif defined(__CYGWIN__) || defined(__GLIBC__)
744
// putenv("A") removes A from the environment with the GNU runtime.
745
// It cannot put the memory in the environment since there is no  "=" syntax.
746
747
static int kwsysUnPutEnv(std::string const& env)
748
{
749
  int err = 0;
750
  std::string buf = env.substr(0, env.find('='));
751
  if (putenv(&buf[0]) < 0 && errno != EINVAL) {
752
    err = errno;
753
  }
754
  if (err) {
755
    errno = err;
756
    return -1;
757
  }
758
  return 0;
759
}
760
761
#elif defined(_WIN32)
762
// putenv("A=") removes A from the environment with the MSVC runtime.
763
764
static int kwsysUnPutEnv(std::string const& env)
765
{
766
  std::wstring wEnv = Encoding::ToWide(env);
767
  size_t const pos = wEnv.find('=');
768
  size_t const len = pos == std::string::npos ? wEnv.size() : pos;
769
  wEnv.resize(len + 1, L'=');
770
  return _wputenv(wEnv.c_str());
771
}
772
773
#else
774
/* Manipulate the "environ" global directly.  */
775
static int kwsysUnPutEnv(std::string const& env)
776
{
777
  size_t pos = env.find('=');
778
  size_t const len = pos == std::string::npos ? env.size() : pos;
779
  int in = 0;
780
  int out = 0;
781
  while (environ[in]) {
782
    if (strlen(environ[in]) > len && environ[in][len] == '=' &&
783
        strncmp(env.c_str(), environ[in], len) == 0) {
784
      ++in;
785
    } else {
786
      environ[out++] = environ[in++];
787
    }
788
  }
789
  while (out < in) {
790
    environ[out++] = 0;
791
  }
792
  return 0;
793
}
794
#endif
795
796
#if KWSYS_CXX_HAS_SETENV
797
798
/* setenv("A", "B", 1) will set A=B in the environment and makes its
799
   own copies of the strings.  */
800
bool SystemTools::PutEnv(std::string const& env)
801
0
{
802
0
  size_t pos = env.find('=');
803
0
  if (pos != std::string::npos) {
804
0
    std::string name = env.substr(0, pos);
805
0
    return setenv(name.c_str(), env.c_str() + pos + 1, 1) == 0;
806
0
  } else {
807
0
    return kwsysUnPutEnv(env) == 0;
808
0
  }
809
0
}
810
811
bool SystemTools::UnPutEnv(std::string const& env)
812
0
{
813
0
  return kwsysUnPutEnv(env) == 0;
814
0
}
815
816
#else
817
818
/* putenv("A=B") will set A=B in the environment.  Most putenv implementations
819
   put their argument directly in the environment.  They never free the memory
820
   on program exit.  Keep an active set of pointers to memory we allocate and
821
   pass to putenv, one per environment key.  At program exit remove any
822
   environment values that may still reference memory we allocated.  Then free
823
   the memory.  This will not affect any environment values we never set.  */
824
825
#  ifdef __INTEL_COMPILER
826
#    pragma warning disable 444 /* base has non-virtual destructor */
827
#  endif
828
829
class kwsysEnv : public kwsysEnvSet
830
{
831
public:
832
  ~kwsysEnv()
833
  {
834
    for (iterator i = this->begin(); i != this->end(); ++i) {
835
#  if defined(_WIN32)
836
      std::string const s = Encoding::ToNarrow(*i);
837
      kwsysUnPutEnv(s);
838
#  else
839
      kwsysUnPutEnv(*i);
840
#  endif
841
      free(const_cast<envchar*>(*i));
842
    }
843
  }
844
  bool Put(char const* env)
845
  {
846
#  if defined(_WIN32)
847
    std::wstring wEnv = Encoding::ToWide(env);
848
    wchar_t* newEnv = _wcsdup(wEnv.c_str());
849
#  else
850
    char* newEnv = strdup(env);
851
#  endif
852
    Free oldEnv(this->Release(newEnv));
853
    this->insert(newEnv);
854
#  if defined(_WIN32)
855
    // `_wputenv` updates both the C runtime library's `_wenviron` array
856
    // and the process's environment block.
857
    if (_wputenv(newEnv) != 0) {
858
      return false;
859
    }
860
    // There seems to be no way to add an empty variable to `_wenviron`
861
    // through the C runtime library: `_wputenv("A=")` removes "A".
862
    // Add it directly to the process's environment block.
863
    // This is used by child processes, and by our `GetEnv`.
864
    std::string::size_type const eqPos = wEnv.find(L'=');
865
    if (eqPos != std::string::npos && (eqPos + 1) == wEnv.size()) {
866
      wEnv.resize(eqPos);
867
      return SetEnvironmentVariableW(wEnv.c_str(), L"");
868
    }
869
    return true;
870
#  else
871
    return putenv(newEnv) == 0;
872
#  endif
873
  }
874
  bool UnPut(char const* env)
875
  {
876
#  if defined(_WIN32)
877
    std::wstring const wEnv = Encoding::ToWide(env);
878
    Free oldEnv(this->Release(wEnv.c_str()));
879
#  else
880
    Free oldEnv(this->Release(env));
881
#  endif
882
    return kwsysUnPutEnv(env) == 0;
883
  }
884
};
885
886
static kwsysEnv kwsysEnvInstance;
887
888
bool SystemTools::PutEnv(std::string const& env)
889
{
890
  return kwsysEnvInstance.Put(env.c_str());
891
}
892
893
bool SystemTools::UnPutEnv(std::string const& env)
894
{
895
  return kwsysEnvInstance.UnPut(env.c_str());
896
}
897
898
#endif
899
900
char const* SystemTools::GetExecutableExtension()
901
0
{
902
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__VMS)
903
  return ".exe";
904
#else
905
0
  return "";
906
0
#endif
907
0
}
908
909
FILE* SystemTools::Fopen(std::string const& file, char const* mode)
910
0
{
911
#ifdef _WIN32
912
  // Remove any 'e', which is supported on UNIX, but not Windows.
913
  std::wstring trimmedMode = Encoding::ToWide(mode);
914
  trimmedMode.erase(std::remove(trimmedMode.begin(), trimmedMode.end(), L'e'),
915
                    trimmedMode.end());
916
  return _wfopen(Encoding::ToWindowsExtendedPath(file).c_str(),
917
                 trimmedMode.c_str());
918
#else
919
0
  return fopen(file.c_str(), mode);
920
0
#endif
921
0
}
922
923
Status SystemTools::MakeDirectory(char const* path, mode_t const* mode)
924
0
{
925
0
  if (!path) {
926
0
    return Status::POSIX(EINVAL);
927
0
  }
928
0
  return SystemTools::MakeDirectory(std::string(path), mode);
929
0
}
930
931
Status SystemTools::MakeDirectory(std::string const& path, mode_t const* mode)
932
18.9k
{
933
18.9k
  if (path.empty()) {
934
0
    return Status::POSIX(EINVAL);
935
0
  }
936
18.9k
  if (SystemTools::PathExists(path)) {
937
8.35k
    if (SystemTools::FileIsDirectory(path)) {
938
8.35k
      return Status::Success();
939
8.35k
    }
940
0
    return Status::POSIX(EEXIST);
941
8.35k
  }
942
10.6k
  std::string dir = path;
943
10.6k
  SystemTools::ConvertToUnixSlashes(dir);
944
945
10.6k
  std::string::size_type pos = 0;
946
10.6k
  std::string topdir;
947
42.4k
  while ((pos = dir.find('/', pos)) != std::string::npos) {
948
    // all underlying functions use C strings, so temporarily
949
    // end the string here
950
31.8k
    dir[pos] = '\0';
951
952
31.8k
    Mkdir(dir, mode);
953
31.8k
    dir[pos] = '/';
954
955
31.8k
    ++pos;
956
31.8k
  }
957
10.6k
  topdir = dir;
958
10.6k
  if (Mkdir(topdir, mode) != 0 && errno != EEXIST) {
959
0
    return Status::POSIX_errno();
960
0
  }
961
962
10.6k
  return Status::Success();
963
10.6k
}
964
965
// replace replace with with as many times as it shows up in source.
966
// write the result into source.
967
void SystemTools::ReplaceString(std::string& source,
968
                                std::string const& replace,
969
                                std::string const& with)
970
0
{
971
  // do while hangs if replaceSize is 0
972
0
  if (replace.empty()) {
973
0
    return;
974
0
  }
975
976
0
  SystemToolsStatic::ReplaceString(source, replace.c_str(), replace.size(),
977
0
                                   with);
978
0
}
979
980
void SystemTools::ReplaceString(std::string& source, char const* replace,
981
                                char const* with)
982
0
{
983
  // do while hangs if replaceSize is 0
984
0
  if (!*replace) {
985
0
    return;
986
0
  }
987
988
0
  SystemToolsStatic::ReplaceString(source, replace, strlen(replace),
989
0
                                   with ? with : "");
990
0
}
991
992
void SystemToolsStatic::ReplaceString(std::string& source, char const* replace,
993
                                      size_t replaceSize,
994
                                      std::string const& with)
995
0
{
996
0
  char const* src = source.c_str();
997
0
  char* searchPos = const_cast<char*>(strstr(src, replace));
998
999
  // get out quick if string is not found
1000
0
  if (!searchPos) {
1001
0
    return;
1002
0
  }
1003
1004
  // perform replacements until done
1005
0
  char* orig = strdup(src);
1006
0
  char* currentPos = orig;
1007
0
  searchPos = searchPos - src + orig;
1008
1009
  // initialize the result
1010
0
  source.erase(source.begin(), source.end());
1011
0
  do {
1012
0
    *searchPos = '\0';
1013
0
    source += currentPos;
1014
0
    currentPos = searchPos + replaceSize;
1015
    // replace
1016
0
    source += with;
1017
0
    searchPos = strstr(currentPos, replace);
1018
0
  } while (searchPos);
1019
1020
  // copy any trailing text
1021
0
  source += currentPos;
1022
0
  free(orig);
1023
0
}
1024
1025
#if defined(_WIN32) && !defined(__CYGWIN__)
1026
1027
#  if defined(KEY_WOW64_32KEY) && defined(KEY_WOW64_64KEY)
1028
#    define KWSYS_ST_KEY_WOW64_32KEY KEY_WOW64_32KEY
1029
#    define KWSYS_ST_KEY_WOW64_64KEY KEY_WOW64_64KEY
1030
#  else
1031
#    define KWSYS_ST_KEY_WOW64_32KEY 0x0200
1032
#    define KWSYS_ST_KEY_WOW64_64KEY 0x0100
1033
#  endif
1034
1035
static bool hasPrefix(std::string const& s, char const* pattern,
1036
                      std::string::size_type spos)
1037
{
1038
  size_t plen = strlen(pattern);
1039
  if (spos != plen)
1040
    return false;
1041
  return s.compare(0, plen, pattern) == 0;
1042
}
1043
1044
static bool SystemToolsParseRegistryKey(std::string const& key,
1045
                                        HKEY& primaryKey, std::wstring& second,
1046
                                        std::string* valuename)
1047
{
1048
  size_t start = key.find('\\');
1049
  if (start == std::string::npos) {
1050
    return false;
1051
  }
1052
1053
  size_t valuenamepos = key.find(';');
1054
  if (valuenamepos != std::string::npos && valuename) {
1055
    *valuename = key.substr(valuenamepos + 1);
1056
  }
1057
1058
  second = Encoding::ToWide(key.substr(start + 1, valuenamepos - start - 1));
1059
1060
  if (hasPrefix(key, "HKEY_CURRENT_USER", start)) {
1061
    primaryKey = HKEY_CURRENT_USER;
1062
  } else if (hasPrefix(key, "HKEY_CURRENT_CONFIG", start)) {
1063
    primaryKey = HKEY_CURRENT_CONFIG;
1064
  } else if (hasPrefix(key, "HKEY_CLASSES_ROOT", start)) {
1065
    primaryKey = HKEY_CLASSES_ROOT;
1066
  } else if (hasPrefix(key, "HKEY_LOCAL_MACHINE", start)) {
1067
    primaryKey = HKEY_LOCAL_MACHINE;
1068
  } else if (hasPrefix(key, "HKEY_USERS", start)) {
1069
    primaryKey = HKEY_USERS;
1070
  }
1071
1072
  return true;
1073
}
1074
1075
static DWORD SystemToolsMakeRegistryMode(DWORD mode,
1076
                                         SystemTools::KeyWOW64 view)
1077
{
1078
  // only add the modes when on a system that supports Wow64.
1079
  static FARPROC wow64p =
1080
    GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process");
1081
  if (!wow64p) {
1082
    return mode;
1083
  }
1084
1085
  if (view == SystemTools::KeyWOW64_32) {
1086
    return mode | KWSYS_ST_KEY_WOW64_32KEY;
1087
  } else if (view == SystemTools::KeyWOW64_64) {
1088
    return mode | KWSYS_ST_KEY_WOW64_64KEY;
1089
  }
1090
  return mode;
1091
}
1092
#endif
1093
1094
#if defined(_WIN32) && !defined(__CYGWIN__)
1095
bool SystemTools::GetRegistrySubKeys(std::string const& key,
1096
                                     std::vector<std::string>& subkeys,
1097
                                     KeyWOW64 view)
1098
{
1099
  HKEY primaryKey = HKEY_CURRENT_USER;
1100
  std::wstring second;
1101
  if (!SystemToolsParseRegistryKey(key, primaryKey, second, nullptr)) {
1102
    return false;
1103
  }
1104
1105
  HKEY hKey;
1106
  if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1107
                    SystemToolsMakeRegistryMode(KEY_READ, view),
1108
                    &hKey) != ERROR_SUCCESS) {
1109
    return false;
1110
  } else {
1111
    wchar_t name[1024];
1112
    DWORD dwNameSize = sizeof(name) / sizeof(name[0]);
1113
1114
    DWORD i = 0;
1115
    while (RegEnumKeyW(hKey, i, name, dwNameSize) == ERROR_SUCCESS) {
1116
      subkeys.push_back(Encoding::ToNarrow(name));
1117
      ++i;
1118
    }
1119
1120
    RegCloseKey(hKey);
1121
  }
1122
1123
  return true;
1124
}
1125
#else
1126
bool SystemTools::GetRegistrySubKeys(std::string const&,
1127
                                     std::vector<std::string>&, KeyWOW64)
1128
0
{
1129
0
  return false;
1130
0
}
1131
#endif
1132
1133
// Read a registry value.
1134
// Example :
1135
//      HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath
1136
//      =>  will return the data of the "default" value of the key
1137
//      HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root
1138
//      =>  will return the data of the "Root" value of the key
1139
1140
#if defined(_WIN32) && !defined(__CYGWIN__)
1141
bool SystemTools::ReadRegistryValue(std::string const& key, std::string& value,
1142
                                    KeyWOW64 view)
1143
{
1144
  bool valueset = false;
1145
  HKEY primaryKey = HKEY_CURRENT_USER;
1146
  std::wstring second;
1147
  std::string valuename;
1148
  if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
1149
    return false;
1150
  }
1151
1152
  HKEY hKey;
1153
  if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1154
                    SystemToolsMakeRegistryMode(KEY_READ, view),
1155
                    &hKey) != ERROR_SUCCESS) {
1156
    return false;
1157
  } else {
1158
    DWORD dwType, dwSize;
1159
    dwSize = 1023;
1160
    wchar_t data[1024];
1161
    if (RegQueryValueExW(hKey, Encoding::ToWide(valuename).c_str(), nullptr,
1162
                         &dwType, (BYTE*)data, &dwSize) == ERROR_SUCCESS) {
1163
      if (dwType == REG_SZ) {
1164
        value = Encoding::ToNarrow(data);
1165
        valueset = true;
1166
      } else if (dwType == REG_EXPAND_SZ) {
1167
        wchar_t expanded[1024];
1168
        DWORD dwExpandedSize = sizeof(expanded) / sizeof(expanded[0]);
1169
        if (ExpandEnvironmentStringsW(data, expanded, dwExpandedSize)) {
1170
          value = Encoding::ToNarrow(expanded);
1171
          valueset = true;
1172
        }
1173
      }
1174
    }
1175
1176
    RegCloseKey(hKey);
1177
  }
1178
1179
  return valueset;
1180
}
1181
#else
1182
bool SystemTools::ReadRegistryValue(std::string const&, std::string&, KeyWOW64)
1183
0
{
1184
0
  return false;
1185
0
}
1186
#endif
1187
1188
// Write a registry value.
1189
// Example :
1190
//      HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath
1191
//      =>  will set the data of the "default" value of the key
1192
//      HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root
1193
//      =>  will set the data of the "Root" value of the key
1194
1195
#if defined(_WIN32) && !defined(__CYGWIN__)
1196
bool SystemTools::WriteRegistryValue(std::string const& key,
1197
                                     std::string const& value, KeyWOW64 view)
1198
{
1199
  HKEY primaryKey = HKEY_CURRENT_USER;
1200
  std::wstring second;
1201
  std::string valuename;
1202
  if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
1203
    return false;
1204
  }
1205
1206
  HKEY hKey;
1207
  DWORD dwDummy;
1208
  wchar_t lpClass[] = L"";
1209
  if (RegCreateKeyExW(primaryKey, second.c_str(), 0, lpClass,
1210
                      REG_OPTION_NON_VOLATILE,
1211
                      SystemToolsMakeRegistryMode(KEY_WRITE, view), nullptr,
1212
                      &hKey, &dwDummy) != ERROR_SUCCESS) {
1213
    return false;
1214
  }
1215
1216
  std::wstring wvalue = Encoding::ToWide(value);
1217
  if (RegSetValueExW(hKey, Encoding::ToWide(valuename).c_str(), 0, REG_SZ,
1218
                     (CONST BYTE*)wvalue.c_str(),
1219
                     (DWORD)(sizeof(wchar_t) * (wvalue.size() + 1))) ==
1220
      ERROR_SUCCESS) {
1221
    return true;
1222
  }
1223
  return false;
1224
}
1225
#else
1226
bool SystemTools::WriteRegistryValue(std::string const&, std::string const&,
1227
                                     KeyWOW64)
1228
0
{
1229
0
  return false;
1230
0
}
1231
#endif
1232
1233
// Delete a registry value.
1234
// Example :
1235
//      HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath
1236
//      =>  will delete the data of the "default" value of the key
1237
//      HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root
1238
//      =>  will delete the data of the "Root" value of the key
1239
1240
#if defined(_WIN32) && !defined(__CYGWIN__)
1241
bool SystemTools::DeleteRegistryValue(std::string const& key, KeyWOW64 view)
1242
{
1243
  HKEY primaryKey = HKEY_CURRENT_USER;
1244
  std::wstring second;
1245
  std::string valuename;
1246
  if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
1247
    return false;
1248
  }
1249
1250
  HKEY hKey;
1251
  if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1252
                    SystemToolsMakeRegistryMode(KEY_WRITE, view),
1253
                    &hKey) != ERROR_SUCCESS) {
1254
    return false;
1255
  } else {
1256
    if (RegDeleteValue(hKey, (LPTSTR)valuename.c_str()) == ERROR_SUCCESS) {
1257
      RegCloseKey(hKey);
1258
      return true;
1259
    }
1260
  }
1261
  return false;
1262
}
1263
#else
1264
bool SystemTools::DeleteRegistryValue(std::string const&, KeyWOW64)
1265
0
{
1266
0
  return false;
1267
0
}
1268
#endif
1269
1270
#ifdef _WIN32
1271
SystemTools::WindowsFileId::WindowsFileId(unsigned long volumeSerialNumber,
1272
                                          unsigned long fileIndexHigh,
1273
                                          unsigned long fileIndexLow)
1274
  : m_volumeSerialNumber(volumeSerialNumber)
1275
  , m_fileIndexHigh(fileIndexHigh)
1276
  , m_fileIndexLow(fileIndexLow)
1277
{
1278
}
1279
1280
bool SystemTools::WindowsFileId::operator==(WindowsFileId const& o) const
1281
{
1282
  return (m_volumeSerialNumber == o.m_volumeSerialNumber &&
1283
          m_fileIndexHigh == o.m_fileIndexHigh &&
1284
          m_fileIndexLow == o.m_fileIndexLow);
1285
}
1286
1287
bool SystemTools::WindowsFileId::operator!=(WindowsFileId const& o) const
1288
{
1289
  return !(*this == o);
1290
}
1291
#else
1292
SystemTools::UnixFileId::UnixFileId(dev_t volumeSerialNumber,
1293
                                    ino_t fileSerialNumber, off_t fileSize)
1294
0
  : m_volumeSerialNumber(volumeSerialNumber)
1295
0
  , m_fileSerialNumber(fileSerialNumber)
1296
0
  , m_fileSize(fileSize)
1297
0
{
1298
0
}
1299
1300
bool SystemTools::UnixFileId::operator==(UnixFileId const& o) const
1301
0
{
1302
0
  return (m_volumeSerialNumber == o.m_volumeSerialNumber &&
1303
0
          m_fileSerialNumber == o.m_fileSerialNumber &&
1304
0
          m_fileSize == o.m_fileSize);
1305
0
}
1306
1307
bool SystemTools::UnixFileId::operator!=(UnixFileId const& o) const
1308
0
{
1309
0
  return !(*this == o);
1310
0
}
1311
#endif
1312
1313
bool SystemTools::GetFileId(std::string const& file, FileId& id)
1314
0
{
1315
#ifdef _WIN32
1316
  HANDLE hFile =
1317
    CreateFileW(Encoding::ToWide(file).c_str(), GENERIC_READ, FILE_SHARE_READ,
1318
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1319
  if (hFile != INVALID_HANDLE_VALUE) {
1320
    BY_HANDLE_FILE_INFORMATION fiBuf;
1321
    GetFileInformationByHandle(hFile, &fiBuf);
1322
    CloseHandle(hFile);
1323
    id = FileId(fiBuf.dwVolumeSerialNumber, fiBuf.nFileIndexHigh,
1324
                fiBuf.nFileIndexLow);
1325
    return true;
1326
  } else {
1327
    return false;
1328
  }
1329
#else
1330
0
  struct stat fileStat;
1331
0
  if (stat(file.c_str(), &fileStat) == 0) {
1332
0
    id = FileId(fileStat.st_dev, fileStat.st_ino, fileStat.st_size);
1333
0
    return true;
1334
0
  }
1335
0
  return false;
1336
0
#endif
1337
0
}
1338
1339
bool SystemTools::SameFile(std::string const& file1, std::string const& file2)
1340
0
{
1341
#ifdef _WIN32
1342
  HANDLE hFile1, hFile2;
1343
1344
  hFile1 =
1345
    CreateFileW(Encoding::ToWide(file1).c_str(), GENERIC_READ, FILE_SHARE_READ,
1346
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1347
  hFile2 =
1348
    CreateFileW(Encoding::ToWide(file2).c_str(), GENERIC_READ, FILE_SHARE_READ,
1349
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1350
  if (hFile1 == INVALID_HANDLE_VALUE || hFile2 == INVALID_HANDLE_VALUE) {
1351
    if (hFile1 != INVALID_HANDLE_VALUE) {
1352
      CloseHandle(hFile1);
1353
    }
1354
    if (hFile2 != INVALID_HANDLE_VALUE) {
1355
      CloseHandle(hFile2);
1356
    }
1357
    return false;
1358
  }
1359
1360
  BY_HANDLE_FILE_INFORMATION fiBuf1;
1361
  BY_HANDLE_FILE_INFORMATION fiBuf2;
1362
  GetFileInformationByHandle(hFile1, &fiBuf1);
1363
  GetFileInformationByHandle(hFile2, &fiBuf2);
1364
  CloseHandle(hFile1);
1365
  CloseHandle(hFile2);
1366
  return (fiBuf1.dwVolumeSerialNumber == fiBuf2.dwVolumeSerialNumber &&
1367
          fiBuf1.nFileIndexHigh == fiBuf2.nFileIndexHigh &&
1368
          fiBuf1.nFileIndexLow == fiBuf2.nFileIndexLow);
1369
#else
1370
0
  struct stat fileStat1, fileStat2;
1371
0
  if (stat(file1.c_str(), &fileStat1) == 0 &&
1372
0
      stat(file2.c_str(), &fileStat2) == 0) {
1373
    // see if the files are the same file
1374
    // check the device inode and size
1375
0
    if (memcmp(&fileStat2.st_dev, &fileStat1.st_dev,
1376
0
               sizeof(fileStat1.st_dev)) == 0 &&
1377
0
        memcmp(&fileStat2.st_ino, &fileStat1.st_ino,
1378
0
               sizeof(fileStat1.st_ino)) == 0 &&
1379
0
        fileStat2.st_size == fileStat1.st_size) {
1380
0
      return true;
1381
0
    }
1382
0
  }
1383
0
  return false;
1384
0
#endif
1385
0
}
1386
1387
bool SystemTools::PathExists(std::string const& path)
1388
60.9k
{
1389
60.9k
  if (path.empty()) {
1390
0
    return false;
1391
0
  }
1392
#if defined(_WIN32)
1393
  return (GetFileAttributesW(Encoding::ToWindowsExtendedPath(path).c_str()) !=
1394
          INVALID_FILE_ATTRIBUTES);
1395
#else
1396
60.9k
  struct stat st;
1397
60.9k
  return lstat(path.c_str(), &st) == 0;
1398
60.9k
#endif
1399
60.9k
}
1400
1401
bool SystemTools::FileExists(char const* filename)
1402
1
{
1403
1
  if (!filename) {
1404
0
    return false;
1405
0
  }
1406
1
  return SystemTools::FileExists(std::string(filename));
1407
1
}
1408
1409
bool SystemTools::FileExists(std::string const& filename)
1410
3
{
1411
3
  if (filename.empty()) {
1412
0
    return false;
1413
0
  }
1414
#if defined(_WIN32)
1415
  std::wstring const path = Encoding::ToWindowsExtendedPath(filename);
1416
  DWORD attr = GetFileAttributesW(path.c_str());
1417
  if (attr == INVALID_FILE_ATTRIBUTES) {
1418
    return false;
1419
  }
1420
1421
  if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
1422
    // Using 0 instead of GENERIC_READ as it allows reading of file attributes
1423
    // even if we do not have permission to read the file itself
1424
    HANDLE handle = CreateFileW(path.c_str(), 0, 0, nullptr, OPEN_EXISTING,
1425
                                FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1426
1427
    if (handle == INVALID_HANDLE_VALUE) {
1428
      // A reparse point may be an execution alias (Windows Store app), which
1429
      // is similar to a symlink but it cannot be opened as a regular file.
1430
      // We must look at the reparse point data explicitly.
1431
      handle = CreateFileW(
1432
        path.c_str(), 0, 0, nullptr, OPEN_EXISTING,
1433
        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1434
1435
      if (handle == INVALID_HANDLE_VALUE) {
1436
        return false;
1437
      }
1438
1439
      byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1440
      DWORD bytesReturned = 0;
1441
1442
      if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
1443
                           MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
1444
                           nullptr)) {
1445
        CloseHandle(handle);
1446
        return false;
1447
      }
1448
1449
      CloseHandle(handle);
1450
1451
      PREPARSE_DATA_BUFFER data =
1452
        reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
1453
1454
      // Assume that file exists if it is an execution alias.
1455
      return data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK;
1456
    }
1457
1458
    CloseHandle(handle);
1459
  }
1460
1461
  return true;
1462
#else
1463
// SCO OpenServer 5.0.7/3.2's command has 711 permission.
1464
#  if defined(_SCO_DS)
1465
  return access(filename.c_str(), F_OK) == 0;
1466
#  else
1467
3
  return access(filename.c_str(), R_OK) == 0;
1468
3
#  endif
1469
3
#endif
1470
3
}
1471
1472
bool SystemTools::FileExists(char const* filename, bool isFile)
1473
0
{
1474
0
  if (!filename) {
1475
0
    return false;
1476
0
  }
1477
0
  return SystemTools::FileExists(std::string(filename), isFile);
1478
0
}
1479
1480
bool SystemTools::FileExists(std::string const& filename, bool isFile)
1481
1
{
1482
1
  if (SystemTools::FileExists(filename)) {
1483
    // If isFile is set return not FileIsDirectory,
1484
    // so this will only be true if it is a file
1485
1
    return !isFile || !SystemTools::FileIsDirectory(filename);
1486
1
  }
1487
0
  return false;
1488
1
}
1489
1490
bool SystemTools::TestFileAccess(char const* filename,
1491
                                 TestFilePermissions permissions)
1492
0
{
1493
0
  if (!filename) {
1494
0
    return false;
1495
0
  }
1496
0
  return SystemTools::TestFileAccess(std::string(filename), permissions);
1497
0
}
1498
1499
bool SystemTools::TestFileAccess(std::string const& filename,
1500
                                 TestFilePermissions permissions)
1501
0
{
1502
0
  if (filename.empty()) {
1503
0
    return false;
1504
0
  }
1505
#if defined(_WIN32) && !defined(__CYGWIN__)
1506
  // If execute set, change to read permission (all files on Windows
1507
  // are executable if they are readable).  The CRT will always fail
1508
  // if you pass an execute bit.
1509
  if (permissions & TEST_FILE_EXECUTE) {
1510
    permissions &= ~TEST_FILE_EXECUTE;
1511
    permissions |= TEST_FILE_READ;
1512
  }
1513
  return _waccess(Encoding::ToWindowsExtendedPath(filename).c_str(),
1514
                  permissions) == 0;
1515
#else
1516
0
  return access(filename.c_str(), permissions) == 0;
1517
0
#endif
1518
0
}
1519
1520
int SystemTools::Stat(char const* path, SystemTools::Stat_t* buf)
1521
0
{
1522
0
  if (!path) {
1523
0
    errno = EFAULT;
1524
0
    return -1;
1525
0
  }
1526
0
  return SystemTools::Stat(std::string(path), buf);
1527
0
}
1528
1529
int SystemTools::Stat(std::string const& path, SystemTools::Stat_t* buf)
1530
0
{
1531
0
  if (path.empty()) {
1532
0
    errno = ENOENT;
1533
0
    return -1;
1534
0
  }
1535
#if defined(_WIN32) && !defined(__CYGWIN__)
1536
  // Ideally we should use Encoding::ToWindowsExtendedPath to support
1537
  // long paths, but _wstat64 rejects paths with '?' in them, thinking
1538
  // they are wildcards.
1539
  std::wstring const& wpath = Encoding::ToWide(path);
1540
  return _wstat64(wpath.c_str(), buf);
1541
#else
1542
0
  return stat(path.c_str(), buf);
1543
0
#endif
1544
0
}
1545
1546
Status SystemTools::Touch(std::string const& filename, bool create)
1547
0
{
1548
0
  if (!SystemTools::FileExists(filename)) {
1549
0
    if (create) {
1550
0
      FILE* file = Fopen(filename, "a+b");
1551
0
      if (file) {
1552
0
        fclose(file);
1553
0
        return Status::Success();
1554
0
      }
1555
0
      return Status::POSIX_errno();
1556
0
    } else {
1557
0
      return Status::Success();
1558
0
    }
1559
0
  }
1560
#if defined(_WIN32) && !defined(__CYGWIN__)
1561
  HANDLE h = CreateFileW(Encoding::ToWindowsExtendedPath(filename).c_str(),
1562
                         FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, 0,
1563
                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
1564
  if (!h) {
1565
    return Status::Windows_GetLastError();
1566
  }
1567
  FILETIME mtime;
1568
  GetSystemTimeAsFileTime(&mtime);
1569
  if (!SetFileTime(h, 0, 0, &mtime)) {
1570
    Status status = Status::Windows_GetLastError();
1571
    CloseHandle(h);
1572
    return status;
1573
  }
1574
  CloseHandle(h);
1575
#elif KWSYS_CXX_HAS_UTIMENSAT
1576
  // utimensat is only available on newer Unixes and macOS 10.13+
1577
0
  if (utimensat(AT_FDCWD, filename.c_str(), nullptr, 0) < 0) {
1578
0
    return Status::POSIX_errno();
1579
0
  }
1580
#else
1581
  // fall back to utimes
1582
  if (utimes(filename.c_str(), nullptr) < 0) {
1583
    return Status::POSIX_errno();
1584
  }
1585
#endif
1586
0
  return Status::Success();
1587
0
}
1588
1589
Status SystemTools::FileTimeCompare(std::string const& f1,
1590
                                    std::string const& f2, int* result)
1591
0
{
1592
  // Default to same time.
1593
0
  *result = 0;
1594
0
#if !defined(_WIN32) || defined(__CYGWIN__)
1595
  // POSIX version.  Use stat function to get file modification time.
1596
0
  struct stat s1;
1597
0
  if (stat(f1.c_str(), &s1) != 0) {
1598
0
    return Status::POSIX_errno();
1599
0
  }
1600
0
  struct stat s2;
1601
0
  if (stat(f2.c_str(), &s2) != 0) {
1602
0
    return Status::POSIX_errno();
1603
0
  }
1604
0
#  if KWSYS_CXX_STAT_HAS_ST_MTIM
1605
  // Compare using nanosecond resolution.
1606
0
  if (s1.st_mtim.tv_sec < s2.st_mtim.tv_sec) {
1607
0
    *result = -1;
1608
0
  } else if (s1.st_mtim.tv_sec > s2.st_mtim.tv_sec) {
1609
0
    *result = 1;
1610
0
  } else if (s1.st_mtim.tv_nsec < s2.st_mtim.tv_nsec) {
1611
0
    *result = -1;
1612
0
  } else if (s1.st_mtim.tv_nsec > s2.st_mtim.tv_nsec) {
1613
0
    *result = 1;
1614
0
  }
1615
#  elif KWSYS_CXX_STAT_HAS_ST_MTIMESPEC
1616
  // Compare using nanosecond resolution.
1617
  if (s1.st_mtimespec.tv_sec < s2.st_mtimespec.tv_sec) {
1618
    *result = -1;
1619
  } else if (s1.st_mtimespec.tv_sec > s2.st_mtimespec.tv_sec) {
1620
    *result = 1;
1621
  } else if (s1.st_mtimespec.tv_nsec < s2.st_mtimespec.tv_nsec) {
1622
    *result = -1;
1623
  } else if (s1.st_mtimespec.tv_nsec > s2.st_mtimespec.tv_nsec) {
1624
    *result = 1;
1625
  }
1626
#  else
1627
  // Compare using 1 second resolution.
1628
  if (s1.st_mtime < s2.st_mtime) {
1629
    *result = -1;
1630
  } else if (s1.st_mtime > s2.st_mtime) {
1631
    *result = 1;
1632
  }
1633
#  endif
1634
#else
1635
  // Windows version.  Get the modification time from extended file attributes.
1636
  WIN32_FILE_ATTRIBUTE_DATA f1d;
1637
  WIN32_FILE_ATTRIBUTE_DATA f2d;
1638
  if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f1).c_str(),
1639
                            GetFileExInfoStandard, &f1d)) {
1640
    return Status::Windows_GetLastError();
1641
  }
1642
  if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f2).c_str(),
1643
                            GetFileExInfoStandard, &f2d)) {
1644
    return Status::Windows_GetLastError();
1645
  }
1646
1647
  // Compare the file times using resolution provided by system call.
1648
  *result = (int)CompareFileTime(&f1d.ftLastWriteTime, &f2d.ftLastWriteTime);
1649
#endif
1650
0
  return Status::Success();
1651
0
}
1652
1653
// Return a capitalized string (i.e the first letter is uppercased, all other
1654
// are lowercased)
1655
std::string SystemTools::Capitalized(std::string const& s)
1656
0
{
1657
0
  std::string n;
1658
0
  if (s.empty()) {
1659
0
    return n;
1660
0
  }
1661
0
  n.resize(s.size());
1662
0
  n[0] = static_cast<std::string::value_type>(
1663
0
    toupper(static_cast<unsigned char>(s[0])));
1664
0
  for (size_t i = 1; i < s.size(); i++) {
1665
0
    n[i] = static_cast<std::string::value_type>(
1666
0
      tolower(static_cast<unsigned char>(s[i])));
1667
0
  }
1668
0
  return n;
1669
0
}
1670
1671
// Return capitalized words
1672
std::string SystemTools::CapitalizedWords(std::string const& s)
1673
0
{
1674
0
  std::string n(s);
1675
0
  for (size_t i = 0; i < s.size(); i++) {
1676
#if defined(_MSC_VER) && defined(_MT) && defined(_DEBUG)
1677
    // MS has an assert that will fail if s[i] < 0; setting
1678
    // LC_CTYPE using setlocale() does *not* help. Painful.
1679
    if ((int)s[i] >= 0 && isalpha(s[i]) &&
1680
        (i == 0 || ((int)s[i - 1] >= 0 && isspace(s[i - 1]))))
1681
#else
1682
0
    if (isalpha(s[i]) && (i == 0 || isspace(s[i - 1])))
1683
0
#endif
1684
0
    {
1685
0
      n[i] = static_cast<std::string::value_type>(
1686
0
        toupper(static_cast<unsigned char>(s[i])));
1687
0
    }
1688
0
  }
1689
0
  return n;
1690
0
}
1691
1692
// Return uncapitalized words
1693
std::string SystemTools::UnCapitalizedWords(std::string const& s)
1694
0
{
1695
0
  std::string n(s);
1696
0
  for (size_t i = 0; i < s.size(); i++) {
1697
#if defined(_MSC_VER) && defined(_MT) && defined(_DEBUG)
1698
    // MS has an assert that will fail if s[i] < 0; setting
1699
    // LC_CTYPE using setlocale() does *not* help. Painful.
1700
    if ((int)s[i] >= 0 && isalpha(s[i]) &&
1701
        (i == 0 || ((int)s[i - 1] >= 0 && isspace(s[i - 1]))))
1702
#else
1703
0
    if (isalpha(s[i]) && (i == 0 || isspace(s[i - 1])))
1704
0
#endif
1705
0
    {
1706
0
      n[i] = static_cast<std::string::value_type>(
1707
0
        tolower(static_cast<unsigned char>(s[i])));
1708
0
    }
1709
0
  }
1710
0
  return n;
1711
0
}
1712
1713
// only works for words with at least two letters
1714
std::string SystemTools::AddSpaceBetweenCapitalizedWords(std::string const& s)
1715
0
{
1716
0
  std::string n;
1717
0
  if (!s.empty()) {
1718
0
    n.reserve(s.size());
1719
0
    n += s[0];
1720
0
    for (size_t i = 1; i < s.size(); i++) {
1721
0
      if (isupper(s[i]) && !isspace(s[i - 1]) && !isupper(s[i - 1])) {
1722
0
        n += ' ';
1723
0
      }
1724
0
      n += s[i];
1725
0
    }
1726
0
  }
1727
0
  return n;
1728
0
}
1729
1730
char* SystemTools::AppendStrings(char const* str1, char const* str2)
1731
0
{
1732
0
  if (!str1) {
1733
0
    return SystemTools::DuplicateString(str2);
1734
0
  }
1735
0
  if (!str2) {
1736
0
    return SystemTools::DuplicateString(str1);
1737
0
  }
1738
0
  size_t len1 = strlen(str1);
1739
0
  char* newstr = new char[len1 + strlen(str2) + 1];
1740
0
  if (!newstr) {
1741
0
    return nullptr;
1742
0
  }
1743
0
  strcpy(newstr, str1);
1744
0
  strcat(newstr + len1, str2);
1745
0
  return newstr;
1746
0
}
1747
1748
char* SystemTools::AppendStrings(char const* str1, char const* str2,
1749
                                 char const* str3)
1750
0
{
1751
0
  if (!str1) {
1752
0
    return SystemTools::AppendStrings(str2, str3);
1753
0
  }
1754
0
  if (!str2) {
1755
0
    return SystemTools::AppendStrings(str1, str3);
1756
0
  }
1757
0
  if (!str3) {
1758
0
    return SystemTools::AppendStrings(str1, str2);
1759
0
  }
1760
1761
0
  size_t len1 = strlen(str1), len2 = strlen(str2);
1762
0
  char* newstr = new char[len1 + len2 + strlen(str3) + 1];
1763
0
  if (!newstr) {
1764
0
    return nullptr;
1765
0
  }
1766
0
  strcpy(newstr, str1);
1767
0
  strcat(newstr + len1, str2);
1768
0
  strcat(newstr + len1 + len2, str3);
1769
0
  return newstr;
1770
0
}
1771
1772
std::string SystemTools::LowerCase(std::string s)
1773
1.50k
{
1774
1.50k
  std::transform(s.begin(), s.end(), s.begin(),
1775
1.81M
                 [](unsigned char c) { return std::tolower(c); });
1776
1.50k
  return s;
1777
1.50k
}
1778
1779
std::string SystemTools::UpperCase(std::string s)
1780
1.50k
{
1781
1.50k
  std::transform(s.begin(), s.end(), s.begin(),
1782
1.81M
                 [](unsigned char c) { return std::toupper(c); });
1783
1.50k
  return s;
1784
1.50k
}
1785
1786
// Count char in string
1787
size_t SystemTools::CountChar(char const* str, char c)
1788
0
{
1789
0
  size_t count = 0;
1790
1791
0
  if (str) {
1792
0
    while (*str) {
1793
0
      if (*str == c) {
1794
0
        ++count;
1795
0
      }
1796
0
      ++str;
1797
0
    }
1798
0
  }
1799
0
  return count;
1800
0
}
1801
1802
// Remove chars in string
1803
char* SystemTools::RemoveChars(char const* str, char const* toremove)
1804
0
{
1805
0
  if (!str) {
1806
0
    return nullptr;
1807
0
  }
1808
0
  char* clean_str = new char[strlen(str) + 1];
1809
0
  char* ptr = clean_str;
1810
0
  while (*str) {
1811
0
    char const* str2 = toremove;
1812
0
    while (*str2 && *str != *str2) {
1813
0
      ++str2;
1814
0
    }
1815
0
    if (!*str2) {
1816
0
      *ptr++ = *str;
1817
0
    }
1818
0
    ++str;
1819
0
  }
1820
0
  *ptr = '\0';
1821
0
  return clean_str;
1822
0
}
1823
1824
// Remove chars in string
1825
char* SystemTools::RemoveCharsButUpperHex(char const* str)
1826
0
{
1827
0
  if (!str) {
1828
0
    return nullptr;
1829
0
  }
1830
0
  char* clean_str = new char[strlen(str) + 1];
1831
0
  char* ptr = clean_str;
1832
0
  while (*str) {
1833
0
    if ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'F')) {
1834
0
      *ptr++ = *str;
1835
0
    }
1836
0
    ++str;
1837
0
  }
1838
0
  *ptr = '\0';
1839
0
  return clean_str;
1840
0
}
1841
1842
// Replace chars in string
1843
char* SystemTools::ReplaceChars(char* str, char const* toreplace,
1844
                                char replacement)
1845
0
{
1846
0
  if (str) {
1847
0
    char* ptr = str;
1848
0
    while (*ptr) {
1849
0
      char const* ptr2 = toreplace;
1850
0
      while (*ptr2) {
1851
0
        if (*ptr == *ptr2) {
1852
0
          *ptr = replacement;
1853
0
        }
1854
0
        ++ptr2;
1855
0
      }
1856
0
      ++ptr;
1857
0
    }
1858
0
  }
1859
0
  return str;
1860
0
}
1861
1862
// Returns if string starts with another string
1863
bool SystemTools::StringStartsWith(char const* str1, char const* str2)
1864
0
{
1865
0
  if (!str1 || !str2) {
1866
0
    return false;
1867
0
  }
1868
0
  size_t len1 = strlen(str1), len2 = strlen(str2);
1869
0
  return len1 >= len2 && !strncmp(str1, str2, len2) ? true : false;
1870
0
}
1871
1872
// Returns if string starts with another string
1873
bool SystemTools::StringStartsWith(std::string const& str1, char const* str2)
1874
1.81k
{
1875
1.81k
  if (!str2) {
1876
0
    return false;
1877
0
  }
1878
1.81k
  size_t len1 = str1.size(), len2 = strlen(str2);
1879
1.81k
  return len1 >= len2 && !strncmp(str1.c_str(), str2, len2) ? true : false;
1880
1.81k
}
1881
1882
// Returns if string ends with another string
1883
bool SystemTools::StringEndsWith(char const* str1, char const* str2)
1884
0
{
1885
0
  if (!str1 || !str2) {
1886
0
    return false;
1887
0
  }
1888
0
  size_t len1 = strlen(str1), len2 = strlen(str2);
1889
0
  return len1 >= len2 && !strncmp(str1 + (len1 - len2), str2, len2) ? true
1890
0
                                                                    : false;
1891
0
}
1892
1893
// Returns if string ends with another string
1894
bool SystemTools::StringEndsWith(std::string const& str1, char const* str2)
1895
1.81k
{
1896
1.81k
  if (!str2) {
1897
0
    return false;
1898
0
  }
1899
1.81k
  size_t len1 = str1.size(), len2 = strlen(str2);
1900
1.81k
  return len1 >= len2 && !strncmp(str1.c_str() + (len1 - len2), str2, len2)
1901
1.81k
    ? true
1902
1.81k
    : false;
1903
1.81k
}
1904
1905
// Returns a pointer to the last occurrence of str2 in str1
1906
char const* SystemTools::FindLastString(char const* str1, char const* str2)
1907
0
{
1908
0
  if (!str1 || !str2) {
1909
0
    return nullptr;
1910
0
  }
1911
1912
0
  size_t len1 = strlen(str1), len2 = strlen(str2);
1913
0
  if (len1 >= len2) {
1914
0
    char const* ptr = str1 + len1 - len2;
1915
0
    do {
1916
0
      if (!strncmp(ptr, str2, len2)) {
1917
0
        return ptr;
1918
0
      }
1919
0
    } while (ptr-- != str1);
1920
0
  }
1921
1922
0
  return nullptr;
1923
0
}
1924
1925
// Duplicate string
1926
char* SystemTools::DuplicateString(char const* str)
1927
0
{
1928
0
  if (str) {
1929
0
    char* newstr = new char[strlen(str) + 1];
1930
0
    return strcpy(newstr, str);
1931
0
  }
1932
0
  return nullptr;
1933
0
}
1934
1935
// Return a cropped string
1936
std::string SystemTools::CropString(std::string const& s, size_t max_len)
1937
0
{
1938
0
  if (s.empty() || max_len == 0 || max_len >= s.size()) {
1939
0
    return s;
1940
0
  }
1941
1942
0
  std::string n;
1943
0
  n.reserve(max_len);
1944
1945
0
  size_t middle = max_len / 2;
1946
1947
0
  n.assign(s, 0, middle);
1948
0
  n += s.substr(s.size() - (max_len - middle));
1949
1950
0
  if (max_len > 2) {
1951
0
    n[middle] = '.';
1952
0
    if (max_len > 3) {
1953
0
      n[middle - 1] = '.';
1954
0
      if (max_len > 4) {
1955
0
        n[middle + 1] = '.';
1956
0
      }
1957
0
    }
1958
0
  }
1959
1960
0
  return n;
1961
0
}
1962
1963
std::vector<std::string> SystemTools::SplitString(std::string const& p,
1964
                                                  char sep, bool isPath)
1965
0
{
1966
0
  std::string path = p;
1967
0
  std::vector<std::string> paths;
1968
0
  if (path.empty()) {
1969
0
    return paths;
1970
0
  }
1971
0
  if (isPath && path[0] == '/') {
1972
0
    path.erase(path.begin());
1973
0
    paths.emplace_back("/");
1974
0
  }
1975
0
  std::string::size_type pos1 = 0;
1976
0
  std::string::size_type pos2 = path.find(sep, pos1);
1977
0
  while (pos2 != std::string::npos) {
1978
0
    paths.push_back(path.substr(pos1, pos2 - pos1));
1979
0
    pos1 = pos2 + 1;
1980
0
    pos2 = path.find(sep, pos1 + 1);
1981
0
  }
1982
0
  paths.push_back(path.substr(pos1, pos2 - pos1));
1983
1984
0
  return paths;
1985
0
}
1986
1987
int SystemTools::EstimateFormatLength(char const* format, va_list ap)
1988
0
{
1989
0
  if (!format) {
1990
0
    return 0;
1991
0
  }
1992
1993
  // Quick-hack attempt at estimating the length of the string.
1994
  // Should never under-estimate.
1995
1996
  // Start with the length of the format string itself.
1997
1998
0
  size_t length = strlen(format);
1999
2000
  // Increase the length for every argument in the format.
2001
2002
0
  char const* cur = format;
2003
0
  while (*cur) {
2004
0
    if (*cur++ == '%') {
2005
      // Skip "%%" since it doesn't correspond to a va_arg.
2006
0
      if (*cur != '%') {
2007
0
        while (!int(isalpha(*cur))) {
2008
0
          ++cur;
2009
0
        }
2010
0
        switch (*cur) {
2011
0
          case 's': {
2012
            // Check the length of the string.
2013
0
            char* s = va_arg(ap, char*);
2014
0
            if (s) {
2015
0
              length += strlen(s);
2016
0
            }
2017
0
          } break;
2018
0
          case 'e':
2019
0
          case 'f':
2020
0
          case 'g': {
2021
            // Assume the argument contributes no more than 64 characters.
2022
0
            length += 64;
2023
2024
            // Eat the argument.
2025
0
            static_cast<void>(va_arg(ap, double));
2026
0
          } break;
2027
0
          default: {
2028
            // Assume the argument contributes no more than 64 characters.
2029
0
            length += 64;
2030
2031
            // Eat the argument.
2032
0
            static_cast<void>(va_arg(ap, int));
2033
0
          } break;
2034
0
        }
2035
0
      }
2036
2037
      // Move past the characters just tested.
2038
0
      ++cur;
2039
0
    }
2040
0
  }
2041
2042
0
  return static_cast<int>(length);
2043
0
}
2044
2045
std::string SystemTools::EscapeChars(char const* str,
2046
                                     char const* chars_to_escape,
2047
                                     char escape_char)
2048
0
{
2049
0
  std::string n;
2050
0
  if (str) {
2051
0
    if (!chars_to_escape || !*chars_to_escape) {
2052
0
      n.append(str);
2053
0
    } else {
2054
0
      n.reserve(strlen(str));
2055
0
      while (*str) {
2056
0
        char const* ptr = chars_to_escape;
2057
0
        while (*ptr) {
2058
0
          if (*str == *ptr) {
2059
0
            n += escape_char;
2060
0
            break;
2061
0
          }
2062
0
          ++ptr;
2063
0
        }
2064
0
        n += *str;
2065
0
        ++str;
2066
0
      }
2067
0
    }
2068
0
  }
2069
0
  return n;
2070
0
}
2071
2072
#ifdef __VMS
2073
static void ConvertVMSToUnix(std::string& path)
2074
{
2075
  std::string::size_type rootEnd = path.find(":[");
2076
  std::string::size_type pathEnd = path.find("]");
2077
  if (rootEnd != std::string::npos) {
2078
    std::string root = path.substr(0, rootEnd);
2079
    std::string pathPart = path.substr(rootEnd + 2, pathEnd - rootEnd - 2);
2080
    char const* pathCString = pathPart.c_str();
2081
    char const* pos0 = pathCString;
2082
    for (std::string::size_type pos = 0; *pos0; ++pos) {
2083
      if (*pos0 == '.') {
2084
        pathPart[pos] = '/';
2085
      }
2086
      pos0++;
2087
    }
2088
    path = "/" + root + "/" + pathPart;
2089
  }
2090
}
2091
#endif
2092
2093
// convert windows slashes to unix slashes
2094
void SystemTools::ConvertToUnixSlashes(std::string& path)
2095
28.2k
{
2096
28.2k
  if (path.empty()) {
2097
1
    return;
2098
1
  }
2099
2100
#ifdef __VMS
2101
  ConvertVMSToUnix(path);
2102
#else
2103
  // replace backslashes
2104
28.2k
  std::replace(path.begin(), path.end(), '\\', '/');
2105
2106
  // collapse repeated slashes, except exactly two leading slashes are
2107
  // meaningful and must be preserved.
2108
28.2k
  bool hasDoubleSlash = path[0] == '/' && path[1] == '/' && path[2] != '/';
2109
28.2k
  auto uniqueEnd = std::unique(
2110
28.2k
    path.begin() + hasDoubleSlash, path.end(),
2111
2.78M
    [](char c1, char c2) -> bool { return c1 == '/' && c1 == c2; });
2112
28.2k
  path.erase(uniqueEnd, path.end());
2113
28.2k
#endif
2114
2115
  // if there is a tilda ~ then replace it with HOME
2116
28.2k
  if (path[0] == '~' && (path[1] == '/' || path[1] == '\0')) {
2117
111
    std::string homeEnv;
2118
111
    if (SystemTools::GetEnv("HOME", homeEnv)) {
2119
111
      path.replace(0, 1, homeEnv);
2120
111
    }
2121
111
  }
2122
28.0k
#ifdef HAVE_GETPWNAM
2123
28.0k
  else if (path[0] == '~') {
2124
254
    std::string::size_type idx = path.find('/');
2125
254
    std::string user = path.substr(1, idx - 1);
2126
254
    passwd* pw = getpwnam(user.c_str());
2127
254
    if (pw) {
2128
9
      path.replace(0, idx, pw->pw_dir);
2129
9
    }
2130
254
  }
2131
28.2k
#endif
2132
  // remove trailing slash, but preserve the root slash and the slash
2133
  // after windows drive letter (c:/).
2134
28.2k
  size_t size = path.size();
2135
28.2k
  if (size > 1 && path.back() == '/') {
2136
227
    if (!(size == 3 && path[1] == ':') && path[size - 2] != '/') {
2137
221
      path.resize(size - 1);
2138
221
    }
2139
227
  }
2140
28.2k
}
2141
2142
#ifdef _WIN32
2143
std::wstring SystemTools::ConvertToWindowsExtendedPath(
2144
  std::string const& source)
2145
{
2146
  return Encoding::ToWindowsExtendedPath(source);
2147
}
2148
#endif
2149
2150
// change // to /, and escape any spaces in the path
2151
std::string SystemTools::ConvertToUnixOutputPath(std::string const& path)
2152
0
{
2153
0
  std::string ret = path;
2154
2155
  // remove // except at the beginning might be a cygwin drive
2156
0
  std::string::size_type pos = 1;
2157
0
  while ((pos = ret.find("//", pos)) != std::string::npos) {
2158
0
    ret.erase(pos, 1);
2159
0
  }
2160
  // escape spaces and () in the path
2161
0
  if (ret.find_first_of(' ') != std::string::npos) {
2162
0
    std::string result;
2163
0
    char lastch = 1;
2164
0
    for (char const* ch = ret.c_str(); *ch != '\0'; ++ch) {
2165
      // if it is already escaped then don't try to escape it again
2166
0
      if ((*ch == ' ') && lastch != '\\') {
2167
0
        result += '\\';
2168
0
      }
2169
0
      result += *ch;
2170
0
      lastch = *ch;
2171
0
    }
2172
0
    ret = result;
2173
0
  }
2174
0
  return ret;
2175
0
}
2176
2177
std::string SystemTools::ConvertToOutputPath(std::string const& path)
2178
0
{
2179
#if defined(_WIN32) && !defined(__CYGWIN__)
2180
  return SystemTools::ConvertToWindowsOutputPath(path);
2181
#else
2182
0
  return SystemTools::ConvertToUnixOutputPath(path);
2183
0
#endif
2184
0
}
2185
2186
// remove double slashes not at the start
2187
std::string SystemTools::ConvertToWindowsOutputPath(std::string const& path)
2188
0
{
2189
0
  std::string ret;
2190
  // make it big enough for all of path and double quotes
2191
0
  ret.reserve(path.size() + 3);
2192
  // put path into the string
2193
0
  ret = path;
2194
0
  std::string::size_type pos = 0;
2195
  // first convert all of the slashes
2196
0
  while ((pos = ret.find('/', pos)) != std::string::npos) {
2197
0
    ret[pos] = '\\';
2198
0
    pos++;
2199
0
  }
2200
  // check for really small paths
2201
0
  if (ret.size() < 2) {
2202
0
    return ret;
2203
0
  }
2204
  // now clean up a bit and remove double slashes
2205
  // Only if it is not the first position in the path which is a network
2206
  // path on windows
2207
0
  pos = 1; // start at position 1
2208
0
  if (ret[0] == '\"') {
2209
0
    pos = 2; // if the string is already quoted then start at 2
2210
0
    if (ret.size() < 3) {
2211
0
      return ret;
2212
0
    }
2213
0
  }
2214
0
  while ((pos = ret.find("\\\\", pos)) != std::string::npos) {
2215
0
    ret.erase(pos, 1);
2216
0
  }
2217
  // now double quote the path if it has spaces in it
2218
  // and is not already double quoted
2219
0
  if (ret.find(' ') != std::string::npos && ret[0] != '\"') {
2220
0
    ret.insert(static_cast<std::string::size_type>(0),
2221
0
               static_cast<std::string::size_type>(1), '\"');
2222
0
    ret.append(1, '\"');
2223
0
  }
2224
0
  return ret;
2225
0
}
2226
2227
/**
2228
 * Append the filename from the path source to the directory name dir.
2229
 */
2230
static std::string FileInDir(std::string const& source, std::string const& dir)
2231
0
{
2232
0
  std::string new_destination = dir;
2233
0
  SystemTools::ConvertToUnixSlashes(new_destination);
2234
0
  return new_destination + '/' + SystemTools::GetFilenameName(source);
2235
0
}
2236
2237
SystemTools::CopyStatus SystemTools::CopyFileIfDifferent(
2238
  std::string const& source, std::string const& destination)
2239
0
{
2240
  // special check for a destination that is a directory
2241
  // FilesDiffer does not handle file to directory compare
2242
0
  if (SystemTools::FileIsDirectory(destination)) {
2243
0
    std::string const new_destination = FileInDir(source, destination);
2244
0
    if (!SystemTools::ComparePath(new_destination, destination)) {
2245
0
      return SystemTools::CopyFileIfDifferent(source, new_destination);
2246
0
    }
2247
0
  } else {
2248
    // source and destination are files so do a copy if they
2249
    // are different
2250
0
    if (SystemTools::FilesDiffer(source, destination)) {
2251
0
      return SystemTools::CopyFileAlways(source, destination);
2252
0
    }
2253
0
  }
2254
  // at this point the files must be the same so return true
2255
0
  return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2256
0
}
2257
2258
SystemTools::CopyStatus SystemTools::CopyFileIfNewer(
2259
  std::string const& source, std::string const& destination)
2260
0
{
2261
  // special check for a destination that is a directory
2262
  // FileTimeCompare does not handle file to directory compare
2263
0
  if (SystemTools::FileIsDirectory(destination)) {
2264
0
    std::string const new_destination = FileInDir(source, destination);
2265
0
    if (!SystemTools::ComparePath(new_destination, destination)) {
2266
0
      return SystemTools::CopyFileIfNewer(source, new_destination);
2267
0
    }
2268
    // If source and destination are the same path, don't copy
2269
0
    return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2270
0
  }
2271
2272
  // source and destination are files so do a copy if source is newer
2273
  // Check if source file exists first
2274
0
  if (!SystemTools::FileExists(source)) {
2275
0
    return CopyStatus{ Status::POSIX(ENOENT), CopyStatus::SourcePath };
2276
0
  }
2277
  // If destination doesn't exist, always copy
2278
0
  if (!SystemTools::FileExists(destination)) {
2279
0
    return SystemTools::CopyFileAlways(source, destination);
2280
0
  }
2281
  // Check if source is newer than destination
2282
0
  int timeResult;
2283
0
  Status timeStatus =
2284
0
    SystemTools::FileTimeCompare(source, destination, &timeResult);
2285
0
  if (timeStatus.IsSuccess()) {
2286
0
    if (timeResult > 0) {
2287
      // Source is newer, copy it
2288
0
      return SystemTools::CopyFileAlways(source, destination);
2289
0
    } else {
2290
      // Source is not newer, no need to copy
2291
0
      return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2292
0
    }
2293
0
  } else {
2294
    // Time comparison failed, be conservative and copy to ensure updates are
2295
    // not missed
2296
0
    return SystemTools::CopyFileAlways(source, destination);
2297
0
  }
2298
0
}
2299
2300
0
#define KWSYS_ST_BUFFER 4096
2301
2302
bool SystemTools::FilesDiffer(std::string const& source,
2303
                              std::string const& destination)
2304
0
{
2305
2306
#if defined(_WIN32)
2307
  WIN32_FILE_ATTRIBUTE_DATA statSource;
2308
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(source).c_str(),
2309
                           GetFileExInfoStandard, &statSource) == 0) {
2310
    return true;
2311
  }
2312
2313
  WIN32_FILE_ATTRIBUTE_DATA statDestination;
2314
  if (GetFileAttributesExW(
2315
        Encoding::ToWindowsExtendedPath(destination).c_str(),
2316
        GetFileExInfoStandard, &statDestination) == 0) {
2317
    return true;
2318
  }
2319
2320
  if (statSource.nFileSizeHigh != statDestination.nFileSizeHigh ||
2321
      statSource.nFileSizeLow != statDestination.nFileSizeLow) {
2322
    return true;
2323
  }
2324
2325
  if (statSource.nFileSizeHigh == 0 && statSource.nFileSizeLow == 0) {
2326
    return false;
2327
  }
2328
  auto nleft =
2329
    ((__int64)statSource.nFileSizeHigh << 32) + statSource.nFileSizeLow;
2330
2331
#else
2332
2333
0
  struct stat statSource;
2334
0
  if (stat(source.c_str(), &statSource) != 0) {
2335
0
    return true;
2336
0
  }
2337
2338
0
  struct stat statDestination;
2339
0
  if (stat(destination.c_str(), &statDestination) != 0) {
2340
0
    return true;
2341
0
  }
2342
2343
0
  if (statSource.st_size != statDestination.st_size) {
2344
0
    return true;
2345
0
  }
2346
2347
0
  if (statSource.st_size == 0) {
2348
0
    return false;
2349
0
  }
2350
0
  off_t nleft = statSource.st_size;
2351
0
#endif
2352
2353
#if defined(_WIN32)
2354
  kwsys::ifstream finSource(source.c_str(), (std::ios::binary | std::ios::in));
2355
  kwsys::ifstream finDestination(destination.c_str(),
2356
                                 (std::ios::binary | std::ios::in));
2357
#else
2358
0
  kwsys::ifstream finSource(source.c_str());
2359
0
  kwsys::ifstream finDestination(destination.c_str());
2360
0
#endif
2361
0
  if (!finSource || !finDestination) {
2362
0
    return true;
2363
0
  }
2364
2365
  // Compare the files a block at a time.
2366
0
  char source_buf[KWSYS_ST_BUFFER];
2367
0
  char dest_buf[KWSYS_ST_BUFFER];
2368
0
  while (nleft > 0) {
2369
    // Read a block from each file.
2370
0
    std::streamsize nnext = (nleft > KWSYS_ST_BUFFER)
2371
0
      ? KWSYS_ST_BUFFER
2372
0
      : static_cast<std::streamsize>(nleft);
2373
0
    finSource.read(source_buf, nnext);
2374
0
    finDestination.read(dest_buf, nnext);
2375
2376
    // If either failed to read assume they are different.
2377
0
    if (static_cast<std::streamsize>(finSource.gcount()) != nnext ||
2378
0
        static_cast<std::streamsize>(finDestination.gcount()) != nnext) {
2379
0
      return true;
2380
0
    }
2381
2382
    // If this block differs the file differs.
2383
0
    if (memcmp(static_cast<void const*>(source_buf),
2384
0
               static_cast<void const*>(dest_buf),
2385
0
               static_cast<size_t>(nnext)) != 0) {
2386
0
      return true;
2387
0
    }
2388
2389
    // Update the byte count remaining.
2390
0
    nleft -= nnext;
2391
0
  }
2392
2393
  // No differences found.
2394
0
  return false;
2395
0
}
2396
2397
bool SystemTools::TextFilesDiffer(std::string const& path1,
2398
                                  std::string const& path2)
2399
0
{
2400
0
  kwsys::ifstream if1(path1.c_str());
2401
0
  kwsys::ifstream if2(path2.c_str());
2402
0
  if (!if1 || !if2) {
2403
0
    return true;
2404
0
  }
2405
2406
0
  for (;;) {
2407
0
    std::string line1, line2;
2408
0
    bool hasData1 = GetLineFromStream(if1, line1);
2409
0
    bool hasData2 = GetLineFromStream(if2, line2);
2410
0
    if (hasData1 != hasData2) {
2411
0
      return true;
2412
0
    }
2413
0
    if (!hasData1) {
2414
0
      break;
2415
0
    }
2416
0
    if (line1 != line2) {
2417
0
      return true;
2418
0
    }
2419
0
  }
2420
0
  return false;
2421
0
}
2422
2423
SystemTools::CopyStatus SystemTools::CopyFileContentBlockwise(
2424
  std::string const& source, std::string const& destination)
2425
0
{
2426
  // Open files
2427
0
  kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
2428
0
  if (!fin) {
2429
0
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::SourcePath };
2430
0
  }
2431
2432
  // try and remove the destination file so that read only destination files
2433
  // can be written to.
2434
  // If the remove fails continue so that files in read only directories
2435
  // that do not allow file removal can be modified.
2436
0
  SystemTools::RemoveFile(destination);
2437
2438
0
  kwsys::ofstream fout(destination.c_str(),
2439
0
                       std::ios::out | std::ios::trunc | std::ios::binary);
2440
0
  if (!fout) {
2441
0
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::DestPath };
2442
0
  }
2443
2444
  // This copy loop is very sensitive on certain platforms with
2445
  // slightly broken stream libraries (like HPUX).  Normally, it is
2446
  // incorrect to not check the error condition on the fin.read()
2447
  // before using the data, but the fin.gcount() will be zero if an
2448
  // error occurred.  Therefore, the loop should be safe everywhere.
2449
0
  while (fin) {
2450
0
    int const bufferSize = 4096;
2451
0
    char buffer[bufferSize];
2452
2453
0
    fin.read(buffer, bufferSize);
2454
0
    if (fin.gcount()) {
2455
0
      fout.write(buffer, fin.gcount());
2456
0
    } else {
2457
0
      break;
2458
0
    }
2459
0
  }
2460
2461
  // Make sure the operating system has finished writing the file
2462
  // before closing it.  This will ensure the file is finished before
2463
  // the check below.
2464
0
  fout.flush();
2465
2466
0
  fin.close();
2467
0
  fout.close();
2468
2469
0
  if (!fout) {
2470
0
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::DestPath };
2471
0
  }
2472
2473
0
  return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2474
0
}
2475
2476
/**
2477
 * Attempt to copy source file to the destination file using
2478
 * operating system mechanisms.
2479
 *
2480
 * If available, copy-on-write/clone will be used.
2481
 * On Linux, the FICLONE ioctl is used to create a clone of the source file.
2482
 * On macOS, the copyfile() call is used to make a clone of the file, and
2483
 * it will fall back to a regular copy if that's not possible.
2484
 *
2485
 * This function will follow symlinks (ie copy the file being
2486
 * pointed-to, not the symlink itself), and the resultant
2487
 * file will be owned by the uid of this process. It will overwrite
2488
 * an existing destination file.
2489
 *
2490
 * Examples of why this method may fail -
2491
 * - We're running on an OS for which this method is not implemented.
2492
 * - The underlying OS won't do a copy for us, and -
2493
 *   - The source and destination are on different file systems
2494
 *     thus a clone is not possible.
2495
 *   - The underlying filesystem does not support file cloning.
2496
 */
2497
SystemTools::CopyStatus SystemTools::CloneFileContent(
2498
  std::string const& source, std::string const& destination)
2499
0
{
2500
0
#if defined(__linux) && defined(FICLONE)
2501
0
  int in = open(source.c_str(), O_RDONLY);
2502
0
  if (in < 0) {
2503
0
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::SourcePath };
2504
0
  }
2505
2506
0
  SystemTools::RemoveFile(destination);
2507
2508
0
  int out =
2509
0
    open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
2510
0
  if (out < 0) {
2511
0
    CopyStatus status{ Status::POSIX_errno(), CopyStatus::DestPath };
2512
0
    close(in);
2513
0
    return status;
2514
0
  }
2515
2516
0
  CopyStatus status{ Status::Success(), CopyStatus::NoPath };
2517
0
  if (ioctl(out, FICLONE, in) < 0) {
2518
0
    status = CopyStatus{ Status::POSIX_errno(), CopyStatus::NoPath };
2519
0
  }
2520
0
  close(in);
2521
0
  close(out);
2522
2523
0
  return status;
2524
#elif defined(__APPLE__) &&                                                   \
2525
  defined(KWSYS_SYSTEMTOOLS_HAVE_MACOS_COPYFILE_CLONE)
2526
  // When running as root, copyfile() copies more metadata than we
2527
  // want, such as ownership.  Pretend it is not available.
2528
  if (getuid() == 0) {
2529
    return CopyStatus{ Status::POSIX(ENOSYS), CopyStatus::NoPath };
2530
  }
2531
2532
  // NOTE: we cannot use `clonefile` as the {a,c,m}time for the file needs to
2533
  // be updated by `copy_file_if_different` and `copy_file`.
2534
  //
2535
  // COPYFILE_CLONE forces COPYFILE_NOFOLLOW_SRC and that violates the
2536
  // invariant that this should result in a file. We used to manually specify
2537
  // COPYFILE_EXCL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA here, but
2538
  // what the copyfile() manpage does not tell you is that COPYFILE_DATA
2539
  // appears to disable cloning all together. Instead, explicitly reject
2540
  // copying symlinks here.
2541
  //
2542
  // COPYFILE_CLONE implies a few flags, including COPYFILE_EXCL.
2543
  // We add COPYFILE_UNLINK to be consistent with the Linux implementation
2544
  // above, as well as CopyFileContentBlockwise(). This will remove the
2545
  // destination file before cloning, allowing this call to complete
2546
  // if the destination file already exists.
2547
  //
2548
  if (SystemTools::FileIsSymlink(source)) {
2549
    return CopyStatus{ Status::POSIX(ENOSYS), CopyStatus::NoPath };
2550
  }
2551
2552
  if (copyfile(source.c_str(), destination.c_str(), nullptr,
2553
               COPYFILE_METADATA | COPYFILE_CLONE | COPYFILE_UNLINK) < 0) {
2554
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::NoPath };
2555
  }
2556
#  if KWSYS_CXX_HAS_UTIMENSAT
2557
  // utimensat is only available on newer Unixes and macOS 10.13+
2558
  if (utimensat(AT_FDCWD, destination.c_str(), nullptr, 0) < 0) {
2559
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::DestPath };
2560
  }
2561
#  else
2562
  // fall back to utimes
2563
  if (utimes(destination.c_str(), nullptr) < 0) {
2564
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::DestPath };
2565
  }
2566
#  endif
2567
  return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2568
#else
2569
  (void)source;
2570
  (void)destination;
2571
  return CopyStatus{ Status::POSIX(ENOSYS), CopyStatus::NoPath };
2572
#endif
2573
0
}
2574
2575
/**
2576
 * Copy a file named by "source" to the file named by "destination".
2577
 */
2578
SystemTools::CopyStatus SystemTools::CopyFileAlways(
2579
  std::string const& source, std::string const& destination)
2580
0
{
2581
0
  CopyStatus status;
2582
0
  mode_t perm = 0;
2583
0
  Status perms = SystemTools::GetPermissions(source, perm);
2584
0
  std::string real_destination = destination;
2585
2586
0
  if (SystemTools::FileIsDirectory(source)) {
2587
0
    status = CopyStatus{ SystemTools::MakeDirectory(destination),
2588
0
                         CopyStatus::DestPath };
2589
0
    if (!status.IsSuccess()) {
2590
0
      return status;
2591
0
    }
2592
0
  } else {
2593
    // If destination is a directory, try to create a file with the same
2594
    // name as the source in that directory.
2595
2596
0
    std::string destination_dir;
2597
0
    if (SystemTools::FileIsDirectory(destination)) {
2598
0
      destination_dir = real_destination;
2599
0
      SystemTools::ConvertToUnixSlashes(real_destination);
2600
0
      real_destination += '/';
2601
0
      std::string source_name = source;
2602
0
      real_destination += SystemTools::GetFilenameName(source_name);
2603
0
    } else {
2604
0
      destination_dir = SystemTools::GetFilenamePath(destination);
2605
0
    }
2606
    // If files are the same do not copy
2607
0
    if (SystemTools::SameFile(source, real_destination)) {
2608
0
      return status;
2609
0
    }
2610
2611
    // Create destination directory
2612
0
    if (!destination_dir.empty()) {
2613
0
      status = CopyStatus{ SystemTools::MakeDirectory(destination_dir),
2614
0
                           CopyStatus::DestPath };
2615
0
      if (!status.IsSuccess()) {
2616
0
        return status;
2617
0
      }
2618
0
    }
2619
2620
0
    status = SystemTools::CloneFileContent(source, real_destination);
2621
    // if cloning did not succeed, fall back to blockwise copy
2622
0
    if (!status.IsSuccess()) {
2623
0
      status = SystemTools::CopyFileContentBlockwise(source, real_destination);
2624
0
    }
2625
0
    if (!status.IsSuccess()) {
2626
0
      return status;
2627
0
    }
2628
0
  }
2629
0
  if (perms) {
2630
0
    status = CopyStatus{ SystemTools::SetPermissions(real_destination, perm),
2631
0
                         CopyStatus::DestPath };
2632
0
  }
2633
0
  return status;
2634
0
}
2635
2636
SystemTools::CopyStatus SystemTools::CopyAFile(std::string const& source,
2637
                                               std::string const& destination,
2638
                                               SystemTools::CopyWhen when)
2639
0
{
2640
0
  switch (when) {
2641
0
    case SystemTools::CopyWhen::Always:
2642
0
      return SystemTools::CopyFileAlways(source, destination);
2643
0
    case SystemTools::CopyWhen::OnlyIfDifferent:
2644
0
      return SystemTools::CopyFileIfDifferent(source, destination);
2645
0
    case SystemTools::CopyWhen::OnlyIfNewer:
2646
0
      return SystemTools::CopyFileIfNewer(source, destination);
2647
0
    default:
2648
0
      break;
2649
0
  }
2650
  // Should not reach here
2651
0
  return CopyStatus{ Status::POSIX_errno(), CopyStatus::NoPath };
2652
0
}
2653
2654
SystemTools::CopyStatus SystemTools::CopyAFile(std::string const& source,
2655
                                               std::string const& destination,
2656
                                               bool always)
2657
0
{
2658
0
  return SystemTools::CopyAFile(source, destination,
2659
0
                                always ? CopyWhen::Always
2660
0
                                       : CopyWhen::OnlyIfDifferent);
2661
0
}
2662
2663
/**
2664
 * Copy a directory content from "source" directory to the directory named by
2665
 * "destination".
2666
 */
2667
Status SystemTools::CopyADirectory(std::string const& source,
2668
                                   std::string const& destination,
2669
                                   SystemTools::CopyWhen when)
2670
0
{
2671
0
  Status status;
2672
0
  Directory dir;
2673
0
  status = dir.Load(source);
2674
0
  if (!status.IsSuccess()) {
2675
0
    return status;
2676
0
  }
2677
0
  status = SystemTools::MakeDirectory(destination);
2678
0
  if (!status.IsSuccess()) {
2679
0
    return status;
2680
0
  }
2681
2682
0
  for (size_t fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
2683
0
    if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") != 0 &&
2684
0
        strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..") != 0) {
2685
0
      std::string fullPath = source;
2686
0
      fullPath += "/";
2687
0
      fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
2688
0
      if (SystemTools::FileIsDirectory(fullPath)) {
2689
0
        std::string fullDestPath = destination;
2690
0
        fullDestPath += "/";
2691
0
        fullDestPath += dir.GetFile(static_cast<unsigned long>(fileNum));
2692
0
        status = SystemTools::CopyADirectory(fullPath, fullDestPath, when);
2693
0
        if (!status.IsSuccess()) {
2694
0
          return status;
2695
0
        }
2696
0
      } else {
2697
0
        status = SystemTools::CopyAFile(fullPath, destination, when);
2698
0
        if (!status.IsSuccess()) {
2699
0
          return status;
2700
0
        }
2701
0
      }
2702
0
    }
2703
0
  }
2704
2705
0
  return status;
2706
0
}
2707
2708
Status SystemTools::CopyADirectory(std::string const& source,
2709
                                   std::string const& destination, bool always)
2710
0
{
2711
0
  return SystemTools::CopyADirectory(source, destination,
2712
0
                                     always ? CopyWhen::Always
2713
0
                                            : CopyWhen::OnlyIfDifferent);
2714
0
}
2715
2716
// return size of file; also returns zero if no file exists
2717
unsigned long SystemTools::FileLength(std::string const& filename)
2718
0
{
2719
0
  unsigned long length = 0;
2720
#ifdef _WIN32
2721
  WIN32_FILE_ATTRIBUTE_DATA fs;
2722
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2723
                           GetFileExInfoStandard, &fs) != 0) {
2724
    /* To support the full 64-bit file size, use fs.nFileSizeHigh
2725
     * and fs.nFileSizeLow to construct the 64 bit size
2726
2727
    length = ((__int64)fs.nFileSizeHigh << 32) + fs.nFileSizeLow;
2728
     */
2729
    length = static_cast<unsigned long>(fs.nFileSizeLow);
2730
  }
2731
#else
2732
0
  struct stat fs;
2733
0
  if (stat(filename.c_str(), &fs) == 0) {
2734
0
    length = static_cast<unsigned long>(fs.st_size);
2735
0
  }
2736
0
#endif
2737
0
  return length;
2738
0
}
2739
2740
int SystemTools::Strucmp(char const* l, char const* r)
2741
0
{
2742
0
  int lc;
2743
0
  int rc;
2744
0
  do {
2745
0
    lc = tolower(static_cast<unsigned char>(*l++));
2746
0
    rc = tolower(static_cast<unsigned char>(*r++));
2747
0
  } while (lc == rc && lc);
2748
0
  return lc - rc;
2749
0
}
2750
2751
// return file's modified time
2752
long int SystemTools::ModifiedTime(std::string const& filename)
2753
0
{
2754
0
  long int mt = 0;
2755
#ifdef _WIN32
2756
  WIN32_FILE_ATTRIBUTE_DATA fs;
2757
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2758
                           GetFileExInfoStandard, &fs) != 0) {
2759
    mt = windows_filetime_to_posix_time(fs.ftLastWriteTime);
2760
  }
2761
#else
2762
0
  struct stat fs;
2763
0
  if (stat(filename.c_str(), &fs) == 0) {
2764
0
    mt = static_cast<long int>(fs.st_mtime);
2765
0
  }
2766
0
#endif
2767
0
  return mt;
2768
0
}
2769
2770
// return file's creation time
2771
long int SystemTools::CreationTime(std::string const& filename)
2772
0
{
2773
0
  long int ct = 0;
2774
#ifdef _WIN32
2775
  WIN32_FILE_ATTRIBUTE_DATA fs;
2776
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2777
                           GetFileExInfoStandard, &fs) != 0) {
2778
    ct = windows_filetime_to_posix_time(fs.ftCreationTime);
2779
  }
2780
#else
2781
0
  struct stat fs;
2782
0
  if (stat(filename.c_str(), &fs) == 0) {
2783
0
    ct = fs.st_ctime >= 0 ? static_cast<long int>(fs.st_ctime) : 0;
2784
0
  }
2785
0
#endif
2786
0
  return ct;
2787
0
}
2788
2789
std::string SystemTools::GetLastSystemError()
2790
0
{
2791
0
  int e = errno;
2792
0
  return strerror(e);
2793
0
}
2794
2795
Status SystemTools::RemoveFile(std::string const& source)
2796
12.9k
{
2797
#ifdef _WIN32
2798
  std::wstring const& ws = Encoding::ToWindowsExtendedPath(source);
2799
  if (DeleteFileW(ws.c_str())) {
2800
    return Status::Success();
2801
  }
2802
  DWORD err = GetLastError();
2803
  if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
2804
    return Status::Success();
2805
  }
2806
  if (err != ERROR_ACCESS_DENIED) {
2807
    return Status::Windows(err);
2808
  }
2809
  /* The file may be read-only.  Try adding write permission.  */
2810
  mode_t mode;
2811
  if (!SystemTools::GetPermissions(source, mode) ||
2812
      !SystemTools::SetPermissions(source, S_IWRITE)) {
2813
    SetLastError(err);
2814
    return Status::Windows(err);
2815
  }
2816
2817
  const DWORD DIRECTORY_SOFT_LINK_ATTRS =
2818
    FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT;
2819
  DWORD attrs = GetFileAttributesW(ws.c_str());
2820
  if (attrs != INVALID_FILE_ATTRIBUTES &&
2821
      (attrs & DIRECTORY_SOFT_LINK_ATTRS) == DIRECTORY_SOFT_LINK_ATTRS &&
2822
      RemoveDirectoryW(ws.c_str())) {
2823
    return Status::Success();
2824
  }
2825
  if (DeleteFileW(ws.c_str()) || GetLastError() == ERROR_FILE_NOT_FOUND ||
2826
      GetLastError() == ERROR_PATH_NOT_FOUND) {
2827
    return Status::Success();
2828
  }
2829
  /* Try to restore the original permissions.  */
2830
  SystemTools::SetPermissions(source, mode);
2831
  SetLastError(err);
2832
  return Status::Windows(err);
2833
#else
2834
12.9k
  if (unlink(source.c_str()) != 0 && errno != ENOENT) {
2835
3.17k
    return Status::POSIX_errno();
2836
3.17k
  }
2837
9.80k
  return Status::Success();
2838
12.9k
#endif
2839
12.9k
}
2840
2841
Status SystemTools::RemoveADirectory(std::string const& source)
2842
47.2k
{
2843
  // Add read and write permission to the directory so we can read
2844
  // and modify its content to remove files and directories from it.
2845
47.2k
  mode_t mode = 0;
2846
47.2k
  if (SystemTools::GetPermissions(source, mode)) {
2847
#if defined(_WIN32) && !defined(__CYGWIN__)
2848
    mode |= S_IREAD | S_IWRITE;
2849
#else
2850
41.9k
    mode |= S_IRUSR | S_IWUSR;
2851
41.9k
#endif
2852
41.9k
    SystemTools::SetPermissions(source, mode);
2853
41.9k
  }
2854
2855
47.2k
  Status status;
2856
47.2k
  Directory dir;
2857
47.2k
  status = dir.Load(source);
2858
47.2k
  if (!status.IsSuccess()) {
2859
14.6k
    return status;
2860
14.6k
  }
2861
2862
32.6k
  size_t fileNum;
2863
85.9k
  for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
2864
75.1k
    if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") != 0 &&
2865
54.9k
        strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..") != 0) {
2866
22.3k
      std::string fullPath = source;
2867
22.3k
      fullPath += "/";
2868
22.3k
      fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
2869
22.3k
      if (SystemTools::FileIsDirectory(fullPath) &&
2870
18.8k
          !SystemTools::FileIsSymlink(fullPath)) {
2871
18.8k
        status = SystemTools::RemoveADirectory(fullPath);
2872
18.8k
        if (!status.IsSuccess()) {
2873
18.7k
          return status;
2874
18.7k
        }
2875
18.8k
      } else {
2876
3.51k
        status = SystemTools::RemoveFile(fullPath);
2877
3.51k
        if (!status.IsSuccess()) {
2878
3.17k
          return status;
2879
3.17k
        }
2880
3.51k
      }
2881
22.3k
    }
2882
75.1k
  }
2883
2884
10.7k
  if (Rmdir(source) != 0) {
2885
0
    status = Status::POSIX_errno();
2886
0
  }
2887
10.7k
  return status;
2888
32.6k
}
2889
2890
/**
2891
 */
2892
size_t SystemTools::GetMaximumFilePathLength()
2893
0
{
2894
0
  return KWSYS_SYSTEMTOOLS_MAXPATH;
2895
0
}
2896
2897
/**
2898
 * Find the file the given name.  Searches the given path and then
2899
 * the system search path.  Returns the full path to the file if it is
2900
 * found.  Otherwise, the empty string is returned.
2901
 */
2902
std::string SystemToolsStatic::FindName(
2903
  std::string const& name, std::vector<std::string> const& userPaths,
2904
  bool no_system_path)
2905
0
{
2906
  // Add the system search path to our path first
2907
0
  std::vector<std::string> path;
2908
0
  if (!no_system_path) {
2909
0
    SystemTools::GetPath(path, "CMAKE_FILE_PATH");
2910
0
    SystemTools::GetPath(path);
2911
0
  }
2912
  // now add the additional paths
2913
0
  path.reserve(path.size() + userPaths.size());
2914
0
  path.insert(path.end(), userPaths.begin(), userPaths.end());
2915
  // now look for the file
2916
0
  for (std::string const& p : path) {
2917
0
    std::string tryPath = p;
2918
0
    if (tryPath.empty() || tryPath.back() != '/') {
2919
0
      tryPath += '/';
2920
0
    }
2921
0
    tryPath += name;
2922
0
    if (SystemTools::FileExists(tryPath)) {
2923
0
      return tryPath;
2924
0
    }
2925
0
  }
2926
  // Couldn't find the file.
2927
0
  return "";
2928
0
}
2929
2930
/**
2931
 * Find the file the given name.  Searches the given path and then
2932
 * the system search path.  Returns the full path to the file if it is
2933
 * found.  Otherwise, the empty string is returned.
2934
 */
2935
std::string SystemTools::FindFile(std::string const& name,
2936
                                  std::vector<std::string> const& userPaths,
2937
                                  bool no_system_path)
2938
0
{
2939
0
  std::string tryPath =
2940
0
    SystemToolsStatic::FindName(name, userPaths, no_system_path);
2941
0
  if (!tryPath.empty() && !SystemTools::FileIsDirectory(tryPath)) {
2942
0
    return SystemTools::CollapseFullPath(tryPath);
2943
0
  }
2944
  // Couldn't find the file.
2945
0
  return "";
2946
0
}
2947
2948
/**
2949
 * Find the directory the given name.  Searches the given path and then
2950
 * the system search path.  Returns the full path to the directory if it is
2951
 * found.  Otherwise, the empty string is returned.
2952
 */
2953
std::string SystemTools::FindDirectory(
2954
  std::string const& name, std::vector<std::string> const& userPaths,
2955
  bool no_system_path)
2956
0
{
2957
0
  std::string tryPath =
2958
0
    SystemToolsStatic::FindName(name, userPaths, no_system_path);
2959
0
  if (!tryPath.empty() && SystemTools::FileIsDirectory(tryPath)) {
2960
0
    return SystemTools::CollapseFullPath(tryPath);
2961
0
  }
2962
  // Couldn't find the file.
2963
0
  return "";
2964
0
}
2965
2966
/**
2967
 * Find the executable with the given name.  Searches the given path and then
2968
 * the system search path.  Returns the full path to the executable if it is
2969
 * found.  Otherwise, the empty string is returned.
2970
 */
2971
std::string SystemTools::FindProgram(std::string const& name,
2972
                                     std::vector<std::string> const& userPaths,
2973
                                     bool no_system_path)
2974
0
{
2975
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
2976
  std::vector<std::string> extensions;
2977
  // check to see if the name already has a .xxx at
2978
  // the end of it
2979
  // on windows try .com then .exe
2980
  if (name.size() <= 3 || name[name.size() - 4] != '.') {
2981
    extensions.emplace_back(".com");
2982
    extensions.emplace_back(".exe");
2983
2984
    // first try with extensions if the os supports them
2985
    for (std::string const& ext : extensions) {
2986
      std::string tryPath = name;
2987
      tryPath += ext;
2988
      if (SystemTools::FileIsExecutable(tryPath)) {
2989
        return SystemTools::CollapseFullPath(tryPath);
2990
      }
2991
    }
2992
  }
2993
#endif
2994
2995
  // now try just the name
2996
0
  if (SystemTools::FileIsExecutable(name)) {
2997
0
    return SystemTools::CollapseFullPath(name);
2998
0
  }
2999
  // now construct the path
3000
0
  std::vector<std::string> path;
3001
  // Add the system search path to our path.
3002
0
  if (!no_system_path) {
3003
0
    SystemTools::GetPath(path);
3004
0
  }
3005
  // now add the additional paths
3006
0
  path.reserve(path.size() + userPaths.size());
3007
0
  path.insert(path.end(), userPaths.begin(), userPaths.end());
3008
  // Add a trailing slash to all paths to aid the search process.
3009
0
  for (std::string& p : path) {
3010
0
    if (p.empty() || p.back() != '/') {
3011
0
      p += '/';
3012
0
    }
3013
0
  }
3014
  // Try each path
3015
0
  for (std::string& p : path) {
3016
#ifdef _WIN32
3017
    // Remove double quotes from the path on windows
3018
    SystemTools::ReplaceString(p, "\"", "");
3019
#endif
3020
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
3021
    // first try with extensions
3022
    for (std::string const& ext : extensions) {
3023
      std::string tryPath = p;
3024
      tryPath += name;
3025
      tryPath += ext;
3026
      if (SystemTools::FileIsExecutable(tryPath)) {
3027
        return SystemTools::CollapseFullPath(tryPath);
3028
      }
3029
    }
3030
#endif
3031
    // now try it without them
3032
0
    std::string tryPath = p;
3033
0
    tryPath += name;
3034
0
    if (SystemTools::FileIsExecutable(tryPath)) {
3035
0
      return SystemTools::CollapseFullPath(tryPath);
3036
0
    }
3037
0
  }
3038
  // Couldn't find the program.
3039
0
  return "";
3040
0
}
3041
3042
std::string SystemTools::GetRealPath(std::string const& path,
3043
                                     std::string* errorMessage)
3044
10
{
3045
10
  std::string ret;
3046
10
  Realpath(path, ret, errorMessage);
3047
10
  return ret;
3048
10
}
3049
3050
// Remove any trailing slash from the name except in a root component.
3051
static char const* RemoveTrailingSlashes(
3052
  std::string const& inName, char (&local_buffer)[KWSYS_SYSTEMTOOLS_MAXPATH],
3053
  std::string& string_buffer)
3054
30.6k
{
3055
30.6k
  size_t length = inName.size();
3056
30.6k
  char const* name = inName.c_str();
3057
3058
30.6k
  size_t last = length - 1;
3059
30.6k
  if (last > 0 && (name[last] == '/' || name[last] == '\\') &&
3060
12.5k
      strcmp(name, "/") != 0 && name[last - 1] != ':') {
3061
12.5k
    if (last < sizeof(local_buffer)) {
3062
12.5k
      memcpy(local_buffer, name, last);
3063
12.5k
      local_buffer[last] = '\0';
3064
12.5k
      name = local_buffer;
3065
12.5k
    } else {
3066
0
      string_buffer.append(name, last);
3067
0
      name = string_buffer.c_str();
3068
0
    }
3069
12.5k
  }
3070
3071
30.6k
  return name;
3072
30.6k
}
3073
3074
bool SystemTools::FileIsDirectory(std::string const& inName)
3075
30.6k
{
3076
30.6k
  if (inName.empty()) {
3077
0
    return false;
3078
0
  }
3079
3080
30.6k
  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3081
30.6k
  std::string string_buffer;
3082
30.6k
  auto const name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
3083
3084
// Now check the file node type.
3085
#if defined(_WIN32)
3086
  DWORD attr =
3087
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
3088
  return (attr != INVALID_FILE_ATTRIBUTES) &&
3089
    (attr & FILE_ATTRIBUTE_DIRECTORY);
3090
#else
3091
30.6k
  struct stat fs;
3092
3093
30.6k
  return (stat(name, &fs) == 0) && S_ISDIR(fs.st_mode);
3094
30.6k
#endif
3095
30.6k
}
3096
3097
bool SystemTools::FileIsExecutable(std::string const& inName)
3098
0
{
3099
#ifdef _WIN32
3100
  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3101
  std::string string_buffer;
3102
  auto const name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
3103
  auto const attr =
3104
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
3105
3106
  // On Windows any file that exists and is not a directory is considered
3107
  // readable and therefore also executable:
3108
  return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
3109
#else
3110
0
  return !FileIsDirectory(inName) && TestFileAccess(inName, TEST_FILE_EXECUTE);
3111
0
#endif
3112
0
}
3113
3114
#if defined(_WIN32)
3115
bool SystemTools::FileIsSymlinkWithAttr(std::wstring const& path,
3116
                                        unsigned long attr)
3117
{
3118
  if (attr != INVALID_FILE_ATTRIBUTES) {
3119
    if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
3120
      // FILE_ATTRIBUTE_REPARSE_POINT means:
3121
      // * a file or directory that has an associated reparse point, or
3122
      // * a file that is a symbolic link.
3123
      HANDLE hFile = CreateFileW(
3124
        path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
3125
        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3126
      if (hFile == INVALID_HANDLE_VALUE) {
3127
        return false;
3128
      }
3129
      byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3130
      DWORD bytesReturned = 0;
3131
      if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3132
                           MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3133
                           nullptr)) {
3134
        CloseHandle(hFile);
3135
        // Since FILE_ATTRIBUTE_REPARSE_POINT is set this file must be
3136
        // a symbolic link if it is not a reparse point.
3137
        return GetLastError() == ERROR_NOT_A_REPARSE_POINT;
3138
      }
3139
      CloseHandle(hFile);
3140
      ULONG reparseTag =
3141
        reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0])->ReparseTag;
3142
      return (reparseTag == IO_REPARSE_TAG_SYMLINK) ||
3143
        (reparseTag == IO_REPARSE_TAG_MOUNT_POINT);
3144
    }
3145
    return false;
3146
  }
3147
3148
  return false;
3149
}
3150
#endif
3151
3152
bool SystemTools::FileIsSymlink(std::string const& name)
3153
18.8k
{
3154
#if defined(_WIN32)
3155
  std::wstring path = Encoding::ToWindowsExtendedPath(name);
3156
  return FileIsSymlinkWithAttr(path, GetFileAttributesW(path.c_str()));
3157
#else
3158
18.8k
  struct stat fs;
3159
18.8k
  return (lstat(name.c_str(), &fs) == 0) && S_ISLNK(fs.st_mode);
3160
18.8k
#endif
3161
18.8k
}
3162
3163
bool SystemTools::FileIsFIFO(std::string const& name)
3164
0
{
3165
#if defined(_WIN32)
3166
  HANDLE hFile =
3167
    CreateFileW(Encoding::ToWide(name).c_str(), GENERIC_READ, FILE_SHARE_READ,
3168
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3169
  if (hFile == INVALID_HANDLE_VALUE) {
3170
    return false;
3171
  }
3172
  const DWORD type = GetFileType(hFile);
3173
  CloseHandle(hFile);
3174
  return type == FILE_TYPE_PIPE;
3175
#else
3176
0
  struct stat fs;
3177
0
  return (lstat(name.c_str(), &fs) == 0) && S_ISFIFO(fs.st_mode);
3178
0
#endif
3179
0
}
3180
3181
Status SystemTools::CreateSymlink(std::string const& origName,
3182
                                  std::string const& newName)
3183
0
{
3184
#if defined(_WIN32) && !defined(__CYGWIN__)
3185
  DWORD flags;
3186
  if (FileIsDirectory(origName)) {
3187
    flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
3188
  } else {
3189
    flags = 0;
3190
  }
3191
3192
  std::wstring origPath = Encoding::ToWindowsExtendedPath(origName);
3193
  std::wstring newPath = Encoding::ToWindowsExtendedPath(newName);
3194
3195
  Status status;
3196
  if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(),
3197
                           flags |
3198
                             SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
3199
    status = Status::Windows_GetLastError();
3200
  }
3201
  // Older Windows versions do not understand
3202
  // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
3203
  if (status.GetWindows() == ERROR_INVALID_PARAMETER) {
3204
    status = Status::Success();
3205
    if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(), flags)) {
3206
      status = Status::Windows_GetLastError();
3207
    }
3208
  }
3209
3210
  return status;
3211
#else
3212
0
  if (symlink(origName.c_str(), newName.c_str()) < 0) {
3213
0
    return Status::POSIX_errno();
3214
0
  }
3215
0
  return Status::Success();
3216
0
#endif
3217
0
}
3218
3219
Status SystemTools::ReadSymlink(std::string const& newName,
3220
                                std::string& origName)
3221
0
{
3222
#if defined(_WIN32) && !defined(__CYGWIN__)
3223
  std::wstring newPath = Encoding::ToWindowsExtendedPath(newName);
3224
  // FILE_ATTRIBUTE_REPARSE_POINT means:
3225
  // * a file or directory that has an associated reparse point, or
3226
  // * a file that is a symbolic link.
3227
  HANDLE hFile = CreateFileW(
3228
    newPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
3229
    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3230
  if (hFile == INVALID_HANDLE_VALUE) {
3231
    return Status::Windows_GetLastError();
3232
  }
3233
  byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3234
  DWORD bytesReturned = 0;
3235
  Status status;
3236
  if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3237
                       MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3238
                       nullptr)) {
3239
    status = Status::Windows_GetLastError();
3240
  }
3241
  CloseHandle(hFile);
3242
  if (!status.IsSuccess()) {
3243
    return status;
3244
  }
3245
  PREPARSE_DATA_BUFFER data =
3246
    reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
3247
  USHORT substituteNameLength;
3248
  PCWSTR substituteNameData;
3249
  if (data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
3250
    substituteNameLength =
3251
      data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
3252
    substituteNameData = data->SymbolicLinkReparseBuffer.PathBuffer +
3253
      data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
3254
  } else if (data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
3255
    substituteNameLength =
3256
      data->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
3257
    substituteNameData = data->MountPointReparseBuffer.PathBuffer +
3258
      data->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
3259
  } else if (data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
3260
    // The reparse buffer is a list of 0-terminated non-empty strings,
3261
    // terminated by an empty string (0-0).  We need the third string.
3262
    size_t destLen;
3263
    substituteNameData = GetAppExecLink(data, destLen);
3264
    if (!substituteNameData || destLen == 0) {
3265
      return Status::Windows(ERROR_SYMLINK_NOT_SUPPORTED);
3266
    }
3267
    substituteNameLength = static_cast<USHORT>(destLen);
3268
  } else {
3269
    return Status::Windows(ERROR_REPARSE_TAG_MISMATCH);
3270
  }
3271
  std::wstring substituteName(substituteNameData, substituteNameLength);
3272
  origName = Encoding::ToNarrow(substituteName);
3273
  // Symbolic links to absolute paths may use a NT Object Path prefix.
3274
  // If the path begins with "\??\UNC\", replace it with "\\".
3275
  // Otherwise, if the path begins with "\??\", remove the prefix.
3276
  if (origName.compare(0, 8, "\\??\\UNC\\") == 0) {
3277
    origName.erase(1, 6);
3278
  } else if (origName.compare(0, 4, "\\??\\") == 0) {
3279
    origName.erase(0, 4);
3280
  }
3281
#else
3282
0
  char buf[KWSYS_SYSTEMTOOLS_MAXPATH + 1];
3283
0
  int count = static_cast<int>(
3284
0
    readlink(newName.c_str(), buf, KWSYS_SYSTEMTOOLS_MAXPATH));
3285
0
  if (count < 0) {
3286
0
    return Status::POSIX_errno();
3287
0
  }
3288
  // Add null-terminator.
3289
0
  buf[count] = 0;
3290
0
  origName = buf;
3291
0
#endif
3292
0
  return Status::Success();
3293
0
}
3294
3295
Status SystemTools::ChangeDirectory(std::string const& dir)
3296
37.9k
{
3297
37.9k
  if (Chdir(dir) < 0) {
3298
0
    return Status::POSIX_errno();
3299
0
  }
3300
37.9k
  return Status::Success();
3301
37.9k
}
3302
3303
std::string SystemTools::GetCurrentWorkingDirectory()
3304
16.0k
{
3305
16.0k
  char buf[2048];
3306
16.0k
  char const* cwd = Getcwd(buf, 2048);
3307
16.0k
  std::string path;
3308
16.0k
  if (cwd) {
3309
16.0k
    path = cwd;
3310
16.0k
    SystemTools::ConvertToUnixSlashes(path);
3311
16.0k
  }
3312
16.0k
  return path;
3313
16.0k
}
3314
3315
std::string SystemTools::GetProgramPath(std::string const& in_name)
3316
0
{
3317
0
  std::string dir, file;
3318
0
  SystemTools::SplitProgramPath(in_name, dir, file);
3319
0
  return dir;
3320
0
}
3321
3322
bool SystemTools::SplitProgramPath(std::string const& in_name,
3323
                                   std::string& dir, std::string& file, bool)
3324
0
{
3325
0
  dir = in_name;
3326
0
  file.clear();
3327
0
  SystemTools::ConvertToUnixSlashes(dir);
3328
3329
0
  if (!SystemTools::FileIsDirectory(dir)) {
3330
0
    std::string::size_type slashPos = dir.rfind('/');
3331
0
    if (slashPos != std::string::npos) {
3332
0
      file = dir.substr(slashPos + 1);
3333
0
      dir.resize(slashPos);
3334
0
    } else {
3335
0
      file = dir;
3336
0
      dir.clear();
3337
0
    }
3338
0
  }
3339
0
  if (!(dir.empty()) && !SystemTools::FileIsDirectory(dir)) {
3340
0
    std::string oldDir = in_name;
3341
0
    SystemTools::ConvertToUnixSlashes(oldDir);
3342
0
    dir = in_name;
3343
0
    return false;
3344
0
  }
3345
0
  return true;
3346
0
}
3347
3348
static void SystemToolsAppendComponents(
3349
  std::vector<std::string>& out_components,
3350
  std::vector<std::string>::iterator first,
3351
  std::vector<std::string>::iterator last)
3352
57.2k
{
3353
57.2k
  static std::string const up = "..";
3354
57.2k
  static std::string const cur = ".";
3355
1.75M
  for (std::vector<std::string>::const_iterator i = first; i != last; ++i) {
3356
1.69M
    if (*i == up) {
3357
      // Remove the previous component if possible.  Ignore ../ components
3358
      // that try to go above the root.  Keep ../ components if they are
3359
      // at the beginning of a relative path (base path is relative).
3360
19.2k
      if (out_components.size() > 1 && out_components.back() != up) {
3361
17.8k
        out_components.resize(out_components.size() - 1);
3362
17.8k
      } else if (!out_components.empty() && out_components[0].empty()) {
3363
0
        out_components.emplace_back(std::move(*i));
3364
0
      }
3365
1.67M
    } else if (!i->empty() && *i != cur) {
3366
574k
      out_components.emplace_back(std::move(*i));
3367
574k
    }
3368
1.69M
  }
3369
57.2k
}
3370
3371
namespace {
3372
3373
std::string CollapseFullPathImpl(std::string const& in_path,
3374
                                 std::string const* in_base)
3375
50.7k
{
3376
  // Collect the output path components.
3377
50.7k
  std::vector<std::string> out_components;
3378
3379
  // Split the input path components.
3380
50.7k
  std::vector<std::string> path_components;
3381
50.7k
  SystemTools::SplitPath(in_path, path_components);
3382
50.7k
  out_components.reserve(path_components.size());
3383
3384
  // If the input path is relative, start with a base path.
3385
50.7k
  if (path_components[0].empty()) {
3386
6.49k
    std::vector<std::string> base_components;
3387
3388
6.49k
    if (in_base) {
3389
      // Use the given base path.
3390
0
      SystemTools::SplitPath(*in_base, base_components);
3391
6.49k
    } else {
3392
      // Use the current working directory as a base path.
3393
6.49k
      std::string cwd = SystemTools::GetCurrentWorkingDirectory();
3394
6.49k
      SystemTools::SplitPath(cwd, base_components);
3395
6.49k
    }
3396
3397
    // Append base path components to the output path.
3398
6.49k
    out_components.push_back(base_components[0]);
3399
6.49k
    SystemToolsAppendComponents(out_components, base_components.begin() + 1,
3400
6.49k
                                base_components.end());
3401
6.49k
  }
3402
3403
  // Append input path components to the output path.
3404
50.7k
  SystemToolsAppendComponents(out_components, path_components.begin(),
3405
50.7k
                              path_components.end());
3406
3407
  // Transform the path back to a string.
3408
50.7k
  std::string newPath = SystemTools::JoinPath(out_components);
3409
3410
#ifdef _WIN32
3411
  SystemTools::ConvertToUnixSlashes(newPath);
3412
#endif
3413
  // Return the reconstructed path.
3414
50.7k
  return newPath;
3415
50.7k
}
3416
}
3417
3418
std::string SystemTools::CollapseFullPath(std::string const& in_path)
3419
50.7k
{
3420
50.7k
  return CollapseFullPathImpl(in_path, nullptr);
3421
50.7k
}
3422
3423
std::string SystemTools::CollapseFullPath(std::string const& in_path,
3424
                                          char const* in_base)
3425
0
{
3426
0
  if (!in_base) {
3427
0
    return CollapseFullPathImpl(in_path, nullptr);
3428
0
  }
3429
0
  std::string tmp_base = in_base;
3430
0
  return CollapseFullPathImpl(in_path, &tmp_base);
3431
0
}
3432
3433
std::string SystemTools::CollapseFullPath(std::string const& in_path,
3434
                                          std::string const& in_base)
3435
1
{
3436
1
  return CollapseFullPathImpl(in_path, &in_base);
3437
1
}
3438
3439
// compute the relative path from here to there
3440
std::string SystemTools::RelativePath(std::string const& local,
3441
                                      std::string const& remote)
3442
0
{
3443
0
  if (!SystemTools::FileIsFullPath(local)) {
3444
0
    return "";
3445
0
  }
3446
0
  if (!SystemTools::FileIsFullPath(remote)) {
3447
0
    return "";
3448
0
  }
3449
3450
0
  std::string l = SystemTools::CollapseFullPath(local);
3451
0
  std::string r = SystemTools::CollapseFullPath(remote);
3452
3453
  // split up both paths into arrays of strings using / as a separator
3454
0
  std::vector<std::string> localSplit = SystemTools::SplitString(l, '/', true);
3455
0
  std::vector<std::string> remoteSplit =
3456
0
    SystemTools::SplitString(r, '/', true);
3457
0
  std::vector<std::string>
3458
0
    commonPath; // store shared parts of path in this array
3459
0
  std::vector<std::string> finalPath; // store the final relative path here
3460
  // count up how many matching directory names there are from the start
3461
0
  unsigned int sameCount = 0;
3462
0
  while (((sameCount <= (localSplit.size() - 1)) &&
3463
0
          (sameCount <= (remoteSplit.size() - 1))) &&
3464
// for Windows and Apple do a case insensitive string compare
3465
#if defined(_WIN32) || defined(__APPLE__)
3466
         SystemTools::Strucmp(localSplit[sameCount].c_str(),
3467
                              remoteSplit[sameCount].c_str()) == 0
3468
#else
3469
0
         localSplit[sameCount] == remoteSplit[sameCount]
3470
0
#endif
3471
0
  ) {
3472
    // put the common parts of the path into the commonPath array
3473
0
    commonPath.push_back(localSplit[sameCount]);
3474
    // erase the common parts of the path from the original path arrays
3475
0
    localSplit[sameCount] = "";
3476
0
    remoteSplit[sameCount] = "";
3477
0
    sameCount++;
3478
0
  }
3479
3480
  // If there is nothing in common at all then just return the full
3481
  // path.  This is the case only on windows when the paths have
3482
  // different drive letters.  On unix two full paths always at least
3483
  // have the root "/" in common so we will return a relative path
3484
  // that passes through the root directory.
3485
0
  if (sameCount == 0) {
3486
0
    return remote;
3487
0
  }
3488
3489
  // for each entry that is not common in the local path
3490
  // add a ../ to the finalpath array, this gets us out of the local
3491
  // path into the remote dir
3492
0
  for (std::string const& lp : localSplit) {
3493
0
    if (!lp.empty()) {
3494
0
      finalPath.emplace_back("../");
3495
0
    }
3496
0
  }
3497
  // for each entry that is not common in the remote path add it
3498
  // to the final path.
3499
0
  for (std::string const& rp : remoteSplit) {
3500
0
    if (!rp.empty()) {
3501
0
      finalPath.push_back(rp);
3502
0
    }
3503
0
  }
3504
0
  std::string relativePath; // result string
3505
  // now turn the array of directories into a unix path by puttint /
3506
  // between each entry that does not already have one
3507
0
  for (std::string const& fp : finalPath) {
3508
0
    if (!relativePath.empty() && relativePath.back() != '/') {
3509
0
      relativePath += '/';
3510
0
    }
3511
0
    relativePath += fp;
3512
0
  }
3513
0
  return relativePath;
3514
0
}
3515
3516
std::string SystemTools::GetActualCaseForPath(std::string const& p)
3517
0
{
3518
#ifdef _WIN32
3519
  return SystemToolsStatic::GetCasePathName(p);
3520
#else
3521
0
  return p;
3522
0
#endif
3523
0
}
3524
3525
char const* SystemTools::SplitPathRootComponent(std::string const& p,
3526
                                                std::string* root)
3527
100k
{
3528
  // Identify the root component.
3529
100k
  char const* c = p.c_str();
3530
100k
  if ((c[0] == '/' && c[1] == '/') || (c[0] == '\\' && c[1] == '\\')) {
3531
    // Network path.
3532
1.24k
    if (root) {
3533
1.24k
      *root = "//";
3534
1.24k
    }
3535
1.24k
    c += 2;
3536
99.0k
  } else if (c[0] == '/' || c[0] == '\\') {
3537
    // Unix path (or Windows path w/out drive letter).
3538
49.4k
    if (root) {
3539
49.4k
      *root = "/";
3540
49.4k
    }
3541
49.4k
    c += 1;
3542
49.6k
  } else if (c[0] && c[1] == ':' && (c[2] == '/' || c[2] == '\\')) {
3543
    // Windows path.
3544
566
    if (root) {
3545
566
      (*root) = "_:/";
3546
566
      (*root)[0] = c[0];
3547
566
    }
3548
566
    c += 3;
3549
49.0k
  } else if (c[0] && c[1] == ':') {
3550
    // Path relative to a windows drive working directory.
3551
35.3k
    if (root) {
3552
35.3k
      (*root) = "_:";
3553
35.3k
      (*root)[0] = c[0];
3554
35.3k
    }
3555
35.3k
    c += 2;
3556
35.3k
  } else if (c[0] == '~') {
3557
    // Home directory.  The returned root should always have a
3558
    // trailing slash so that appending components as
3559
    // c[0]c[1]/c[2]/... works.  The remaining path returned should
3560
    // skip the first slash if it exists:
3561
    //
3562
    //   "~"    : root = "~/" , return ""
3563
    //   "~/    : root = "~/" , return ""
3564
    //   "~/x   : root = "~/" , return "x"
3565
    //   "~u"   : root = "~u/", return ""
3566
    //   "~u/"  : root = "~u/", return ""
3567
    //   "~u/x" : root = "~u/", return "x"
3568
7.17k
    size_t n = 1;
3569
612k
    while (c[n] && c[n] != '/') {
3570
604k
      ++n;
3571
604k
    }
3572
7.17k
    if (root) {
3573
7.17k
      root->assign(c, n);
3574
7.17k
      *root += '/';
3575
7.17k
    }
3576
7.17k
    if (c[n] == '/') {
3577
1.41k
      ++n;
3578
1.41k
    }
3579
7.17k
    c += n;
3580
7.17k
  } else {
3581
    // Relative path.
3582
6.49k
    if (root) {
3583
6.49k
      *root = "";
3584
6.49k
    }
3585
6.49k
  }
3586
3587
  // Return the remaining path.
3588
100k
  return c;
3589
100k
}
3590
3591
void SystemTools::SplitPath(std::string const& p,
3592
                            std::vector<std::string>& components,
3593
                            bool expand_home_dir)
3594
100k
{
3595
100k
  char const* c;
3596
100k
  components.clear();
3597
3598
  // Identify the root component.
3599
100k
  {
3600
100k
    std::string root;
3601
100k
    c = SystemTools::SplitPathRootComponent(p, &root);
3602
3603
    // Expand home directory references if requested.
3604
100k
    if (expand_home_dir && !root.empty() && root[0] == '~') {
3605
43.0k
      std::string homedir;
3606
43.0k
      root.resize(root.size() - 1);
3607
43.0k
      if (root.size() == 1) {
3608
#if defined(_WIN32) && !defined(__CYGWIN__)
3609
        if (!SystemTools::GetEnv("USERPROFILE", homedir))
3610
#endif
3611
37.1k
          SystemTools::GetEnv("HOME", homedir);
3612
37.1k
      }
3613
5.89k
#ifdef HAVE_GETPWNAM
3614
5.89k
      else if (passwd* pw = getpwnam(root.c_str() + 1)) {
3615
236
        if (pw->pw_dir) {
3616
236
          homedir = pw->pw_dir;
3617
236
        }
3618
236
      }
3619
43.0k
#endif
3620
43.0k
      if (!homedir.empty() &&
3621
37.4k
          (homedir.back() == '/' || homedir.back() == '\\')) {
3622
0
        homedir.resize(homedir.size() - 1);
3623
0
      }
3624
43.0k
      SystemTools::SplitPath(homedir, components);
3625
57.2k
    } else {
3626
57.2k
      components.push_back(root);
3627
57.2k
    }
3628
100k
  }
3629
3630
  // Parse the remaining components.
3631
100k
  char const* first = c;
3632
100k
  char const* last = first;
3633
9.67M
  for (; *last; ++last) {
3634
9.57M
    if (*last == '/' || *last == '\\') {
3635
      // End of a component.  Save it.
3636
1.59M
      components.emplace_back(first, last);
3637
1.59M
      first = last + 1;
3638
1.59M
    }
3639
9.57M
  }
3640
3641
  // Save the last component unless there were no components.
3642
100k
  if (last != c) {
3643
51.7k
    components.emplace_back(first, last);
3644
51.7k
  }
3645
100k
}
3646
3647
std::string SystemTools::JoinPath(std::vector<std::string> const& components)
3648
50.7k
{
3649
50.7k
  return SystemTools::JoinPath(components.begin(), components.end());
3650
50.7k
}
3651
3652
std::string SystemTools::JoinPath(
3653
  std::vector<std::string>::const_iterator first,
3654
  std::vector<std::string>::const_iterator last)
3655
50.7k
{
3656
  // Construct result in a single string.
3657
50.7k
  std::string result;
3658
50.7k
  size_t len = 0;
3659
613k
  for (auto i = first; i != last; ++i) {
3660
562k
    len += 1 + i->size();
3661
562k
  }
3662
50.7k
  result.reserve(len);
3663
3664
  // The first two components do not add a slash.
3665
50.7k
  if (first != last) {
3666
50.7k
    result.append(*first++);
3667
50.7k
  }
3668
50.7k
  if (first != last) {
3669
48.7k
    result.append(*first++);
3670
48.7k
  }
3671
3672
  // All remaining components are always separated with a slash.
3673
513k
  while (first != last) {
3674
463k
    result.push_back('/');
3675
463k
    result.append((*first++));
3676
463k
  }
3677
3678
  // Return the concatenated result.
3679
50.7k
  return result;
3680
50.7k
}
3681
3682
bool SystemTools::ComparePath(std::string const& c1, std::string const& c2)
3683
0
{
3684
#if defined(_WIN32) || defined(__APPLE__)
3685
#  ifdef _MSC_VER
3686
  return _stricmp(c1.c_str(), c2.c_str()) == 0;
3687
#  elif defined(__APPLE__) || defined(__GNUC__)
3688
  return strcasecmp(c1.c_str(), c2.c_str()) == 0;
3689
#  else
3690
  return SystemTools::Strucmp(c1.c_str(), c2.c_str()) == 0;
3691
#  endif
3692
#else
3693
0
  return c1 == c2;
3694
0
#endif
3695
0
}
3696
3697
bool SystemTools::Split(std::string const& str,
3698
                        std::vector<std::string>& lines, char separator)
3699
0
{
3700
0
  std::string data(str);
3701
0
  std::string::size_type lpos = 0;
3702
0
  while (lpos < data.length()) {
3703
0
    std::string::size_type rpos = data.find_first_of(separator, lpos);
3704
0
    if (rpos == std::string::npos) {
3705
      // String ends at end of string without a separator.
3706
0
      lines.push_back(data.substr(lpos));
3707
0
      return false;
3708
0
    } else {
3709
      // String ends in a separator, remove the character.
3710
0
      lines.push_back(data.substr(lpos, rpos - lpos));
3711
0
    }
3712
0
    lpos = rpos + 1;
3713
0
  }
3714
0
  return true;
3715
0
}
3716
3717
bool SystemTools::Split(std::string const& str,
3718
                        std::vector<std::string>& lines)
3719
0
{
3720
0
  std::string data(str);
3721
0
  std::string::size_type lpos = 0;
3722
0
  while (lpos < data.length()) {
3723
0
    std::string::size_type rpos = data.find_first_of('\n', lpos);
3724
0
    if (rpos == std::string::npos) {
3725
      // Line ends at end of string without a newline.
3726
0
      lines.push_back(data.substr(lpos));
3727
0
      return false;
3728
0
    }
3729
0
    if ((rpos > lpos) && (data[rpos - 1] == '\r')) {
3730
      // Line ends in a "\r\n" pair, remove both characters.
3731
0
      lines.push_back(data.substr(lpos, (rpos - 1) - lpos));
3732
0
    } else {
3733
      // Line ends in a "\n", remove the character.
3734
0
      lines.push_back(data.substr(lpos, rpos - lpos));
3735
0
    }
3736
0
    lpos = rpos + 1;
3737
0
  }
3738
0
  return true;
3739
0
}
3740
3741
std::string SystemTools::Join(std::vector<std::string> const& list,
3742
                              std::string const& separator)
3743
0
{
3744
0
  std::string result;
3745
0
  if (list.empty()) {
3746
0
    return result;
3747
0
  }
3748
3749
0
  size_t total_size = separator.size() * (list.size() - 1);
3750
0
  for (std::string const& string : list) {
3751
0
    total_size += string.size();
3752
0
  }
3753
3754
0
  result.reserve(total_size);
3755
0
  bool needs_separator = false;
3756
0
  for (std::string const& string : list) {
3757
0
    if (needs_separator) {
3758
0
      result += separator;
3759
0
    }
3760
0
    result += string;
3761
0
    needs_separator = true;
3762
0
  }
3763
3764
0
  return result;
3765
0
}
3766
3767
/**
3768
 * Return path of a full filename (no trailing slashes).
3769
 * Warning: returned path is converted to Unix slashes format.
3770
 */
3771
std::string SystemTools::GetFilenamePath(std::string const& filename)
3772
1.50k
{
3773
1.50k
  std::string fn = filename;
3774
1.50k
  SystemTools::ConvertToUnixSlashes(fn);
3775
3776
1.50k
  std::string::size_type slash_pos = fn.rfind('/');
3777
1.50k
  if (slash_pos == 0) {
3778
133
    return "/";
3779
133
  }
3780
1.37k
  if (slash_pos == 2 && fn[1] == ':') {
3781
    // keep the / after a drive letter
3782
4
    fn.resize(3);
3783
4
    return fn;
3784
4
  }
3785
1.36k
  if (slash_pos == std::string::npos) {
3786
665
    return "";
3787
665
  }
3788
704
  fn.resize(slash_pos);
3789
704
  return fn;
3790
1.36k
}
3791
3792
/**
3793
 * Return file name of a full filename (i.e. file name without path).
3794
 */
3795
std::string SystemTools::GetFilenameName(std::string const& filename)
3796
6.01k
{
3797
#if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
3798
  char const* separators = "/\\";
3799
#else
3800
6.01k
  char separators = '/';
3801
6.01k
#endif
3802
6.01k
  std::string::size_type slash_pos = filename.find_last_of(separators);
3803
6.01k
  if (slash_pos != std::string::npos) {
3804
2.14k
    return filename.substr(slash_pos + 1);
3805
3.87k
  } else {
3806
3.87k
    return filename;
3807
3.87k
  }
3808
6.01k
}
3809
3810
/**
3811
 * Return file extension of a full filename (dot included).
3812
 * Warning: this is the longest extension (for example: .tar.gz)
3813
 */
3814
std::string SystemTools::GetFilenameExtension(std::string const& filename)
3815
1.50k
{
3816
1.50k
  std::string name = SystemTools::GetFilenameName(filename);
3817
1.50k
  std::string::size_type dot_pos = name.find('.');
3818
1.50k
  if (dot_pos != std::string::npos) {
3819
363
    name.erase(0, dot_pos);
3820
363
    return name;
3821
1.14k
  } else {
3822
1.14k
    return "";
3823
1.14k
  }
3824
1.50k
}
3825
3826
/**
3827
 * Return file extension of a full filename (dot included).
3828
 * Warning: this is the shortest extension (for example: .gz of .tar.gz)
3829
 */
3830
std::string SystemTools::GetFilenameLastExtension(std::string const& filename)
3831
1.50k
{
3832
1.50k
  std::string name = SystemTools::GetFilenameName(filename);
3833
1.50k
  std::string::size_type dot_pos = name.rfind('.');
3834
1.50k
  if (dot_pos != std::string::npos) {
3835
363
    name.erase(0, dot_pos);
3836
363
    return name;
3837
1.14k
  } else {
3838
1.14k
    return "";
3839
1.14k
  }
3840
1.50k
}
3841
3842
/**
3843
 * Return file name without extension of a full filename (i.e. without path).
3844
 * Warning: it considers the longest extension (for example: .tar.gz)
3845
 */
3846
std::string SystemTools::GetFilenameWithoutExtension(
3847
  std::string const& filename)
3848
1.50k
{
3849
1.50k
  std::string name = SystemTools::GetFilenameName(filename);
3850
1.50k
  std::string::size_type dot_pos = name.find('.');
3851
1.50k
  if (dot_pos != std::string::npos) {
3852
363
    name.resize(dot_pos);
3853
363
  }
3854
1.50k
  return name;
3855
1.50k
}
3856
3857
/**
3858
 * Return file name without extension of a full filename (i.e. without path).
3859
 * Warning: it considers the last extension (for example: removes .gz
3860
 * from .tar.gz)
3861
 */
3862
std::string SystemTools::GetFilenameWithoutLastExtension(
3863
  std::string const& filename)
3864
0
{
3865
0
  std::string name = SystemTools::GetFilenameName(filename);
3866
0
  std::string::size_type dot_pos = name.rfind('.');
3867
0
  if (dot_pos != std::string::npos) {
3868
0
    name.resize(dot_pos);
3869
0
  }
3870
0
  return name;
3871
0
}
3872
3873
bool SystemTools::FileHasSignature(char const* filename, char const* signature,
3874
                                   long offset)
3875
0
{
3876
0
  if (!filename || !signature) {
3877
0
    return false;
3878
0
  }
3879
3880
0
  FILE* fp = Fopen(filename, "rb");
3881
0
  if (!fp) {
3882
0
    return false;
3883
0
  }
3884
3885
0
  fseek(fp, offset, SEEK_SET);
3886
3887
0
  bool res = false;
3888
0
  size_t signature_len = strlen(signature);
3889
0
  char* buffer = new char[signature_len];
3890
3891
0
  if (fread(buffer, 1, signature_len, fp) == signature_len) {
3892
0
    res = (!strncmp(buffer, signature, signature_len) ? true : false);
3893
0
  }
3894
3895
0
  delete[] buffer;
3896
3897
0
  fclose(fp);
3898
0
  return res;
3899
0
}
3900
3901
SystemTools::FileTypeEnum SystemTools::DetectFileType(char const* filename,
3902
                                                      unsigned long length,
3903
                                                      double percent_bin)
3904
0
{
3905
0
  if (!filename || percent_bin < 0) {
3906
0
    return SystemTools::FileTypeUnknown;
3907
0
  }
3908
3909
0
  if (SystemTools::FileIsDirectory(filename)) {
3910
0
    return SystemTools::FileTypeUnknown;
3911
0
  }
3912
3913
0
  FILE* fp = Fopen(filename, "rb");
3914
0
  if (!fp) {
3915
0
    return SystemTools::FileTypeUnknown;
3916
0
  }
3917
3918
  // Allocate buffer and read bytes
3919
3920
0
  auto* buffer = new unsigned char[length];
3921
0
  size_t read_length = fread(buffer, 1, length, fp);
3922
0
  fclose(fp);
3923
0
  if (read_length == 0) {
3924
0
    delete[] buffer;
3925
0
    return SystemTools::FileTypeUnknown;
3926
0
  }
3927
3928
  // Loop over contents and count
3929
3930
0
  size_t text_count = 0;
3931
3932
0
  unsigned char const* ptr = buffer;
3933
0
  unsigned char const* buffer_end = buffer + read_length;
3934
3935
0
  while (ptr != buffer_end) {
3936
0
    if ((*ptr >= 0x20 && *ptr <= 0x7F) || *ptr == '\n' || *ptr == '\r' ||
3937
0
        *ptr == '\t') {
3938
0
      text_count++;
3939
0
    }
3940
0
    ptr++;
3941
0
  }
3942
3943
0
  delete[] buffer;
3944
3945
0
  double current_percent_bin = (static_cast<double>(read_length - text_count) /
3946
0
                                static_cast<double>(read_length));
3947
3948
0
  if (current_percent_bin >= percent_bin) {
3949
0
    return SystemTools::FileTypeBinary;
3950
0
  }
3951
3952
0
  return SystemTools::FileTypeText;
3953
0
}
3954
3955
bool SystemTools::LocateFileInDir(char const* filename, char const* dir,
3956
                                  std::string& filename_found,
3957
                                  int try_filename_dirs)
3958
0
{
3959
0
  if (!filename || !dir) {
3960
0
    return false;
3961
0
  }
3962
3963
  // Get the basename of 'filename'
3964
3965
0
  std::string filename_base = SystemTools::GetFilenameName(filename);
3966
3967
  // Check if 'dir' is really a directory
3968
  // If win32 and matches something like C:, accept it as a dir
3969
3970
0
  std::string real_dir;
3971
0
  if (!SystemTools::FileIsDirectory(dir)) {
3972
#if defined(_WIN32)
3973
    size_t dir_len = strlen(dir);
3974
    if (dir_len < 2 || dir[dir_len - 1] != ':') {
3975
#endif
3976
0
      real_dir = SystemTools::GetFilenamePath(dir);
3977
0
      dir = real_dir.c_str();
3978
#if defined(_WIN32)
3979
    }
3980
#endif
3981
0
  }
3982
3983
  // Try to find the file in 'dir'
3984
3985
0
  bool res = false;
3986
0
  if (!filename_base.empty() && dir) {
3987
0
    size_t dir_len = strlen(dir);
3988
0
    int need_slash =
3989
0
      (dir_len && dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\');
3990
3991
0
    std::string temp = dir;
3992
0
    if (need_slash) {
3993
0
      temp += "/";
3994
0
    }
3995
0
    temp += filename_base;
3996
3997
0
    if (SystemTools::FileExists(temp)) {
3998
0
      res = true;
3999
0
      filename_found = temp;
4000
0
    }
4001
4002
    // If not found, we can try harder by appending part of the file to
4003
    // to the directory to look inside.
4004
    // Example: if we were looking for /foo/bar/yo.txt in /d1/d2, then
4005
    // try to find yo.txt in /d1/d2/bar, then /d1/d2/foo/bar, etc.
4006
4007
0
    else if (try_filename_dirs) {
4008
0
      std::string filename_dir(filename);
4009
0
      std::string filename_dir_base;
4010
0
      std::string filename_dir_bases;
4011
0
      do {
4012
0
        filename_dir = SystemTools::GetFilenamePath(filename_dir);
4013
0
        filename_dir_base = SystemTools::GetFilenameName(filename_dir);
4014
#if defined(_WIN32)
4015
        if (filename_dir_base.empty() || filename_dir_base.back() == ':')
4016
#else
4017
0
        if (filename_dir_base.empty())
4018
0
#endif
4019
0
        {
4020
0
          break;
4021
0
        }
4022
4023
0
        filename_dir_bases = filename_dir_base + "/" + filename_dir_bases;
4024
4025
0
        temp = dir;
4026
0
        if (need_slash) {
4027
0
          temp += "/";
4028
0
        }
4029
0
        temp += filename_dir_bases;
4030
4031
0
        res = SystemTools::LocateFileInDir(filename_base.c_str(), temp.c_str(),
4032
0
                                           filename_found, 0);
4033
4034
0
      } while (!res && !filename_dir_base.empty());
4035
0
    }
4036
0
  }
4037
4038
0
  return res;
4039
0
}
4040
4041
bool SystemTools::FileIsFullPath(std::string const& in_name)
4042
177k
{
4043
177k
  return SystemToolsStatic::FileIsFullPath(in_name.c_str(), in_name.size());
4044
177k
}
4045
4046
bool SystemTools::FileIsFullPath(char const* in_name)
4047
0
{
4048
0
  return SystemToolsStatic::FileIsFullPath(
4049
0
    in_name, in_name[0] ? (in_name[1] ? 2 : 1) : 0);
4050
0
}
4051
4052
bool SystemToolsStatic::FileIsFullPath(char const* in_name, size_t len)
4053
177k
{
4054
#if defined(_WIN32) && !defined(__CYGWIN__)
4055
  // On Windows, the name must be at least two characters long.
4056
  if (len < 2) {
4057
    return false;
4058
  }
4059
  if (in_name[1] == ':') {
4060
    return true;
4061
  }
4062
  if (in_name[0] == '\\') {
4063
    return true;
4064
  }
4065
#else
4066
  // On UNIX, the name must be at least one character long.
4067
177k
  if (len < 1) {
4068
0
    return false;
4069
0
  }
4070
177k
#endif
4071
177k
#if !defined(_WIN32)
4072
177k
  if (in_name[0] == '~') {
4073
42.7k
    return true;
4074
42.7k
  }
4075
135k
#endif
4076
  // On UNIX, the name must begin in a '/'.
4077
  // On Windows, if the name begins in a '/', then it is a full
4078
  // network path.
4079
135k
  if (in_name[0] == '/') {
4080
6.53k
    return true;
4081
6.53k
  }
4082
128k
  return false;
4083
135k
}
4084
4085
Status SystemTools::GetShortPath(std::string const& path,
4086
                                 std::string& shortPath)
4087
0
{
4088
#if defined(_WIN32) && !defined(__CYGWIN__)
4089
  std::string tempPath = path; // create a buffer
4090
4091
  // if the path passed in has quotes around it, first remove the quotes
4092
  if (!path.empty() && path[0] == '"' && path.back() == '"') {
4093
    tempPath.resize(path.length() - 1);
4094
    tempPath.erase(0, 1);
4095
  }
4096
4097
  std::wstring wtempPath = Encoding::ToWide(tempPath);
4098
  DWORD ret = GetShortPathNameW(wtempPath.c_str(), nullptr, 0);
4099
  std::vector<wchar_t> buffer(ret);
4100
  if (ret != 0) {
4101
    ret = GetShortPathNameW(wtempPath.c_str(), &buffer[0],
4102
                            static_cast<DWORD>(buffer.size()));
4103
  }
4104
4105
  if (ret == 0) {
4106
    return Status::Windows_GetLastError();
4107
  } else {
4108
    shortPath = Encoding::ToNarrow(&buffer[0]);
4109
    return Status::Success();
4110
  }
4111
#else
4112
0
  shortPath = path;
4113
0
  return Status::Success();
4114
0
#endif
4115
0
}
4116
4117
std::tm SystemTools::LocalTime(std::time_t timep)
4118
0
{
4119
0
  std::tm out;
4120
#if defined(_WIN32) && !defined(__CYGWIN__)
4121
  localtime_s(&out, &timep);
4122
#else
4123
0
  localtime_r(&timep, &out);
4124
0
#endif
4125
0
  return out;
4126
0
}
4127
4128
std::tm SystemTools::GMTime(std::time_t timep)
4129
0
{
4130
0
  std::tm out;
4131
#if defined(_WIN32) && !defined(__CYGWIN__)
4132
  gmtime_s(&out, &timep);
4133
#else
4134
0
  gmtime_r(&timep, &out);
4135
0
#endif
4136
0
  return out;
4137
0
}
4138
4139
std::string SystemTools::GetCurrentDateTime(char const* format)
4140
0
{
4141
0
  char buf[1024];
4142
0
  tm const t = LocalTime(std::time(nullptr));
4143
0
  strftime(buf, sizeof(buf), format, &t);
4144
0
  return std::string(buf);
4145
0
}
4146
4147
std::string SystemTools::MakeCidentifier(std::string const& s)
4148
0
{
4149
0
  std::string str(s);
4150
0
  if (str.find_first_of("0123456789") == 0) {
4151
0
    str = "_" + str;
4152
0
  }
4153
4154
0
  std::string permited_chars("_"
4155
0
                             "abcdefghijklmnopqrstuvwxyz"
4156
0
                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
4157
0
                             "0123456789");
4158
0
  std::string::size_type pos = 0;
4159
0
  while ((pos = str.find_first_not_of(permited_chars, pos)) !=
4160
0
         std::string::npos) {
4161
0
    str[pos] = '_';
4162
0
  }
4163
0
  return str;
4164
0
}
4165
4166
// Convenience function around std::getline which removes a trailing carriage
4167
// return and can truncate the buffer as needed.  Returns true
4168
// if any data were read before the end-of-file was reached.
4169
bool SystemTools::GetLineFromStream(
4170
  std::istream& is, std::string& line, bool* has_newline /* = 0 */,
4171
  std::string::size_type sizeLimit /* = std::string::npos */)
4172
0
{
4173
  // Start with an empty line.
4174
0
  line = "";
4175
4176
  // Early short circuit return if stream is no good. Just return
4177
  // false and the empty line. (Probably means caller tried to
4178
  // create a file stream with a non-existent file name...)
4179
  //
4180
0
  if (!is) {
4181
0
    if (has_newline) {
4182
0
      *has_newline = false;
4183
0
    }
4184
0
    return false;
4185
0
  }
4186
4187
0
  std::getline(is, line);
4188
0
  bool haveData = !line.empty() || !is.eof();
4189
0
  if (!line.empty()) {
4190
    // Avoid storing a carriage return character.
4191
0
    if (line.back() == '\r') {
4192
0
      line.resize(line.size() - 1);
4193
0
    }
4194
4195
    // if we read too much then truncate the buffer
4196
0
    if (sizeLimit != std::string::npos && line.size() > sizeLimit) {
4197
0
      line.resize(sizeLimit);
4198
0
    }
4199
0
  }
4200
4201
  // Return the results.
4202
0
  if (has_newline) {
4203
0
    *has_newline = !is.eof();
4204
0
  }
4205
0
  return haveData;
4206
0
}
4207
4208
int SystemTools::GetTerminalWidth()
4209
0
{
4210
0
  int width = -1;
4211
0
#ifdef HAVE_TTY_INFO
4212
0
  struct winsize ws;
4213
0
  std::string columns; /* Unix98 environment variable */
4214
0
  if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0 && ws.ws_row > 0) {
4215
0
    width = ws.ws_col;
4216
0
  }
4217
0
  if (!isatty(STDOUT_FILENO)) {
4218
0
    width = -1;
4219
0
  }
4220
0
  if (SystemTools::GetEnv("COLUMNS", columns) && !columns.empty()) {
4221
0
    long t;
4222
0
    char* endptr;
4223
0
    t = strtol(columns.c_str(), &endptr, 0);
4224
0
    if (endptr && !*endptr && (t > 0) && (t < 1000)) {
4225
0
      width = static_cast<int>(t);
4226
0
    }
4227
0
  }
4228
0
  if (width < 9) {
4229
0
    width = -1;
4230
0
  }
4231
0
#endif
4232
0
  return width;
4233
0
}
4234
4235
Status SystemTools::GetPermissions(char const* file, mode_t& mode)
4236
0
{
4237
0
  if (!file) {
4238
0
    return Status::POSIX(EINVAL);
4239
0
  }
4240
0
  return SystemTools::GetPermissions(std::string(file), mode);
4241
0
}
4242
4243
Status SystemTools::GetPermissions(std::string const& file, mode_t& mode)
4244
47.2k
{
4245
#if defined(_WIN32)
4246
  DWORD attr =
4247
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(file).c_str());
4248
  if (attr == INVALID_FILE_ATTRIBUTES) {
4249
    return Status::Windows_GetLastError();
4250
  }
4251
  if ((attr & FILE_ATTRIBUTE_READONLY) != 0) {
4252
    mode = (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4253
  } else {
4254
    mode = (_S_IWRITE | (_S_IWRITE >> 3) | (_S_IWRITE >> 6)) |
4255
      (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4256
  }
4257
  if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
4258
    mode |= S_IFDIR | (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4259
  } else {
4260
    mode |= S_IFREG;
4261
  }
4262
  size_t dotPos = file.rfind('.');
4263
  char const* ext = dotPos == std::string::npos ? 0 : (file.c_str() + dotPos);
4264
  if (ext &&
4265
      (Strucmp(ext, ".exe") == 0 || Strucmp(ext, ".com") == 0 ||
4266
       Strucmp(ext, ".cmd") == 0 || Strucmp(ext, ".bat") == 0)) {
4267
    mode |= (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4268
  }
4269
#else
4270
47.2k
  struct stat st;
4271
47.2k
  if (stat(file.c_str(), &st) < 0) {
4272
5.29k
    return Status::POSIX_errno();
4273
5.29k
  }
4274
41.9k
  mode = st.st_mode;
4275
41.9k
#endif
4276
41.9k
  return Status::Success();
4277
47.2k
}
4278
4279
Status SystemTools::SetPermissions(char const* file, mode_t mode,
4280
                                   bool honor_umask)
4281
0
{
4282
0
  if (!file) {
4283
0
    return Status::POSIX(EINVAL);
4284
0
  }
4285
0
  return SystemTools::SetPermissions(std::string(file), mode, honor_umask);
4286
0
}
4287
4288
Status SystemTools::SetPermissions(std::string const& file, mode_t mode,
4289
                                   bool honor_umask)
4290
41.9k
{
4291
41.9k
  if (!SystemTools::PathExists(file)) {
4292
0
    return Status::POSIX(ENOENT);
4293
0
  }
4294
41.9k
  if (honor_umask) {
4295
0
    mode_t currentMask = umask(0);
4296
0
    umask(currentMask);
4297
0
    mode &= ~currentMask;
4298
0
  }
4299
#ifdef _WIN32
4300
  if (_wchmod(Encoding::ToWindowsExtendedPath(file).c_str(), mode) < 0)
4301
#else
4302
41.9k
  if (chmod(file.c_str(), mode) < 0)
4303
0
#endif
4304
0
  {
4305
0
    return Status::POSIX_errno();
4306
0
  }
4307
4308
41.9k
  return Status::Success();
4309
41.9k
}
4310
4311
std::string SystemTools::GetParentDirectory(std::string const& fileOrDir)
4312
0
{
4313
0
  return SystemTools::GetFilenamePath(fileOrDir);
4314
0
}
4315
4316
bool SystemTools::IsSubDirectory(std::string const& cSubdir,
4317
                                 std::string const& cDir)
4318
1
{
4319
1
  if (cDir.empty()) {
4320
0
    return false;
4321
0
  }
4322
1
  std::string subdir = cSubdir;
4323
1
  std::string dir = cDir;
4324
1
  SystemTools::ConvertToUnixSlashes(subdir);
4325
1
  SystemTools::ConvertToUnixSlashes(dir);
4326
1
  if (subdir.size() <= dir.size() || dir.empty()) {
4327
1
    return false;
4328
1
  }
4329
0
  bool isRootPath = dir.back() == '/'; // like "/" or "C:/"
4330
0
  size_t expectedSlashPosition = isRootPath ? dir.size() - 1u : dir.size();
4331
0
  if (subdir[expectedSlashPosition] != '/') {
4332
0
    return false;
4333
0
  }
4334
0
  subdir.resize(dir.size());
4335
0
  return SystemTools::ComparePath(subdir, dir);
4336
0
}
4337
4338
void SystemTools::Delay(unsigned int msec)
4339
0
{
4340
#ifdef _WIN32
4341
  Sleep(msec);
4342
#else
4343
  // The sleep function gives 1 second resolution and the usleep
4344
  // function gives 1e-6 second resolution but on some platforms has a
4345
  // maximum sleep time of 1 second.  This could be re-implemented to
4346
  // use select with masked signals or pselect to mask signals
4347
  // atomically.  If select is given empty sets and zero as the max
4348
  // file descriptor but a non-zero timeout it can be used to block
4349
  // for a precise amount of time.
4350
0
  if (msec >= 1000) {
4351
0
    sleep(msec / 1000);
4352
0
    usleep((msec % 1000) * 1000);
4353
0
  } else {
4354
0
    usleep(msec * 1000);
4355
0
  }
4356
0
#endif
4357
0
}
4358
4359
std::string SystemTools::GetOperatingSystemNameAndVersion()
4360
0
{
4361
0
  std::string res;
4362
4363
#ifdef _WIN32
4364
  char buffer[256];
4365
4366
  OSVERSIONINFOEXA osvi;
4367
  BOOL bOsVersionInfoEx;
4368
4369
  ZeroMemory(&osvi, sizeof(osvi));
4370
  osvi.dwOSVersionInfoSize = sizeof(osvi);
4371
4372
#  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4373
#    pragma warning(push)
4374
#    ifdef __INTEL_COMPILER
4375
#      pragma warning(disable : 1478)
4376
#    elif defined __clang__
4377
#      pragma clang diagnostic push
4378
#      pragma clang diagnostic ignored "-Wdeprecated-declarations"
4379
#    else
4380
#      pragma warning(disable : 4996)
4381
#    endif
4382
#  endif
4383
  bOsVersionInfoEx = GetVersionExA((OSVERSIONINFOA*)&osvi);
4384
  if (!bOsVersionInfoEx) {
4385
    return "";
4386
  }
4387
#  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4388
#    ifdef __clang__
4389
#      pragma clang diagnostic pop
4390
#    else
4391
#      pragma warning(pop)
4392
#    endif
4393
#  endif
4394
4395
  switch (osvi.dwPlatformId) {
4396
      // Test for the Windows NT product family.
4397
4398
    case VER_PLATFORM_WIN32_NT:
4399
4400
      // Test for the specific product family.
4401
      if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0) {
4402
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4403
          res += "Microsoft Windows 10";
4404
        } else {
4405
          res += "Microsoft Windows Server 2016 family";
4406
        }
4407
      }
4408
4409
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) {
4410
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4411
          res += "Microsoft Windows 8.1";
4412
        } else {
4413
          res += "Microsoft Windows Server 2012 R2 family";
4414
        }
4415
      }
4416
4417
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) {
4418
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4419
          res += "Microsoft Windows 8";
4420
        } else {
4421
          res += "Microsoft Windows Server 2012 family";
4422
        }
4423
      }
4424
4425
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) {
4426
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4427
          res += "Microsoft Windows 7";
4428
        } else {
4429
          res += "Microsoft Windows Server 2008 R2 family";
4430
        }
4431
      }
4432
4433
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) {
4434
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4435
          res += "Microsoft Windows Vista";
4436
        } else {
4437
          res += "Microsoft Windows Server 2008 family";
4438
        }
4439
      }
4440
4441
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4442
        res += "Microsoft Windows Server 2003 family";
4443
      }
4444
4445
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
4446
        res += "Microsoft Windows XP";
4447
      }
4448
4449
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4450
        res += "Microsoft Windows 2000";
4451
      }
4452
4453
      if (osvi.dwMajorVersion <= 4) {
4454
        res += "Microsoft Windows NT";
4455
      }
4456
4457
      // Test for specific product on Windows NT 4.0 SP6 and later.
4458
4459
      if (bOsVersionInfoEx) {
4460
        // Test for the workstation type.
4461
4462
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4463
          if (osvi.dwMajorVersion == 4) {
4464
            res += " Workstation 4.0";
4465
          } else if (osvi.dwMajorVersion == 5) {
4466
            if (osvi.wSuiteMask & VER_SUITE_PERSONAL) {
4467
              res += " Home Edition";
4468
            } else {
4469
              res += " Professional";
4470
            }
4471
          }
4472
        }
4473
4474
        // Test for the server type.
4475
4476
        else if (osvi.wProductType == VER_NT_SERVER) {
4477
          if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4478
            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
4479
              res += " Datacenter Edition";
4480
            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4481
              res += " Enterprise Edition";
4482
            } else if (osvi.wSuiteMask == VER_SUITE_BLADE) {
4483
              res += " Web Edition";
4484
            } else {
4485
              res += " Standard Edition";
4486
            }
4487
          }
4488
4489
          else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4490
            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
4491
              res += " Datacenter Server";
4492
            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4493
              res += " Advanced Server";
4494
            } else {
4495
              res += " Server";
4496
            }
4497
          }
4498
4499
          else if (osvi.dwMajorVersion <= 4) // Windows NT 4.0
4500
          {
4501
            if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4502
              res += " Server 4.0, Enterprise Edition";
4503
            } else {
4504
              res += " Server 4.0";
4505
            }
4506
          }
4507
        }
4508
      }
4509
4510
      // Test for specific product on Windows NT 4.0 SP5 and earlier
4511
4512
      else {
4513
        HKEY hKey;
4514
#  define BUFSIZE 80
4515
        wchar_t szProductType[BUFSIZE];
4516
        DWORD dwBufLen = BUFSIZE;
4517
        LONG lRet;
4518
4519
        lRet =
4520
          RegOpenKeyExW(HKEY_LOCAL_MACHINE,
4521
                        L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
4522
                        0, KEY_QUERY_VALUE, &hKey);
4523
        if (lRet != ERROR_SUCCESS) {
4524
          return "";
4525
        }
4526
4527
        lRet = RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr,
4528
                                (LPBYTE)szProductType, &dwBufLen);
4529
4530
        if ((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE)) {
4531
          return "";
4532
        }
4533
4534
        RegCloseKey(hKey);
4535
4536
        if (lstrcmpiW(L"WINNT", szProductType) == 0) {
4537
          res += " Workstation";
4538
        }
4539
        if (lstrcmpiW(L"LANMANNT", szProductType) == 0) {
4540
          res += " Server";
4541
        }
4542
        if (lstrcmpiW(L"SERVERNT", szProductType) == 0) {
4543
          res += " Advanced Server";
4544
        }
4545
4546
        res += " ";
4547
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMajorVersion);
4548
        res += buffer;
4549
        res += ".";
4550
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMinorVersion);
4551
        res += buffer;
4552
      }
4553
4554
      // Display service pack (if any) and build number.
4555
4556
      if (osvi.dwMajorVersion == 4 &&
4557
          lstrcmpiA(osvi.szCSDVersion, "Service Pack 6") == 0) {
4558
        HKEY hKey;
4559
        LONG lRet;
4560
4561
        // Test for SP6 versus SP6a.
4562
4563
        lRet = RegOpenKeyExW(
4564
          HKEY_LOCAL_MACHINE,
4565
          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
4566
          0, KEY_QUERY_VALUE, &hKey);
4567
4568
        if (lRet == ERROR_SUCCESS) {
4569
          res += " Service Pack 6a (Build ";
4570
          snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4571
          res += buffer;
4572
          res += ")";
4573
        } else // Windows NT 4.0 prior to SP6a
4574
        {
4575
          res += " ";
4576
          res += osvi.szCSDVersion;
4577
          res += " (Build ";
4578
          snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4579
          res += buffer;
4580
          res += ")";
4581
        }
4582
4583
        RegCloseKey(hKey);
4584
      } else // Windows NT 3.51 and earlier or Windows 2000 and later
4585
      {
4586
        res += " ";
4587
        res += osvi.szCSDVersion;
4588
        res += " (Build ";
4589
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4590
        res += buffer;
4591
        res += ")";
4592
      }
4593
4594
      break;
4595
4596
      // Test for the Windows 95 product family.
4597
4598
    case VER_PLATFORM_WIN32_WINDOWS:
4599
4600
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) {
4601
        res += "Microsoft Windows 95";
4602
        if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') {
4603
          res += " OSR2";
4604
        }
4605
      }
4606
4607
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) {
4608
        res += "Microsoft Windows 98";
4609
        if (osvi.szCSDVersion[1] == 'A') {
4610
          res += " SE";
4611
        }
4612
      }
4613
4614
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) {
4615
        res += "Microsoft Windows Millennium Edition";
4616
      }
4617
      break;
4618
4619
    case VER_PLATFORM_WIN32s:
4620
4621
      res += "Microsoft Win32s";
4622
      break;
4623
  }
4624
#endif
4625
4626
0
  return res;
4627
0
}
4628
4629
bool SystemTools::ParseURLProtocol(std::string const& URL,
4630
                                   std::string& protocol,
4631
                                   std::string& dataglom, bool decode)
4632
0
{
4633
  // match 0 entire url
4634
  // match 1 protocol
4635
  // match 2 dataglom following protocol://
4636
0
  kwsys::RegularExpression urlRe(VTK_URL_PROTOCOL_REGEX);
4637
4638
0
  if (!urlRe.find(URL))
4639
0
    return false;
4640
4641
0
  protocol = urlRe.match(1);
4642
0
  dataglom = urlRe.match(2);
4643
4644
0
  if (decode) {
4645
0
    dataglom = DecodeURL(dataglom);
4646
0
  }
4647
4648
0
  return true;
4649
0
}
4650
4651
bool SystemTools::ParseURL(std::string const& URL, std::string& protocol,
4652
                           std::string& username, std::string& password,
4653
                           std::string& hostname, std::string& dataport,
4654
                           std::string& database, bool decode)
4655
0
{
4656
0
  kwsys::RegularExpression urlRe(VTK_URL_REGEX);
4657
0
  if (!urlRe.find(URL))
4658
0
    return false;
4659
4660
  // match 0 URL
4661
  // match 1 protocol
4662
  // match 2 mangled user
4663
  // match 3 username
4664
  // match 4 mangled password
4665
  // match 5 password
4666
  // match 6 hostname
4667
  // match 7 mangled port
4668
  // match 8 dataport
4669
  // match 9 database name
4670
4671
0
  protocol = urlRe.match(1);
4672
0
  username = urlRe.match(3);
4673
0
  password = urlRe.match(5);
4674
0
  hostname = urlRe.match(6);
4675
0
  dataport = urlRe.match(8);
4676
0
  database = urlRe.match(9);
4677
4678
0
  if (decode) {
4679
0
    username = DecodeURL(username);
4680
0
    password = DecodeURL(password);
4681
0
    hostname = DecodeURL(hostname);
4682
0
    dataport = DecodeURL(dataport);
4683
0
    database = DecodeURL(database);
4684
0
  }
4685
4686
0
  return true;
4687
0
}
4688
4689
// ----------------------------------------------------------------------
4690
std::string SystemTools::DecodeURL(std::string const& url)
4691
0
{
4692
0
  kwsys::RegularExpression urlByteRe(VTK_URL_BYTE_REGEX);
4693
0
  std::string ret;
4694
0
  for (size_t i = 0; i < url.length(); i++) {
4695
0
    if (urlByteRe.find(url.substr(i, 3))) {
4696
0
      char bytes[] = { url[i + 1], url[i + 2], '\0' };
4697
0
      ret += static_cast<char>(strtoul(bytes, nullptr, 16));
4698
0
      i += 2;
4699
0
    } else {
4700
0
      ret += url[i];
4701
0
    }
4702
0
  }
4703
0
  return ret;
4704
0
}
4705
4706
// ----------------------------------------------------------------------
4707
// Do NOT initialize.  Default initialization to zero is necessary.
4708
static unsigned int SystemToolsManagerCount;
4709
4710
// SystemToolsManager manages the SystemTools singleton.
4711
// SystemToolsManager should be included in any translation unit
4712
// that will use SystemTools or that implements the singleton
4713
// pattern. It makes sure that the SystemTools singleton is created
4714
// before and destroyed after all other singletons in CMake.
4715
4716
SystemToolsManager::SystemToolsManager()
4717
1.29k
{
4718
1.29k
  if (++SystemToolsManagerCount == 1) {
4719
16
    SystemTools::ClassInitialize();
4720
16
  }
4721
1.29k
}
4722
4723
SystemToolsManager::~SystemToolsManager()
4724
0
{
4725
0
  if (--SystemToolsManagerCount == 0) {
4726
0
    SystemTools::ClassFinalize();
4727
0
  }
4728
0
}
4729
4730
#if defined(__VMS)
4731
// On VMS we configure the run time C library to be more UNIX like.
4732
// https://h71000.www7.hp.com/doc/732final/5763/5763pro_004.html
4733
extern "C" int decc$feature_get_index(char* name);
4734
extern "C" int decc$feature_set_value(int index, int mode, int value);
4735
static int SetVMSFeature(char* name, int value)
4736
{
4737
  int i;
4738
  errno = 0;
4739
  i = decc$feature_get_index(name);
4740
  return i >= 0 && (decc$feature_set_value(i, 1, value) >= 0 || errno == 0);
4741
}
4742
#endif
4743
4744
void SystemTools::ClassInitialize()
4745
16
{
4746
#ifdef __VMS
4747
  SetVMSFeature("DECC$FILENAME_UNIX_ONLY", 1);
4748
#endif
4749
4750
  // Create statics singleton instance
4751
16
  SystemToolsStatics = new SystemToolsStatic;
4752
16
}
4753
4754
void SystemTools::ClassFinalize()
4755
0
{
4756
0
  delete SystemToolsStatics;
4757
0
}
4758
4759
} // namespace KWSYS_NAMESPACE
4760
4761
#if defined(_MSC_VER) && defined(_DEBUG)
4762
#  include <crtdbg.h>
4763
#  include <stdio.h>
4764
#  include <stdlib.h>
4765
namespace KWSYS_NAMESPACE {
4766
4767
static int SystemToolsDebugReport(int, char* message, int* ret)
4768
{
4769
  if (ret) {
4770
    // Pretend user clicked on Retry button in popup.
4771
    *ret = 1;
4772
  }
4773
  fprintf(stderr, "%s", message);
4774
  fflush(stderr);
4775
  return 1; // no further reporting required
4776
}
4777
4778
void SystemTools::EnableMSVCDebugHook()
4779
{
4780
  if (SystemTools::HasEnv("DART_TEST_FROM_DART") ||
4781
      SystemTools::HasEnv("DASHBOARD_TEST_FROM_CTEST")) {
4782
    _CrtSetReportHook(SystemToolsDebugReport);
4783
  }
4784
}
4785
4786
} // namespace KWSYS_NAMESPACE
4787
#else
4788
namespace KWSYS_NAMESPACE {
4789
void SystemTools::EnableMSVCDebugHook()
4790
0
{
4791
0
}
4792
} // namespace KWSYS_NAMESPACE
4793
#endif