Coverage Report

Created: 2026-07-30 06:52

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