Coverage Report

Created: 2026-03-12 06:35

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
64.4k
{
377
64.4k
  return mkdir(dir.c_str(), mode ? *mode : 00777);
378
64.4k
}
379
inline int Rmdir(std::string const& dir)
380
16.4k
{
381
16.4k
  return rmdir(dir.c_str());
382
16.4k
}
383
inline char const* Getcwd(char* buf, unsigned int len)
384
20.9k
{
385
20.9k
  return getcwd(buf, len);
386
20.9k
}
387
388
inline int Chdir(std::string const& dir)
389
58.1k
{
390
58.1k
  return chdir(dir.c_str());
391
58.1k
}
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.6k
{
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.6k
  char const* v = getenv(key);
698
36.6k
  if (v) {
699
36.6k
    result = v;
700
36.6k
    return true;
701
36.6k
  }
702
17
#endif
703
17
  return false;
704
36.6k
}
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
0
{
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
0
  return fopen(file.c_str(), mode);
919
0
#endif
920
0
}
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
29.1k
{
932
29.1k
  if (path.empty()) {
933
0
    return Status::POSIX(EINVAL);
934
0
  }
935
29.1k
  if (SystemTools::PathExists(path)) {
936
12.9k
    if (SystemTools::FileIsDirectory(path)) {
937
12.9k
      return Status::Success();
938
12.9k
    }
939
0
    return Status::POSIX(EEXIST);
940
12.9k
  }
941
16.1k
  std::string dir = path;
942
16.1k
  SystemTools::ConvertToUnixSlashes(dir);
943
944
16.1k
  std::string::size_type pos = 0;
945
16.1k
  std::string topdir;
946
64.4k
  while ((pos = dir.find('/', pos)) != std::string::npos) {
947
    // all underlying functions use C strings, so temporarily
948
    // end the string here
949
48.3k
    dir[pos] = '\0';
950
951
48.3k
    Mkdir(dir, mode);
952
48.3k
    dir[pos] = '/';
953
954
48.3k
    ++pos;
955
48.3k
  }
956
16.1k
  topdir = dir;
957
16.1k
  if (Mkdir(topdir, mode) != 0 && errno != EEXIST) {
958
0
    return Status::POSIX_errno();
959
0
  }
960
961
16.1k
  return Status::Success();
962
16.1k
}
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 (RegDeleteValue(hKey, (LPTSTR)valuename.c_str()) == ERROR_SUCCESS) {
1256
      RegCloseKey(hKey);
1257
      return true;
1258
    }
1259
  }
1260
  return false;
1261
}
1262
#else
1263
bool SystemTools::DeleteRegistryValue(std::string const&, KeyWOW64)
1264
0
{
1265
0
  return false;
1266
0
}
1267
#endif
1268
1269
#ifdef _WIN32
1270
SystemTools::WindowsFileId::WindowsFileId(unsigned long volumeSerialNumber,
1271
                                          unsigned long fileIndexHigh,
1272
                                          unsigned long fileIndexLow)
1273
  : m_volumeSerialNumber(volumeSerialNumber)
1274
  , m_fileIndexHigh(fileIndexHigh)
1275
  , m_fileIndexLow(fileIndexLow)
1276
{
1277
}
1278
1279
bool SystemTools::WindowsFileId::operator==(WindowsFileId const& o) const
1280
{
1281
  return (m_volumeSerialNumber == o.m_volumeSerialNumber &&
1282
          m_fileIndexHigh == o.m_fileIndexHigh &&
1283
          m_fileIndexLow == o.m_fileIndexLow);
1284
}
1285
1286
bool SystemTools::WindowsFileId::operator!=(WindowsFileId const& o) const
1287
{
1288
  return !(*this == o);
1289
}
1290
#else
1291
SystemTools::UnixFileId::UnixFileId(dev_t volumeSerialNumber,
1292
                                    ino_t fileSerialNumber, off_t fileSize)
1293
0
  : m_volumeSerialNumber(volumeSerialNumber)
1294
0
  , m_fileSerialNumber(fileSerialNumber)
1295
0
  , m_fileSize(fileSize)
1296
0
{
1297
0
}
1298
1299
bool SystemTools::UnixFileId::operator==(UnixFileId const& o) const
1300
0
{
1301
0
  return (m_volumeSerialNumber == o.m_volumeSerialNumber &&
1302
0
          m_fileSerialNumber == o.m_fileSerialNumber &&
1303
0
          m_fileSize == o.m_fileSize);
1304
0
}
1305
1306
bool SystemTools::UnixFileId::operator!=(UnixFileId const& o) const
1307
0
{
1308
0
  return !(*this == o);
1309
0
}
1310
#endif
1311
1312
bool SystemTools::GetFileId(std::string const& file, FileId& id)
1313
0
{
1314
#ifdef _WIN32
1315
  HANDLE hFile =
1316
    CreateFileW(Encoding::ToWide(file).c_str(), GENERIC_READ, FILE_SHARE_READ,
1317
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1318
  if (hFile != INVALID_HANDLE_VALUE) {
1319
    BY_HANDLE_FILE_INFORMATION fiBuf;
1320
    GetFileInformationByHandle(hFile, &fiBuf);
1321
    CloseHandle(hFile);
1322
    id = FileId(fiBuf.dwVolumeSerialNumber, fiBuf.nFileIndexHigh,
1323
                fiBuf.nFileIndexLow);
1324
    return true;
1325
  } else {
1326
    return false;
1327
  }
1328
#else
1329
0
  struct stat fileStat;
1330
0
  if (stat(file.c_str(), &fileStat) == 0) {
1331
0
    id = FileId(fileStat.st_dev, fileStat.st_ino, fileStat.st_size);
1332
0
    return true;
1333
0
  }
1334
0
  return false;
1335
0
#endif
1336
0
}
1337
1338
bool SystemTools::SameFile(std::string const& file1, std::string const& file2)
1339
0
{
1340
#ifdef _WIN32
1341
  HANDLE hFile1, hFile2;
1342
1343
  hFile1 =
1344
    CreateFileW(Encoding::ToWide(file1).c_str(), GENERIC_READ, FILE_SHARE_READ,
1345
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1346
  hFile2 =
1347
    CreateFileW(Encoding::ToWide(file2).c_str(), GENERIC_READ, FILE_SHARE_READ,
1348
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1349
  if (hFile1 == INVALID_HANDLE_VALUE || hFile2 == INVALID_HANDLE_VALUE) {
1350
    if (hFile1 != INVALID_HANDLE_VALUE) {
1351
      CloseHandle(hFile1);
1352
    }
1353
    if (hFile2 != INVALID_HANDLE_VALUE) {
1354
      CloseHandle(hFile2);
1355
    }
1356
    return false;
1357
  }
1358
1359
  BY_HANDLE_FILE_INFORMATION fiBuf1;
1360
  BY_HANDLE_FILE_INFORMATION fiBuf2;
1361
  GetFileInformationByHandle(hFile1, &fiBuf1);
1362
  GetFileInformationByHandle(hFile2, &fiBuf2);
1363
  CloseHandle(hFile1);
1364
  CloseHandle(hFile2);
1365
  return (fiBuf1.dwVolumeSerialNumber == fiBuf2.dwVolumeSerialNumber &&
1366
          fiBuf1.nFileIndexHigh == fiBuf2.nFileIndexHigh &&
1367
          fiBuf1.nFileIndexLow == fiBuf2.nFileIndexLow);
1368
#else
1369
0
  struct stat fileStat1, fileStat2;
1370
0
  if (stat(file1.c_str(), &fileStat1) == 0 &&
1371
0
      stat(file2.c_str(), &fileStat2) == 0) {
1372
    // see if the files are the same file
1373
    // check the device inode and size
1374
0
    if (memcmp(&fileStat2.st_dev, &fileStat1.st_dev,
1375
0
               sizeof(fileStat1.st_dev)) == 0 &&
1376
0
        memcmp(&fileStat2.st_ino, &fileStat1.st_ino,
1377
0
               sizeof(fileStat1.st_ino)) == 0 &&
1378
0
        fileStat2.st_size == fileStat1.st_size) {
1379
0
      return true;
1380
0
    }
1381
0
  }
1382
0
  return false;
1383
0
#endif
1384
0
}
1385
1386
bool SystemTools::PathExists(std::string const& path)
1387
84.1k
{
1388
84.1k
  if (path.empty()) {
1389
0
    return false;
1390
0
  }
1391
#if defined(_WIN32)
1392
  return (GetFileAttributesW(Encoding::ToWindowsExtendedPath(path).c_str()) !=
1393
          INVALID_FILE_ATTRIBUTES);
1394
#else
1395
84.1k
  struct stat st;
1396
84.1k
  return lstat(path.c_str(), &st) == 0;
1397
84.1k
#endif
1398
84.1k
}
1399
1400
bool SystemTools::FileExists(char const* filename)
1401
0
{
1402
0
  if (!filename) {
1403
0
    return false;
1404
0
  }
1405
0
  return SystemTools::FileExists(std::string(filename));
1406
0
}
1407
1408
bool SystemTools::FileExists(std::string const& filename)
1409
3
{
1410
3
  if (filename.empty()) {
1411
0
    return false;
1412
0
  }
1413
#if defined(_WIN32)
1414
  std::wstring const path = Encoding::ToWindowsExtendedPath(filename);
1415
  DWORD attr = GetFileAttributesW(path.c_str());
1416
  if (attr == INVALID_FILE_ATTRIBUTES) {
1417
    return false;
1418
  }
1419
1420
  if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
1421
    // Using 0 instead of GENERIC_READ as it allows reading of file attributes
1422
    // even if we do not have permission to read the file itself
1423
    HANDLE handle = CreateFileW(path.c_str(), 0, 0, nullptr, OPEN_EXISTING,
1424
                                FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1425
1426
    if (handle == INVALID_HANDLE_VALUE) {
1427
      // A reparse point may be an execution alias (Windows Store app), which
1428
      // is similar to a symlink but it cannot be opened as a regular file.
1429
      // We must look at the reparse point data explicitly.
1430
      handle = CreateFileW(
1431
        path.c_str(), 0, 0, nullptr, OPEN_EXISTING,
1432
        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1433
1434
      if (handle == INVALID_HANDLE_VALUE) {
1435
        return false;
1436
      }
1437
1438
      byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1439
      DWORD bytesReturned = 0;
1440
1441
      if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
1442
                           MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
1443
                           nullptr)) {
1444
        CloseHandle(handle);
1445
        return false;
1446
      }
1447
1448
      CloseHandle(handle);
1449
1450
      PREPARSE_DATA_BUFFER data =
1451
        reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
1452
1453
      // Assume that file exists if it is an execution alias.
1454
      return data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK;
1455
    }
1456
1457
    CloseHandle(handle);
1458
  }
1459
1460
  return true;
1461
#else
1462
// SCO OpenServer 5.0.7/3.2's command has 711 permission.
1463
#  if defined(_SCO_DS)
1464
  return access(filename.c_str(), F_OK) == 0;
1465
#  else
1466
3
  return access(filename.c_str(), R_OK) == 0;
1467
3
#  endif
1468
3
#endif
1469
3
}
1470
1471
bool SystemTools::FileExists(char const* filename, bool isFile)
1472
0
{
1473
0
  if (!filename) {
1474
0
    return false;
1475
0
  }
1476
0
  return SystemTools::FileExists(std::string(filename), isFile);
1477
0
}
1478
1479
bool SystemTools::FileExists(std::string const& filename, bool isFile)
1480
1
{
1481
1
  if (SystemTools::FileExists(filename)) {
1482
    // If isFile is set return not FileIsDirectory,
1483
    // so this will only be true if it is a file
1484
1
    return !isFile || !SystemTools::FileIsDirectory(filename);
1485
1
  }
1486
0
  return false;
1487
1
}
1488
1489
bool SystemTools::TestFileAccess(char const* filename,
1490
                                 TestFilePermissions permissions)
1491
0
{
1492
0
  if (!filename) {
1493
0
    return false;
1494
0
  }
1495
0
  return SystemTools::TestFileAccess(std::string(filename), permissions);
1496
0
}
1497
1498
bool SystemTools::TestFileAccess(std::string const& filename,
1499
                                 TestFilePermissions permissions)
1500
0
{
1501
0
  if (filename.empty()) {
1502
0
    return false;
1503
0
  }
1504
#if defined(_WIN32) && !defined(__CYGWIN__)
1505
  // If execute set, change to read permission (all files on Windows
1506
  // are executable if they are readable).  The CRT will always fail
1507
  // if you pass an execute bit.
1508
  if (permissions & TEST_FILE_EXECUTE) {
1509
    permissions &= ~TEST_FILE_EXECUTE;
1510
    permissions |= TEST_FILE_READ;
1511
  }
1512
  return _waccess(Encoding::ToWindowsExtendedPath(filename).c_str(),
1513
                  permissions) == 0;
1514
#else
1515
0
  return access(filename.c_str(), permissions) == 0;
1516
0
#endif
1517
0
}
1518
1519
int SystemTools::Stat(char const* path, SystemTools::Stat_t* buf)
1520
0
{
1521
0
  if (!path) {
1522
0
    errno = EFAULT;
1523
0
    return -1;
1524
0
  }
1525
0
  return SystemTools::Stat(std::string(path), buf);
1526
0
}
1527
1528
int SystemTools::Stat(std::string const& path, SystemTools::Stat_t* buf)
1529
0
{
1530
0
  if (path.empty()) {
1531
0
    errno = ENOENT;
1532
0
    return -1;
1533
0
  }
1534
#if defined(_WIN32) && !defined(__CYGWIN__)
1535
  // Ideally we should use Encoding::ToWindowsExtendedPath to support
1536
  // long paths, but _wstat64 rejects paths with '?' in them, thinking
1537
  // they are wildcards.
1538
  std::wstring const& wpath = Encoding::ToWide(path);
1539
  return _wstat64(wpath.c_str(), buf);
1540
#else
1541
0
  return stat(path.c_str(), buf);
1542
0
#endif
1543
0
}
1544
1545
Status SystemTools::Touch(std::string const& filename, bool create)
1546
0
{
1547
0
  if (!SystemTools::FileExists(filename)) {
1548
0
    if (create) {
1549
0
      FILE* file = Fopen(filename, "a+b");
1550
0
      if (file) {
1551
0
        fclose(file);
1552
0
        return Status::Success();
1553
0
      }
1554
0
      return Status::POSIX_errno();
1555
0
    } else {
1556
0
      return Status::Success();
1557
0
    }
1558
0
  }
1559
#if defined(_WIN32) && !defined(__CYGWIN__)
1560
  HANDLE h = CreateFileW(Encoding::ToWindowsExtendedPath(filename).c_str(),
1561
                         FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, 0,
1562
                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
1563
  if (!h) {
1564
    return Status::Windows_GetLastError();
1565
  }
1566
  FILETIME mtime;
1567
  GetSystemTimeAsFileTime(&mtime);
1568
  if (!SetFileTime(h, 0, 0, &mtime)) {
1569
    Status status = Status::Windows_GetLastError();
1570
    CloseHandle(h);
1571
    return status;
1572
  }
1573
  CloseHandle(h);
1574
#elif KWSYS_CXX_HAS_UTIMENSAT
1575
  // utimensat is only available on newer Unixes and macOS 10.13+
1576
0
  if (utimensat(AT_FDCWD, filename.c_str(), nullptr, 0) < 0) {
1577
0
    return Status::POSIX_errno();
1578
0
  }
1579
#else
1580
  // fall back to utimes
1581
  if (utimes(filename.c_str(), nullptr) < 0) {
1582
    return Status::POSIX_errno();
1583
  }
1584
#endif
1585
0
  return Status::Success();
1586
0
}
1587
1588
Status SystemTools::FileTimeCompare(std::string const& f1,
1589
                                    std::string const& f2, int* result)
1590
0
{
1591
  // Default to same time.
1592
0
  *result = 0;
1593
0
#if !defined(_WIN32) || defined(__CYGWIN__)
1594
  // POSIX version.  Use stat function to get file modification time.
1595
0
  struct stat s1;
1596
0
  if (stat(f1.c_str(), &s1) != 0) {
1597
0
    return Status::POSIX_errno();
1598
0
  }
1599
0
  struct stat s2;
1600
0
  if (stat(f2.c_str(), &s2) != 0) {
1601
0
    return Status::POSIX_errno();
1602
0
  }
1603
0
#  if KWSYS_CXX_STAT_HAS_ST_MTIM
1604
  // Compare using nanosecond resolution.
1605
0
  if (s1.st_mtim.tv_sec < s2.st_mtim.tv_sec) {
1606
0
    *result = -1;
1607
0
  } else if (s1.st_mtim.tv_sec > s2.st_mtim.tv_sec) {
1608
0
    *result = 1;
1609
0
  } else if (s1.st_mtim.tv_nsec < s2.st_mtim.tv_nsec) {
1610
0
    *result = -1;
1611
0
  } else if (s1.st_mtim.tv_nsec > s2.st_mtim.tv_nsec) {
1612
0
    *result = 1;
1613
0
  }
1614
#  elif KWSYS_CXX_STAT_HAS_ST_MTIMESPEC
1615
  // Compare using nanosecond resolution.
1616
  if (s1.st_mtimespec.tv_sec < s2.st_mtimespec.tv_sec) {
1617
    *result = -1;
1618
  } else if (s1.st_mtimespec.tv_sec > s2.st_mtimespec.tv_sec) {
1619
    *result = 1;
1620
  } else if (s1.st_mtimespec.tv_nsec < s2.st_mtimespec.tv_nsec) {
1621
    *result = -1;
1622
  } else if (s1.st_mtimespec.tv_nsec > s2.st_mtimespec.tv_nsec) {
1623
    *result = 1;
1624
  }
1625
#  else
1626
  // Compare using 1 second resolution.
1627
  if (s1.st_mtime < s2.st_mtime) {
1628
    *result = -1;
1629
  } else if (s1.st_mtime > s2.st_mtime) {
1630
    *result = 1;
1631
  }
1632
#  endif
1633
#else
1634
  // Windows version.  Get the modification time from extended file attributes.
1635
  WIN32_FILE_ATTRIBUTE_DATA f1d;
1636
  WIN32_FILE_ATTRIBUTE_DATA f2d;
1637
  if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f1).c_str(),
1638
                            GetFileExInfoStandard, &f1d)) {
1639
    return Status::Windows_GetLastError();
1640
  }
1641
  if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f2).c_str(),
1642
                            GetFileExInfoStandard, &f2d)) {
1643
    return Status::Windows_GetLastError();
1644
  }
1645
1646
  // Compare the file times using resolution provided by system call.
1647
  *result = (int)CompareFileTime(&f1d.ftLastWriteTime, &f2d.ftLastWriteTime);
1648
#endif
1649
0
  return Status::Success();
1650
0
}
1651
1652
// Return a capitalized string (i.e the first letter is uppercased, all other
1653
// are lowercased)
1654
std::string SystemTools::Capitalized(std::string const& s)
1655
0
{
1656
0
  std::string n;
1657
0
  if (s.empty()) {
1658
0
    return n;
1659
0
  }
1660
0
  n.resize(s.size());
1661
0
  n[0] = static_cast<std::string::value_type>(kwsysString_toupper(s[0]));
1662
0
  for (size_t i = 1; i < s.size(); i++) {
1663
0
    n[i] = static_cast<std::string::value_type>(kwsysString_tolower(s[i]));
1664
0
  }
1665
0
  return n;
1666
0
}
1667
1668
// Return capitalized words
1669
std::string SystemTools::CapitalizedWords(std::string const& s)
1670
0
{
1671
0
  std::string n(s);
1672
0
  for (size_t i = 0; i < s.size(); i++) {
1673
0
    if (kwsysString_isalpha(s[i]) &&
1674
0
        (i == 0 || kwsysString_isspace(s[i - 1]))) {
1675
0
      n[i] = static_cast<std::string::value_type>(kwsysString_toupper(s[i]));
1676
0
    }
1677
0
  }
1678
0
  return n;
1679
0
}
1680
1681
// Return uncapitalized words
1682
std::string SystemTools::UnCapitalizedWords(std::string const& s)
1683
0
{
1684
0
  std::string n(s);
1685
0
  for (size_t i = 0; i < s.size(); i++) {
1686
0
    if (kwsysString_isalpha(s[i]) &&
1687
0
        (i == 0 || kwsysString_isspace(s[i - 1]))) {
1688
0
      n[i] = static_cast<std::string::value_type>(kwsysString_tolower(s[i]));
1689
0
    }
1690
0
  }
1691
0
  return n;
1692
0
}
1693
1694
// only works for words with at least two letters
1695
std::string SystemTools::AddSpaceBetweenCapitalizedWords(std::string const& s)
1696
0
{
1697
0
  std::string n;
1698
0
  if (!s.empty()) {
1699
0
    n.reserve(s.size());
1700
0
    n += s[0];
1701
0
    for (size_t i = 1; i < s.size(); i++) {
1702
0
      if (kwsysString_isupper(s[i]) && !kwsysString_isspace(s[i - 1]) &&
1703
0
          !kwsysString_isupper(s[i - 1])) {
1704
0
        n += ' ';
1705
0
      }
1706
0
      n += s[i];
1707
0
    }
1708
0
  }
1709
0
  return n;
1710
0
}
1711
1712
char* SystemTools::AppendStrings(char const* str1, char const* str2)
1713
0
{
1714
0
  if (!str1) {
1715
0
    return SystemTools::DuplicateString(str2);
1716
0
  }
1717
0
  if (!str2) {
1718
0
    return SystemTools::DuplicateString(str1);
1719
0
  }
1720
0
  size_t len1 = strlen(str1);
1721
0
  char* newstr = new char[len1 + strlen(str2) + 1];
1722
0
  if (!newstr) {
1723
0
    return nullptr;
1724
0
  }
1725
0
  strcpy(newstr, str1);
1726
0
  strcat(newstr + len1, str2);
1727
0
  return newstr;
1728
0
}
1729
1730
char* SystemTools::AppendStrings(char const* str1, char const* str2,
1731
                                 char const* str3)
1732
0
{
1733
0
  if (!str1) {
1734
0
    return SystemTools::AppendStrings(str2, str3);
1735
0
  }
1736
0
  if (!str2) {
1737
0
    return SystemTools::AppendStrings(str1, str3);
1738
0
  }
1739
0
  if (!str3) {
1740
0
    return SystemTools::AppendStrings(str1, str2);
1741
0
  }
1742
1743
0
  size_t len1 = strlen(str1), len2 = strlen(str2);
1744
0
  char* newstr = new char[len1 + len2 + strlen(str3) + 1];
1745
0
  if (!newstr) {
1746
0
    return nullptr;
1747
0
  }
1748
0
  strcpy(newstr, str1);
1749
0
  strcat(newstr + len1, str2);
1750
0
  strcat(newstr + len1 + len2, str3);
1751
0
  return newstr;
1752
0
}
1753
1754
std::string SystemTools::LowerCase(std::string s)
1755
1.61k
{
1756
1.61k
  std::transform(s.begin(), s.end(), s.begin(), kwsysString_tolower);
1757
1.61k
  return s;
1758
1.61k
}
1759
1760
std::string SystemTools::UpperCase(std::string s)
1761
1.61k
{
1762
1.61k
  std::transform(s.begin(), s.end(), s.begin(), kwsysString_toupper);
1763
1.61k
  return s;
1764
1.61k
}
1765
1766
// Count char in string
1767
size_t SystemTools::CountChar(char const* str, char c)
1768
0
{
1769
0
  size_t count = 0;
1770
1771
0
  if (str) {
1772
0
    while (*str) {
1773
0
      if (*str == c) {
1774
0
        ++count;
1775
0
      }
1776
0
      ++str;
1777
0
    }
1778
0
  }
1779
0
  return count;
1780
0
}
1781
1782
// Remove chars in string
1783
char* SystemTools::RemoveChars(char const* str, char const* toremove)
1784
0
{
1785
0
  if (!str) {
1786
0
    return nullptr;
1787
0
  }
1788
0
  char* clean_str = new char[strlen(str) + 1];
1789
0
  char* ptr = clean_str;
1790
0
  while (*str) {
1791
0
    char const* str2 = toremove;
1792
0
    while (*str2 && *str != *str2) {
1793
0
      ++str2;
1794
0
    }
1795
0
    if (!*str2) {
1796
0
      *ptr++ = *str;
1797
0
    }
1798
0
    ++str;
1799
0
  }
1800
0
  *ptr = '\0';
1801
0
  return clean_str;
1802
0
}
1803
1804
// Remove chars in string
1805
char* SystemTools::RemoveCharsButUpperHex(char const* str)
1806
0
{
1807
0
  if (!str) {
1808
0
    return nullptr;
1809
0
  }
1810
0
  char* clean_str = new char[strlen(str) + 1];
1811
0
  char* ptr = clean_str;
1812
0
  while (*str) {
1813
0
    if ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'F')) {
1814
0
      *ptr++ = *str;
1815
0
    }
1816
0
    ++str;
1817
0
  }
1818
0
  *ptr = '\0';
1819
0
  return clean_str;
1820
0
}
1821
1822
// Replace chars in string
1823
char* SystemTools::ReplaceChars(char* str, char const* toreplace,
1824
                                char replacement)
1825
0
{
1826
0
  if (str) {
1827
0
    char* ptr = str;
1828
0
    while (*ptr) {
1829
0
      char const* ptr2 = toreplace;
1830
0
      while (*ptr2) {
1831
0
        if (*ptr == *ptr2) {
1832
0
          *ptr = replacement;
1833
0
        }
1834
0
        ++ptr2;
1835
0
      }
1836
0
      ++ptr;
1837
0
    }
1838
0
  }
1839
0
  return str;
1840
0
}
1841
1842
// Returns if string starts with another string
1843
bool SystemTools::StringStartsWith(char const* str1, char const* str2)
1844
0
{
1845
0
  if (!str1 || !str2) {
1846
0
    return false;
1847
0
  }
1848
0
  size_t len1 = strlen(str1), len2 = strlen(str2);
1849
0
  return len1 >= len2 && !strncmp(str1, str2, len2) ? true : false;
1850
0
}
1851
1852
// Returns if string starts with another string
1853
bool SystemTools::StringStartsWith(std::string const& str1, char const* str2)
1854
2.03k
{
1855
2.03k
  if (!str2) {
1856
0
    return false;
1857
0
  }
1858
2.03k
  size_t len1 = str1.size(), len2 = strlen(str2);
1859
2.03k
  return len1 >= len2 && !strncmp(str1.c_str(), str2, len2) ? true : false;
1860
2.03k
}
1861
1862
// Returns if string ends with another string
1863
bool SystemTools::StringEndsWith(char const* str1, char const* str2)
1864
0
{
1865
0
  if (!str1 || !str2) {
1866
0
    return false;
1867
0
  }
1868
0
  size_t len1 = strlen(str1), len2 = strlen(str2);
1869
0
  return len1 >= len2 && !strncmp(str1 + (len1 - len2), str2, len2) ? true
1870
0
                                                                    : false;
1871
0
}
1872
1873
// Returns if string ends with another string
1874
bool SystemTools::StringEndsWith(std::string const& str1, char const* str2)
1875
2.03k
{
1876
2.03k
  if (!str2) {
1877
0
    return false;
1878
0
  }
1879
2.03k
  size_t len1 = str1.size(), len2 = strlen(str2);
1880
2.03k
  return len1 >= len2 && !strncmp(str1.c_str() + (len1 - len2), str2, len2)
1881
2.03k
    ? true
1882
2.03k
    : false;
1883
2.03k
}
1884
1885
// Returns a pointer to the last occurrence of str2 in str1
1886
char const* SystemTools::FindLastString(char const* str1, char const* str2)
1887
0
{
1888
0
  if (!str1 || !str2) {
1889
0
    return nullptr;
1890
0
  }
1891
1892
0
  size_t len1 = strlen(str1), len2 = strlen(str2);
1893
0
  if (len1 >= len2) {
1894
0
    char const* ptr = str1 + len1 - len2;
1895
0
    do {
1896
0
      if (!strncmp(ptr, str2, len2)) {
1897
0
        return ptr;
1898
0
      }
1899
0
    } while (ptr-- != str1);
1900
0
  }
1901
1902
0
  return nullptr;
1903
0
}
1904
1905
// Duplicate string
1906
char* SystemTools::DuplicateString(char const* str)
1907
0
{
1908
0
  if (str) {
1909
0
    char* newstr = new char[strlen(str) + 1];
1910
0
    return strcpy(newstr, str);
1911
0
  }
1912
0
  return nullptr;
1913
0
}
1914
1915
// Return a cropped string
1916
std::string SystemTools::CropString(std::string const& s, size_t max_len)
1917
0
{
1918
0
  if (s.empty() || max_len == 0 || max_len >= s.size()) {
1919
0
    return s;
1920
0
  }
1921
1922
0
  std::string n;
1923
0
  n.reserve(max_len);
1924
1925
0
  size_t middle = max_len / 2;
1926
1927
0
  n.assign(s, 0, middle);
1928
0
  n += s.substr(s.size() - (max_len - middle));
1929
1930
0
  if (max_len > 2) {
1931
0
    n[middle] = '.';
1932
0
    if (max_len > 3) {
1933
0
      n[middle - 1] = '.';
1934
0
      if (max_len > 4) {
1935
0
        n[middle + 1] = '.';
1936
0
      }
1937
0
    }
1938
0
  }
1939
1940
0
  return n;
1941
0
}
1942
1943
std::vector<std::string> SystemTools::SplitString(std::string const& p,
1944
                                                  char sep, bool isPath)
1945
0
{
1946
0
  std::string path = p;
1947
0
  std::vector<std::string> paths;
1948
0
  if (path.empty()) {
1949
0
    return paths;
1950
0
  }
1951
0
  if (isPath && path[0] == '/') {
1952
0
    path.erase(path.begin());
1953
0
    paths.emplace_back("/");
1954
0
  }
1955
0
  std::string::size_type pos1 = 0;
1956
0
  std::string::size_type pos2 = path.find(sep, pos1);
1957
0
  while (pos2 != std::string::npos) {
1958
0
    paths.push_back(path.substr(pos1, pos2 - pos1));
1959
0
    pos1 = pos2 + 1;
1960
0
    pos2 = path.find(sep, pos1 + 1);
1961
0
  }
1962
0
  paths.push_back(path.substr(pos1, pos2 - pos1));
1963
1964
0
  return paths;
1965
0
}
1966
1967
int SystemTools::EstimateFormatLength(char const* format, va_list ap)
1968
0
{
1969
0
  if (!format) {
1970
0
    return 0;
1971
0
  }
1972
1973
  // Quick-hack attempt at estimating the length of the string.
1974
  // Should never under-estimate.
1975
1976
  // Start with the length of the format string itself.
1977
1978
0
  size_t length = strlen(format);
1979
1980
  // Increase the length for every argument in the format.
1981
1982
0
  char const* cur = format;
1983
0
  while (*cur) {
1984
0
    if (*cur++ == '%') {
1985
      // Skip "%%" since it doesn't correspond to a va_arg.
1986
0
      if (*cur != '%') {
1987
0
        while (!kwsysString_isalpha(*cur)) {
1988
0
          ++cur;
1989
0
        }
1990
0
        switch (*cur) {
1991
0
          case 's': {
1992
            // Check the length of the string.
1993
0
            char* s = va_arg(ap, char*);
1994
0
            if (s) {
1995
0
              length += strlen(s);
1996
0
            }
1997
0
          } break;
1998
0
          case 'e':
1999
0
          case 'f':
2000
0
          case 'g': {
2001
            // Assume the argument contributes no more than 64 characters.
2002
0
            length += 64;
2003
2004
            // Eat the argument.
2005
0
            static_cast<void>(va_arg(ap, double));
2006
0
          } break;
2007
0
          default: {
2008
            // Assume the argument contributes no more than 64 characters.
2009
0
            length += 64;
2010
2011
            // Eat the argument.
2012
0
            static_cast<void>(va_arg(ap, int));
2013
0
          } break;
2014
0
        }
2015
0
      }
2016
2017
      // Move past the characters just tested.
2018
0
      ++cur;
2019
0
    }
2020
0
  }
2021
2022
0
  return static_cast<int>(length);
2023
0
}
2024
2025
std::string SystemTools::EscapeChars(char const* str,
2026
                                     char const* chars_to_escape,
2027
                                     char escape_char)
2028
0
{
2029
0
  std::string n;
2030
0
  if (str) {
2031
0
    if (!chars_to_escape || !*chars_to_escape) {
2032
0
      n.append(str);
2033
0
    } else {
2034
0
      n.reserve(strlen(str));
2035
0
      while (*str) {
2036
0
        char const* ptr = chars_to_escape;
2037
0
        while (*ptr) {
2038
0
          if (*str == *ptr) {
2039
0
            n += escape_char;
2040
0
            break;
2041
0
          }
2042
0
          ++ptr;
2043
0
        }
2044
0
        n += *str;
2045
0
        ++str;
2046
0
      }
2047
0
    }
2048
0
  }
2049
0
  return n;
2050
0
}
2051
2052
#ifdef __VMS
2053
static void ConvertVMSToUnix(std::string& path)
2054
{
2055
  std::string::size_type rootEnd = path.find(":[");
2056
  std::string::size_type pathEnd = path.find(']');
2057
  if (rootEnd != std::string::npos) {
2058
    std::string root = path.substr(0, rootEnd);
2059
    std::string pathPart = path.substr(rootEnd + 2, pathEnd - rootEnd - 2);
2060
    char const* pathCString = pathPart.c_str();
2061
    char const* pos0 = pathCString;
2062
    for (std::string::size_type pos = 0; *pos0; ++pos) {
2063
      if (*pos0 == '.') {
2064
        pathPart[pos] = '/';
2065
      }
2066
      pos0++;
2067
    }
2068
    path = '/' + root + '/' + pathPart;
2069
  }
2070
}
2071
#endif
2072
2073
// convert windows slashes to unix slashes
2074
void SystemTools::ConvertToUnixSlashes(std::string& path)
2075
38.7k
{
2076
38.7k
  if (path.empty()) {
2077
1
    return;
2078
1
  }
2079
2080
#ifdef __VMS
2081
  ConvertVMSToUnix(path);
2082
#else
2083
  // replace backslashes
2084
38.7k
  std::replace(path.begin(), path.end(), '\\', '/');
2085
2086
  // collapse repeated slashes, except exactly two leading slashes are
2087
  // meaningful and must be preserved.
2088
38.7k
  bool hasDoubleSlash = path[0] == '/' && path[1] == '/' && path[2] != '/';
2089
38.7k
  auto uniqueEnd = std::unique(
2090
38.7k
    path.begin() + hasDoubleSlash, path.end(),
2091
3.29M
    [](char c1, char c2) -> bool { return c1 == '/' && c1 == c2; });
2092
38.7k
  path.erase(uniqueEnd, path.end());
2093
38.7k
#endif
2094
2095
  // if there is a tilda ~ then replace it with HOME
2096
38.7k
  if (path[0] == '~' && (path[1] == '/' || path[1] == '\0')) {
2097
160
    std::string homeEnv;
2098
160
    if (SystemTools::GetEnv("HOME", homeEnv)) {
2099
160
      path.replace(0, 1, homeEnv);
2100
160
    }
2101
160
  }
2102
38.5k
#ifdef HAVE_GETPWNAM
2103
38.5k
  else if (path[0] == '~') {
2104
267
    std::string::size_type idx = path.find('/');
2105
267
    std::string user = path.substr(1, idx - 1);
2106
267
    passwd* pw = getpwnam(user.c_str());
2107
267
    if (pw) {
2108
18
      path.replace(0, idx, pw->pw_dir);
2109
18
    }
2110
267
  }
2111
38.7k
#endif
2112
  // remove trailing slash, but preserve the root slash and the slash
2113
  // after windows drive letter (c:/).
2114
38.7k
  size_t size = path.size();
2115
38.7k
  if (size > 1 && path.back() == '/') {
2116
271
    if (!(size == 3 && path[1] == ':') && path[size - 2] != '/') {
2117
265
      path.resize(size - 1);
2118
265
    }
2119
271
  }
2120
38.7k
}
2121
2122
#ifdef _WIN32
2123
std::wstring SystemTools::ConvertToWindowsExtendedPath(
2124
  std::string const& source)
2125
{
2126
  return Encoding::ToWindowsExtendedPath(source);
2127
}
2128
#endif
2129
2130
// change // to /, and escape any spaces in the path
2131
std::string SystemTools::ConvertToUnixOutputPath(std::string const& path)
2132
0
{
2133
0
  std::string ret = path;
2134
2135
  // remove // except at the beginning might be a cygwin drive
2136
0
  std::string::size_type pos = 1;
2137
0
  while ((pos = ret.find("//", pos)) != std::string::npos) {
2138
0
    ret.erase(pos, 1);
2139
0
  }
2140
  // escape spaces and () in the path
2141
0
  if (ret.find_first_of(' ') != std::string::npos) {
2142
0
    std::string result;
2143
0
    char lastch = 1;
2144
0
    for (char const* ch = ret.c_str(); *ch != '\0'; ++ch) {
2145
      // if it is already escaped then don't try to escape it again
2146
0
      if ((*ch == ' ') && lastch != '\\') {
2147
0
        result += '\\';
2148
0
      }
2149
0
      result += *ch;
2150
0
      lastch = *ch;
2151
0
    }
2152
0
    ret = result;
2153
0
  }
2154
0
  return ret;
2155
0
}
2156
2157
std::string SystemTools::ConvertToOutputPath(std::string const& path)
2158
0
{
2159
#if defined(_WIN32) && !defined(__CYGWIN__)
2160
  return SystemTools::ConvertToWindowsOutputPath(path);
2161
#else
2162
0
  return SystemTools::ConvertToUnixOutputPath(path);
2163
0
#endif
2164
0
}
2165
2166
// remove double slashes not at the start
2167
std::string SystemTools::ConvertToWindowsOutputPath(std::string const& path)
2168
0
{
2169
0
  std::string ret;
2170
  // make it big enough for all of path and double quotes
2171
0
  ret.reserve(path.size() + 3);
2172
  // put path into the string
2173
0
  ret = path;
2174
0
  std::string::size_type pos = 0;
2175
  // first convert all of the slashes
2176
0
  while ((pos = ret.find('/', pos)) != std::string::npos) {
2177
0
    ret[pos] = '\\';
2178
0
    pos++;
2179
0
  }
2180
  // check for really small paths
2181
0
  if (ret.size() < 2) {
2182
0
    return ret;
2183
0
  }
2184
  // now clean up a bit and remove double slashes
2185
  // Only if it is not the first position in the path which is a network
2186
  // path on windows
2187
0
  pos = 1; // start at position 1
2188
0
  if (ret[0] == '\"') {
2189
0
    pos = 2; // if the string is already quoted then start at 2
2190
0
    if (ret.size() < 3) {
2191
0
      return ret;
2192
0
    }
2193
0
  }
2194
0
  while ((pos = ret.find("\\\\", pos)) != std::string::npos) {
2195
0
    ret.erase(pos, 1);
2196
0
  }
2197
  // now double quote the path if it has spaces in it
2198
  // and is not already double quoted
2199
0
  if (ret.find(' ') != std::string::npos && ret[0] != '\"') {
2200
0
    ret.insert(static_cast<std::string::size_type>(0),
2201
0
               static_cast<std::string::size_type>(1), '\"');
2202
0
    ret.append(1, '\"');
2203
0
  }
2204
0
  return ret;
2205
0
}
2206
2207
/**
2208
 * Append the filename from the path source to the directory name dir.
2209
 */
2210
static std::string FileInDir(std::string const& source, std::string const& dir)
2211
0
{
2212
0
  std::string new_destination = dir;
2213
0
  SystemTools::ConvertToUnixSlashes(new_destination);
2214
0
  return new_destination + '/' + SystemTools::GetFilenameName(source);
2215
0
}
2216
2217
SystemTools::CopyStatus SystemTools::CopyFileIfDifferent(
2218
  std::string const& source, std::string const& destination)
2219
0
{
2220
  // special check for a destination that is a directory
2221
  // FilesDiffer does not handle file to directory compare
2222
0
  if (SystemTools::FileIsDirectory(destination)) {
2223
0
    std::string const new_destination = FileInDir(source, destination);
2224
0
    if (!SystemTools::ComparePath(new_destination, destination)) {
2225
0
      return SystemTools::CopyFileIfDifferent(source, new_destination);
2226
0
    }
2227
0
  } else {
2228
    // source and destination are files so do a copy if they
2229
    // are different
2230
0
    if (SystemTools::FilesDiffer(source, destination)) {
2231
0
      return SystemTools::CopyFileAlways(source, destination);
2232
0
    }
2233
0
  }
2234
  // at this point the files must be the same so return true
2235
0
  return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2236
0
}
2237
2238
SystemTools::CopyStatus SystemTools::CopyFileIfNewer(
2239
  std::string const& source, std::string const& destination)
2240
0
{
2241
  // special check for a destination that is a directory
2242
  // FileTimeCompare does not handle file to directory compare
2243
0
  if (SystemTools::FileIsDirectory(destination)) {
2244
0
    std::string const new_destination = FileInDir(source, destination);
2245
0
    if (!SystemTools::ComparePath(new_destination, destination)) {
2246
0
      return SystemTools::CopyFileIfNewer(source, new_destination);
2247
0
    }
2248
    // If source and destination are the same path, don't copy
2249
0
    return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2250
0
  }
2251
2252
  // source and destination are files so do a copy if source is newer
2253
  // Check if source file exists first
2254
0
  if (!SystemTools::FileExists(source)) {
2255
0
    return CopyStatus{ Status::POSIX(ENOENT), CopyStatus::SourcePath };
2256
0
  }
2257
  // If destination doesn't exist, always copy
2258
0
  if (!SystemTools::FileExists(destination)) {
2259
0
    return SystemTools::CopyFileAlways(source, destination);
2260
0
  }
2261
  // Check if source is newer than destination
2262
0
  int timeResult;
2263
0
  Status timeStatus =
2264
0
    SystemTools::FileTimeCompare(source, destination, &timeResult);
2265
0
  if (timeStatus.IsSuccess()) {
2266
0
    if (timeResult > 0) {
2267
      // Source is newer, copy it
2268
0
      return SystemTools::CopyFileAlways(source, destination);
2269
0
    } else {
2270
      // Source is not newer, no need to copy
2271
0
      return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2272
0
    }
2273
0
  } else {
2274
    // Time comparison failed, be conservative and copy to ensure updates are
2275
    // not missed
2276
0
    return SystemTools::CopyFileAlways(source, destination);
2277
0
  }
2278
0
}
2279
2280
0
#define KWSYS_ST_BUFFER 4096
2281
2282
bool SystemTools::FilesDiffer(std::string const& source,
2283
                              std::string const& destination)
2284
0
{
2285
2286
#if defined(_WIN32)
2287
  WIN32_FILE_ATTRIBUTE_DATA statSource;
2288
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(source).c_str(),
2289
                           GetFileExInfoStandard, &statSource) == 0) {
2290
    return true;
2291
  }
2292
2293
  WIN32_FILE_ATTRIBUTE_DATA statDestination;
2294
  if (GetFileAttributesExW(
2295
        Encoding::ToWindowsExtendedPath(destination).c_str(),
2296
        GetFileExInfoStandard, &statDestination) == 0) {
2297
    return true;
2298
  }
2299
2300
  if (statSource.nFileSizeHigh != statDestination.nFileSizeHigh ||
2301
      statSource.nFileSizeLow != statDestination.nFileSizeLow) {
2302
    return true;
2303
  }
2304
2305
  if (statSource.nFileSizeHigh == 0 && statSource.nFileSizeLow == 0) {
2306
    return false;
2307
  }
2308
  auto nleft =
2309
    ((__int64)statSource.nFileSizeHigh << 32) + statSource.nFileSizeLow;
2310
2311
#else
2312
2313
0
  struct stat statSource;
2314
0
  if (stat(source.c_str(), &statSource) != 0) {
2315
0
    return true;
2316
0
  }
2317
2318
0
  struct stat statDestination;
2319
0
  if (stat(destination.c_str(), &statDestination) != 0) {
2320
0
    return true;
2321
0
  }
2322
2323
0
  if (statSource.st_size != statDestination.st_size) {
2324
0
    return true;
2325
0
  }
2326
2327
0
  if (statSource.st_size == 0) {
2328
0
    return false;
2329
0
  }
2330
0
  off_t nleft = statSource.st_size;
2331
0
#endif
2332
2333
#if defined(_WIN32)
2334
  kwsys::ifstream finSource(source.c_str(), (std::ios::binary | std::ios::in));
2335
  kwsys::ifstream finDestination(destination.c_str(),
2336
                                 (std::ios::binary | std::ios::in));
2337
#else
2338
0
  kwsys::ifstream finSource(source.c_str());
2339
0
  kwsys::ifstream finDestination(destination.c_str());
2340
0
#endif
2341
0
  if (!finSource || !finDestination) {
2342
0
    return true;
2343
0
  }
2344
2345
  // Compare the files a block at a time.
2346
0
  char source_buf[KWSYS_ST_BUFFER];
2347
0
  char dest_buf[KWSYS_ST_BUFFER];
2348
0
  while (nleft > 0) {
2349
    // Read a block from each file.
2350
0
    std::streamsize nnext = (nleft > KWSYS_ST_BUFFER)
2351
0
      ? KWSYS_ST_BUFFER
2352
0
      : static_cast<std::streamsize>(nleft);
2353
0
    finSource.read(source_buf, nnext);
2354
0
    finDestination.read(dest_buf, nnext);
2355
2356
    // If either failed to read assume they are different.
2357
0
    if (static_cast<std::streamsize>(finSource.gcount()) != nnext ||
2358
0
        static_cast<std::streamsize>(finDestination.gcount()) != nnext) {
2359
0
      return true;
2360
0
    }
2361
2362
    // If this block differs the file differs.
2363
0
    if (memcmp(static_cast<void const*>(source_buf),
2364
0
               static_cast<void const*>(dest_buf),
2365
0
               static_cast<size_t>(nnext)) != 0) {
2366
0
      return true;
2367
0
    }
2368
2369
    // Update the byte count remaining.
2370
0
    nleft -= nnext;
2371
0
  }
2372
2373
  // No differences found.
2374
0
  return false;
2375
0
}
2376
2377
bool SystemTools::TextFilesDiffer(std::string const& path1,
2378
                                  std::string const& path2)
2379
0
{
2380
0
  kwsys::ifstream if1(path1.c_str());
2381
0
  kwsys::ifstream if2(path2.c_str());
2382
0
  if (!if1 || !if2) {
2383
0
    return true;
2384
0
  }
2385
2386
0
  for (;;) {
2387
0
    std::string line1, line2;
2388
0
    bool hasData1 = GetLineFromStream(if1, line1);
2389
0
    bool hasData2 = GetLineFromStream(if2, line2);
2390
0
    if (hasData1 != hasData2) {
2391
0
      return true;
2392
0
    }
2393
0
    if (!hasData1) {
2394
0
      break;
2395
0
    }
2396
0
    if (line1 != line2) {
2397
0
      return true;
2398
0
    }
2399
0
  }
2400
0
  return false;
2401
0
}
2402
2403
SystemTools::CopyStatus SystemTools::CopyFileContentBlockwise(
2404
  std::string const& source, std::string const& destination)
2405
0
{
2406
  // Open files
2407
0
  kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
2408
0
  if (!fin) {
2409
0
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::SourcePath };
2410
0
  }
2411
2412
  // try and remove the destination file so that read only destination files
2413
  // can be written to.
2414
  // If the remove fails continue so that files in read only directories
2415
  // that do not allow file removal can be modified.
2416
0
  SystemTools::RemoveFile(destination);
2417
2418
0
  kwsys::ofstream fout(destination.c_str(),
2419
0
                       std::ios::out | std::ios::trunc | std::ios::binary);
2420
0
  if (!fout) {
2421
0
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::DestPath };
2422
0
  }
2423
2424
  // This copy loop is very sensitive on certain platforms with
2425
  // slightly broken stream libraries (like HPUX).  Normally, it is
2426
  // incorrect to not check the error condition on the fin.read()
2427
  // before using the data, but the fin.gcount() will be zero if an
2428
  // error occurred.  Therefore, the loop should be safe everywhere.
2429
0
  while (fin) {
2430
0
    int const bufferSize = 4096;
2431
0
    char buffer[bufferSize];
2432
2433
0
    fin.read(buffer, bufferSize);
2434
0
    if (fin.gcount()) {
2435
0
      fout.write(buffer, fin.gcount());
2436
0
    } else {
2437
0
      break;
2438
0
    }
2439
0
  }
2440
2441
  // Make sure the operating system has finished writing the file
2442
  // before closing it.  This will ensure the file is finished before
2443
  // the check below.
2444
0
  fout.flush();
2445
2446
0
  fin.close();
2447
0
  fout.close();
2448
2449
0
  if (!fout) {
2450
0
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::DestPath };
2451
0
  }
2452
2453
0
  return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2454
0
}
2455
2456
/**
2457
 * Attempt to copy source file to the destination file using
2458
 * operating system mechanisms.
2459
 *
2460
 * If available, copy-on-write/clone will be used.
2461
 * On Linux, the FICLONE ioctl is used to create a clone of the source file.
2462
 * On macOS, the copyfile() call is used to make a clone of the file, and
2463
 * it will fall back to a regular copy if that's not possible.
2464
 *
2465
 * This function will follow symlinks (ie copy the file being
2466
 * pointed-to, not the symlink itself), and the resultant
2467
 * file will be owned by the uid of this process. It will overwrite
2468
 * an existing destination file.
2469
 *
2470
 * Examples of why this method may fail -
2471
 * - We're running on an OS for which this method is not implemented.
2472
 * - The underlying OS won't do a copy for us, and -
2473
 *   - The source and destination are on different file systems
2474
 *     thus a clone is not possible.
2475
 *   - The underlying filesystem does not support file cloning.
2476
 */
2477
SystemTools::CopyStatus SystemTools::CloneFileContent(
2478
  std::string const& source, std::string const& destination)
2479
0
{
2480
0
#if defined(__linux) && defined(FICLONE)
2481
0
  int in = open(source.c_str(), O_RDONLY);
2482
0
  if (in < 0) {
2483
0
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::SourcePath };
2484
0
  }
2485
2486
0
  SystemTools::RemoveFile(destination);
2487
2488
0
  int out =
2489
0
    open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
2490
0
  if (out < 0) {
2491
0
    CopyStatus status{ Status::POSIX_errno(), CopyStatus::DestPath };
2492
0
    close(in);
2493
0
    return status;
2494
0
  }
2495
2496
0
  CopyStatus status{ Status::Success(), CopyStatus::NoPath };
2497
0
  if (ioctl(out, FICLONE, in) < 0) {
2498
0
    status = CopyStatus{ Status::POSIX_errno(), CopyStatus::NoPath };
2499
0
  }
2500
0
  close(in);
2501
0
  close(out);
2502
2503
0
  return status;
2504
#elif defined(__APPLE__) &&                                                   \
2505
  defined(KWSYS_SYSTEMTOOLS_HAVE_MACOS_COPYFILE_CLONE)
2506
  // When running as root, copyfile() copies more metadata than we
2507
  // want, such as ownership.  Pretend it is not available.
2508
  if (getuid() == 0) {
2509
    return CopyStatus{ Status::POSIX(ENOSYS), CopyStatus::NoPath };
2510
  }
2511
2512
  // NOTE: we cannot use `clonefile` as the {a,c,m}time for the file needs to
2513
  // be updated by `copy_file_if_different` and `copy_file`.
2514
  //
2515
  // COPYFILE_CLONE forces COPYFILE_NOFOLLOW_SRC and that violates the
2516
  // invariant that this should result in a file. We used to manually specify
2517
  // COPYFILE_EXCL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA here, but
2518
  // what the copyfile() manpage does not tell you is that COPYFILE_DATA
2519
  // appears to disable cloning all together. Instead, explicitly reject
2520
  // copying symlinks here.
2521
  //
2522
  // COPYFILE_CLONE implies a few flags, including COPYFILE_EXCL.
2523
  // We add COPYFILE_UNLINK to be consistent with the Linux implementation
2524
  // above, as well as CopyFileContentBlockwise(). This will remove the
2525
  // destination file before cloning, allowing this call to complete
2526
  // if the destination file already exists.
2527
  //
2528
  if (SystemTools::FileIsSymlink(source)) {
2529
    return CopyStatus{ Status::POSIX(ENOSYS), CopyStatus::NoPath };
2530
  }
2531
2532
  if (copyfile(source.c_str(), destination.c_str(), nullptr,
2533
               COPYFILE_METADATA | COPYFILE_CLONE | COPYFILE_UNLINK) < 0) {
2534
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::NoPath };
2535
  }
2536
#  if KWSYS_CXX_HAS_UTIMENSAT
2537
  // utimensat is only available on newer Unixes and macOS 10.13+
2538
  if (utimensat(AT_FDCWD, destination.c_str(), nullptr, 0) < 0) {
2539
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::DestPath };
2540
  }
2541
#  else
2542
  // fall back to utimes
2543
  if (utimes(destination.c_str(), nullptr) < 0) {
2544
    return CopyStatus{ Status::POSIX_errno(), CopyStatus::DestPath };
2545
  }
2546
#  endif
2547
  return CopyStatus{ Status::Success(), CopyStatus::NoPath };
2548
#else
2549
  (void)source;
2550
  (void)destination;
2551
  return CopyStatus{ Status::POSIX(ENOSYS), CopyStatus::NoPath };
2552
#endif
2553
0
}
2554
2555
/**
2556
 * Copy a file named by "source" to the file named by "destination".
2557
 */
2558
SystemTools::CopyStatus SystemTools::CopyFileAlways(
2559
  std::string const& source, std::string const& destination)
2560
0
{
2561
0
  CopyStatus status;
2562
0
  mode_t perm = 0;
2563
0
  Status perms = SystemTools::GetPermissions(source, perm);
2564
0
  std::string real_destination = destination;
2565
2566
0
  if (SystemTools::FileIsDirectory(source)) {
2567
0
    status = CopyStatus{ SystemTools::MakeDirectory(destination),
2568
0
                         CopyStatus::DestPath };
2569
0
    if (!status.IsSuccess()) {
2570
0
      return status;
2571
0
    }
2572
0
  } else {
2573
    // If destination is a directory, try to create a file with the same
2574
    // name as the source in that directory.
2575
2576
0
    std::string destination_dir;
2577
0
    if (SystemTools::FileIsDirectory(destination)) {
2578
0
      destination_dir = real_destination;
2579
0
      SystemTools::ConvertToUnixSlashes(real_destination);
2580
0
      real_destination += '/';
2581
0
      std::string source_name = source;
2582
0
      real_destination += SystemTools::GetFilenameName(source_name);
2583
0
    } else {
2584
0
      destination_dir = SystemTools::GetFilenamePath(destination);
2585
0
    }
2586
    // If files are the same do not copy
2587
0
    if (SystemTools::SameFile(source, real_destination)) {
2588
0
      return status;
2589
0
    }
2590
2591
    // Create destination directory
2592
0
    if (!destination_dir.empty()) {
2593
0
      status = CopyStatus{ SystemTools::MakeDirectory(destination_dir),
2594
0
                           CopyStatus::DestPath };
2595
0
      if (!status.IsSuccess()) {
2596
0
        return status;
2597
0
      }
2598
0
    }
2599
2600
0
    status = SystemTools::CloneFileContent(source, real_destination);
2601
    // if cloning did not succeed, fall back to blockwise copy
2602
0
    if (!status.IsSuccess()) {
2603
0
      status = SystemTools::CopyFileContentBlockwise(source, real_destination);
2604
0
    }
2605
0
    if (!status.IsSuccess()) {
2606
0
      return status;
2607
0
    }
2608
0
  }
2609
0
  if (perms) {
2610
0
    status = CopyStatus{ SystemTools::SetPermissions(real_destination, perm),
2611
0
                         CopyStatus::DestPath };
2612
0
  }
2613
0
  return status;
2614
0
}
2615
2616
SystemTools::CopyStatus SystemTools::CopyAFile(std::string const& source,
2617
                                               std::string const& destination,
2618
                                               SystemTools::CopyWhen when)
2619
0
{
2620
0
  switch (when) {
2621
0
    case SystemTools::CopyWhen::Always:
2622
0
      return SystemTools::CopyFileAlways(source, destination);
2623
0
    case SystemTools::CopyWhen::OnlyIfDifferent:
2624
0
      return SystemTools::CopyFileIfDifferent(source, destination);
2625
0
    case SystemTools::CopyWhen::OnlyIfNewer:
2626
0
      return SystemTools::CopyFileIfNewer(source, destination);
2627
0
    default:
2628
0
      break;
2629
0
  }
2630
  // Should not reach here
2631
0
  return CopyStatus{ Status::POSIX_errno(), CopyStatus::NoPath };
2632
0
}
2633
2634
SystemTools::CopyStatus SystemTools::CopyAFile(std::string const& source,
2635
                                               std::string const& destination,
2636
                                               bool always)
2637
0
{
2638
0
  return SystemTools::CopyAFile(source, destination,
2639
0
                                always ? CopyWhen::Always
2640
0
                                       : CopyWhen::OnlyIfDifferent);
2641
0
}
2642
2643
/**
2644
 * Copy a directory content from "source" directory to the directory named by
2645
 * "destination".
2646
 */
2647
Status SystemTools::CopyADirectory(std::string const& source,
2648
                                   std::string const& destination,
2649
                                   SystemTools::CopyWhen when)
2650
0
{
2651
0
  Status status;
2652
0
  Directory dir;
2653
0
  status = dir.Load(source);
2654
0
  if (!status.IsSuccess()) {
2655
0
    return status;
2656
0
  }
2657
0
  status = SystemTools::MakeDirectory(destination);
2658
0
  if (!status.IsSuccess()) {
2659
0
    return status;
2660
0
  }
2661
2662
0
  for (size_t fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
2663
0
    std::string const& filename = dir.GetFileName(fileNum);
2664
0
    if (filename != "." && filename != "..") {
2665
0
      std::string fullPath = source;
2666
0
      fullPath += '/';
2667
0
      fullPath += filename;
2668
0
      if (SystemTools::FileIsDirectory(fullPath)) {
2669
0
        std::string fullDestPath = destination;
2670
0
        fullDestPath += '/';
2671
0
        fullDestPath += filename;
2672
0
        status = SystemTools::CopyADirectory(fullPath, fullDestPath, when);
2673
0
        if (!status.IsSuccess()) {
2674
0
          return status;
2675
0
        }
2676
0
      } else {
2677
0
        status = SystemTools::CopyAFile(fullPath, destination, when);
2678
0
        if (!status.IsSuccess()) {
2679
0
          return status;
2680
0
        }
2681
0
      }
2682
0
    }
2683
0
  }
2684
2685
0
  return status;
2686
0
}
2687
2688
Status SystemTools::CopyADirectory(std::string const& source,
2689
                                   std::string const& destination, bool always)
2690
0
{
2691
0
  return SystemTools::CopyADirectory(source, destination,
2692
0
                                     always ? CopyWhen::Always
2693
0
                                            : CopyWhen::OnlyIfDifferent);
2694
0
}
2695
2696
// return size of file; also returns zero if no file exists
2697
unsigned long SystemTools::FileLength(std::string const& filename)
2698
0
{
2699
0
  unsigned long length = 0;
2700
#ifdef _WIN32
2701
  WIN32_FILE_ATTRIBUTE_DATA fs;
2702
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2703
                           GetFileExInfoStandard, &fs) != 0) {
2704
    /* To support the full 64-bit file size, use fs.nFileSizeHigh
2705
     * and fs.nFileSizeLow to construct the 64 bit size
2706
2707
    length = ((__int64)fs.nFileSizeHigh << 32) + fs.nFileSizeLow;
2708
     */
2709
    length = static_cast<unsigned long>(fs.nFileSizeLow);
2710
  }
2711
#else
2712
0
  struct stat fs;
2713
0
  if (stat(filename.c_str(), &fs) == 0) {
2714
0
    length = static_cast<unsigned long>(fs.st_size);
2715
0
  }
2716
0
#endif
2717
0
  return length;
2718
0
}
2719
2720
int SystemTools::Strucmp(char const* l, char const* r)
2721
0
{
2722
0
  int lc;
2723
0
  int rc;
2724
0
  do {
2725
0
    lc = kwsysString_tolower(*l++);
2726
0
    rc = kwsysString_tolower(*r++);
2727
0
  } while (lc == rc && lc);
2728
0
  return lc - rc;
2729
0
}
2730
2731
// return file's modified time
2732
long int SystemTools::ModifiedTime(std::string const& filename)
2733
0
{
2734
0
  long int mt = 0;
2735
#ifdef _WIN32
2736
  WIN32_FILE_ATTRIBUTE_DATA fs;
2737
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2738
                           GetFileExInfoStandard, &fs) != 0) {
2739
    mt = windows_filetime_to_posix_time(fs.ftLastWriteTime);
2740
  }
2741
#else
2742
0
  struct stat fs;
2743
0
  if (stat(filename.c_str(), &fs) == 0) {
2744
0
    mt = static_cast<long int>(fs.st_mtime);
2745
0
  }
2746
0
#endif
2747
0
  return mt;
2748
0
}
2749
2750
// return file's creation time
2751
long int SystemTools::CreationTime(std::string const& filename)
2752
0
{
2753
0
  long int ct = 0;
2754
#ifdef _WIN32
2755
  WIN32_FILE_ATTRIBUTE_DATA fs;
2756
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2757
                           GetFileExInfoStandard, &fs) != 0) {
2758
    ct = windows_filetime_to_posix_time(fs.ftCreationTime);
2759
  }
2760
#else
2761
0
  struct stat fs;
2762
0
  if (stat(filename.c_str(), &fs) == 0) {
2763
0
    ct = fs.st_ctime >= 0 ? static_cast<long int>(fs.st_ctime) : 0;
2764
0
  }
2765
0
#endif
2766
0
  return ct;
2767
0
}
2768
2769
std::string SystemTools::GetLastSystemError()
2770
0
{
2771
0
  int e = errno;
2772
0
  return strerror(e);
2773
0
}
2774
2775
Status SystemTools::RemoveFile(std::string const& source)
2776
34.8k
{
2777
#ifdef _WIN32
2778
  std::wstring const& ws = Encoding::ToWindowsExtendedPath(source);
2779
  if (DeleteFileW(ws.c_str())) {
2780
    return Status::Success();
2781
  }
2782
  DWORD err = GetLastError();
2783
  if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
2784
    return Status::Success();
2785
  }
2786
  if (err != ERROR_ACCESS_DENIED) {
2787
    return Status::Windows(err);
2788
  }
2789
  /* The file may be read-only.  Try adding write permission.  */
2790
  mode_t mode;
2791
  if (!SystemTools::GetPermissions(source, mode) ||
2792
      !SystemTools::SetPermissions(source, S_IWRITE)) {
2793
    SetLastError(err);
2794
    return Status::Windows(err);
2795
  }
2796
2797
  const DWORD DIRECTORY_SOFT_LINK_ATTRS =
2798
    FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT;
2799
  DWORD attrs = GetFileAttributesW(ws.c_str());
2800
  if (attrs != INVALID_FILE_ATTRIBUTES &&
2801
      (attrs & DIRECTORY_SOFT_LINK_ATTRS) == DIRECTORY_SOFT_LINK_ATTRS &&
2802
      RemoveDirectoryW(ws.c_str())) {
2803
    return Status::Success();
2804
  }
2805
  if (DeleteFileW(ws.c_str()) || GetLastError() == ERROR_FILE_NOT_FOUND ||
2806
      GetLastError() == ERROR_PATH_NOT_FOUND) {
2807
    return Status::Success();
2808
  }
2809
  /* Try to restore the original permissions.  */
2810
  SystemTools::SetPermissions(source, mode);
2811
  SetLastError(err);
2812
  return Status::Windows(err);
2813
#else
2814
34.8k
  if (unlink(source.c_str()) != 0 && errno != ENOENT) {
2815
19.4k
    return Status::POSIX_errno();
2816
19.4k
  }
2817
15.3k
  return Status::Success();
2818
34.8k
#endif
2819
34.8k
}
2820
2821
Status SystemTools::RemoveADirectory(std::string const& source)
2822
63.0k
{
2823
  // Add read and write permission to the directory so we can read
2824
  // and modify its content to remove files and directories from it.
2825
63.0k
  mode_t mode = 0;
2826
63.0k
  if (SystemTools::GetPermissions(source, mode)) {
2827
#if defined(_WIN32) && !defined(__CYGWIN__)
2828
    mode |= S_IREAD | S_IWRITE;
2829
#else
2830
54.9k
    mode |= S_IRUSR | S_IWUSR;
2831
54.9k
#endif
2832
54.9k
    SystemTools::SetPermissions(source, mode);
2833
54.9k
  }
2834
2835
63.0k
  Status status;
2836
63.0k
  Directory dir;
2837
63.0k
  status = dir.Load(source);
2838
63.0k
  if (!status.IsSuccess()) {
2839
8.04k
    return status;
2840
8.04k
  }
2841
2842
54.9k
  size_t fileNum;
2843
157k
  for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
2844
141k
    std::string const& filename = dir.GetFileName(fileNum);
2845
141k
    if (filename != "." && filename != "..") {
2846
39.7k
      std::string fullPath = source;
2847
39.7k
      fullPath += '/';
2848
39.7k
      fullPath += filename;
2849
39.7k
      if (SystemTools::FileIsDirectory(fullPath) &&
2850
19.3k
          !SystemTools::FileIsSymlink(fullPath)) {
2851
19.3k
        status = SystemTools::RemoveADirectory(fullPath);
2852
19.3k
        if (!status.IsSuccess()) {
2853
19.0k
          return status;
2854
19.0k
        }
2855
20.3k
      } else {
2856
20.3k
        status = SystemTools::RemoveFile(fullPath);
2857
20.3k
        if (!status.IsSuccess()) {
2858
19.4k
          return status;
2859
19.4k
        }
2860
20.3k
      }
2861
39.7k
    }
2862
141k
  }
2863
2864
16.4k
  if (Rmdir(source) != 0) {
2865
0
    status = Status::POSIX_errno();
2866
0
  }
2867
16.4k
  return status;
2868
54.9k
}
2869
2870
/**
2871
 */
2872
size_t SystemTools::GetMaximumFilePathLength()
2873
0
{
2874
0
  return KWSYS_SYSTEMTOOLS_MAXPATH;
2875
0
}
2876
2877
/**
2878
 * Find the file the given name.  Searches the given path and then
2879
 * the system search path.  Returns the full path to the file if it is
2880
 * found.  Otherwise, the empty string is returned.
2881
 */
2882
std::string SystemToolsStatic::FindName(
2883
  std::string const& name, std::vector<std::string> const& userPaths,
2884
  bool no_system_path)
2885
0
{
2886
  // Add the system search path to our path first
2887
0
  std::vector<std::string> path;
2888
0
  if (!no_system_path) {
2889
0
    SystemTools::GetPath(path, "CMAKE_FILE_PATH");
2890
0
    SystemTools::GetPath(path);
2891
0
  }
2892
  // now add the additional paths
2893
0
  path.reserve(path.size() + userPaths.size());
2894
0
  path.insert(path.end(), userPaths.begin(), userPaths.end());
2895
  // now look for the file
2896
0
  for (std::string const& p : path) {
2897
0
    std::string tryPath = p;
2898
0
    if (tryPath.empty() || tryPath.back() != '/') {
2899
0
      tryPath += '/';
2900
0
    }
2901
0
    tryPath += name;
2902
0
    if (SystemTools::FileExists(tryPath)) {
2903
0
      return tryPath;
2904
0
    }
2905
0
  }
2906
  // Couldn't find the file.
2907
0
  return "";
2908
0
}
2909
2910
/**
2911
 * Find the file the given name.  Searches the given path and then
2912
 * the system search path.  Returns the full path to the file if it is
2913
 * found.  Otherwise, the empty string is returned.
2914
 */
2915
std::string SystemTools::FindFile(std::string const& name,
2916
                                  std::vector<std::string> const& userPaths,
2917
                                  bool no_system_path)
2918
0
{
2919
0
  std::string tryPath =
2920
0
    SystemToolsStatic::FindName(name, userPaths, no_system_path);
2921
0
  if (!tryPath.empty() && !SystemTools::FileIsDirectory(tryPath)) {
2922
0
    return SystemTools::CollapseFullPath(tryPath);
2923
0
  }
2924
  // Couldn't find the file.
2925
0
  return "";
2926
0
}
2927
2928
/**
2929
 * Find the directory the given name.  Searches the given path and then
2930
 * the system search path.  Returns the full path to the directory if it is
2931
 * found.  Otherwise, the empty string is returned.
2932
 */
2933
std::string SystemTools::FindDirectory(
2934
  std::string const& name, std::vector<std::string> const& userPaths,
2935
  bool no_system_path)
2936
0
{
2937
0
  std::string tryPath =
2938
0
    SystemToolsStatic::FindName(name, userPaths, no_system_path);
2939
0
  if (!tryPath.empty() && SystemTools::FileIsDirectory(tryPath)) {
2940
0
    return SystemTools::CollapseFullPath(tryPath);
2941
0
  }
2942
  // Couldn't find the file.
2943
0
  return "";
2944
0
}
2945
2946
/**
2947
 * Find the executable with the given name.  Searches the given path and then
2948
 * the system search path.  Returns the full path to the executable if it is
2949
 * found.  Otherwise, the empty string is returned.
2950
 */
2951
std::string SystemTools::FindProgram(std::string const& name,
2952
                                     std::vector<std::string> const& userPaths,
2953
                                     bool no_system_path)
2954
0
{
2955
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
2956
  std::vector<std::string> extensions;
2957
  // check to see if the name already has a .xxx at
2958
  // the end of it
2959
  // on windows try .com then .exe
2960
  if (name.size() <= 3 || name[name.size() - 4] != '.') {
2961
    extensions.emplace_back(".com");
2962
    extensions.emplace_back(".exe");
2963
2964
    // first try with extensions if the os supports them
2965
    for (std::string const& ext : extensions) {
2966
      std::string tryPath = name;
2967
      tryPath += ext;
2968
      if (SystemTools::FileIsExecutable(tryPath)) {
2969
        return SystemTools::CollapseFullPath(tryPath);
2970
      }
2971
    }
2972
  }
2973
#endif
2974
2975
  // now try just the name
2976
0
  if (SystemTools::FileIsExecutable(name)) {
2977
0
    return SystemTools::CollapseFullPath(name);
2978
0
  }
2979
  // now construct the path
2980
0
  std::vector<std::string> path;
2981
  // Add the system search path to our path.
2982
0
  if (!no_system_path) {
2983
0
    SystemTools::GetPath(path);
2984
0
  }
2985
  // now add the additional paths
2986
0
  path.reserve(path.size() + userPaths.size());
2987
0
  path.insert(path.end(), userPaths.begin(), userPaths.end());
2988
  // Add a trailing slash to all paths to aid the search process.
2989
0
  for (std::string& p : path) {
2990
0
    if (p.empty() || p.back() != '/') {
2991
0
      p += '/';
2992
0
    }
2993
0
  }
2994
  // Try each path
2995
0
  for (std::string& p : path) {
2996
#ifdef _WIN32
2997
    // Remove double quotes from the path on windows
2998
    SystemTools::ReplaceString(p, "\"", "");
2999
#endif
3000
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
3001
    // first try with extensions
3002
    for (std::string const& ext : extensions) {
3003
      std::string tryPath = p;
3004
      tryPath += name;
3005
      tryPath += ext;
3006
      if (SystemTools::FileIsExecutable(tryPath)) {
3007
        return SystemTools::CollapseFullPath(tryPath);
3008
      }
3009
    }
3010
#endif
3011
    // now try it without them
3012
0
    std::string tryPath = p;
3013
0
    tryPath += name;
3014
0
    if (SystemTools::FileIsExecutable(tryPath)) {
3015
0
      return SystemTools::CollapseFullPath(tryPath);
3016
0
    }
3017
0
  }
3018
  // Couldn't find the program.
3019
0
  return "";
3020
0
}
3021
3022
std::string SystemTools::GetRealPath(std::string const& path,
3023
                                     std::string* errorMessage)
3024
10
{
3025
10
  std::string ret;
3026
10
  Realpath(path, ret, errorMessage);
3027
10
  return ret;
3028
10
}
3029
3030
// Remove any trailing slash from the name except in a root component.
3031
static char const* RemoveTrailingSlashes(
3032
  std::string const& inName, char (&local_buffer)[KWSYS_SYSTEMTOOLS_MAXPATH],
3033
  std::string& string_buffer)
3034
52.7k
{
3035
52.7k
  size_t length = inName.size();
3036
52.7k
  char const* name = inName.c_str();
3037
3038
52.7k
  if (length == 0) {
3039
0
    return name;
3040
0
  }
3041
3042
52.7k
  size_t last = length - 1;
3043
52.7k
  if (last > 0 && (name[last] == '/' || name[last] == '\\') &&
3044
19.4k
      name[last - 1] != ':') {
3045
19.4k
    if (last < sizeof(local_buffer)) {
3046
19.4k
      memcpy(local_buffer, name, last);
3047
19.4k
      local_buffer[last] = '\0';
3048
19.4k
      name = local_buffer;
3049
19.4k
    } else {
3050
0
      string_buffer.append(name, last);
3051
0
      name = string_buffer.c_str();
3052
0
    }
3053
19.4k
  }
3054
3055
52.7k
  return name;
3056
52.7k
}
3057
3058
bool SystemTools::FileIsDirectory(std::string const& inName)
3059
52.7k
{
3060
52.7k
  if (inName.empty()) {
3061
0
    return false;
3062
0
  }
3063
3064
52.7k
  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3065
52.7k
  std::string string_buffer;
3066
52.7k
  auto const name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
3067
3068
// Now check the file node type.
3069
#if defined(_WIN32)
3070
  DWORD attr =
3071
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
3072
  return (attr != INVALID_FILE_ATTRIBUTES) &&
3073
    (attr & FILE_ATTRIBUTE_DIRECTORY);
3074
#else
3075
52.7k
  struct stat fs;
3076
3077
52.7k
  return (stat(name, &fs) == 0) && S_ISDIR(fs.st_mode);
3078
52.7k
#endif
3079
52.7k
}
3080
3081
bool SystemTools::FileIsExecutable(std::string const& inName)
3082
0
{
3083
0
  if (inName.empty()) {
3084
0
    return false;
3085
0
  }
3086
3087
#ifdef _WIN32
3088
  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3089
  std::string string_buffer;
3090
  auto const name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
3091
  auto const attr =
3092
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
3093
3094
  // On Windows any file that exists and is not a directory is considered
3095
  // readable and therefore also executable:
3096
  return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
3097
#else
3098
0
  return !FileIsDirectory(inName) && TestFileAccess(inName, TEST_FILE_EXECUTE);
3099
0
#endif
3100
0
}
3101
3102
#if defined(_WIN32)
3103
bool SystemTools::FileIsSymlinkWithAttr(std::wstring const& path,
3104
                                        unsigned long attr)
3105
{
3106
  if (attr != INVALID_FILE_ATTRIBUTES) {
3107
    if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
3108
      // FILE_ATTRIBUTE_REPARSE_POINT means:
3109
      // * a file or directory that has an associated reparse point, or
3110
      // * a file that is a symbolic link.
3111
      HANDLE hFile = CreateFileW(
3112
        path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
3113
        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3114
      if (hFile == INVALID_HANDLE_VALUE) {
3115
        return false;
3116
      }
3117
      byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3118
      DWORD bytesReturned = 0;
3119
      if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3120
                           MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3121
                           nullptr)) {
3122
        CloseHandle(hFile);
3123
        // Since FILE_ATTRIBUTE_REPARSE_POINT is set this file must be
3124
        // a symbolic link if it is not a reparse point.
3125
        return GetLastError() == ERROR_NOT_A_REPARSE_POINT;
3126
      }
3127
      CloseHandle(hFile);
3128
      ULONG reparseTag =
3129
        reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0])->ReparseTag;
3130
      return (reparseTag == IO_REPARSE_TAG_SYMLINK) ||
3131
        (reparseTag == IO_REPARSE_TAG_MOUNT_POINT);
3132
    }
3133
    return false;
3134
  }
3135
3136
  return false;
3137
}
3138
#endif
3139
3140
bool SystemTools::FileIsSymlink(std::string const& name)
3141
19.3k
{
3142
#if defined(_WIN32)
3143
  std::wstring path = Encoding::ToWindowsExtendedPath(name);
3144
  return FileIsSymlinkWithAttr(path, GetFileAttributesW(path.c_str()));
3145
#else
3146
19.3k
  struct stat fs;
3147
19.3k
  return (lstat(name.c_str(), &fs) == 0) && S_ISLNK(fs.st_mode);
3148
19.3k
#endif
3149
19.3k
}
3150
3151
bool SystemTools::FileIsFIFO(std::string const& name)
3152
0
{
3153
#if defined(_WIN32)
3154
  HANDLE hFile =
3155
    CreateFileW(Encoding::ToWide(name).c_str(), GENERIC_READ, FILE_SHARE_READ,
3156
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3157
  if (hFile == INVALID_HANDLE_VALUE) {
3158
    return false;
3159
  }
3160
  const DWORD type = GetFileType(hFile);
3161
  CloseHandle(hFile);
3162
  return type == FILE_TYPE_PIPE;
3163
#else
3164
0
  struct stat fs;
3165
0
  return (lstat(name.c_str(), &fs) == 0) && S_ISFIFO(fs.st_mode);
3166
0
#endif
3167
0
}
3168
3169
Status SystemTools::CreateSymlink(std::string const& origName,
3170
                                  std::string const& newName)
3171
0
{
3172
#if defined(_WIN32) && !defined(__CYGWIN__)
3173
  DWORD flags;
3174
  if (FileIsDirectory(origName)) {
3175
    flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
3176
  } else {
3177
    flags = 0;
3178
  }
3179
3180
  std::wstring origPath = Encoding::ToWindowsExtendedPath(origName);
3181
  std::wstring newPath = Encoding::ToWindowsExtendedPath(newName);
3182
3183
  Status status;
3184
  if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(),
3185
                           flags |
3186
                             SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
3187
    status = Status::Windows_GetLastError();
3188
  }
3189
  // Older Windows versions do not understand
3190
  // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
3191
  if (status.GetWindows() == ERROR_INVALID_PARAMETER) {
3192
    status = Status::Success();
3193
    if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(), flags)) {
3194
      status = Status::Windows_GetLastError();
3195
    }
3196
  }
3197
3198
  return status;
3199
#else
3200
0
  if (symlink(origName.c_str(), newName.c_str()) < 0) {
3201
0
    return Status::POSIX_errno();
3202
0
  }
3203
0
  return Status::Success();
3204
0
#endif
3205
0
}
3206
3207
Status SystemTools::ReadSymlink(std::string const& newName,
3208
                                std::string& origName)
3209
0
{
3210
#if defined(_WIN32) && !defined(__CYGWIN__)
3211
  std::wstring newPath = Encoding::ToWindowsExtendedPath(newName);
3212
  // FILE_ATTRIBUTE_REPARSE_POINT means:
3213
  // * a file or directory that has an associated reparse point, or
3214
  // * a file that is a symbolic link.
3215
  HANDLE hFile = CreateFileW(
3216
    newPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
3217
    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3218
  if (hFile == INVALID_HANDLE_VALUE) {
3219
    return Status::Windows_GetLastError();
3220
  }
3221
  byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3222
  DWORD bytesReturned = 0;
3223
  Status status;
3224
  if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3225
                       MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3226
                       nullptr)) {
3227
    status = Status::Windows_GetLastError();
3228
  }
3229
  CloseHandle(hFile);
3230
  if (!status.IsSuccess()) {
3231
    return status;
3232
  }
3233
  PREPARSE_DATA_BUFFER data =
3234
    reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
3235
  USHORT substituteNameLength;
3236
  PCWSTR substituteNameData;
3237
  if (data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
3238
    substituteNameLength =
3239
      data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
3240
    substituteNameData = data->SymbolicLinkReparseBuffer.PathBuffer +
3241
      data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
3242
  } else if (data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
3243
    substituteNameLength =
3244
      data->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
3245
    substituteNameData = data->MountPointReparseBuffer.PathBuffer +
3246
      data->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
3247
  } else if (data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
3248
    // The reparse buffer is a list of 0-terminated non-empty strings,
3249
    // terminated by an empty string (0-0).  We need the third string.
3250
    size_t destLen;
3251
    substituteNameData = GetAppExecLink(data, destLen);
3252
    if (!substituteNameData || destLen == 0) {
3253
      return Status::Windows(ERROR_SYMLINK_NOT_SUPPORTED);
3254
    }
3255
    substituteNameLength = static_cast<USHORT>(destLen);
3256
  } else {
3257
    return Status::Windows(ERROR_REPARSE_TAG_MISMATCH);
3258
  }
3259
  std::wstring substituteName(substituteNameData, substituteNameLength);
3260
  origName = Encoding::ToNarrow(substituteName);
3261
  // Symbolic links to absolute paths may use a NT Object Path prefix.
3262
  // If the path begins with "\??\UNC\", replace it with "\\".
3263
  // Otherwise, if the path begins with "\??\", remove the prefix.
3264
  if (origName.compare(0, 8, "\\??\\UNC\\") == 0) {
3265
    origName.erase(1, 6);
3266
  } else if (origName.compare(0, 4, "\\??\\") == 0) {
3267
    origName.erase(0, 4);
3268
  }
3269
#else
3270
0
  char buf[KWSYS_SYSTEMTOOLS_MAXPATH + 1];
3271
0
  int count = static_cast<int>(
3272
0
    readlink(newName.c_str(), buf, KWSYS_SYSTEMTOOLS_MAXPATH));
3273
0
  if (count < 0) {
3274
0
    return Status::POSIX_errno();
3275
0
  }
3276
  // Add null-terminator.
3277
0
  buf[count] = 0;
3278
0
  origName = buf;
3279
0
#endif
3280
0
  return Status::Success();
3281
0
}
3282
3283
Status SystemTools::ChangeDirectory(std::string const& dir)
3284
58.1k
{
3285
58.1k
  if (Chdir(dir) < 0) {
3286
0
    return Status::POSIX_errno();
3287
0
  }
3288
58.1k
  return Status::Success();
3289
58.1k
}
3290
3291
std::string SystemTools::GetCurrentWorkingDirectory()
3292
20.9k
{
3293
20.9k
  char buf[2048];
3294
20.9k
  char const* cwd = Getcwd(buf, 2048);
3295
20.9k
  std::string path;
3296
20.9k
  if (cwd) {
3297
20.9k
    path = cwd;
3298
20.9k
    SystemTools::ConvertToUnixSlashes(path);
3299
20.9k
  }
3300
20.9k
  return path;
3301
20.9k
}
3302
3303
std::string SystemTools::GetProgramPath(std::string const& in_name)
3304
0
{
3305
0
  std::string dir, file;
3306
0
  SystemTools::SplitProgramPath(in_name, dir, file);
3307
0
  return dir;
3308
0
}
3309
3310
bool SystemTools::SplitProgramPath(std::string const& in_name,
3311
                                   std::string& dir, std::string& file, bool)
3312
0
{
3313
0
  dir = in_name;
3314
0
  file.clear();
3315
0
  SystemTools::ConvertToUnixSlashes(dir);
3316
3317
0
  if (!SystemTools::FileIsDirectory(dir)) {
3318
0
    std::string::size_type slashPos = dir.rfind('/');
3319
0
    if (slashPos != std::string::npos) {
3320
0
      file = dir.substr(slashPos + 1);
3321
0
      dir.resize(slashPos);
3322
0
    } else {
3323
0
      file = dir;
3324
0
      dir.clear();
3325
0
    }
3326
0
  }
3327
0
  if (!(dir.empty()) && !SystemTools::FileIsDirectory(dir)) {
3328
0
    std::string oldDir = in_name;
3329
0
    SystemTools::ConvertToUnixSlashes(oldDir);
3330
0
    dir = in_name;
3331
0
    return false;
3332
0
  }
3333
0
  return true;
3334
0
}
3335
3336
static void SystemToolsAppendComponents(
3337
  std::vector<std::string>& out_components,
3338
  std::vector<std::string>::iterator first,
3339
  std::vector<std::string>::iterator last)
3340
55.6k
{
3341
55.6k
  static std::string const up = "..";
3342
55.6k
  static std::string const cur = ".";
3343
1.55M
  for (std::vector<std::string>::const_iterator i = first; i != last; ++i) {
3344
1.50M
    if (*i == up) {
3345
      // Remove the previous component if possible.  Ignore ../ components
3346
      // that try to go above the root.  Keep ../ components if they are
3347
      // at the beginning of a relative path (base path is relative).
3348
29.9k
      if (out_components.size() > 1 && out_components.back() != up) {
3349
28.8k
        out_components.resize(out_components.size() - 1);
3350
28.8k
      } else if (!out_components.empty() && out_components[0].empty()) {
3351
0
        out_components.emplace_back(std::move(*i));
3352
0
      }
3353
1.47M
    } else if (!i->empty() && *i != cur) {
3354
688k
      out_components.emplace_back(std::move(*i));
3355
688k
    }
3356
1.50M
  }
3357
55.6k
}
3358
3359
namespace {
3360
3361
std::string CollapseFullPathImpl(std::string const& in_path,
3362
                                 std::string const* in_base)
3363
49.3k
{
3364
  // Collect the output path components.
3365
49.3k
  std::vector<std::string> out_components;
3366
3367
  // Split the input path components.
3368
49.3k
  std::vector<std::string> path_components;
3369
49.3k
  SystemTools::SplitPath(in_path, path_components);
3370
49.3k
  out_components.reserve(path_components.size());
3371
3372
  // If the input path is relative, start with a base path.
3373
49.3k
  if (path_components[0].empty()) {
3374
6.33k
    std::vector<std::string> base_components;
3375
3376
6.33k
    if (in_base) {
3377
      // Use the given base path.
3378
0
      SystemTools::SplitPath(*in_base, base_components);
3379
6.33k
    } else {
3380
      // Use the current working directory as a base path.
3381
6.33k
      std::string cwd = SystemTools::GetCurrentWorkingDirectory();
3382
6.33k
      SystemTools::SplitPath(cwd, base_components);
3383
6.33k
    }
3384
3385
    // Append base path components to the output path.
3386
6.33k
    out_components.push_back(base_components[0]);
3387
6.33k
    SystemToolsAppendComponents(out_components, base_components.begin() + 1,
3388
6.33k
                                base_components.end());
3389
6.33k
  }
3390
3391
  // Append input path components to the output path.
3392
49.3k
  SystemToolsAppendComponents(out_components, path_components.begin(),
3393
49.3k
                              path_components.end());
3394
3395
  // Transform the path back to a string.
3396
49.3k
  std::string newPath = SystemTools::JoinPath(out_components);
3397
3398
#ifdef _WIN32
3399
  SystemTools::ConvertToUnixSlashes(newPath);
3400
#endif
3401
  // Return the reconstructed path.
3402
49.3k
  return newPath;
3403
49.3k
}
3404
}
3405
3406
std::string SystemTools::CollapseFullPath(std::string const& in_path)
3407
49.3k
{
3408
49.3k
  return CollapseFullPathImpl(in_path, nullptr);
3409
49.3k
}
3410
3411
std::string SystemTools::CollapseFullPath(std::string const& in_path,
3412
                                          char const* in_base)
3413
0
{
3414
0
  if (!in_base) {
3415
0
    return CollapseFullPathImpl(in_path, nullptr);
3416
0
  }
3417
0
  std::string tmp_base = in_base;
3418
0
  return CollapseFullPathImpl(in_path, &tmp_base);
3419
0
}
3420
3421
std::string SystemTools::CollapseFullPath(std::string const& in_path,
3422
                                          std::string const& in_base)
3423
1
{
3424
1
  return CollapseFullPathImpl(in_path, &in_base);
3425
1
}
3426
3427
// compute the relative path from here to there
3428
std::string SystemTools::RelativePath(std::string const& local,
3429
                                      std::string const& remote)
3430
0
{
3431
0
  if (!SystemTools::FileIsFullPath(local)) {
3432
0
    return "";
3433
0
  }
3434
0
  if (!SystemTools::FileIsFullPath(remote)) {
3435
0
    return "";
3436
0
  }
3437
3438
0
  std::string l = SystemTools::CollapseFullPath(local);
3439
0
  std::string r = SystemTools::CollapseFullPath(remote);
3440
3441
  // split up both paths into arrays of strings using / as a separator
3442
0
  std::vector<std::string> localSplit = SystemTools::SplitString(l, '/', true);
3443
0
  std::vector<std::string> remoteSplit =
3444
0
    SystemTools::SplitString(r, '/', true);
3445
0
  std::vector<std::string>
3446
0
    commonPath; // store shared parts of path in this array
3447
0
  std::vector<std::string> finalPath; // store the final relative path here
3448
  // count up how many matching directory names there are from the start
3449
0
  unsigned int sameCount = 0;
3450
0
  while (((sameCount <= (localSplit.size() - 1)) &&
3451
0
          (sameCount <= (remoteSplit.size() - 1))) &&
3452
// for Windows and Apple do a case insensitive string compare
3453
#if defined(_WIN32) || defined(__APPLE__)
3454
         SystemTools::Strucmp(localSplit[sameCount].c_str(),
3455
                              remoteSplit[sameCount].c_str()) == 0
3456
#else
3457
0
         localSplit[sameCount] == remoteSplit[sameCount]
3458
0
#endif
3459
0
  ) {
3460
    // put the common parts of the path into the commonPath array
3461
0
    commonPath.push_back(localSplit[sameCount]);
3462
    // erase the common parts of the path from the original path arrays
3463
0
    localSplit[sameCount] = "";
3464
0
    remoteSplit[sameCount] = "";
3465
0
    sameCount++;
3466
0
  }
3467
3468
  // If there is nothing in common at all then just return the full
3469
  // path.  This is the case only on windows when the paths have
3470
  // different drive letters.  On unix two full paths always at least
3471
  // have the root "/" in common so we will return a relative path
3472
  // that passes through the root directory.
3473
0
  if (sameCount == 0) {
3474
0
    return remote;
3475
0
  }
3476
3477
  // for each entry that is not common in the local path
3478
  // add a ../ to the finalpath array, this gets us out of the local
3479
  // path into the remote dir
3480
0
  for (std::string const& lp : localSplit) {
3481
0
    if (!lp.empty()) {
3482
0
      finalPath.emplace_back("../");
3483
0
    }
3484
0
  }
3485
  // for each entry that is not common in the remote path add it
3486
  // to the final path.
3487
0
  for (std::string const& rp : remoteSplit) {
3488
0
    if (!rp.empty()) {
3489
0
      finalPath.push_back(rp);
3490
0
    }
3491
0
  }
3492
0
  std::string relativePath; // result string
3493
  // now turn the array of directories into a unix path by puttint /
3494
  // between each entry that does not already have one
3495
0
  for (std::string const& fp : finalPath) {
3496
0
    if (!relativePath.empty() && relativePath.back() != '/') {
3497
0
      relativePath += '/';
3498
0
    }
3499
0
    relativePath += fp;
3500
0
  }
3501
0
  return relativePath;
3502
0
}
3503
3504
std::string SystemTools::GetActualCaseForPath(std::string const& p)
3505
0
{
3506
#ifdef _WIN32
3507
  return SystemToolsStatic::GetCasePathName(p);
3508
#else
3509
0
  return p;
3510
0
#endif
3511
0
}
3512
3513
char const* SystemTools::SplitPathRootComponent(std::string const& p,
3514
                                                std::string* root)
3515
98.0k
{
3516
  // Identify the root component.
3517
98.0k
  char const* c = p.c_str();
3518
98.0k
  if ((c[0] == '/' && c[1] == '/') || (c[0] == '\\' && c[1] == '\\')) {
3519
    // Network path.
3520
735
    if (root) {
3521
735
      *root = "//";
3522
735
    }
3523
735
    c += 2;
3524
97.2k
  } else if (c[0] == '/' || c[0] == '\\') {
3525
    // Unix path (or Windows path w/out drive letter).
3526
48.5k
    if (root) {
3527
48.5k
      *root = "/";
3528
48.5k
    }
3529
48.5k
    c += 1;
3530
48.7k
  } else if (c[0] && c[1] == ':' && (c[2] == '/' || c[2] == '\\')) {
3531
    // Windows path.
3532
563
    if (root) {
3533
563
      (*root) = "_:/";
3534
563
      (*root)[0] = c[0];
3535
563
    }
3536
563
    c += 3;
3537
48.1k
  } else if (c[0] && c[1] == ':') {
3538
    // Path relative to a windows drive working directory.
3539
34.8k
    if (root) {
3540
34.8k
      (*root) = "_:";
3541
34.8k
      (*root)[0] = c[0];
3542
34.8k
    }
3543
34.8k
    c += 2;
3544
34.8k
  } else if (c[0] == '~') {
3545
    // Home directory.  The returned root should always have a
3546
    // trailing slash so that appending components as
3547
    // c[0]c[1]/c[2]/... works.  The remaining path returned should
3548
    // skip the first slash if it exists:
3549
    //
3550
    //   "~"    : root = "~/" , return ""
3551
    //   "~/    : root = "~/" , return ""
3552
    //   "~/x   : root = "~/" , return "x"
3553
    //   "~u"   : root = "~u/", return ""
3554
    //   "~u/"  : root = "~u/", return ""
3555
    //   "~u/x" : root = "~u/", return "x"
3556
6.92k
    size_t n = 1;
3557
769k
    while (c[n] && c[n] != '/') {
3558
762k
      ++n;
3559
762k
    }
3560
6.92k
    if (root) {
3561
6.92k
      root->assign(c, n);
3562
6.92k
      *root += '/';
3563
6.92k
    }
3564
6.92k
    if (c[n] == '/') {
3565
1.12k
      ++n;
3566
1.12k
    }
3567
6.92k
    c += n;
3568
6.92k
  } else {
3569
    // Relative path.
3570
6.33k
    if (root) {
3571
6.33k
      *root = "";
3572
6.33k
    }
3573
6.33k
  }
3574
3575
  // Return the remaining path.
3576
98.0k
  return c;
3577
98.0k
}
3578
3579
void SystemTools::SplitPath(std::string const& p,
3580
                            std::vector<std::string>& components,
3581
                            bool expand_home_dir)
3582
98.0k
{
3583
98.0k
  char const* c;
3584
98.0k
  components.clear();
3585
3586
  // Identify the root component.
3587
98.0k
  {
3588
98.0k
    std::string root;
3589
98.0k
    c = SystemTools::SplitPathRootComponent(p, &root);
3590
3591
    // Expand home directory references if requested.
3592
98.0k
    if (expand_home_dir && !root.empty() && root[0] == '~') {
3593
42.3k
      std::string homedir;
3594
42.3k
      root.resize(root.size() - 1);
3595
42.3k
      if (root.size() == 1) {
3596
#if defined(_WIN32) && !defined(__CYGWIN__)
3597
        if (!SystemTools::GetEnv("USERPROFILE", homedir))
3598
#endif
3599
36.4k
          SystemTools::GetEnv("HOME", homedir);
3600
36.4k
      }
3601
5.84k
#ifdef HAVE_GETPWNAM
3602
5.84k
      else if (passwd* pw = getpwnam(root.c_str() + 1)) {
3603
366
        if (pw->pw_dir) {
3604
366
          homedir = pw->pw_dir;
3605
366
        }
3606
366
      }
3607
42.3k
#endif
3608
42.3k
      if (!homedir.empty() &&
3609
36.8k
          (homedir.back() == '/' || homedir.back() == '\\')) {
3610
0
        homedir.resize(homedir.size() - 1);
3611
0
      }
3612
42.3k
      SystemTools::SplitPath(homedir, components);
3613
55.6k
    } else {
3614
55.6k
      components.push_back(root);
3615
55.6k
    }
3616
98.0k
  }
3617
3618
  // Parse the remaining components.
3619
98.0k
  char const* first = c;
3620
98.0k
  char const* last = first;
3621
8.93M
  for (; *last; ++last) {
3622
8.83M
    if (*last == '/' || *last == '\\') {
3623
      // End of a component.  Save it.
3624
1.40M
      components.emplace_back(first, last);
3625
1.40M
      first = last + 1;
3626
1.40M
    }
3627
8.83M
  }
3628
3629
  // Save the last component unless there were no components.
3630
98.0k
  if (last != c) {
3631
50.1k
    components.emplace_back(first, last);
3632
50.1k
  }
3633
98.0k
}
3634
3635
std::string SystemTools::JoinPath(std::vector<std::string> const& components)
3636
49.3k
{
3637
49.3k
  return SystemTools::JoinPath(components.begin(), components.end());
3638
49.3k
}
3639
3640
std::string SystemTools::JoinPath(
3641
  std::vector<std::string>::const_iterator first,
3642
  std::vector<std::string>::const_iterator last)
3643
49.3k
{
3644
  // Construct result in a single string.
3645
49.3k
  std::string result;
3646
49.3k
  size_t len = 0;
3647
715k
  for (auto i = first; i != last; ++i) {
3648
665k
    len += 1 + i->size();
3649
665k
  }
3650
49.3k
  result.reserve(len);
3651
3652
  // The first two components do not add a slash.
3653
49.3k
  if (first != last) {
3654
49.3k
    result.append(*first++);
3655
49.3k
  }
3656
49.3k
  if (first != last) {
3657
47.8k
    result.append(*first++);
3658
47.8k
  }
3659
3660
  // All remaining components are always separated with a slash.
3661
618k
  while (first != last) {
3662
568k
    result.push_back('/');
3663
568k
    result.append((*first++));
3664
568k
  }
3665
3666
  // Return the concatenated result.
3667
49.3k
  return result;
3668
49.3k
}
3669
3670
bool SystemTools::ComparePath(std::string const& c1, std::string const& c2)
3671
0
{
3672
#if defined(_WIN32) || defined(__APPLE__)
3673
#  ifdef _MSC_VER
3674
  return _stricmp(c1.c_str(), c2.c_str()) == 0;
3675
#  elif defined(__APPLE__) || defined(__GNUC__)
3676
  return strcasecmp(c1.c_str(), c2.c_str()) == 0;
3677
#  else
3678
  return SystemTools::Strucmp(c1.c_str(), c2.c_str()) == 0;
3679
#  endif
3680
#else
3681
0
  return c1 == c2;
3682
0
#endif
3683
0
}
3684
3685
bool SystemTools::Split(std::string const& str,
3686
                        std::vector<std::string>& lines, char separator)
3687
0
{
3688
0
  std::string data(str);
3689
0
  std::string::size_type lpos = 0;
3690
0
  while (lpos < data.length()) {
3691
0
    std::string::size_type rpos = data.find_first_of(separator, lpos);
3692
0
    if (rpos == std::string::npos) {
3693
      // String ends at end of string without a separator.
3694
0
      lines.push_back(data.substr(lpos));
3695
0
      return false;
3696
0
    } else {
3697
      // String ends in a separator, remove the character.
3698
0
      lines.push_back(data.substr(lpos, rpos - lpos));
3699
0
    }
3700
0
    lpos = rpos + 1;
3701
0
  }
3702
0
  return true;
3703
0
}
3704
3705
bool SystemTools::Split(std::string const& str,
3706
                        std::vector<std::string>& lines)
3707
0
{
3708
0
  std::string data(str);
3709
0
  std::string::size_type lpos = 0;
3710
0
  while (lpos < data.length()) {
3711
0
    std::string::size_type rpos = data.find_first_of('\n', lpos);
3712
0
    if (rpos == std::string::npos) {
3713
      // Line ends at end of string without a newline.
3714
0
      lines.push_back(data.substr(lpos));
3715
0
      return false;
3716
0
    }
3717
0
    if ((rpos > lpos) && (data[rpos - 1] == '\r')) {
3718
      // Line ends in a "\r\n" pair, remove both characters.
3719
0
      lines.push_back(data.substr(lpos, (rpos - 1) - lpos));
3720
0
    } else {
3721
      // Line ends in a "\n", remove the character.
3722
0
      lines.push_back(data.substr(lpos, rpos - lpos));
3723
0
    }
3724
0
    lpos = rpos + 1;
3725
0
  }
3726
0
  return true;
3727
0
}
3728
3729
std::string SystemTools::Join(std::vector<std::string> const& list,
3730
                              std::string const& separator)
3731
0
{
3732
0
  std::string result;
3733
0
  if (list.empty()) {
3734
0
    return result;
3735
0
  }
3736
3737
0
  size_t total_size = separator.size() * (list.size() - 1);
3738
0
  for (std::string const& string : list) {
3739
0
    total_size += string.size();
3740
0
  }
3741
3742
0
  result.reserve(total_size);
3743
0
  bool needs_separator = false;
3744
0
  for (std::string const& string : list) {
3745
0
    if (needs_separator) {
3746
0
      result += separator;
3747
0
    }
3748
0
    result += string;
3749
0
    needs_separator = true;
3750
0
  }
3751
3752
0
  return result;
3753
0
}
3754
3755
/**
3756
 * Return path of a full filename (no trailing slashes).
3757
 * Warning: returned path is converted to Unix slashes format.
3758
 */
3759
std::string SystemTools::GetFilenamePath(std::string const& filename)
3760
1.61k
{
3761
1.61k
  std::string fn = filename;
3762
1.61k
  SystemTools::ConvertToUnixSlashes(fn);
3763
3764
1.61k
  std::string::size_type slash_pos = fn.rfind('/');
3765
1.61k
  if (slash_pos == 0) {
3766
164
    return "/";
3767
164
  }
3768
1.44k
  if (slash_pos == 2 && fn[1] == ':') {
3769
    // keep the / after a drive letter
3770
4
    fn.resize(3);
3771
4
    return fn;
3772
4
  }
3773
1.44k
  if (slash_pos == std::string::npos) {
3774
696
    return "";
3775
696
  }
3776
749
  fn.resize(slash_pos);
3777
749
  return fn;
3778
1.44k
}
3779
3780
/**
3781
 * Return file name of a full filename (i.e. file name without path).
3782
 */
3783
std::string SystemTools::GetFilenameName(std::string const& filename)
3784
6.44k
{
3785
#if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
3786
  char const* separators = "/\\";
3787
#else
3788
6.44k
  char separators = '/';
3789
6.44k
#endif
3790
6.44k
  std::string::size_type slash_pos = filename.find_last_of(separators);
3791
6.44k
  if (slash_pos != std::string::npos) {
3792
2.23k
    return filename.substr(slash_pos + 1);
3793
4.20k
  } else {
3794
4.20k
    return filename;
3795
4.20k
  }
3796
6.44k
}
3797
3798
/**
3799
 * Return file extension of a full filename (dot included).
3800
 * Warning: this is the longest extension (for example: .tar.gz)
3801
 */
3802
std::string SystemTools::GetFilenameExtension(std::string const& filename)
3803
1.61k
{
3804
1.61k
  std::string name = SystemTools::GetFilenameName(filename);
3805
1.61k
  std::string::size_type dot_pos = name.find('.');
3806
1.61k
  if (dot_pos != std::string::npos) {
3807
398
    name.erase(0, dot_pos);
3808
398
    return name;
3809
1.21k
  } else {
3810
1.21k
    return "";
3811
1.21k
  }
3812
1.61k
}
3813
3814
/**
3815
 * Return file extension of a full filename (dot included).
3816
 * Warning: this is the shortest extension (for example: .gz of .tar.gz)
3817
 */
3818
std::string SystemTools::GetFilenameLastExtension(std::string const& filename)
3819
1.61k
{
3820
1.61k
  std::string name = SystemTools::GetFilenameName(filename);
3821
1.61k
  std::string::size_type dot_pos = name.rfind('.');
3822
1.61k
  if (dot_pos != std::string::npos) {
3823
398
    name.erase(0, dot_pos);
3824
398
    return name;
3825
1.21k
  } else {
3826
1.21k
    return "";
3827
1.21k
  }
3828
1.61k
}
3829
3830
/**
3831
 * Return file name without extension of a full filename (i.e. without path).
3832
 * Warning: it considers the longest extension (for example: .tar.gz)
3833
 */
3834
std::string SystemTools::GetFilenameWithoutExtension(
3835
  std::string const& filename)
3836
1.61k
{
3837
1.61k
  std::string name = SystemTools::GetFilenameName(filename);
3838
1.61k
  std::string::size_type dot_pos = name.find('.');
3839
1.61k
  if (dot_pos != std::string::npos) {
3840
398
    name.resize(dot_pos);
3841
398
  }
3842
1.61k
  return name;
3843
1.61k
}
3844
3845
/**
3846
 * Return file name without extension of a full filename (i.e. without path).
3847
 * Warning: it considers the last extension (for example: removes .gz
3848
 * from .tar.gz)
3849
 */
3850
std::string SystemTools::GetFilenameWithoutLastExtension(
3851
  std::string const& filename)
3852
0
{
3853
0
  std::string name = SystemTools::GetFilenameName(filename);
3854
0
  std::string::size_type dot_pos = name.rfind('.');
3855
0
  if (dot_pos != std::string::npos) {
3856
0
    name.resize(dot_pos);
3857
0
  }
3858
0
  return name;
3859
0
}
3860
3861
bool SystemTools::FileHasSignature(char const* filename, char const* signature,
3862
                                   long offset)
3863
0
{
3864
0
  if (!filename || !signature) {
3865
0
    return false;
3866
0
  }
3867
3868
0
  FILE* fp = Fopen(filename, "rb");
3869
0
  if (!fp) {
3870
0
    return false;
3871
0
  }
3872
3873
0
  fseek(fp, offset, SEEK_SET);
3874
3875
0
  bool res = false;
3876
0
  size_t signature_len = strlen(signature);
3877
0
  char* buffer = new char[signature_len];
3878
3879
0
  if (fread(buffer, 1, signature_len, fp) == signature_len) {
3880
0
    res = (!strncmp(buffer, signature, signature_len) ? true : false);
3881
0
  }
3882
3883
0
  delete[] buffer;
3884
3885
0
  fclose(fp);
3886
0
  return res;
3887
0
}
3888
3889
SystemTools::FileTypeEnum SystemTools::DetectFileType(char const* filename,
3890
                                                      unsigned long length,
3891
                                                      double percent_bin)
3892
0
{
3893
0
  if (!filename || percent_bin < 0) {
3894
0
    return SystemTools::FileTypeUnknown;
3895
0
  }
3896
3897
0
  if (SystemTools::FileIsDirectory(filename)) {
3898
0
    return SystemTools::FileTypeUnknown;
3899
0
  }
3900
3901
0
  FILE* fp = Fopen(filename, "rb");
3902
0
  if (!fp) {
3903
0
    return SystemTools::FileTypeUnknown;
3904
0
  }
3905
3906
  // Allocate buffer and read bytes
3907
3908
0
  auto* buffer = new unsigned char[length];
3909
0
  size_t read_length = fread(buffer, 1, length, fp);
3910
0
  fclose(fp);
3911
0
  if (read_length == 0) {
3912
0
    delete[] buffer;
3913
0
    return SystemTools::FileTypeUnknown;
3914
0
  }
3915
3916
  // Loop over contents and count
3917
3918
0
  size_t text_count = 0;
3919
3920
0
  unsigned char const* ptr = buffer;
3921
0
  unsigned char const* buffer_end = buffer + read_length;
3922
3923
0
  while (ptr != buffer_end) {
3924
0
    if ((*ptr >= 0x20 && *ptr <= 0x7F) || *ptr == '\n' || *ptr == '\r' ||
3925
0
        *ptr == '\t') {
3926
0
      text_count++;
3927
0
    }
3928
0
    ptr++;
3929
0
  }
3930
3931
0
  delete[] buffer;
3932
3933
0
  double current_percent_bin = (static_cast<double>(read_length - text_count) /
3934
0
                                static_cast<double>(read_length));
3935
3936
0
  if (current_percent_bin >= percent_bin) {
3937
0
    return SystemTools::FileTypeBinary;
3938
0
  }
3939
3940
0
  return SystemTools::FileTypeText;
3941
0
}
3942
3943
bool SystemTools::LocateFileInDir(char const* filename, char const* dir,
3944
                                  std::string& filename_found,
3945
                                  int try_filename_dirs)
3946
0
{
3947
0
  if (!filename || !dir) {
3948
0
    return false;
3949
0
  }
3950
3951
  // Get the basename of 'filename'
3952
3953
0
  std::string filename_base = SystemTools::GetFilenameName(filename);
3954
3955
  // Check if 'dir' is really a directory
3956
  // If win32 and matches something like C:, accept it as a dir
3957
3958
0
  std::string real_dir;
3959
0
  if (!SystemTools::FileIsDirectory(dir)) {
3960
#if defined(_WIN32)
3961
    size_t dir_len = strlen(dir);
3962
    if (dir_len < 2 || dir[dir_len - 1] != ':') {
3963
#endif
3964
0
      real_dir = SystemTools::GetFilenamePath(dir);
3965
0
      dir = real_dir.c_str();
3966
#if defined(_WIN32)
3967
    }
3968
#endif
3969
0
  }
3970
3971
  // Try to find the file in 'dir'
3972
3973
0
  bool res = false;
3974
0
  if (!filename_base.empty() && dir) {
3975
0
    size_t dir_len = strlen(dir);
3976
0
    int need_slash =
3977
0
      (dir_len && dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\');
3978
3979
0
    std::string temp = dir;
3980
0
    if (need_slash) {
3981
0
      temp += '/';
3982
0
    }
3983
0
    temp += filename_base;
3984
3985
0
    if (SystemTools::FileExists(temp)) {
3986
0
      res = true;
3987
0
      filename_found = temp;
3988
0
    }
3989
3990
    // If not found, we can try harder by appending part of the file to
3991
    // to the directory to look inside.
3992
    // Example: if we were looking for /foo/bar/yo.txt in /d1/d2, then
3993
    // try to find yo.txt in /d1/d2/bar, then /d1/d2/foo/bar, etc.
3994
3995
0
    else if (try_filename_dirs) {
3996
0
      std::string filename_dir(filename);
3997
0
      std::string filename_dir_base;
3998
0
      std::string filename_dir_bases;
3999
0
      do {
4000
0
        filename_dir = SystemTools::GetFilenamePath(filename_dir);
4001
0
        filename_dir_base = SystemTools::GetFilenameName(filename_dir);
4002
#if defined(_WIN32)
4003
        if (filename_dir_base.empty() || filename_dir_base.back() == ':')
4004
#else
4005
0
        if (filename_dir_base.empty())
4006
0
#endif
4007
0
        {
4008
0
          break;
4009
0
        }
4010
4011
0
        filename_dir_bases = filename_dir_base + '/' + filename_dir_bases;
4012
4013
0
        temp = dir;
4014
0
        if (need_slash) {
4015
0
          temp += '/';
4016
0
        }
4017
0
        temp += filename_dir_bases;
4018
4019
0
        res = SystemTools::LocateFileInDir(filename_base.c_str(), temp.c_str(),
4020
0
                                           filename_found, 0);
4021
4022
0
      } while (!res && !filename_dir_base.empty());
4023
0
    }
4024
0
  }
4025
4026
0
  return res;
4027
0
}
4028
4029
bool SystemTools::FileIsFullPath(std::string const& in_name)
4030
133k
{
4031
133k
  return SystemToolsStatic::FileIsFullPath(in_name.c_str(), in_name.size());
4032
133k
}
4033
4034
bool SystemTools::FileIsFullPath(char const* in_name)
4035
0
{
4036
0
  return SystemToolsStatic::FileIsFullPath(
4037
0
    in_name, in_name[0] ? (in_name[1] ? 2 : 1) : 0);
4038
0
}
4039
4040
bool SystemToolsStatic::FileIsFullPath(char const* in_name, size_t len)
4041
133k
{
4042
#if defined(_WIN32) && !defined(__CYGWIN__)
4043
  // On Windows, the name must be at least two characters long.
4044
  if (len < 2) {
4045
    return false;
4046
  }
4047
  if (in_name[1] == ':') {
4048
    return true;
4049
  }
4050
  if (in_name[0] == '\\') {
4051
    return true;
4052
  }
4053
#else
4054
  // On UNIX, the name must be at least one character long.
4055
133k
  if (len < 1) {
4056
0
    return false;
4057
0
  }
4058
133k
#endif
4059
133k
#if !defined(_WIN32)
4060
133k
  if (in_name[0] == '~') {
4061
41.9k
    return true;
4062
41.9k
  }
4063
91.4k
#endif
4064
  // On UNIX, the name must begin in a '/'.
4065
  // On Windows, if the name begins in a '/', then it is a full
4066
  // network path.
4067
91.4k
  if (in_name[0] == '/') {
4068
5.85k
    return true;
4069
5.85k
  }
4070
85.6k
  return false;
4071
91.4k
}
4072
4073
Status SystemTools::GetShortPath(std::string const& path,
4074
                                 std::string& shortPath)
4075
0
{
4076
#if defined(_WIN32) && !defined(__CYGWIN__)
4077
  std::string tempPath = path; // create a buffer
4078
4079
  // if the path passed in has quotes around it, first remove the quotes
4080
  if (!path.empty() && path[0] == '"' && path.back() == '"') {
4081
    tempPath.resize(path.length() - 1);
4082
    tempPath.erase(0, 1);
4083
  }
4084
4085
  std::wstring wtempPath = Encoding::ToWide(tempPath);
4086
  DWORD ret = GetShortPathNameW(wtempPath.c_str(), nullptr, 0);
4087
  std::vector<wchar_t> buffer(ret);
4088
  if (ret != 0) {
4089
    ret = GetShortPathNameW(wtempPath.c_str(), &buffer[0],
4090
                            static_cast<DWORD>(buffer.size()));
4091
  }
4092
4093
  if (ret == 0) {
4094
    return Status::Windows_GetLastError();
4095
  } else {
4096
    shortPath = Encoding::ToNarrow(&buffer[0]);
4097
    return Status::Success();
4098
  }
4099
#else
4100
0
  shortPath = path;
4101
0
  return Status::Success();
4102
0
#endif
4103
0
}
4104
4105
std::tm SystemTools::LocalTime(std::time_t timep)
4106
0
{
4107
0
  std::tm out;
4108
#if defined(_WIN32) && !defined(__CYGWIN__)
4109
  localtime_s(&out, &timep);
4110
#else
4111
0
  localtime_r(&timep, &out);
4112
0
#endif
4113
0
  return out;
4114
0
}
4115
4116
std::tm SystemTools::GMTime(std::time_t timep)
4117
0
{
4118
0
  std::tm out;
4119
#if defined(_WIN32) && !defined(__CYGWIN__)
4120
  gmtime_s(&out, &timep);
4121
#else
4122
0
  gmtime_r(&timep, &out);
4123
0
#endif
4124
0
  return out;
4125
0
}
4126
4127
std::string SystemTools::GetCurrentDateTime(char const* format)
4128
0
{
4129
0
  char buf[1024];
4130
0
  tm const t = LocalTime(std::time(nullptr));
4131
0
  strftime(buf, sizeof(buf), format, &t);
4132
0
  return std::string(buf);
4133
0
}
4134
4135
std::string SystemTools::MakeCidentifier(std::string const& s)
4136
0
{
4137
0
  std::string str(s);
4138
0
  if (str.find_first_of("0123456789") == 0) {
4139
0
    str = '_' + str;
4140
0
  }
4141
4142
0
  std::string permited_chars("_"
4143
0
                             "abcdefghijklmnopqrstuvwxyz"
4144
0
                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
4145
0
                             "0123456789");
4146
0
  std::string::size_type pos = 0;
4147
0
  while ((pos = str.find_first_not_of(permited_chars, pos)) !=
4148
0
         std::string::npos) {
4149
0
    str[pos] = '_';
4150
0
  }
4151
0
  return str;
4152
0
}
4153
4154
// Convenience function around std::getline which removes a trailing carriage
4155
// return and can truncate the buffer as needed.  Returns true
4156
// if any data were read before the end-of-file was reached.
4157
bool SystemTools::GetLineFromStream(
4158
  std::istream& is, std::string& line, bool* has_newline /* = 0 */,
4159
  std::string::size_type sizeLimit /* = std::string::npos */)
4160
0
{
4161
  // Start with an empty line.
4162
0
  line = "";
4163
4164
  // Early short circuit return if stream is no good. Just return
4165
  // false and the empty line. (Probably means caller tried to
4166
  // create a file stream with a non-existent file name...)
4167
  //
4168
0
  if (!is) {
4169
0
    if (has_newline) {
4170
0
      *has_newline = false;
4171
0
    }
4172
0
    return false;
4173
0
  }
4174
4175
0
  std::getline(is, line);
4176
0
  bool haveData = !line.empty() || !is.eof();
4177
0
  if (!line.empty()) {
4178
    // Avoid storing a carriage return character.
4179
0
    if (line.back() == '\r') {
4180
0
      line.resize(line.size() - 1);
4181
0
    }
4182
4183
    // if we read too much then truncate the buffer
4184
0
    if (sizeLimit != std::string::npos && line.size() > sizeLimit) {
4185
0
      line.resize(sizeLimit);
4186
0
    }
4187
0
  }
4188
4189
  // Return the results.
4190
0
  if (has_newline) {
4191
0
    *has_newline = !is.eof();
4192
0
  }
4193
0
  return haveData;
4194
0
}
4195
4196
int SystemTools::GetTerminalWidth()
4197
0
{
4198
0
  int width = -1;
4199
0
#ifdef HAVE_TTY_INFO
4200
0
  struct winsize ws;
4201
0
  std::string columns; /* Unix98 environment variable */
4202
0
  if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0 && ws.ws_row > 0) {
4203
0
    width = ws.ws_col;
4204
0
  }
4205
0
  if (!isatty(STDOUT_FILENO)) {
4206
0
    width = -1;
4207
0
  }
4208
0
  if (SystemTools::GetEnv("COLUMNS", columns) && !columns.empty()) {
4209
0
    long t;
4210
0
    char* endptr;
4211
0
    t = strtol(columns.c_str(), &endptr, 0);
4212
0
    if (endptr && !*endptr && (t > 0) && (t < 1000)) {
4213
0
      width = static_cast<int>(t);
4214
0
    }
4215
0
  }
4216
0
  if (width < 9) {
4217
0
    width = -1;
4218
0
  }
4219
0
#endif
4220
0
  return width;
4221
0
}
4222
4223
Status SystemTools::GetPermissions(char const* file, mode_t& mode)
4224
0
{
4225
0
  if (!file) {
4226
0
    return Status::POSIX(EINVAL);
4227
0
  }
4228
0
  return SystemTools::GetPermissions(std::string(file), mode);
4229
0
}
4230
4231
Status SystemTools::GetPermissions(std::string const& file, mode_t& mode)
4232
63.0k
{
4233
#if defined(_WIN32)
4234
  DWORD attr =
4235
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(file).c_str());
4236
  if (attr == INVALID_FILE_ATTRIBUTES) {
4237
    return Status::Windows_GetLastError();
4238
  }
4239
  if ((attr & FILE_ATTRIBUTE_READONLY) != 0) {
4240
    mode = (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4241
  } else {
4242
    mode = (_S_IWRITE | (_S_IWRITE >> 3) | (_S_IWRITE >> 6)) |
4243
      (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4244
  }
4245
  if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
4246
    mode |= S_IFDIR | (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4247
  } else {
4248
    mode |= S_IFREG;
4249
  }
4250
  size_t dotPos = file.rfind('.');
4251
  char const* ext = dotPos == std::string::npos ? 0 : (file.c_str() + dotPos);
4252
  if (ext &&
4253
      (Strucmp(ext, ".exe") == 0 || Strucmp(ext, ".com") == 0 ||
4254
       Strucmp(ext, ".cmd") == 0 || Strucmp(ext, ".bat") == 0)) {
4255
    mode |= (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4256
  }
4257
#else
4258
63.0k
  struct stat st;
4259
63.0k
  if (stat(file.c_str(), &st) < 0) {
4260
8.04k
    return Status::POSIX_errno();
4261
8.04k
  }
4262
54.9k
  mode = st.st_mode;
4263
54.9k
#endif
4264
54.9k
  return Status::Success();
4265
63.0k
}
4266
4267
Status SystemTools::SetPermissions(char const* file, mode_t mode,
4268
                                   bool honor_umask)
4269
0
{
4270
0
  if (!file) {
4271
0
    return Status::POSIX(EINVAL);
4272
0
  }
4273
0
  return SystemTools::SetPermissions(std::string(file), mode, honor_umask);
4274
0
}
4275
4276
Status SystemTools::SetPermissions(std::string const& file, mode_t mode,
4277
                                   bool honor_umask)
4278
54.9k
{
4279
54.9k
  if (!SystemTools::PathExists(file)) {
4280
0
    return Status::POSIX(ENOENT);
4281
0
  }
4282
54.9k
  if (honor_umask) {
4283
0
    mode_t currentMask = umask(0);
4284
0
    umask(currentMask);
4285
0
    mode &= ~currentMask;
4286
0
  }
4287
#ifdef _WIN32
4288
  if (_wchmod(Encoding::ToWindowsExtendedPath(file).c_str(), mode) < 0)
4289
#else
4290
54.9k
  if (chmod(file.c_str(), mode) < 0)
4291
0
#endif
4292
0
  {
4293
0
    return Status::POSIX_errno();
4294
0
  }
4295
4296
54.9k
  return Status::Success();
4297
54.9k
}
4298
4299
std::string SystemTools::GetParentDirectory(std::string const& fileOrDir)
4300
0
{
4301
0
  return SystemTools::GetFilenamePath(fileOrDir);
4302
0
}
4303
4304
bool SystemTools::IsSubDirectory(std::string const& cSubdir,
4305
                                 std::string const& cDir)
4306
1
{
4307
1
  if (cDir.empty()) {
4308
0
    return false;
4309
0
  }
4310
1
  std::string subdir = cSubdir;
4311
1
  std::string dir = cDir;
4312
1
  SystemTools::ConvertToUnixSlashes(subdir);
4313
1
  SystemTools::ConvertToUnixSlashes(dir);
4314
1
  if (subdir.size() <= dir.size() || dir.empty()) {
4315
1
    return false;
4316
1
  }
4317
0
  bool isRootPath = dir.back() == '/'; // like "/" or "C:/"
4318
0
  size_t expectedSlashPosition = isRootPath ? dir.size() - 1u : dir.size();
4319
0
  if (subdir[expectedSlashPosition] != '/') {
4320
0
    return false;
4321
0
  }
4322
0
  subdir.resize(dir.size());
4323
0
  return SystemTools::ComparePath(subdir, dir);
4324
0
}
4325
4326
void SystemTools::Delay(unsigned int msec)
4327
0
{
4328
#ifdef _WIN32
4329
  Sleep(msec);
4330
#else
4331
  // The sleep function gives 1 second resolution and the usleep
4332
  // function gives 1e-6 second resolution but on some platforms has a
4333
  // maximum sleep time of 1 second.  This could be re-implemented to
4334
  // use select with masked signals or pselect to mask signals
4335
  // atomically.  If select is given empty sets and zero as the max
4336
  // file descriptor but a non-zero timeout it can be used to block
4337
  // for a precise amount of time.
4338
0
  if (msec >= 1000) {
4339
0
    sleep(msec / 1000);
4340
0
    usleep((msec % 1000) * 1000);
4341
0
  } else {
4342
0
    usleep(msec * 1000);
4343
0
  }
4344
0
#endif
4345
0
}
4346
4347
std::string SystemTools::GetOperatingSystemNameAndVersion()
4348
0
{
4349
0
  std::string res;
4350
4351
#ifdef _WIN32
4352
  char buffer[256];
4353
4354
  OSVERSIONINFOEXA osvi;
4355
  BOOL bOsVersionInfoEx;
4356
4357
  ZeroMemory(&osvi, sizeof(osvi));
4358
  osvi.dwOSVersionInfoSize = sizeof(osvi);
4359
4360
#  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4361
#    pragma warning(push)
4362
#    ifdef __INTEL_COMPILER
4363
#      pragma warning(disable : 1478)
4364
#    elif defined __clang__
4365
#      pragma clang diagnostic push
4366
#      pragma clang diagnostic ignored "-Wdeprecated-declarations"
4367
#    else
4368
#      pragma warning(disable : 4996)
4369
#    endif
4370
#  endif
4371
  bOsVersionInfoEx = GetVersionExA((OSVERSIONINFOA*)&osvi);
4372
  if (!bOsVersionInfoEx) {
4373
    return "";
4374
  }
4375
#  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4376
#    ifdef __clang__
4377
#      pragma clang diagnostic pop
4378
#    else
4379
#      pragma warning(pop)
4380
#    endif
4381
#  endif
4382
4383
  switch (osvi.dwPlatformId) {
4384
      // Test for the Windows NT product family.
4385
4386
    case VER_PLATFORM_WIN32_NT:
4387
4388
      // Test for the specific product family.
4389
      if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0) {
4390
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4391
          res += "Microsoft Windows 10";
4392
        } else {
4393
          res += "Microsoft Windows Server 2016 family";
4394
        }
4395
      }
4396
4397
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) {
4398
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4399
          res += "Microsoft Windows 8.1";
4400
        } else {
4401
          res += "Microsoft Windows Server 2012 R2 family";
4402
        }
4403
      }
4404
4405
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) {
4406
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4407
          res += "Microsoft Windows 8";
4408
        } else {
4409
          res += "Microsoft Windows Server 2012 family";
4410
        }
4411
      }
4412
4413
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) {
4414
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4415
          res += "Microsoft Windows 7";
4416
        } else {
4417
          res += "Microsoft Windows Server 2008 R2 family";
4418
        }
4419
      }
4420
4421
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) {
4422
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4423
          res += "Microsoft Windows Vista";
4424
        } else {
4425
          res += "Microsoft Windows Server 2008 family";
4426
        }
4427
      }
4428
4429
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4430
        res += "Microsoft Windows Server 2003 family";
4431
      }
4432
4433
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
4434
        res += "Microsoft Windows XP";
4435
      }
4436
4437
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4438
        res += "Microsoft Windows 2000";
4439
      }
4440
4441
      if (osvi.dwMajorVersion <= 4) {
4442
        res += "Microsoft Windows NT";
4443
      }
4444
4445
      // Test for specific product on Windows NT 4.0 SP6 and later.
4446
4447
      if (bOsVersionInfoEx) {
4448
        // Test for the workstation type.
4449
4450
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4451
          if (osvi.dwMajorVersion == 4) {
4452
            res += " Workstation 4.0";
4453
          } else if (osvi.dwMajorVersion == 5) {
4454
            if (osvi.wSuiteMask & VER_SUITE_PERSONAL) {
4455
              res += " Home Edition";
4456
            } else {
4457
              res += " Professional";
4458
            }
4459
          }
4460
        }
4461
4462
        // Test for the server type.
4463
4464
        else if (osvi.wProductType == VER_NT_SERVER) {
4465
          if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4466
            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
4467
              res += " Datacenter Edition";
4468
            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4469
              res += " Enterprise Edition";
4470
            } else if (osvi.wSuiteMask == VER_SUITE_BLADE) {
4471
              res += " Web Edition";
4472
            } else {
4473
              res += " Standard Edition";
4474
            }
4475
          }
4476
4477
          else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4478
            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
4479
              res += " Datacenter Server";
4480
            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4481
              res += " Advanced Server";
4482
            } else {
4483
              res += " Server";
4484
            }
4485
          }
4486
4487
          else if (osvi.dwMajorVersion <= 4) // Windows NT 4.0
4488
          {
4489
            if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4490
              res += " Server 4.0, Enterprise Edition";
4491
            } else {
4492
              res += " Server 4.0";
4493
            }
4494
          }
4495
        }
4496
      }
4497
4498
      // Test for specific product on Windows NT 4.0 SP5 and earlier
4499
4500
      else {
4501
        HKEY hKey;
4502
#  define BUFSIZE 80
4503
        wchar_t szProductType[BUFSIZE];
4504
        DWORD dwBufLen = BUFSIZE;
4505
        LONG lRet;
4506
4507
        lRet =
4508
          RegOpenKeyExW(HKEY_LOCAL_MACHINE,
4509
                        L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
4510
                        0, KEY_QUERY_VALUE, &hKey);
4511
        if (lRet != ERROR_SUCCESS) {
4512
          return "";
4513
        }
4514
4515
        lRet = RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr,
4516
                                (LPBYTE)szProductType, &dwBufLen);
4517
4518
        if ((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE)) {
4519
          return "";
4520
        }
4521
4522
        RegCloseKey(hKey);
4523
4524
        if (lstrcmpiW(L"WINNT", szProductType) == 0) {
4525
          res += " Workstation";
4526
        }
4527
        if (lstrcmpiW(L"LANMANNT", szProductType) == 0) {
4528
          res += " Server";
4529
        }
4530
        if (lstrcmpiW(L"SERVERNT", szProductType) == 0) {
4531
          res += " Advanced Server";
4532
        }
4533
4534
        res += ' ';
4535
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMajorVersion);
4536
        res += buffer;
4537
        res += '.';
4538
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMinorVersion);
4539
        res += buffer;
4540
      }
4541
4542
      // Display service pack (if any) and build number.
4543
4544
      if (osvi.dwMajorVersion == 4 &&
4545
          lstrcmpiA(osvi.szCSDVersion, "Service Pack 6") == 0) {
4546
        HKEY hKey;
4547
        LONG lRet;
4548
4549
        // Test for SP6 versus SP6a.
4550
4551
        lRet = RegOpenKeyExW(
4552
          HKEY_LOCAL_MACHINE,
4553
          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
4554
          0, KEY_QUERY_VALUE, &hKey);
4555
4556
        if (lRet == ERROR_SUCCESS) {
4557
          res += " Service Pack 6a (Build ";
4558
          snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4559
          res += buffer;
4560
          res += ')';
4561
        } else // Windows NT 4.0 prior to SP6a
4562
        {
4563
          res += ' ';
4564
          res += osvi.szCSDVersion;
4565
          res += " (Build ";
4566
          snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4567
          res += buffer;
4568
          res += ')';
4569
        }
4570
4571
        RegCloseKey(hKey);
4572
      } else // Windows NT 3.51 and earlier or Windows 2000 and later
4573
      {
4574
        res += ' ';
4575
        res += osvi.szCSDVersion;
4576
        res += " (Build ";
4577
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4578
        res += buffer;
4579
        res += ')';
4580
      }
4581
4582
      break;
4583
4584
      // Test for the Windows 95 product family.
4585
4586
    case VER_PLATFORM_WIN32_WINDOWS:
4587
4588
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) {
4589
        res += "Microsoft Windows 95";
4590
        if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') {
4591
          res += " OSR2";
4592
        }
4593
      }
4594
4595
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) {
4596
        res += "Microsoft Windows 98";
4597
        if (osvi.szCSDVersion[1] == 'A') {
4598
          res += " SE";
4599
        }
4600
      }
4601
4602
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) {
4603
        res += "Microsoft Windows Millennium Edition";
4604
      }
4605
      break;
4606
4607
    case VER_PLATFORM_WIN32s:
4608
4609
      res += "Microsoft Win32s";
4610
      break;
4611
  }
4612
#endif
4613
4614
0
  return res;
4615
0
}
4616
4617
bool SystemTools::ParseURLProtocol(std::string const& URL,
4618
                                   std::string& protocol,
4619
                                   std::string& dataglom, bool decode)
4620
0
{
4621
  // match 0 entire url
4622
  // match 1 protocol
4623
  // match 2 dataglom following protocol://
4624
0
  kwsys::RegularExpression urlRe(VTK_URL_PROTOCOL_REGEX);
4625
4626
0
  if (!urlRe.find(URL))
4627
0
    return false;
4628
4629
0
  protocol = urlRe.match(1);
4630
0
  dataglom = urlRe.match(2);
4631
4632
0
  if (decode) {
4633
0
    dataglom = DecodeURL(dataglom);
4634
0
  }
4635
4636
0
  return true;
4637
0
}
4638
4639
bool SystemTools::ParseURL(std::string const& URL, std::string& protocol,
4640
                           std::string& username, std::string& password,
4641
                           std::string& hostname, std::string& dataport,
4642
                           std::string& database, bool decode)
4643
0
{
4644
0
  kwsys::RegularExpression urlRe(VTK_URL_REGEX);
4645
0
  if (!urlRe.find(URL))
4646
0
    return false;
4647
4648
  // match 0 URL
4649
  // match 1 protocol
4650
  // match 2 mangled user
4651
  // match 3 username
4652
  // match 4 mangled password
4653
  // match 5 password
4654
  // match 6 hostname
4655
  // match 7 mangled port
4656
  // match 8 dataport
4657
  // match 9 database name
4658
4659
0
  protocol = urlRe.match(1);
4660
0
  username = urlRe.match(3);
4661
0
  password = urlRe.match(5);
4662
0
  hostname = urlRe.match(6);
4663
0
  dataport = urlRe.match(8);
4664
0
  database = urlRe.match(9);
4665
4666
0
  if (decode) {
4667
0
    username = DecodeURL(username);
4668
0
    password = DecodeURL(password);
4669
0
    hostname = DecodeURL(hostname);
4670
0
    dataport = DecodeURL(dataport);
4671
0
    database = DecodeURL(database);
4672
0
  }
4673
4674
0
  return true;
4675
0
}
4676
4677
// ----------------------------------------------------------------------
4678
std::string SystemTools::DecodeURL(std::string const& url)
4679
0
{
4680
0
  kwsys::RegularExpression urlByteRe(VTK_URL_BYTE_REGEX);
4681
0
  std::string ret;
4682
0
  for (size_t i = 0; i < url.length(); i++) {
4683
0
    if (urlByteRe.find(url.substr(i, 3))) {
4684
0
      char bytes[] = { url[i + 1], url[i + 2], '\0' };
4685
0
      ret += static_cast<char>(strtoul(bytes, nullptr, 16));
4686
0
      i += 2;
4687
0
    } else {
4688
0
      ret += url[i];
4689
0
    }
4690
0
  }
4691
0
  return ret;
4692
0
}
4693
4694
// ----------------------------------------------------------------------
4695
// Do NOT initialize.  Default initialization to zero is necessary.
4696
static unsigned int SystemToolsManagerCount;
4697
4698
// SystemToolsManager manages the SystemTools singleton.
4699
// SystemToolsManager should be included in any translation unit
4700
// that will use SystemTools or that implements the singleton
4701
// pattern. It makes sure that the SystemTools singleton is created
4702
// before and destroyed after all other singletons in CMake.
4703
4704
SystemToolsManager::SystemToolsManager()
4705
1.30k
{
4706
1.30k
  if (++SystemToolsManagerCount == 1) {
4707
16
    SystemTools::ClassInitialize();
4708
16
  }
4709
1.30k
}
4710
4711
SystemToolsManager::~SystemToolsManager()
4712
0
{
4713
0
  if (--SystemToolsManagerCount == 0) {
4714
0
    SystemTools::ClassFinalize();
4715
0
  }
4716
0
}
4717
4718
#if defined(__VMS)
4719
// On VMS we configure the run time C library to be more UNIX like.
4720
// https://h71000.www7.hp.com/doc/732final/5763/5763pro_004.html
4721
extern "C" int decc$feature_get_index(char* name);
4722
extern "C" int decc$feature_set_value(int index, int mode, int value);
4723
static int SetVMSFeature(char* name, int value)
4724
{
4725
  int i;
4726
  errno = 0;
4727
  i = decc$feature_get_index(name);
4728
  return i >= 0 && (decc$feature_set_value(i, 1, value) >= 0 || errno == 0);
4729
}
4730
#endif
4731
4732
void SystemTools::ClassInitialize()
4733
16
{
4734
#ifdef __VMS
4735
  SetVMSFeature("DECC$FILENAME_UNIX_ONLY", 1);
4736
#endif
4737
4738
  // Create statics singleton instance
4739
16
  SystemToolsStatics = new SystemToolsStatic;
4740
16
}
4741
4742
void SystemTools::ClassFinalize()
4743
0
{
4744
0
  delete SystemToolsStatics;
4745
0
}
4746
4747
} // namespace KWSYS_NAMESPACE
4748
4749
#if defined(_MSC_VER) && defined(_DEBUG)
4750
#  include <crtdbg.h>
4751
#  include <stdio.h>
4752
#  include <stdlib.h>
4753
namespace KWSYS_NAMESPACE {
4754
4755
static int SystemToolsDebugReport(int, char* message, int* ret)
4756
{
4757
  if (ret) {
4758
    // Pretend user clicked on Retry button in popup.
4759
    *ret = 1;
4760
  }
4761
  fprintf(stderr, "%s", message);
4762
  fflush(stderr);
4763
  return 1; // no further reporting required
4764
}
4765
4766
void SystemTools::EnableMSVCDebugHook()
4767
{
4768
  if (SystemTools::HasEnv("DART_TEST_FROM_DART") ||
4769
      SystemTools::HasEnv("DASHBOARD_TEST_FROM_CTEST")) {
4770
    _CrtSetReportHook(SystemToolsDebugReport);
4771
  }
4772
}
4773
4774
} // namespace KWSYS_NAMESPACE
4775
#else
4776
namespace KWSYS_NAMESPACE {
4777
void SystemTools::EnableMSVCDebugHook()
4778
0
{
4779
0
}
4780
} // namespace KWSYS_NAMESPACE
4781
#endif