Coverage Report

Created: 2026-07-14 07:09

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
24.2k
{
377
24.2k
  return mkdir(dir.c_str(), mode ? *mode : 00777);
378
24.2k
}
379
inline int Rmdir(std::string const& dir)
380
12.2k
{
381
12.2k
  return rmdir(dir.c_str());
382
12.2k
}
383
inline char const* Getcwd(char* buf, unsigned int len)
384
21.4k
{
385
21.4k
  return getcwd(buf, len);
386
21.4k
}
387
388
inline int Chdir(std::string const& dir)
389
61.3k
{
390
61.3k
  return chdir(dir.c_str());
391
61.3k
}
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
34.2k
{
679
#if defined(_WIN32)
680
  std::wstring const wKey = Encoding::ToWide(key);
681
  std::vector<wchar_t> heapBuf;
682
  wchar_t stackBuf[256];
683
  DWORD bufSz = static_cast<DWORD>(sizeof(stackBuf) / sizeof(stackBuf[0]));
684
  wchar_t* buf = stackBuf;
685
  DWORD r;
686
  while ((r = GetEnvironmentVariableW(wKey.c_str(), buf, bufSz)) >= bufSz) {
687
    heapBuf.resize(r);
688
    bufSz = r;
689
    buf = &heapBuf[0];
690
  }
691
  if (r == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
692
    return false;
693
  }
694
  result = Encoding::ToNarrow(buf);
695
  return true;
696
#else
697
34.2k
  char const* v = getenv(key);
698
34.2k
  if (v) {
699
34.1k
    result = v;
700
34.1k
    return true;
701
34.1k
  }
702
17
#endif
703
17
  return false;
704
34.2k
}
705
706
bool SystemTools::GetEnv(std::string const& key, std::string& result)
707
17
{
708
17
  return SystemTools::GetEnv(key.c_str(), result);
709
17
}
710
711
bool SystemTools::HasEnv(char const* key)
712
0
{
713
#if defined(_WIN32)
714
  std::wstring const wKey = Encoding::ToWide(key);
715
  DWORD r = GetEnvironmentVariableW(wKey.c_str(), nullptr, 0);
716
  return !(r == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND);
717
#else
718
0
  return getenv(key) != nullptr;
719
0
#endif
720
0
}
721
722
bool SystemTools::HasEnv(std::string const& key)
723
0
{
724
0
  return SystemTools::HasEnv(key.c_str());
725
0
}
726
727
#if KWSYS_CXX_HAS_UNSETENV
728
/* unsetenv("A") removes A from the environment.
729
   On older platforms it returns void instead of int.  */
730
static int kwsysUnPutEnv(std::string const& env)
731
0
{
732
0
  size_t pos = env.find('=');
733
0
  if (pos != std::string::npos) {
734
0
    std::string name = env.substr(0, pos);
735
0
    unsetenv(name.c_str());
736
0
  } else {
737
0
    unsetenv(env.c_str());
738
0
  }
739
0
  return 0;
740
0
}
741
742
#elif defined(__CYGWIN__) || defined(__GLIBC__)
743
// putenv("A") removes A from the environment with the GNU runtime.
744
// It cannot put the memory in the environment since there is no  "=" syntax.
745
746
static int kwsysUnPutEnv(std::string const& env)
747
{
748
  int err = 0;
749
  std::string buf = env.substr(0, env.find('='));
750
  if (putenv(&buf[0]) < 0 && errno != EINVAL) {
751
    err = errno;
752
  }
753
  if (err) {
754
    errno = err;
755
    return -1;
756
  }
757
  return 0;
758
}
759
760
#elif defined(_WIN32)
761
// putenv("A=") removes A from the environment with the MSVC runtime.
762
763
static int kwsysUnPutEnv(std::string const& env)
764
{
765
  std::wstring wEnv = Encoding::ToWide(env);
766
  size_t const pos = wEnv.find('=');
767
  size_t const len = pos == std::string::npos ? wEnv.size() : pos;
768
  wEnv.resize(len + 1, L'=');
769
  return _wputenv(wEnv.c_str());
770
}
771
772
#else
773
/* Manipulate the "environ" global directly.  */
774
static int kwsysUnPutEnv(std::string const& env)
775
{
776
  size_t pos = env.find('=');
777
  size_t const len = pos == std::string::npos ? env.size() : pos;
778
  int in = 0;
779
  int out = 0;
780
  while (environ[in]) {
781
    if (strlen(environ[in]) > len && environ[in][len] == '=' &&
782
        strncmp(env.c_str(), environ[in], len) == 0) {
783
      ++in;
784
    } else {
785
      environ[out++] = environ[in++];
786
    }
787
  }
788
  while (out < in) {
789
    environ[out++] = 0;
790
  }
791
  return 0;
792
}
793
#endif
794
795
#if KWSYS_CXX_HAS_SETENV
796
797
/* setenv("A", "B", 1) will set A=B in the environment and makes its
798
   own copies of the strings.  */
799
bool SystemTools::PutEnv(std::string const& env)
800
0
{
801
0
  size_t pos = env.find('=');
802
0
  if (pos != std::string::npos) {
803
0
    std::string name = env.substr(0, pos);
804
0
    return setenv(name.c_str(), env.c_str() + pos + 1, 1) == 0;
805
0
  } else {
806
0
    return kwsysUnPutEnv(env) == 0;
807
0
  }
808
0
}
809
810
bool SystemTools::UnPutEnv(std::string const& env)
811
0
{
812
0
  return kwsysUnPutEnv(env) == 0;
813
0
}
814
815
#else
816
817
/* putenv("A=B") will set A=B in the environment.  Most putenv implementations
818
   put their argument directly in the environment.  They never free the memory
819
   on program exit.  Keep an active set of pointers to memory we allocate and
820
   pass to putenv, one per environment key.  At program exit remove any
821
   environment values that may still reference memory we allocated.  Then free
822
   the memory.  This will not affect any environment values we never set.  */
823
824
#  ifdef __INTEL_COMPILER
825
#    pragma warning disable 444 /* base has non-virtual destructor */
826
#  endif
827
828
class kwsysEnv : public kwsysEnvSet
829
{
830
public:
831
  ~kwsysEnv()
832
  {
833
    for (iterator i = this->begin(); i != this->end(); ++i) {
834
#  if defined(_WIN32)
835
      std::string const s = Encoding::ToNarrow(*i);
836
      kwsysUnPutEnv(s);
837
#  else
838
      kwsysUnPutEnv(*i);
839
#  endif
840
      free(const_cast<envchar*>(*i));
841
    }
842
  }
843
  bool Put(char const* env)
844
  {
845
#  if defined(_WIN32)
846
    std::wstring wEnv = Encoding::ToWide(env);
847
    wchar_t* newEnv = _wcsdup(wEnv.c_str());
848
#  else
849
    char* newEnv = strdup(env);
850
#  endif
851
    Free oldEnv(this->Release(newEnv));
852
    this->insert(newEnv);
853
#  if defined(_WIN32)
854
    // `_wputenv` updates both the C runtime library's `_wenviron` array
855
    // and the process's environment block.
856
    if (_wputenv(newEnv) != 0) {
857
      return false;
858
    }
859
    // There seems to be no way to add an empty variable to `_wenviron`
860
    // through the C runtime library: `_wputenv("A=")` removes "A".
861
    // Add it directly to the process's environment block.
862
    // This is used by child processes, and by our `GetEnv`.
863
    std::string::size_type const eqPos = wEnv.find(L'=');
864
    if (eqPos != std::string::npos && (eqPos + 1) == wEnv.size()) {
865
      wEnv.resize(eqPos);
866
      return SetEnvironmentVariableW(wEnv.c_str(), L"");
867
    }
868
    return true;
869
#  else
870
    return putenv(newEnv) == 0;
871
#  endif
872
  }
873
  bool UnPut(char const* env)
874
  {
875
#  if defined(_WIN32)
876
    std::wstring const wEnv = Encoding::ToWide(env);
877
    Free oldEnv(this->Release(wEnv.c_str()));
878
#  else
879
    Free oldEnv(this->Release(env));
880
#  endif
881
    return kwsysUnPutEnv(env) == 0;
882
  }
883
};
884
885
static kwsysEnv kwsysEnvInstance;
886
887
bool SystemTools::PutEnv(std::string const& env)
888
{
889
  return kwsysEnvInstance.Put(env.c_str());
890
}
891
892
bool SystemTools::UnPutEnv(std::string const& env)
893
{
894
  return kwsysEnvInstance.UnPut(env.c_str());
895
}
896
897
#endif
898
899
char const* SystemTools::GetExecutableExtension()
900
0
{
901
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__VMS)
902
  return ".exe";
903
#else
904
0
  return "";
905
0
#endif
906
0
}
907
908
FILE* SystemTools::Fopen(std::string const& file, char const* mode)
909
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
30.7k
{
932
30.7k
  if (path.empty()) {
933
0
    return Status::POSIX(EINVAL);
934
0
  }
935
30.7k
  if (SystemTools::PathExists(path)) {
936
24.6k
    if (SystemTools::FileIsDirectory(path)) {
937
24.6k
      return Status::Success();
938
24.6k
    }
939
0
    return Status::POSIX(EEXIST);
940
24.6k
  }
941
6.07k
  std::string dir = path;
942
6.07k
  SystemTools::ConvertToUnixSlashes(dir);
943
944
6.07k
  std::string::size_type pos = 0;
945
6.07k
  std::string topdir;
946
24.2k
  while ((pos = dir.find('/', pos)) != std::string::npos) {
947
    // all underlying functions use C strings, so temporarily
948
    // end the string here
949
18.1k
    dir[pos] = '\0';
950
951
18.1k
    Mkdir(dir, mode);
952
18.1k
    dir[pos] = '/';
953
954
18.1k
    ++pos;
955
18.1k
  }
956
6.07k
  topdir = dir;
957
6.07k
  if (Mkdir(topdir, mode) != 0 && errno != EEXIST) {
958
0
    return Status::POSIX_errno();
959
0
  }
960
961
6.07k
  return Status::Success();
962
6.07k
}
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
297k
{
1388
297k
  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
297k
  struct stat st;
1396
297k
  return lstat(path.c_str(), &st) == 0;
1397
297k
#endif
1398
297k
}
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.66k
{
1756
1.66k
  std::transform(s.begin(), s.end(), s.begin(), kwsysString_tolower);
1757
1.66k
  return s;
1758
1.66k
}
1759
1760
std::string SystemTools::UpperCase(std::string s)
1761
1.66k
{
1762
1.66k
  std::transform(s.begin(), s.end(), s.begin(), kwsysString_toupper);
1763
1.66k
  return s;
1764
1.66k
}
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.01k
{
1855
2.01k
  if (!str2) {
1856
0
    return false;
1857
0
  }
1858
2.01k
  size_t len1 = str1.size(), len2 = strlen(str2);
1859
2.01k
  return len1 >= len2 && !strncmp(str1.c_str(), str2, len2) ? true : false;
1860
2.01k
}
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.01k
{
1876
2.01k
  if (!str2) {
1877
0
    return false;
1878
0
  }
1879
2.01k
  size_t len1 = str1.size(), len2 = strlen(str2);
1880
2.01k
  return len1 >= len2 && !strncmp(str1.c_str() + (len1 - len2), str2, len2)
1881
2.01k
    ? true
1882
2.01k
    : false;
1883
2.01k
}
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
29.2k
{
2076
29.2k
  if (path.empty()) {
2077
1
    return;
2078
1
  }
2079
2080
#ifdef __VMS
2081
  ConvertVMSToUnix(path);
2082
#else
2083
  // replace backslashes
2084
29.2k
  std::replace(path.begin(), path.end(), '\\', '/');
2085
2086
  // collapse repeated slashes, except exactly two leading slashes are
2087
  // meaningful and must be preserved.
2088
29.2k
  bool hasDoubleSlash = path[0] == '/' && path[1] == '/' && path[2] != '/';
2089
29.2k
  auto uniqueEnd = std::unique(
2090
29.2k
    path.begin() + hasDoubleSlash, path.end(),
2091
2.98M
    [](char c1, char c2) -> bool { return c1 == '/' && c1 == c2; });
2092
29.2k
  path.erase(uniqueEnd, path.end());
2093
29.2k
#endif
2094
2095
  // if there is a tilda ~ then replace it with HOME
2096
29.2k
  if (path[0] == '~' && (path[1] == '/' || path[1] == '\0')) {
2097
165
    std::string homeEnv;
2098
165
    if (SystemTools::GetEnv("HOME", homeEnv)) {
2099
165
      path.replace(0, 1, homeEnv);
2100
165
    }
2101
165
  }
2102
29.0k
#ifdef HAVE_GETPWNAM
2103
29.0k
  else if (path[0] == '~') {
2104
278
    std::string::size_type idx = path.find('/');
2105
278
    std::string user = path.substr(1, idx - 1);
2106
278
    passwd* pw = getpwnam(user.c_str());
2107
278
    if (pw) {
2108
13
      path.replace(0, idx, pw->pw_dir);
2109
13
    }
2110
278
  }
2111
29.2k
#endif
2112
  // remove trailing slash, but preserve the root slash and the slash
2113
  // after windows drive letter (c:/).
2114
29.2k
  size_t size = path.size();
2115
29.2k
  if (size > 1 && path.back() == '/') {
2116
318
    if (!(size == 3 && path[1] == ':') && path[size - 2] != '/') {
2117
313
      path.resize(size - 1);
2118
313
    }
2119
318
  }
2120
29.2k
}
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
int SystemTools::Strnucmp(char const* l, char const* r, size_t n)
2732
0
{
2733
0
  int lc;
2734
0
  int rc;
2735
0
  size_t count = 0;
2736
0
  do {
2737
0
    lc = kwsysString_tolower(*l++);
2738
0
    rc = kwsysString_tolower(*r++);
2739
0
    count++;
2740
0
    if (count >= n) {
2741
0
      return lc - rc;
2742
0
    }
2743
0
  } while (lc == rc && lc);
2744
0
  return lc - rc;
2745
0
}
2746
2747
// return file's modified time
2748
long int SystemTools::ModifiedTime(std::string const& filename)
2749
0
{
2750
0
  long int mt = 0;
2751
#ifdef _WIN32
2752
  WIN32_FILE_ATTRIBUTE_DATA fs;
2753
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2754
                           GetFileExInfoStandard, &fs) != 0) {
2755
    mt = windows_filetime_to_posix_time(fs.ftLastWriteTime);
2756
  }
2757
#else
2758
0
  struct stat fs;
2759
0
  if (stat(filename.c_str(), &fs) == 0) {
2760
0
    mt = static_cast<long int>(fs.st_mtime);
2761
0
  }
2762
0
#endif
2763
0
  return mt;
2764
0
}
2765
2766
// return file's creation time
2767
long int SystemTools::CreationTime(std::string const& filename)
2768
0
{
2769
0
  long int ct = 0;
2770
#ifdef _WIN32
2771
  WIN32_FILE_ATTRIBUTE_DATA fs;
2772
  if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2773
                           GetFileExInfoStandard, &fs) != 0) {
2774
    ct = windows_filetime_to_posix_time(fs.ftCreationTime);
2775
  }
2776
#else
2777
0
  struct stat fs;
2778
0
  if (stat(filename.c_str(), &fs) == 0) {
2779
0
    ct = fs.st_ctime >= 0 ? static_cast<long int>(fs.st_ctime) : 0;
2780
0
  }
2781
0
#endif
2782
0
  return ct;
2783
0
}
2784
2785
std::string SystemTools::GetLastSystemError()
2786
0
{
2787
0
  int e = errno;
2788
0
  return strerror(e);
2789
0
}
2790
2791
Status SystemTools::RemoveFile(std::string const& source)
2792
53.6k
{
2793
#ifdef _WIN32
2794
  std::wstring const& ws = Encoding::ToWindowsExtendedPath(source);
2795
  if (DeleteFileW(ws.c_str())) {
2796
    return Status::Success();
2797
  }
2798
  DWORD err = GetLastError();
2799
  if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
2800
    return Status::Success();
2801
  }
2802
  if (err != ERROR_ACCESS_DENIED) {
2803
    return Status::Windows(err);
2804
  }
2805
  /* The file may be read-only.  Try adding write permission.  */
2806
  mode_t mode;
2807
  if (!SystemTools::GetPermissions(source, mode) ||
2808
      !SystemTools::SetPermissions(source, S_IWRITE)) {
2809
    SetLastError(err);
2810
    return Status::Windows(err);
2811
  }
2812
2813
  const DWORD DIRECTORY_SOFT_LINK_ATTRS =
2814
    FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT;
2815
  DWORD attrs = GetFileAttributesW(ws.c_str());
2816
  if (attrs != INVALID_FILE_ATTRIBUTES &&
2817
      (attrs & DIRECTORY_SOFT_LINK_ATTRS) == DIRECTORY_SOFT_LINK_ATTRS &&
2818
      RemoveDirectoryW(ws.c_str())) {
2819
    return Status::Success();
2820
  }
2821
  if (DeleteFileW(ws.c_str()) || GetLastError() == ERROR_FILE_NOT_FOUND ||
2822
      GetLastError() == ERROR_PATH_NOT_FOUND) {
2823
    return Status::Success();
2824
  }
2825
  /* Try to restore the original permissions.  */
2826
  SystemTools::SetPermissions(source, mode);
2827
  SetLastError(err);
2828
  return Status::Windows(err);
2829
#else
2830
53.6k
  if (unlink(source.c_str()) != 0 && errno != ENOENT) {
2831
36.9k
    return Status::POSIX_errno();
2832
36.9k
  }
2833
16.6k
  return Status::Success();
2834
53.6k
#endif
2835
53.6k
}
2836
2837
Status SystemTools::RemoveADirectory(std::string const& source)
2838
270k
{
2839
  // Add read and write permission to the directory so we can read
2840
  // and modify its content to remove files and directories from it.
2841
270k
  mode_t mode = 0;
2842
270k
  if (SystemTools::GetPermissions(source, mode)) {
2843
#if defined(_WIN32) && !defined(__CYGWIN__)
2844
    mode |= S_IREAD | S_IWRITE;
2845
#else
2846
267k
    mode |= S_IRUSR | S_IWUSR;
2847
267k
#endif
2848
267k
    SystemTools::SetPermissions(source, mode);
2849
267k
  }
2850
2851
270k
  Status status;
2852
270k
  Directory dir;
2853
270k
  status = dir.Load(source);
2854
270k
  if (!status.IsSuccess()) {
2855
3.02k
    return status;
2856
3.02k
  }
2857
2858
267k
  size_t fileNum;
2859
735k
  for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
2860
723k
    std::string const& filename = dir.GetFileName(fileNum);
2861
723k
    if (filename != "." && filename != "..") {
2862
262k
      std::string fullPath = source;
2863
262k
      fullPath += '/';
2864
262k
      fullPath += filename;
2865
262k
      if (SystemTools::FileIsDirectory(fullPath) &&
2866
224k
          !SystemTools::FileIsSymlink(fullPath)) {
2867
224k
        status = SystemTools::RemoveADirectory(fullPath);
2868
224k
        if (!status.IsSuccess()) {
2869
218k
          return status;
2870
218k
        }
2871
224k
      } else {
2872
38.2k
        status = SystemTools::RemoveFile(fullPath);
2873
38.2k
        if (!status.IsSuccess()) {
2874
36.9k
          return status;
2875
36.9k
        }
2876
38.2k
      }
2877
262k
    }
2878
723k
  }
2879
2880
12.2k
  if (Rmdir(source) != 0) {
2881
0
    status = Status::POSIX_errno();
2882
0
  }
2883
12.2k
  return status;
2884
267k
}
2885
2886
/**
2887
 */
2888
size_t SystemTools::GetMaximumFilePathLength()
2889
0
{
2890
0
  return KWSYS_SYSTEMTOOLS_MAXPATH;
2891
0
}
2892
2893
/**
2894
 * Find the file the given name.  Searches the given path and then
2895
 * the system search path.  Returns the full path to the file if it is
2896
 * found.  Otherwise, the empty string is returned.
2897
 */
2898
std::string SystemToolsStatic::FindName(
2899
  std::string const& name, std::vector<std::string> const& userPaths,
2900
  bool no_system_path)
2901
0
{
2902
  // Add the system search path to our path first
2903
0
  std::vector<std::string> path;
2904
0
  if (!no_system_path) {
2905
0
    SystemTools::GetPath(path, "CMAKE_FILE_PATH");
2906
0
    SystemTools::GetPath(path);
2907
0
  }
2908
  // now add the additional paths
2909
0
  path.reserve(path.size() + userPaths.size());
2910
0
  path.insert(path.end(), userPaths.begin(), userPaths.end());
2911
  // now look for the file
2912
0
  for (std::string const& p : path) {
2913
0
    std::string tryPath = p;
2914
0
    if (tryPath.empty() || tryPath.back() != '/') {
2915
0
      tryPath += '/';
2916
0
    }
2917
0
    tryPath += name;
2918
0
    if (SystemTools::FileExists(tryPath)) {
2919
0
      return tryPath;
2920
0
    }
2921
0
  }
2922
  // Couldn't find the file.
2923
0
  return "";
2924
0
}
2925
2926
/**
2927
 * Find the file the given name.  Searches the given path and then
2928
 * the system search path.  Returns the full path to the file if it is
2929
 * found.  Otherwise, the empty string is returned.
2930
 */
2931
std::string SystemTools::FindFile(std::string const& name,
2932
                                  std::vector<std::string> const& userPaths,
2933
                                  bool no_system_path)
2934
0
{
2935
0
  std::string tryPath =
2936
0
    SystemToolsStatic::FindName(name, userPaths, no_system_path);
2937
0
  if (!tryPath.empty() && !SystemTools::FileIsDirectory(tryPath)) {
2938
0
    return SystemTools::CollapseFullPath(tryPath);
2939
0
  }
2940
  // Couldn't find the file.
2941
0
  return "";
2942
0
}
2943
2944
/**
2945
 * Find the directory the given name.  Searches the given path and then
2946
 * the system search path.  Returns the full path to the directory if it is
2947
 * found.  Otherwise, the empty string is returned.
2948
 */
2949
std::string SystemTools::FindDirectory(
2950
  std::string const& name, std::vector<std::string> const& userPaths,
2951
  bool no_system_path)
2952
0
{
2953
0
  std::string tryPath =
2954
0
    SystemToolsStatic::FindName(name, userPaths, no_system_path);
2955
0
  if (!tryPath.empty() && SystemTools::FileIsDirectory(tryPath)) {
2956
0
    return SystemTools::CollapseFullPath(tryPath);
2957
0
  }
2958
  // Couldn't find the file.
2959
0
  return "";
2960
0
}
2961
2962
/**
2963
 * Find the executable with the given name.  Searches the given path and then
2964
 * the system search path.  Returns the full path to the executable if it is
2965
 * found.  Otherwise, the empty string is returned.
2966
 */
2967
std::string SystemTools::FindProgram(std::string const& name,
2968
                                     std::vector<std::string> const& userPaths,
2969
                                     bool no_system_path)
2970
0
{
2971
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
2972
  std::vector<std::string> extensions;
2973
  // check to see if the name already has a .xxx at
2974
  // the end of it
2975
  // on windows try .com then .exe
2976
  if (name.size() <= 3 || name[name.size() - 4] != '.') {
2977
    extensions.emplace_back(".com");
2978
    extensions.emplace_back(".exe");
2979
2980
    // first try with extensions if the os supports them
2981
    for (std::string const& ext : extensions) {
2982
      std::string tryPath = name;
2983
      tryPath += ext;
2984
      if (SystemTools::FileIsExecutable(tryPath)) {
2985
        return SystemTools::CollapseFullPath(tryPath);
2986
      }
2987
    }
2988
  }
2989
#endif
2990
2991
  // now try just the name
2992
0
  if (SystemTools::FileIsExecutable(name)) {
2993
0
    return SystemTools::CollapseFullPath(name);
2994
0
  }
2995
  // now construct the path
2996
0
  std::vector<std::string> path;
2997
  // Add the system search path to our path.
2998
0
  if (!no_system_path) {
2999
0
    SystemTools::GetPath(path);
3000
0
  }
3001
  // now add the additional paths
3002
0
  path.reserve(path.size() + userPaths.size());
3003
0
  path.insert(path.end(), userPaths.begin(), userPaths.end());
3004
  // Add a trailing slash to all paths to aid the search process.
3005
0
  for (std::string& p : path) {
3006
0
    if (p.empty() || p.back() != '/') {
3007
0
      p += '/';
3008
0
    }
3009
0
  }
3010
  // Try each path
3011
0
  for (std::string& p : path) {
3012
#ifdef _WIN32
3013
    // Remove double quotes from the path on windows
3014
    SystemTools::ReplaceString(p, "\"", "");
3015
#endif
3016
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
3017
    // first try with extensions
3018
    for (std::string const& ext : extensions) {
3019
      std::string tryPath = p;
3020
      tryPath += name;
3021
      tryPath += ext;
3022
      if (SystemTools::FileIsExecutable(tryPath)) {
3023
        return SystemTools::CollapseFullPath(tryPath);
3024
      }
3025
    }
3026
#endif
3027
    // now try it without them
3028
0
    std::string tryPath = p;
3029
0
    tryPath += name;
3030
0
    if (SystemTools::FileIsExecutable(tryPath)) {
3031
0
      return SystemTools::CollapseFullPath(tryPath);
3032
0
    }
3033
0
  }
3034
  // Couldn't find the program.
3035
0
  return "";
3036
0
}
3037
3038
std::string SystemTools::GetRealPath(std::string const& path,
3039
                                     std::string* errorMessage)
3040
10
{
3041
10
  std::string ret;
3042
10
  Realpath(path, ret, errorMessage);
3043
10
  return ret;
3044
10
}
3045
3046
// Remove any trailing slash from the name except in a root component.
3047
static char const* RemoveTrailingSlashes(
3048
  std::string const& inName, char (&local_buffer)[KWSYS_SYSTEMTOOLS_MAXPATH],
3049
  std::string& string_buffer)
3050
287k
{
3051
287k
  size_t length = inName.size();
3052
287k
  char const* name = inName.c_str();
3053
3054
287k
  if (length == 0) {
3055
0
    return name;
3056
0
  }
3057
3058
287k
  size_t last = length - 1;
3059
287k
  if (last > 0 && (name[last] == '/' || name[last] == '\\') &&
3060
32.9k
      name[last - 1] != ':') {
3061
32.9k
    if (last < sizeof(local_buffer)) {
3062
32.9k
      memcpy(local_buffer, name, last);
3063
32.9k
      local_buffer[last] = '\0';
3064
32.9k
      name = local_buffer;
3065
32.9k
    } else {
3066
0
      string_buffer.append(name, last);
3067
0
      name = string_buffer.c_str();
3068
0
    }
3069
32.9k
  }
3070
3071
287k
  return name;
3072
287k
}
3073
3074
bool SystemTools::FileIsDirectory(std::string const& inName)
3075
287k
{
3076
287k
  if (inName.empty()) {
3077
0
    return false;
3078
0
  }
3079
3080
287k
  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3081
287k
  std::string string_buffer;
3082
287k
  auto const name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
3083
3084
// Now check the file node type.
3085
#if defined(_WIN32)
3086
  DWORD attr =
3087
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
3088
  return (attr != INVALID_FILE_ATTRIBUTES) &&
3089
    (attr & FILE_ATTRIBUTE_DIRECTORY);
3090
#else
3091
287k
  struct stat fs;
3092
3093
287k
  return (stat(name, &fs) == 0) && S_ISDIR(fs.st_mode);
3094
287k
#endif
3095
287k
}
3096
3097
bool SystemTools::FileIsExecutable(std::string const& inName)
3098
0
{
3099
0
  if (inName.empty()) {
3100
0
    return false;
3101
0
  }
3102
3103
#ifdef _WIN32
3104
  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3105
  std::string string_buffer;
3106
  auto const name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
3107
  auto const attr =
3108
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
3109
3110
  // On Windows any file that exists and is not a directory is considered
3111
  // readable and therefore also executable:
3112
  return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
3113
#else
3114
0
  return !FileIsDirectory(inName) && TestFileAccess(inName, TEST_FILE_EXECUTE);
3115
0
#endif
3116
0
}
3117
3118
#if defined(_WIN32)
3119
bool SystemTools::FileIsSymlinkWithAttr(std::wstring const& path,
3120
                                        unsigned long attr)
3121
{
3122
  if (attr != INVALID_FILE_ATTRIBUTES) {
3123
    if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
3124
      // FILE_ATTRIBUTE_REPARSE_POINT means:
3125
      // * a file or directory that has an associated reparse point, or
3126
      // * a file that is a symbolic link.
3127
      HANDLE hFile = CreateFileW(
3128
        path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
3129
        FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3130
      if (hFile == INVALID_HANDLE_VALUE) {
3131
        return false;
3132
      }
3133
      byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3134
      DWORD bytesReturned = 0;
3135
      if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3136
                           MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3137
                           nullptr)) {
3138
        CloseHandle(hFile);
3139
        // Since FILE_ATTRIBUTE_REPARSE_POINT is set this file must be
3140
        // a symbolic link if it is not a reparse point.
3141
        return GetLastError() == ERROR_NOT_A_REPARSE_POINT;
3142
      }
3143
      CloseHandle(hFile);
3144
      ULONG reparseTag =
3145
        reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0])->ReparseTag;
3146
      return (reparseTag == IO_REPARSE_TAG_SYMLINK) ||
3147
        (reparseTag == IO_REPARSE_TAG_MOUNT_POINT);
3148
    }
3149
    return false;
3150
  }
3151
3152
  return false;
3153
}
3154
#endif
3155
3156
bool SystemTools::FileIsSymlink(std::string const& name)
3157
224k
{
3158
#if defined(_WIN32)
3159
  std::wstring path = Encoding::ToWindowsExtendedPath(name);
3160
  return FileIsSymlinkWithAttr(path, GetFileAttributesW(path.c_str()));
3161
#else
3162
224k
  struct stat fs;
3163
224k
  return (lstat(name.c_str(), &fs) == 0) && S_ISLNK(fs.st_mode);
3164
224k
#endif
3165
224k
}
3166
3167
bool SystemTools::FileIsFIFO(std::string const& name)
3168
0
{
3169
#if defined(_WIN32)
3170
  HANDLE hFile =
3171
    CreateFileW(Encoding::ToWide(name).c_str(), GENERIC_READ, FILE_SHARE_READ,
3172
                nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3173
  if (hFile == INVALID_HANDLE_VALUE) {
3174
    return false;
3175
  }
3176
  const DWORD type = GetFileType(hFile);
3177
  CloseHandle(hFile);
3178
  return type == FILE_TYPE_PIPE;
3179
#else
3180
0
  struct stat fs;
3181
0
  return (lstat(name.c_str(), &fs) == 0) && S_ISFIFO(fs.st_mode);
3182
0
#endif
3183
0
}
3184
3185
Status SystemTools::ReadSymlink(std::string const& newName,
3186
                                std::string& origName)
3187
0
{
3188
#if defined(_WIN32) && !defined(__CYGWIN__)
3189
  std::wstring newPath = Encoding::ToWindowsExtendedPath(newName);
3190
  // FILE_ATTRIBUTE_REPARSE_POINT means:
3191
  // * a file or directory that has an associated reparse point, or
3192
  // * a file that is a symbolic link.
3193
  HANDLE hFile = CreateFileW(
3194
    newPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
3195
    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3196
  if (hFile == INVALID_HANDLE_VALUE) {
3197
    return Status::Windows_GetLastError();
3198
  }
3199
  byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3200
  DWORD bytesReturned = 0;
3201
  Status status;
3202
  if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3203
                       MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3204
                       nullptr)) {
3205
    status = Status::Windows_GetLastError();
3206
  }
3207
  CloseHandle(hFile);
3208
  if (!status.IsSuccess()) {
3209
    return status;
3210
  }
3211
  PREPARSE_DATA_BUFFER data =
3212
    reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
3213
  USHORT substituteNameLength;
3214
  PCWSTR substituteNameData;
3215
  if (data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
3216
    substituteNameLength =
3217
      data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
3218
    substituteNameData = data->SymbolicLinkReparseBuffer.PathBuffer +
3219
      data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
3220
  } else if (data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
3221
    substituteNameLength =
3222
      data->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
3223
    substituteNameData = data->MountPointReparseBuffer.PathBuffer +
3224
      data->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
3225
  } else if (data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
3226
    // The reparse buffer is a list of 0-terminated non-empty strings,
3227
    // terminated by an empty string (0-0).  We need the third string.
3228
    size_t destLen;
3229
    substituteNameData = GetAppExecLink(data, destLen);
3230
    if (!substituteNameData || destLen == 0) {
3231
      return Status::Windows(ERROR_SYMLINK_NOT_SUPPORTED);
3232
    }
3233
    substituteNameLength = static_cast<USHORT>(destLen);
3234
  } else {
3235
    return Status::Windows(ERROR_REPARSE_TAG_MISMATCH);
3236
  }
3237
  std::wstring substituteName(substituteNameData, substituteNameLength);
3238
  origName = Encoding::ToNarrow(substituteName);
3239
  // Symbolic links to absolute paths may use a NT Object Path prefix.
3240
  // If the path begins with "\??\UNC\", replace it with "\\".
3241
  // Otherwise, if the path begins with "\??\", remove the prefix.
3242
  if (origName.compare(0, 8, "\\??\\UNC\\") == 0) {
3243
    origName.erase(1, 6);
3244
  } else if (origName.compare(0, 4, "\\??\\") == 0) {
3245
    origName.erase(0, 4);
3246
  }
3247
#else
3248
0
  char buf[KWSYS_SYSTEMTOOLS_MAXPATH + 1];
3249
0
  int count = static_cast<int>(
3250
0
    readlink(newName.c_str(), buf, KWSYS_SYSTEMTOOLS_MAXPATH));
3251
0
  if (count < 0) {
3252
0
    return Status::POSIX_errno();
3253
0
  }
3254
  // Add null-terminator.
3255
0
  buf[count] = 0;
3256
0
  origName = buf;
3257
0
#endif
3258
0
  return Status::Success();
3259
0
}
3260
3261
Status SystemTools::ChangeDirectory(std::string const& dir)
3262
61.3k
{
3263
61.3k
  if (Chdir(dir) < 0) {
3264
0
    return Status::POSIX_errno();
3265
0
  }
3266
61.3k
  return Status::Success();
3267
61.3k
}
3268
3269
std::string SystemTools::GetCurrentWorkingDirectory()
3270
21.4k
{
3271
21.4k
  char buf[2048];
3272
21.4k
  char const* cwd = Getcwd(buf, 2048);
3273
21.4k
  std::string path;
3274
21.4k
  if (cwd) {
3275
21.4k
    path = cwd;
3276
21.4k
    SystemTools::ConvertToUnixSlashes(path);
3277
21.4k
  }
3278
21.4k
  return path;
3279
21.4k
}
3280
3281
std::string SystemTools::GetProgramPath(std::string const& in_name)
3282
0
{
3283
0
  std::string dir, file;
3284
0
  SystemTools::SplitProgramPath(in_name, dir, file);
3285
0
  return dir;
3286
0
}
3287
3288
bool SystemTools::SplitProgramPath(std::string const& in_name,
3289
                                   std::string& dir, std::string& file, bool)
3290
0
{
3291
0
  dir = in_name;
3292
0
  file.clear();
3293
0
  SystemTools::ConvertToUnixSlashes(dir);
3294
3295
0
  if (!SystemTools::FileIsDirectory(dir)) {
3296
0
    std::string::size_type slashPos = dir.rfind('/');
3297
0
    if (slashPos != std::string::npos) {
3298
0
      file = dir.substr(slashPos + 1);
3299
0
      dir.resize(slashPos);
3300
0
    } else {
3301
0
      file = dir;
3302
0
      dir.clear();
3303
0
    }
3304
0
  }
3305
0
  if (!(dir.empty()) && !SystemTools::FileIsDirectory(dir)) {
3306
0
    std::string oldDir = in_name;
3307
0
    SystemTools::ConvertToUnixSlashes(oldDir);
3308
0
    dir = in_name;
3309
0
    return false;
3310
0
  }
3311
0
  return true;
3312
0
}
3313
3314
static void SystemToolsAppendComponents(
3315
  std::vector<std::string>& out_components,
3316
  std::vector<std::string>::iterator first,
3317
  std::vector<std::string>::iterator last)
3318
51.5k
{
3319
51.5k
  static std::string const up = "..";
3320
51.5k
  static std::string const cur = ".";
3321
1.21M
  for (std::vector<std::string>::const_iterator i = first; i != last; ++i) {
3322
1.16M
    if (*i == up) {
3323
      // Remove the previous component if possible.  Ignore ../ components
3324
      // that try to go above the root.  Keep ../ components if they are
3325
      // at the beginning of a relative path (base path is relative).
3326
6.55k
      if (out_components.size() > 1 && out_components.back() != up) {
3327
5.74k
        out_components.resize(out_components.size() - 1);
3328
5.74k
      } else if (!out_components.empty() && out_components[0].empty()) {
3329
0
        out_components.emplace_back(std::move(*i));
3330
0
      }
3331
1.15M
    } else if (!i->empty() && *i != cur) {
3332
877k
      out_components.emplace_back(std::move(*i));
3333
877k
    }
3334
1.16M
  }
3335
51.5k
}
3336
3337
namespace {
3338
3339
std::string CollapseFullPathImpl(std::string const& in_path,
3340
                                 std::string const* in_base)
3341
45.4k
{
3342
  // Collect the output path components.
3343
45.4k
  std::vector<std::string> out_components;
3344
3345
  // Split the input path components.
3346
45.4k
  std::vector<std::string> path_components;
3347
45.4k
  SystemTools::SplitPath(in_path, path_components);
3348
45.4k
  out_components.reserve(path_components.size());
3349
3350
  // If the input path is relative, start with a base path.
3351
45.4k
  if (path_components[0].empty()) {
3352
6.04k
    std::vector<std::string> base_components;
3353
3354
6.04k
    if (in_base) {
3355
      // Use the given base path.
3356
0
      SystemTools::SplitPath(*in_base, base_components);
3357
6.04k
    } else {
3358
      // Use the current working directory as a base path.
3359
6.04k
      std::string cwd = SystemTools::GetCurrentWorkingDirectory();
3360
6.04k
      SystemTools::SplitPath(cwd, base_components);
3361
6.04k
    }
3362
3363
    // Append base path components to the output path.
3364
6.04k
    out_components.push_back(base_components[0]);
3365
6.04k
    SystemToolsAppendComponents(out_components, base_components.begin() + 1,
3366
6.04k
                                base_components.end());
3367
6.04k
  }
3368
3369
  // Append input path components to the output path.
3370
45.4k
  SystemToolsAppendComponents(out_components, path_components.begin(),
3371
45.4k
                              path_components.end());
3372
3373
  // Transform the path back to a string.
3374
45.4k
  std::string newPath = SystemTools::JoinPath(out_components);
3375
3376
#ifdef _WIN32
3377
  SystemTools::ConvertToUnixSlashes(newPath);
3378
#endif
3379
  // Return the reconstructed path.
3380
45.4k
  return newPath;
3381
45.4k
}
3382
}
3383
3384
std::string SystemTools::CollapseFullPath(std::string const& in_path)
3385
45.4k
{
3386
45.4k
  return CollapseFullPathImpl(in_path, nullptr);
3387
45.4k
}
3388
3389
std::string SystemTools::CollapseFullPath(std::string const& in_path,
3390
                                          char const* in_base)
3391
0
{
3392
0
  if (!in_base) {
3393
0
    return CollapseFullPathImpl(in_path, nullptr);
3394
0
  }
3395
0
  std::string tmp_base = in_base;
3396
0
  return CollapseFullPathImpl(in_path, &tmp_base);
3397
0
}
3398
3399
std::string SystemTools::CollapseFullPath(std::string const& in_path,
3400
                                          std::string const& in_base)
3401
1
{
3402
1
  return CollapseFullPathImpl(in_path, &in_base);
3403
1
}
3404
3405
// compute the relative path from here to there
3406
std::string SystemTools::RelativePath(std::string const& local,
3407
                                      std::string const& remote)
3408
0
{
3409
0
  if (!SystemTools::FileIsFullPath(local)) {
3410
0
    return "";
3411
0
  }
3412
0
  if (!SystemTools::FileIsFullPath(remote)) {
3413
0
    return "";
3414
0
  }
3415
3416
0
  std::string l = SystemTools::CollapseFullPath(local);
3417
0
  std::string r = SystemTools::CollapseFullPath(remote);
3418
3419
  // split up both paths into arrays of strings using / as a separator
3420
0
  std::vector<std::string> localSplit = SystemTools::SplitString(l, '/', true);
3421
0
  std::vector<std::string> remoteSplit =
3422
0
    SystemTools::SplitString(r, '/', true);
3423
0
  std::vector<std::string>
3424
0
    commonPath; // store shared parts of path in this array
3425
0
  std::vector<std::string> finalPath; // store the final relative path here
3426
  // count up how many matching directory names there are from the start
3427
0
  unsigned int sameCount = 0;
3428
0
  while (((sameCount <= (localSplit.size() - 1)) &&
3429
0
          (sameCount <= (remoteSplit.size() - 1))) &&
3430
// for Windows and Apple do a case insensitive string compare
3431
#if defined(_WIN32) || defined(__APPLE__)
3432
         SystemTools::Strucmp(localSplit[sameCount].c_str(),
3433
                              remoteSplit[sameCount].c_str()) == 0
3434
#else
3435
0
         localSplit[sameCount] == remoteSplit[sameCount]
3436
0
#endif
3437
0
  ) {
3438
    // put the common parts of the path into the commonPath array
3439
0
    commonPath.push_back(localSplit[sameCount]);
3440
    // erase the common parts of the path from the original path arrays
3441
0
    localSplit[sameCount] = "";
3442
0
    remoteSplit[sameCount] = "";
3443
0
    sameCount++;
3444
0
  }
3445
3446
  // If there is nothing in common at all then just return the full
3447
  // path.  This is the case only on windows when the paths have
3448
  // different drive letters.  On unix two full paths always at least
3449
  // have the root "/" in common so we will return a relative path
3450
  // that passes through the root directory.
3451
0
  if (sameCount == 0) {
3452
0
    return remote;
3453
0
  }
3454
3455
  // for each entry that is not common in the local path
3456
  // add a ../ to the finalpath array, this gets us out of the local
3457
  // path into the remote dir
3458
0
  for (std::string const& lp : localSplit) {
3459
0
    if (!lp.empty()) {
3460
0
      finalPath.emplace_back("../");
3461
0
    }
3462
0
  }
3463
  // for each entry that is not common in the remote path add it
3464
  // to the final path.
3465
0
  for (std::string const& rp : remoteSplit) {
3466
0
    if (!rp.empty()) {
3467
0
      finalPath.push_back(rp);
3468
0
    }
3469
0
  }
3470
0
  std::string relativePath; // result string
3471
  // now turn the array of directories into a unix path by puttint /
3472
  // between each entry that does not already have one
3473
0
  for (std::string const& fp : finalPath) {
3474
0
    if (!relativePath.empty() && relativePath.back() != '/') {
3475
0
      relativePath += '/';
3476
0
    }
3477
0
    relativePath += fp;
3478
0
  }
3479
0
  return relativePath;
3480
0
}
3481
3482
std::string SystemTools::GetActualCaseForPath(std::string const& p)
3483
0
{
3484
#ifdef _WIN32
3485
  return SystemToolsStatic::GetCasePathName(p);
3486
#else
3487
0
  return p;
3488
0
#endif
3489
0
}
3490
3491
char const* SystemTools::SplitPathRootComponent(std::string const& p,
3492
                                                std::string* root)
3493
90.9k
{
3494
  // Identify the root component.
3495
90.9k
  char const* c = p.c_str();
3496
90.9k
  if ((c[0] == '/' && c[1] == '/') || (c[0] == '\\' && c[1] == '\\')) {
3497
    // Network path.
3498
1.01k
    if (root) {
3499
1.01k
      *root = "//";
3500
1.01k
    }
3501
1.01k
    c += 2;
3502
89.9k
  } else if (c[0] == '/' || c[0] == '\\') {
3503
    // Unix path (or Windows path w/out drive letter).
3504
44.4k
    if (root) {
3505
44.4k
      *root = "/";
3506
44.4k
    }
3507
44.4k
    c += 1;
3508
45.5k
  } else if (c[0] && c[1] == ':' && (c[2] == '/' || c[2] == '\\')) {
3509
    // Windows path.
3510
709
    if (root) {
3511
709
      (*root) = "_:/";
3512
709
      (*root)[0] = c[0];
3513
709
    }
3514
709
    c += 3;
3515
44.8k
  } else if (c[0] && c[1] == ':') {
3516
    // Path relative to a windows drive working directory.
3517
1.49k
    if (root) {
3518
1.49k
      (*root) = "_:";
3519
1.49k
      (*root)[0] = c[0];
3520
1.49k
    }
3521
1.49k
    c += 2;
3522
43.3k
  } else if (c[0] == '~') {
3523
    // Home directory.  The returned root should always have a
3524
    // trailing slash so that appending components as
3525
    // c[0]c[1]/c[2]/... works.  The remaining path returned should
3526
    // skip the first slash if it exists:
3527
    //
3528
    //   "~"    : root = "~/" , return ""
3529
    //   "~/    : root = "~/" , return ""
3530
    //   "~/x   : root = "~/" , return "x"
3531
    //   "~u"   : root = "~u/", return ""
3532
    //   "~u/"  : root = "~u/", return ""
3533
    //   "~u/x" : root = "~u/", return "x"
3534
37.2k
    size_t n = 1;
3535
873k
    while (c[n] && c[n] != '/') {
3536
836k
      ++n;
3537
836k
    }
3538
37.2k
    if (root) {
3539
37.2k
      root->assign(c, n);
3540
37.2k
      *root += '/';
3541
37.2k
    }
3542
37.2k
    if (c[n] == '/') {
3543
1.01k
      ++n;
3544
1.01k
    }
3545
37.2k
    c += n;
3546
37.2k
  } else {
3547
    // Relative path.
3548
6.04k
    if (root) {
3549
6.04k
      *root = "";
3550
6.04k
    }
3551
6.04k
  }
3552
3553
  // Return the remaining path.
3554
90.9k
  return c;
3555
90.9k
}
3556
3557
void SystemTools::SplitPath(std::string const& p,
3558
                            std::vector<std::string>& components,
3559
                            bool expand_home_dir)
3560
90.9k
{
3561
90.9k
  char const* c;
3562
90.9k
  components.clear();
3563
3564
  // Identify the root component.
3565
90.9k
  {
3566
90.9k
    std::string root;
3567
90.9k
    c = SystemTools::SplitPathRootComponent(p, &root);
3568
3569
    // Expand home directory references if requested.
3570
90.9k
    if (expand_home_dir && !root.empty() && root[0] == '~') {
3571
39.4k
      std::string homedir;
3572
39.4k
      root.resize(root.size() - 1);
3573
39.4k
      if (root.size() == 1) {
3574
#if defined(_WIN32) && !defined(__CYGWIN__)
3575
        if (!SystemTools::GetEnv("USERPROFILE", homedir))
3576
#endif
3577
34.0k
          SystemTools::GetEnv("HOME", homedir);
3578
34.0k
      }
3579
5.40k
#ifdef HAVE_GETPWNAM
3580
5.40k
      else if (passwd* pw = getpwnam(root.c_str() + 1)) {
3581
256
        if (pw->pw_dir) {
3582
256
          homedir = pw->pw_dir;
3583
256
        }
3584
256
      }
3585
39.4k
#endif
3586
39.4k
      if (!homedir.empty() &&
3587
34.2k
          (homedir.back() == '/' || homedir.back() == '\\')) {
3588
0
        homedir.resize(homedir.size() - 1);
3589
0
      }
3590
39.4k
      SystemTools::SplitPath(homedir, components);
3591
51.5k
    } else {
3592
51.5k
      components.push_back(root);
3593
51.5k
    }
3594
90.9k
  }
3595
3596
  // Parse the remaining components.
3597
90.9k
  char const* first = c;
3598
90.9k
  char const* last = first;
3599
8.50M
  for (; *last; ++last) {
3600
8.41M
    if (*last == '/' || *last == '\\') {
3601
      // End of a component.  Save it.
3602
1.07M
      components.emplace_back(first, last);
3603
1.07M
      first = last + 1;
3604
1.07M
    }
3605
8.41M
  }
3606
3607
  // Save the last component unless there were no components.
3608
90.9k
  if (last != c) {
3609
46.5k
    components.emplace_back(first, last);
3610
46.5k
  }
3611
90.9k
}
3612
3613
std::string SystemTools::JoinPath(std::vector<std::string> const& components)
3614
45.4k
{
3615
45.4k
  return SystemTools::JoinPath(components.begin(), components.end());
3616
45.4k
}
3617
3618
std::string SystemTools::JoinPath(
3619
  std::vector<std::string>::const_iterator first,
3620
  std::vector<std::string>::const_iterator last)
3621
45.4k
{
3622
  // Construct result in a single string.
3623
45.4k
  std::string result;
3624
45.4k
  size_t len = 0;
3625
923k
  for (auto i = first; i != last; ++i) {
3626
877k
    len += 1 + i->size();
3627
877k
  }
3628
45.4k
  result.reserve(len);
3629
3630
  // The first two components do not add a slash.
3631
45.4k
  if (first != last) {
3632
45.4k
    result.append(*first++);
3633
45.4k
  }
3634
45.4k
  if (first != last) {
3635
44.1k
    result.append(*first++);
3636
44.1k
  }
3637
3638
  // All remaining components are always separated with a slash.
3639
833k
  while (first != last) {
3640
787k
    result.push_back('/');
3641
787k
    result.append((*first++));
3642
787k
  }
3643
3644
  // Return the concatenated result.
3645
45.4k
  return result;
3646
45.4k
}
3647
3648
bool SystemTools::ComparePath(std::string const& c1, std::string const& c2)
3649
0
{
3650
#if defined(_WIN32) || defined(__APPLE__)
3651
#  ifdef _MSC_VER
3652
  return _stricmp(c1.c_str(), c2.c_str()) == 0;
3653
#  elif defined(__APPLE__) || defined(__GNUC__)
3654
  return strcasecmp(c1.c_str(), c2.c_str()) == 0;
3655
#  else
3656
  return SystemTools::Strucmp(c1.c_str(), c2.c_str()) == 0;
3657
#  endif
3658
#else
3659
0
  return c1 == c2;
3660
0
#endif
3661
0
}
3662
3663
bool SystemTools::Split(std::string const& str,
3664
                        std::vector<std::string>& lines, char separator)
3665
0
{
3666
0
  std::string data(str);
3667
0
  std::string::size_type lpos = 0;
3668
0
  while (lpos < data.length()) {
3669
0
    std::string::size_type rpos = data.find_first_of(separator, lpos);
3670
0
    if (rpos == std::string::npos) {
3671
      // String ends at end of string without a separator.
3672
0
      lines.push_back(data.substr(lpos));
3673
0
      return false;
3674
0
    } else {
3675
      // String ends in a separator, remove the character.
3676
0
      lines.push_back(data.substr(lpos, rpos - lpos));
3677
0
    }
3678
0
    lpos = rpos + 1;
3679
0
  }
3680
0
  return true;
3681
0
}
3682
3683
bool SystemTools::Split(std::string const& str,
3684
                        std::vector<std::string>& lines)
3685
0
{
3686
0
  std::string data(str);
3687
0
  std::string::size_type lpos = 0;
3688
0
  while (lpos < data.length()) {
3689
0
    std::string::size_type rpos = data.find_first_of('\n', lpos);
3690
0
    if (rpos == std::string::npos) {
3691
      // Line ends at end of string without a newline.
3692
0
      lines.push_back(data.substr(lpos));
3693
0
      return false;
3694
0
    }
3695
0
    if ((rpos > lpos) && (data[rpos - 1] == '\r')) {
3696
      // Line ends in a "\r\n" pair, remove both characters.
3697
0
      lines.push_back(data.substr(lpos, (rpos - 1) - lpos));
3698
0
    } else {
3699
      // Line ends in a "\n", remove the character.
3700
0
      lines.push_back(data.substr(lpos, rpos - lpos));
3701
0
    }
3702
0
    lpos = rpos + 1;
3703
0
  }
3704
0
  return true;
3705
0
}
3706
3707
std::string SystemTools::Join(std::vector<std::string> const& list,
3708
                              std::string const& separator)
3709
0
{
3710
0
  std::string result;
3711
0
  if (list.empty()) {
3712
0
    return result;
3713
0
  }
3714
3715
0
  size_t total_size = separator.size() * (list.size() - 1);
3716
0
  for (std::string const& string : list) {
3717
0
    total_size += string.size();
3718
0
  }
3719
3720
0
  result.reserve(total_size);
3721
0
  bool needs_separator = false;
3722
0
  for (std::string const& string : list) {
3723
0
    if (needs_separator) {
3724
0
      result += separator;
3725
0
    }
3726
0
    result += string;
3727
0
    needs_separator = true;
3728
0
  }
3729
3730
0
  return result;
3731
0
}
3732
3733
/**
3734
 * Return path of a full filename (no trailing slashes).
3735
 * Warning: returned path is converted to Unix slashes format.
3736
 */
3737
std::string SystemTools::GetFilenamePath(std::string const& filename)
3738
1.66k
{
3739
1.66k
  std::string fn = filename;
3740
1.66k
  SystemTools::ConvertToUnixSlashes(fn);
3741
3742
1.66k
  std::string::size_type slash_pos = fn.rfind('/');
3743
1.66k
  if (slash_pos == 0) {
3744
162
    return "/";
3745
162
  }
3746
1.50k
  if (slash_pos == 2 && fn[1] == ':') {
3747
    // keep the / after a drive letter
3748
3
    fn.resize(3);
3749
3
    return fn;
3750
3
  }
3751
1.50k
  if (slash_pos == std::string::npos) {
3752
706
    return "";
3753
706
  }
3754
798
  fn.resize(slash_pos);
3755
798
  return fn;
3756
1.50k
}
3757
3758
/**
3759
 * Return file name of a full filename (i.e. file name without path).
3760
 */
3761
std::string SystemTools::GetFilenameName(std::string const& filename)
3762
6.66k
{
3763
#if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
3764
  char const* separators = "/\\";
3765
#else
3766
6.66k
  char separators = '/';
3767
6.66k
#endif
3768
6.66k
  std::string::size_type slash_pos = filename.find_last_of(separators);
3769
6.66k
  if (slash_pos != std::string::npos) {
3770
2.28k
    return filename.substr(slash_pos + 1);
3771
4.38k
  } else {
3772
4.38k
    return filename;
3773
4.38k
  }
3774
6.66k
}
3775
3776
/**
3777
 * Return file extension of a full filename (dot included).
3778
 * Warning: this is the longest extension (for example: .tar.gz)
3779
 */
3780
std::string SystemTools::GetFilenameExtension(std::string const& filename)
3781
1.66k
{
3782
1.66k
  std::string name = SystemTools::GetFilenameName(filename);
3783
1.66k
  std::string::size_type dot_pos = name.find('.');
3784
1.66k
  if (dot_pos != std::string::npos) {
3785
403
    name.erase(0, dot_pos);
3786
403
    return name;
3787
1.26k
  } else {
3788
1.26k
    return "";
3789
1.26k
  }
3790
1.66k
}
3791
3792
/**
3793
 * Return file extension of a full filename (dot included).
3794
 * Warning: this is the shortest extension (for example: .gz of .tar.gz)
3795
 */
3796
std::string SystemTools::GetFilenameLastExtension(std::string const& filename)
3797
1.66k
{
3798
1.66k
  std::string name = SystemTools::GetFilenameName(filename);
3799
1.66k
  std::string::size_type dot_pos = name.rfind('.');
3800
1.66k
  if (dot_pos != std::string::npos) {
3801
403
    name.erase(0, dot_pos);
3802
403
    return name;
3803
1.26k
  } else {
3804
1.26k
    return "";
3805
1.26k
  }
3806
1.66k
}
3807
3808
/**
3809
 * Return file name without extension of a full filename (i.e. without path).
3810
 * Warning: it considers the longest extension (for example: .tar.gz)
3811
 */
3812
std::string SystemTools::GetFilenameWithoutExtension(
3813
  std::string const& filename)
3814
1.66k
{
3815
1.66k
  std::string name = SystemTools::GetFilenameName(filename);
3816
1.66k
  std::string::size_type dot_pos = name.find('.');
3817
1.66k
  if (dot_pos != std::string::npos) {
3818
403
    name.resize(dot_pos);
3819
403
  }
3820
1.66k
  return name;
3821
1.66k
}
3822
3823
/**
3824
 * Return file name without extension of a full filename (i.e. without path).
3825
 * Warning: it considers the last extension (for example: removes .gz
3826
 * from .tar.gz)
3827
 */
3828
std::string SystemTools::GetFilenameWithoutLastExtension(
3829
  std::string const& filename)
3830
0
{
3831
0
  std::string name = SystemTools::GetFilenameName(filename);
3832
0
  std::string::size_type dot_pos = name.rfind('.');
3833
0
  if (dot_pos != std::string::npos) {
3834
0
    name.resize(dot_pos);
3835
0
  }
3836
0
  return name;
3837
0
}
3838
3839
bool SystemTools::FileHasSignature(char const* filename, char const* signature,
3840
                                   long offset)
3841
0
{
3842
0
  if (!filename || !signature) {
3843
0
    return false;
3844
0
  }
3845
3846
0
  FILE* fp = Fopen(filename, "rb");
3847
0
  if (!fp) {
3848
0
    return false;
3849
0
  }
3850
3851
0
  fseek(fp, offset, SEEK_SET);
3852
3853
0
  bool res = false;
3854
0
  size_t signature_len = strlen(signature);
3855
0
  char* buffer = new char[signature_len];
3856
3857
0
  if (fread(buffer, 1, signature_len, fp) == signature_len) {
3858
0
    res = (!strncmp(buffer, signature, signature_len) ? true : false);
3859
0
  }
3860
3861
0
  delete[] buffer;
3862
3863
0
  fclose(fp);
3864
0
  return res;
3865
0
}
3866
3867
SystemTools::FileTypeEnum SystemTools::DetectFileType(char const* filename,
3868
                                                      unsigned long length,
3869
                                                      double percent_bin)
3870
0
{
3871
0
  if (!filename || percent_bin < 0) {
3872
0
    return SystemTools::FileTypeUnknown;
3873
0
  }
3874
3875
0
  if (SystemTools::FileIsDirectory(filename)) {
3876
0
    return SystemTools::FileTypeUnknown;
3877
0
  }
3878
3879
0
  FILE* fp = Fopen(filename, "rb");
3880
0
  if (!fp) {
3881
0
    return SystemTools::FileTypeUnknown;
3882
0
  }
3883
3884
  // Allocate buffer and read bytes
3885
3886
0
  auto* buffer = new unsigned char[length];
3887
0
  size_t read_length = fread(buffer, 1, length, fp);
3888
0
  fclose(fp);
3889
0
  if (read_length == 0) {
3890
0
    delete[] buffer;
3891
0
    return SystemTools::FileTypeUnknown;
3892
0
  }
3893
3894
  // Loop over contents and count
3895
3896
0
  size_t text_count = 0;
3897
3898
0
  unsigned char const* ptr = buffer;
3899
0
  unsigned char const* buffer_end = buffer + read_length;
3900
3901
0
  while (ptr != buffer_end) {
3902
0
    if ((*ptr >= 0x20 && *ptr <= 0x7F) || *ptr == '\n' || *ptr == '\r' ||
3903
0
        *ptr == '\t') {
3904
0
      text_count++;
3905
0
    }
3906
0
    ptr++;
3907
0
  }
3908
3909
0
  delete[] buffer;
3910
3911
0
  double current_percent_bin = (static_cast<double>(read_length - text_count) /
3912
0
                                static_cast<double>(read_length));
3913
3914
0
  if (current_percent_bin >= percent_bin) {
3915
0
    return SystemTools::FileTypeBinary;
3916
0
  }
3917
3918
0
  return SystemTools::FileTypeText;
3919
0
}
3920
3921
bool SystemTools::LocateFileInDir(char const* filename, char const* dir,
3922
                                  std::string& filename_found,
3923
                                  int try_filename_dirs)
3924
0
{
3925
0
  if (!filename || !dir) {
3926
0
    return false;
3927
0
  }
3928
3929
  // Get the basename of 'filename'
3930
3931
0
  std::string filename_base = SystemTools::GetFilenameName(filename);
3932
3933
  // Check if 'dir' is really a directory
3934
  // If win32 and matches something like C:, accept it as a dir
3935
3936
0
  std::string real_dir;
3937
0
  if (!SystemTools::FileIsDirectory(dir)) {
3938
#if defined(_WIN32)
3939
    size_t dir_len = strlen(dir);
3940
    if (dir_len < 2 || dir[dir_len - 1] != ':') {
3941
#endif
3942
0
      real_dir = SystemTools::GetFilenamePath(dir);
3943
0
      dir = real_dir.c_str();
3944
#if defined(_WIN32)
3945
    }
3946
#endif
3947
0
  }
3948
3949
  // Try to find the file in 'dir'
3950
3951
0
  bool res = false;
3952
0
  if (!filename_base.empty() && dir) {
3953
0
    size_t dir_len = strlen(dir);
3954
0
    int need_slash =
3955
0
      (dir_len && dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\');
3956
3957
0
    std::string temp = dir;
3958
0
    if (need_slash) {
3959
0
      temp += '/';
3960
0
    }
3961
0
    temp += filename_base;
3962
3963
0
    if (SystemTools::FileExists(temp)) {
3964
0
      res = true;
3965
0
      filename_found = temp;
3966
0
    }
3967
3968
    // If not found, we can try harder by appending part of the file to
3969
    // to the directory to look inside.
3970
    // Example: if we were looking for /foo/bar/yo.txt in /d1/d2, then
3971
    // try to find yo.txt in /d1/d2/bar, then /d1/d2/foo/bar, etc.
3972
3973
0
    else if (try_filename_dirs) {
3974
0
      std::string filename_dir(filename);
3975
0
      std::string filename_dir_base;
3976
0
      std::string filename_dir_bases;
3977
0
      do {
3978
0
        filename_dir = SystemTools::GetFilenamePath(filename_dir);
3979
0
        filename_dir_base = SystemTools::GetFilenameName(filename_dir);
3980
#if defined(_WIN32)
3981
        if (filename_dir_base.empty() || filename_dir_base.back() == ':')
3982
#else
3983
0
        if (filename_dir_base.empty())
3984
0
#endif
3985
0
        {
3986
0
          break;
3987
0
        }
3988
3989
0
        filename_dir_bases = filename_dir_base + '/' + filename_dir_bases;
3990
3991
0
        temp = dir;
3992
0
        if (need_slash) {
3993
0
          temp += '/';
3994
0
        }
3995
0
        temp += filename_dir_bases;
3996
3997
0
        res = SystemTools::LocateFileInDir(filename_base.c_str(), temp.c_str(),
3998
0
                                           filename_found, 0);
3999
4000
0
      } while (!res && !filename_dir_base.empty());
4001
0
    }
4002
0
  }
4003
4004
0
  return res;
4005
0
}
4006
4007
bool SystemTools::FileIsFullPath(std::string const& in_name)
4008
110k
{
4009
110k
  return SystemToolsStatic::FileIsFullPath(in_name.c_str(), in_name.size());
4010
110k
}
4011
4012
bool SystemTools::FileIsFullPath(char const* in_name)
4013
0
{
4014
0
  return SystemToolsStatic::FileIsFullPath(
4015
0
    in_name, in_name[0] ? (in_name[1] ? 2 : 1) : 0);
4016
0
}
4017
4018
bool SystemToolsStatic::FileIsFullPath(char const* in_name, size_t len)
4019
110k
{
4020
#if defined(_WIN32) && !defined(__CYGWIN__)
4021
  // On Windows, the name must be at least two characters long.
4022
  if (len < 2) {
4023
    return false;
4024
  }
4025
  if (in_name[1] == ':') {
4026
    return true;
4027
  }
4028
  if (in_name[0] == '\\') {
4029
    return true;
4030
  }
4031
#else
4032
  // On UNIX, the name must be at least one character long.
4033
110k
  if (len < 1) {
4034
0
    return false;
4035
0
  }
4036
110k
#endif
4037
110k
#if !defined(_WIN32)
4038
110k
  if (in_name[0] == '~') {
4039
38.9k
    return true;
4040
38.9k
  }
4041
71.9k
#endif
4042
  // On UNIX, the name must begin in a '/'.
4043
  // On Windows, if the name begins in a '/', then it is a full
4044
  // network path.
4045
71.9k
  if (in_name[0] == '/') {
4046
4.85k
    return true;
4047
4.85k
  }
4048
67.0k
  return false;
4049
71.9k
}
4050
4051
Status SystemTools::GetShortPath(std::string const& path,
4052
                                 std::string& shortPath)
4053
0
{
4054
#if defined(_WIN32) && !defined(__CYGWIN__)
4055
  std::string tempPath = path; // create a buffer
4056
4057
  // if the path passed in has quotes around it, first remove the quotes
4058
  if (!path.empty() && path[0] == '"' && path.back() == '"') {
4059
    tempPath.resize(path.length() - 1);
4060
    tempPath.erase(0, 1);
4061
  }
4062
4063
  std::wstring wtempPath = Encoding::ToWide(tempPath);
4064
  DWORD ret = GetShortPathNameW(wtempPath.c_str(), nullptr, 0);
4065
  std::vector<wchar_t> buffer(ret);
4066
  if (ret != 0) {
4067
    ret = GetShortPathNameW(wtempPath.c_str(), &buffer[0],
4068
                            static_cast<DWORD>(buffer.size()));
4069
  }
4070
4071
  if (ret == 0) {
4072
    return Status::Windows_GetLastError();
4073
  } else {
4074
    shortPath = Encoding::ToNarrow(&buffer[0]);
4075
    return Status::Success();
4076
  }
4077
#else
4078
0
  shortPath = path;
4079
0
  return Status::Success();
4080
0
#endif
4081
0
}
4082
4083
std::tm SystemTools::LocalTime(std::time_t timep)
4084
0
{
4085
0
  std::tm out;
4086
#if defined(_WIN32) && !defined(__CYGWIN__)
4087
  localtime_s(&out, &timep);
4088
#else
4089
0
  localtime_r(&timep, &out);
4090
0
#endif
4091
0
  return out;
4092
0
}
4093
4094
std::tm SystemTools::GMTime(std::time_t timep)
4095
0
{
4096
0
  std::tm out;
4097
#if defined(_WIN32) && !defined(__CYGWIN__)
4098
  gmtime_s(&out, &timep);
4099
#else
4100
0
  gmtime_r(&timep, &out);
4101
0
#endif
4102
0
  return out;
4103
0
}
4104
4105
std::string SystemTools::GetCurrentDateTime(char const* format)
4106
0
{
4107
0
  char buf[1024];
4108
0
  tm const t = LocalTime(std::time(nullptr));
4109
0
  strftime(buf, sizeof(buf), format, &t);
4110
0
  return std::string(buf);
4111
0
}
4112
4113
std::string SystemTools::MakeCidentifier(std::string const& s)
4114
0
{
4115
0
  std::string str(s);
4116
0
  if (str.find_first_of("0123456789") == 0) {
4117
0
    str = '_' + str;
4118
0
  }
4119
4120
0
  std::string permited_chars("_"
4121
0
                             "abcdefghijklmnopqrstuvwxyz"
4122
0
                             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
4123
0
                             "0123456789");
4124
0
  std::string::size_type pos = 0;
4125
0
  while ((pos = str.find_first_not_of(permited_chars, pos)) !=
4126
0
         std::string::npos) {
4127
0
    str[pos] = '_';
4128
0
  }
4129
0
  return str;
4130
0
}
4131
4132
// Convenience function around std::getline which removes a trailing carriage
4133
// return and can truncate the buffer as needed.  Returns true
4134
// if any data were read before the end-of-file was reached.
4135
bool SystemTools::GetLineFromStream(
4136
  std::istream& is, std::string& line, bool* has_newline /* = 0 */,
4137
  std::string::size_type sizeLimit /* = std::string::npos */)
4138
0
{
4139
  // Start with an empty line.
4140
0
  line = "";
4141
4142
  // Early short circuit return if stream is no good. Just return
4143
  // false and the empty line. (Probably means caller tried to
4144
  // create a file stream with a non-existent file name...)
4145
  //
4146
0
  if (!is) {
4147
0
    if (has_newline) {
4148
0
      *has_newline = false;
4149
0
    }
4150
0
    return false;
4151
0
  }
4152
4153
0
  std::getline(is, line);
4154
0
  bool haveData = !line.empty() || !is.eof();
4155
0
  if (!line.empty()) {
4156
    // Avoid storing a carriage return character.
4157
0
    if (line.back() == '\r') {
4158
0
      line.resize(line.size() - 1);
4159
0
    }
4160
4161
    // if we read too much then truncate the buffer
4162
0
    if (sizeLimit != std::string::npos && line.size() > sizeLimit) {
4163
0
      line.resize(sizeLimit);
4164
0
    }
4165
0
  }
4166
4167
  // Return the results.
4168
0
  if (has_newline) {
4169
0
    *has_newline = !is.eof();
4170
0
  }
4171
0
  return haveData;
4172
0
}
4173
4174
int SystemTools::GetTerminalWidth()
4175
0
{
4176
0
  int width = -1;
4177
0
#ifdef HAVE_TTY_INFO
4178
0
  struct winsize ws;
4179
0
  std::string columns; /* Unix98 environment variable */
4180
0
  if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0 && ws.ws_row > 0) {
4181
0
    width = ws.ws_col;
4182
0
  }
4183
0
  if (!isatty(STDOUT_FILENO)) {
4184
0
    width = -1;
4185
0
  }
4186
0
  if (SystemTools::GetEnv("COLUMNS", columns) && !columns.empty()) {
4187
0
    long t;
4188
0
    char* endptr;
4189
0
    t = strtol(columns.c_str(), &endptr, 0);
4190
0
    if (endptr && !*endptr && (t > 0) && (t < 1000)) {
4191
0
      width = static_cast<int>(t);
4192
0
    }
4193
0
  }
4194
0
  if (width < 9) {
4195
0
    width = -1;
4196
0
  }
4197
0
#endif
4198
0
  return width;
4199
0
}
4200
4201
Status SystemTools::GetPermissions(char const* file, mode_t& mode)
4202
0
{
4203
0
  if (!file) {
4204
0
    return Status::POSIX(EINVAL);
4205
0
  }
4206
0
  return SystemTools::GetPermissions(std::string(file), mode);
4207
0
}
4208
4209
Status SystemTools::GetPermissions(std::string const& file, mode_t& mode)
4210
270k
{
4211
#if defined(_WIN32)
4212
  DWORD attr =
4213
    GetFileAttributesW(Encoding::ToWindowsExtendedPath(file).c_str());
4214
  if (attr == INVALID_FILE_ATTRIBUTES) {
4215
    return Status::Windows_GetLastError();
4216
  }
4217
  if ((attr & FILE_ATTRIBUTE_READONLY) != 0) {
4218
    mode = (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4219
  } else {
4220
    mode = (_S_IWRITE | (_S_IWRITE >> 3) | (_S_IWRITE >> 6)) |
4221
      (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4222
  }
4223
  if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
4224
    mode |= S_IFDIR | (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4225
  } else {
4226
    mode |= S_IFREG;
4227
  }
4228
  size_t dotPos = file.rfind('.');
4229
  char const* ext = dotPos == std::string::npos ? 0 : (file.c_str() + dotPos);
4230
  if (ext &&
4231
      (Strucmp(ext, ".exe") == 0 || Strucmp(ext, ".com") == 0 ||
4232
       Strucmp(ext, ".cmd") == 0 || Strucmp(ext, ".bat") == 0)) {
4233
    mode |= (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4234
  }
4235
#else
4236
270k
  struct stat st;
4237
270k
  if (stat(file.c_str(), &st) < 0) {
4238
3.02k
    return Status::POSIX_errno();
4239
3.02k
  }
4240
267k
  mode = st.st_mode;
4241
267k
#endif
4242
267k
  return Status::Success();
4243
270k
}
4244
4245
Status SystemTools::SetPermissions(char const* file, mode_t mode,
4246
                                   bool honor_umask)
4247
0
{
4248
0
  if (!file) {
4249
0
    return Status::POSIX(EINVAL);
4250
0
  }
4251
0
  return SystemTools::SetPermissions(std::string(file), mode, honor_umask);
4252
0
}
4253
4254
Status SystemTools::SetPermissions(std::string const& file, mode_t mode,
4255
                                   bool honor_umask)
4256
267k
{
4257
267k
  if (!SystemTools::PathExists(file)) {
4258
0
    return Status::POSIX(ENOENT);
4259
0
  }
4260
267k
  if (honor_umask) {
4261
0
    mode_t currentMask = umask(0);
4262
0
    umask(currentMask);
4263
0
    mode &= ~currentMask;
4264
0
  }
4265
#ifdef _WIN32
4266
  if (_wchmod(Encoding::ToWindowsExtendedPath(file).c_str(), mode) < 0)
4267
#else
4268
267k
  if (chmod(file.c_str(), mode) < 0)
4269
0
#endif
4270
0
  {
4271
0
    return Status::POSIX_errno();
4272
0
  }
4273
4274
267k
  return Status::Success();
4275
267k
}
4276
4277
std::string SystemTools::GetParentDirectory(std::string const& fileOrDir)
4278
0
{
4279
0
  return SystemTools::GetFilenamePath(fileOrDir);
4280
0
}
4281
4282
bool SystemTools::IsSubDirectory(std::string const& cSubdir,
4283
                                 std::string const& cDir)
4284
1
{
4285
1
  if (cDir.empty()) {
4286
0
    return false;
4287
0
  }
4288
1
  std::string subdir = cSubdir;
4289
1
  std::string dir = cDir;
4290
1
  SystemTools::ConvertToUnixSlashes(subdir);
4291
1
  SystemTools::ConvertToUnixSlashes(dir);
4292
1
  if (subdir.size() <= dir.size() || dir.empty()) {
4293
1
    return false;
4294
1
  }
4295
0
  bool isRootPath = dir.back() == '/'; // like "/" or "C:/"
4296
0
  size_t expectedSlashPosition = isRootPath ? dir.size() - 1u : dir.size();
4297
0
  if (subdir[expectedSlashPosition] != '/') {
4298
0
    return false;
4299
0
  }
4300
0
  subdir.resize(dir.size());
4301
0
  return SystemTools::ComparePath(subdir, dir);
4302
0
}
4303
4304
void SystemTools::Delay(unsigned int msec)
4305
0
{
4306
#ifdef _WIN32
4307
  Sleep(msec);
4308
#else
4309
  // The sleep function gives 1 second resolution and the usleep
4310
  // function gives 1e-6 second resolution but on some platforms has a
4311
  // maximum sleep time of 1 second.  This could be re-implemented to
4312
  // use select with masked signals or pselect to mask signals
4313
  // atomically.  If select is given empty sets and zero as the max
4314
  // file descriptor but a non-zero timeout it can be used to block
4315
  // for a precise amount of time.
4316
0
  if (msec >= 1000) {
4317
0
    sleep(msec / 1000);
4318
0
    usleep((msec % 1000) * 1000);
4319
0
  } else {
4320
0
    usleep(msec * 1000);
4321
0
  }
4322
0
#endif
4323
0
}
4324
4325
std::string SystemTools::GetOperatingSystemNameAndVersion()
4326
0
{
4327
0
  std::string res;
4328
4329
#ifdef _WIN32
4330
  char buffer[256];
4331
4332
  OSVERSIONINFOEXA osvi;
4333
  BOOL bOsVersionInfoEx;
4334
4335
  ZeroMemory(&osvi, sizeof(osvi));
4336
  osvi.dwOSVersionInfoSize = sizeof(osvi);
4337
4338
#  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4339
#    pragma warning(push)
4340
#    ifdef __INTEL_COMPILER
4341
#      pragma warning(disable : 1478)
4342
#    elif defined __clang__
4343
#      pragma clang diagnostic push
4344
#      pragma clang diagnostic ignored "-Wdeprecated-declarations"
4345
#    else
4346
#      pragma warning(disable : 4996)
4347
#    endif
4348
#  endif
4349
  bOsVersionInfoEx = GetVersionExA((OSVERSIONINFOA*)&osvi);
4350
  if (!bOsVersionInfoEx) {
4351
    return "";
4352
  }
4353
#  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4354
#    ifdef __clang__
4355
#      pragma clang diagnostic pop
4356
#    else
4357
#      pragma warning(pop)
4358
#    endif
4359
#  endif
4360
4361
  switch (osvi.dwPlatformId) {
4362
      // Test for the Windows NT product family.
4363
4364
    case VER_PLATFORM_WIN32_NT:
4365
4366
      // Test for the specific product family.
4367
      if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0) {
4368
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4369
          res += "Microsoft Windows 10";
4370
        } else {
4371
          res += "Microsoft Windows Server 2016 family";
4372
        }
4373
      }
4374
4375
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) {
4376
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4377
          res += "Microsoft Windows 8.1";
4378
        } else {
4379
          res += "Microsoft Windows Server 2012 R2 family";
4380
        }
4381
      }
4382
4383
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) {
4384
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4385
          res += "Microsoft Windows 8";
4386
        } else {
4387
          res += "Microsoft Windows Server 2012 family";
4388
        }
4389
      }
4390
4391
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) {
4392
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4393
          res += "Microsoft Windows 7";
4394
        } else {
4395
          res += "Microsoft Windows Server 2008 R2 family";
4396
        }
4397
      }
4398
4399
      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) {
4400
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4401
          res += "Microsoft Windows Vista";
4402
        } else {
4403
          res += "Microsoft Windows Server 2008 family";
4404
        }
4405
      }
4406
4407
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4408
        res += "Microsoft Windows Server 2003 family";
4409
      }
4410
4411
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
4412
        res += "Microsoft Windows XP";
4413
      }
4414
4415
      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4416
        res += "Microsoft Windows 2000";
4417
      }
4418
4419
      if (osvi.dwMajorVersion <= 4) {
4420
        res += "Microsoft Windows NT";
4421
      }
4422
4423
      // Test for specific product on Windows NT 4.0 SP6 and later.
4424
4425
      if (bOsVersionInfoEx) {
4426
        // Test for the workstation type.
4427
4428
        if (osvi.wProductType == VER_NT_WORKSTATION) {
4429
          if (osvi.dwMajorVersion == 4) {
4430
            res += " Workstation 4.0";
4431
          } else if (osvi.dwMajorVersion == 5) {
4432
            if (osvi.wSuiteMask & VER_SUITE_PERSONAL) {
4433
              res += " Home Edition";
4434
            } else {
4435
              res += " Professional";
4436
            }
4437
          }
4438
        }
4439
4440
        // Test for the server type.
4441
4442
        else if (osvi.wProductType == VER_NT_SERVER) {
4443
          if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4444
            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
4445
              res += " Datacenter Edition";
4446
            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4447
              res += " Enterprise Edition";
4448
            } else if (osvi.wSuiteMask == VER_SUITE_BLADE) {
4449
              res += " Web Edition";
4450
            } else {
4451
              res += " Standard Edition";
4452
            }
4453
          }
4454
4455
          else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4456
            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
4457
              res += " Datacenter Server";
4458
            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4459
              res += " Advanced Server";
4460
            } else {
4461
              res += " Server";
4462
            }
4463
          }
4464
4465
          else if (osvi.dwMajorVersion <= 4) // Windows NT 4.0
4466
          {
4467
            if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4468
              res += " Server 4.0, Enterprise Edition";
4469
            } else {
4470
              res += " Server 4.0";
4471
            }
4472
          }
4473
        }
4474
      }
4475
4476
      // Test for specific product on Windows NT 4.0 SP5 and earlier
4477
4478
      else {
4479
        HKEY hKey;
4480
#  define BUFSIZE 80
4481
        wchar_t szProductType[BUFSIZE];
4482
        DWORD dwBufLen = BUFSIZE;
4483
        LONG lRet;
4484
4485
        lRet =
4486
          RegOpenKeyExW(HKEY_LOCAL_MACHINE,
4487
                        L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
4488
                        0, KEY_QUERY_VALUE, &hKey);
4489
        if (lRet != ERROR_SUCCESS) {
4490
          return "";
4491
        }
4492
4493
        lRet = RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr,
4494
                                (LPBYTE)szProductType, &dwBufLen);
4495
4496
        if ((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE)) {
4497
          return "";
4498
        }
4499
4500
        RegCloseKey(hKey);
4501
4502
        if (lstrcmpiW(L"WINNT", szProductType) == 0) {
4503
          res += " Workstation";
4504
        }
4505
        if (lstrcmpiW(L"LANMANNT", szProductType) == 0) {
4506
          res += " Server";
4507
        }
4508
        if (lstrcmpiW(L"SERVERNT", szProductType) == 0) {
4509
          res += " Advanced Server";
4510
        }
4511
4512
        res += ' ';
4513
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMajorVersion);
4514
        res += buffer;
4515
        res += '.';
4516
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMinorVersion);
4517
        res += buffer;
4518
      }
4519
4520
      // Display service pack (if any) and build number.
4521
4522
      if (osvi.dwMajorVersion == 4 &&
4523
          lstrcmpiA(osvi.szCSDVersion, "Service Pack 6") == 0) {
4524
        HKEY hKey;
4525
        LONG lRet;
4526
4527
        // Test for SP6 versus SP6a.
4528
4529
        lRet = RegOpenKeyExW(
4530
          HKEY_LOCAL_MACHINE,
4531
          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
4532
          0, KEY_QUERY_VALUE, &hKey);
4533
4534
        if (lRet == ERROR_SUCCESS) {
4535
          res += " Service Pack 6a (Build ";
4536
          snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4537
          res += buffer;
4538
          res += ')';
4539
        } else // Windows NT 4.0 prior to SP6a
4540
        {
4541
          res += ' ';
4542
          res += osvi.szCSDVersion;
4543
          res += " (Build ";
4544
          snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4545
          res += buffer;
4546
          res += ')';
4547
        }
4548
4549
        RegCloseKey(hKey);
4550
      } else // Windows NT 3.51 and earlier or Windows 2000 and later
4551
      {
4552
        res += ' ';
4553
        res += osvi.szCSDVersion;
4554
        res += " (Build ";
4555
        snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4556
        res += buffer;
4557
        res += ')';
4558
      }
4559
4560
      break;
4561
4562
      // Test for the Windows 95 product family.
4563
4564
    case VER_PLATFORM_WIN32_WINDOWS:
4565
4566
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) {
4567
        res += "Microsoft Windows 95";
4568
        if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') {
4569
          res += " OSR2";
4570
        }
4571
      }
4572
4573
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) {
4574
        res += "Microsoft Windows 98";
4575
        if (osvi.szCSDVersion[1] == 'A') {
4576
          res += " SE";
4577
        }
4578
      }
4579
4580
      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) {
4581
        res += "Microsoft Windows Millennium Edition";
4582
      }
4583
      break;
4584
4585
    case VER_PLATFORM_WIN32s:
4586
4587
      res += "Microsoft Win32s";
4588
      break;
4589
  }
4590
#endif
4591
4592
0
  return res;
4593
0
}
4594
4595
bool SystemTools::ParseURLProtocol(std::string const& URL,
4596
                                   std::string& protocol,
4597
                                   std::string& dataglom, bool decode)
4598
0
{
4599
  // match 0 entire url
4600
  // match 1 protocol
4601
  // match 2 dataglom following protocol://
4602
0
  kwsys::RegularExpression urlRe(VTK_URL_PROTOCOL_REGEX);
4603
4604
0
  if (!urlRe.find(URL))
4605
0
    return false;
4606
4607
0
  protocol = urlRe.match(1);
4608
0
  dataglom = urlRe.match(2);
4609
4610
0
  if (decode) {
4611
0
    dataglom = DecodeURL(dataglom);
4612
0
  }
4613
4614
0
  return true;
4615
0
}
4616
4617
bool SystemTools::ParseURL(std::string const& URL, std::string& protocol,
4618
                           std::string& username, std::string& password,
4619
                           std::string& hostname, std::string& dataport,
4620
                           std::string& database, bool decode)
4621
0
{
4622
0
  kwsys::RegularExpression urlRe(VTK_URL_REGEX);
4623
0
  if (!urlRe.find(URL))
4624
0
    return false;
4625
4626
  // match 0 URL
4627
  // match 1 protocol
4628
  // match 2 mangled user
4629
  // match 3 username
4630
  // match 4 mangled password
4631
  // match 5 password
4632
  // match 6 hostname
4633
  // match 7 mangled port
4634
  // match 8 dataport
4635
  // match 9 database name
4636
4637
0
  protocol = urlRe.match(1);
4638
0
  username = urlRe.match(3);
4639
0
  password = urlRe.match(5);
4640
0
  hostname = urlRe.match(6);
4641
0
  dataport = urlRe.match(8);
4642
0
  database = urlRe.match(9);
4643
4644
0
  if (decode) {
4645
0
    username = DecodeURL(username);
4646
0
    password = DecodeURL(password);
4647
0
    hostname = DecodeURL(hostname);
4648
0
    dataport = DecodeURL(dataport);
4649
0
    database = DecodeURL(database);
4650
0
  }
4651
4652
0
  return true;
4653
0
}
4654
4655
// ----------------------------------------------------------------------
4656
std::string SystemTools::DecodeURL(std::string const& url)
4657
0
{
4658
0
  kwsys::RegularExpression urlByteRe(VTK_URL_BYTE_REGEX);
4659
0
  std::string ret;
4660
0
  for (size_t i = 0; i < url.length(); i++) {
4661
0
    if (urlByteRe.find(url.substr(i, 3))) {
4662
0
      char bytes[] = { url[i + 1], url[i + 2], '\0' };
4663
0
      ret += static_cast<char>(strtoul(bytes, nullptr, 16));
4664
0
      i += 2;
4665
0
    } else {
4666
0
      ret += url[i];
4667
0
    }
4668
0
  }
4669
0
  return ret;
4670
0
}
4671
4672
// ----------------------------------------------------------------------
4673
// Do NOT initialize.  Default initialization to zero is necessary.
4674
static unsigned int SystemToolsManagerCount;
4675
4676
// SystemToolsManager manages the SystemTools singleton.
4677
// SystemToolsManager should be included in any translation unit
4678
// that will use SystemTools or that implements the singleton
4679
// pattern. It makes sure that the SystemTools singleton is created
4680
// before and destroyed after all other singletons in CMake.
4681
4682
SystemToolsManager::SystemToolsManager()
4683
1.33k
{
4684
1.33k
  if (++SystemToolsManagerCount == 1) {
4685
16
    SystemTools::ClassInitialize();
4686
16
  }
4687
1.33k
}
4688
4689
SystemToolsManager::~SystemToolsManager()
4690
0
{
4691
0
  if (--SystemToolsManagerCount == 0) {
4692
0
    SystemTools::ClassFinalize();
4693
0
  }
4694
0
}
4695
4696
#if defined(__VMS)
4697
// On VMS we configure the run time C library to be more UNIX like.
4698
// https://h71000.www7.hp.com/doc/732final/5763/5763pro_004.html
4699
extern "C" int decc$feature_get_index(char* name);
4700
extern "C" int decc$feature_set_value(int index, int mode, int value);
4701
static int SetVMSFeature(char* name, int value)
4702
{
4703
  int i;
4704
  errno = 0;
4705
  i = decc$feature_get_index(name);
4706
  return i >= 0 && (decc$feature_set_value(i, 1, value) >= 0 || errno == 0);
4707
}
4708
#endif
4709
4710
void SystemTools::ClassInitialize()
4711
16
{
4712
#ifdef __VMS
4713
  SetVMSFeature("DECC$FILENAME_UNIX_ONLY", 1);
4714
#endif
4715
4716
  // Create statics singleton instance
4717
16
  SystemToolsStatics = new SystemToolsStatic;
4718
16
}
4719
4720
void SystemTools::ClassFinalize()
4721
0
{
4722
0
  delete SystemToolsStatics;
4723
0
}
4724
4725
} // namespace KWSYS_NAMESPACE
4726
4727
#if defined(_MSC_VER) && defined(_DEBUG)
4728
#  include <crtdbg.h>
4729
#  include <stdio.h>
4730
#  include <stdlib.h>
4731
namespace KWSYS_NAMESPACE {
4732
4733
static int SystemToolsDebugReport(int, char* message, int* ret)
4734
{
4735
  if (ret) {
4736
    // Pretend user clicked on Retry button in popup.
4737
    *ret = 1;
4738
  }
4739
  fprintf(stderr, "%s", message);
4740
  fflush(stderr);
4741
  return 1; // no further reporting required
4742
}
4743
4744
void SystemTools::EnableMSVCDebugHook()
4745
{
4746
  if (SystemTools::HasEnv("DART_TEST_FROM_DART") ||
4747
      SystemTools::HasEnv("DASHBOARD_TEST_FROM_CTEST")) {
4748
    _CrtSetReportHook(SystemToolsDebugReport);
4749
  }
4750
}
4751
4752
} // namespace KWSYS_NAMESPACE
4753
#else
4754
namespace KWSYS_NAMESPACE {
4755
void SystemTools::EnableMSVCDebugHook()
4756
0
{
4757
0
}
4758
} // namespace KWSYS_NAMESPACE
4759
#endif