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