Coverage Report

Created: 2026-04-29 07:01

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