Coverage Report

Created: 2026-09-14 06:43

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