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