1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
5 # define _POSIX_C_SOURCE 199506L
6 # define _XOPEN_SOURCE_EXTENDED
9 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
10 # define KWSYS_WINDOWS_DIRS
12 # if defined(__SUNPRO_CC)
17 #if defined(_WIN32) && !defined(_WIN32_WINNT)
18 # define _WIN32_WINNT _WIN32_WINNT_VISTA
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)
41 // Work-around CMake dependency scanning limitation. This must
42 // duplicate the above list of headers.
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"
52 # pragma warning(disable : 4786)
55 #if defined(__sgi) && !defined(__GNUC__)
56 # pragma set woff 1375 /* base class destructor not virtual */
62 # include <malloc.h> /* for malloc/free on QNX */
69 #if defined(_WIN32) && !defined(_MSC_VER) && defined(__GNUC__)
70 # include <strings.h> /* for strcasecmp */
77 // support for realpath call
81 # include <sys/ioctl.h>
82 # include <sys/time.h>
83 # include <sys/wait.h>
87 # include <sys/param.h>
90 # include <csignal> /* sigprocmask */
94 # include <linux/fs.h>
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>
106 # include <windows.h>
107 # include <winioctl.h>
108 # ifndef INVALID_FILE_ATTRIBUTES
109 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
111 # ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
112 # define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
114 # if defined(_MSC_VER) && _MSC_VER >= 1800
115 # define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
117 # ifndef IO_REPARSE_TAG_APPEXECLINK
118 # define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL)
120 // from ntifs.h, which can only be used by drivers
121 typedef struct _REPARSE_DATA_BUFFER
124 USHORT ReparseDataLength;
130 USHORT SubstituteNameOffset;
131 USHORT SubstituteNameLength;
132 USHORT PrintNameOffset;
133 USHORT PrintNameLength;
136 } SymbolicLinkReparseBuffer;
139 USHORT SubstituteNameOffset;
140 USHORT SubstituteNameLength;
141 USHORT PrintNameOffset;
142 USHORT PrintNameLength;
144 } MountPointReparseBuffer;
148 } GenericReparseBuffer;
153 // In version 3, there are 4 NUL-terminated strings:
157 // * Application Type
158 } AppExecLinkReparseBuffer;
160 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
163 WCHAR* GetAppExecLink(PREPARSE_DATA_BUFFER data, size_t& len)
165 // We only know the layout of version 3.
166 if (data->AppExecLinkReparseBuffer.Version != 3) {
170 WCHAR* pstr = data->AppExecLinkReparseBuffer.StringList;
172 // Skip the package id and entry point strings.
173 for (int i = 0; i < 2; ++i) {
174 len = std::wcslen(pstr);
181 // The third string is the executable path.
182 len = std::wcslen(pstr);
191 #if !KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H
192 extern char** environ;
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
202 # define HAVE_GETPWNAM 1
203 # define HAVE_TTY_INFO 1
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]+))?/" \
210 #define VTK_URL_BYTE_REGEX "%[0-9a-fA-F][0-9a-fA-F]"
212 # include <sys/utime.h>
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
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))
238 #if defined(_WIN32) && (defined(_MSC_VER) || defined(__MINGW32__))
241 # define _unlink unlink
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
250 # define KWSYS_SYSTEMTOOLS_MAXPATH 16384
253 #if defined(__BEOS__) && !defined(__ZETA__)
254 # include <be/kernel/OS.h>
255 # include <be/storage/Path.h>
257 // BeOS 5 doesn't have usleep(), but it has snooze(), which is identical.
258 static inline void usleep(unsigned int msec)
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)
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.
272 if (snprintf(resolved_path, maxlen, "%s", resolved) < maxlen) {
273 return resolved_path;
276 return nullptr; // something went wrong.
281 static time_t windows_filetime_to_posix_time(const FILETIME& ft)
284 date.HighPart = ft.dwHighDateTime;
285 date.LowPart = ft.dwLowDateTime;
287 // removes the diff between 1970 and 1601
288 date.QuadPart -= ((LONGLONG)(369 * 365 + 89) * 24 * 3600 * 10000000);
290 // converts back from 100-nanoseconds to seconds
291 return date.QuadPart / 10000000;
295 #ifdef KWSYS_WINDOWS_DIRS
298 typedef KWSYS_NAMESPACE::SystemTools::mode_t mode_t;
301 inline int Mkdir(const std::string& dir, const mode_t* mode)
304 _wmkdir(KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str());
305 if (ret == 0 && mode)
306 KWSYS_NAMESPACE::SystemTools::SetPermissions(dir, *mode);
309 inline int Rmdir(const std::string& dir)
312 KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str());
314 inline const char* Getcwd(char* buf, unsigned int len)
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)) {
323 // make sure the drive letter is capital
324 if (nlen > 1 && buf[1] == ':') {
325 buf[0] = toupper(buf[0]);
332 inline int Chdir(const std::string& dir)
334 return _wchdir(KWSYS_NAMESPACE::Encoding::ToWide(dir).c_str());
336 inline void Realpath(const std::string& path, std::string& resolved_path,
337 std::string* errorMessage = nullptr)
339 std::wstring tmp = KWSYS_NAMESPACE::Encoding::ToWide(path);
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) {
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);
360 *errorMessage = "Unknown error.";
365 resolved_path = path;
369 # include <sys/types.h>
373 inline int Mkdir(const std::string& dir, const mode_t* mode)
375 return mkdir(dir.c_str(), mode ? *mode : 00777);
377 inline int Rmdir(const std::string& dir)
379 return rmdir(dir.c_str());
381 inline const char* Getcwd(char* buf, unsigned int len)
383 return getcwd(buf, len);
386 inline int Chdir(const std::string& dir)
388 return chdir(dir.c_str());
390 inline void Realpath(const std::string& path, std::string& resolved_path,
391 std::string* errorMessage = nullptr)
393 char resolved_name[KWSYS_SYSTEMTOOLS_MAXPATH];
396 char* ret = realpath(path.c_str(), resolved_name);
399 } else if (errorMessage) {
401 *errorMessage = strerror(errno);
403 *errorMessage = "Unknown error.";
408 // if path resolution fails, return what was passed in
409 resolved_path = path;
414 #if !defined(_WIN32) && defined(__COMO__)
415 // Hack for como strict mode to avoid defining _SVID_SOURCE or _BSD_SOURCE.
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;
426 namespace KWSYS_NAMESPACE {
428 double SystemTools::GetTime()
430 #if defined(_WIN32) && !defined(__CYGWIN__)
432 GetSystemTimeAsFileTime(&ft);
433 return (429.4967296 * ft.dwHighDateTime + 0.0000001 * ft.dwLowDateTime -
437 gettimeofday(&t, nullptr);
438 return 1.0 * double(t.tv_sec) + 0.000001 * double(t.tv_usec);
442 /* Type of character storing the environment. */
444 typedef wchar_t envchar;
446 using envchar = char;
449 /* Order by environment key only (VAR from VAR=VALUE). */
450 struct kwsysEnvCompare
452 bool operator()(const envchar* l, const envchar* r) const
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);
460 return wcsncmp(l, r, llen) < 0;
462 return wcscmp(l, r) < 0;
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);
470 return strncmp(l, r, llen) < 0;
472 return strcmp(l, r) < 0;
478 class kwsysEnvSet : public std::set<const envchar*, kwsysEnvCompare>
486 Free(const envchar* env)
490 ~Free() { free(const_cast<envchar*>(this->Env)); }
492 Free(const Free&) = delete;
493 Free& operator=(const Free&) = delete;
496 const envchar* Release(const envchar* env)
498 const envchar* old = nullptr;
499 auto i = this->find(env);
500 if (i != this->end()) {
509 struct SystemToolsPathCaseCmp
511 bool operator()(std::string const& l, std::string const& r) const
514 return _stricmp(l.c_str(), r.c_str()) < 0;
515 # elif defined(__GNUC__)
516 return strcasecmp(l.c_str(), r.c_str()) < 0;
518 return SystemTools::Strucmp(l.c_str(), r.c_str()) < 0;
525 * SystemTools static variables singleton class.
527 class SystemToolsStatic
530 using StringMap = std::map<std::string, std::string>;
531 #if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
533 * Path translation table from dir to refdir
534 * Each time 'dir' will be found it will be replace by 'refdir'
536 StringMap TranslationMap;
539 static std::string GetCasePathName(std::string const& pathIn,
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;
548 StringMap Cyg2Win32Map;
552 * Actual implementation of ReplaceString.
554 static void ReplaceString(std::string& source, const char* replace,
555 size_t replaceSize, const std::string& with);
558 * Actual implementation of FileIsFullPath.
560 static bool FileIsFullPath(const char*, size_t);
563 * Find a filename (file or directory) in the system PATH, with
564 * optional extra paths.
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);
572 // Do NOT initialize. Default initialization to zero is necessary.
573 static SystemToolsStatic* SystemToolsStatics;
576 std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn,
579 std::string casePath;
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.
591 std::vector<std::string> path_components;
592 SystemTools::SplitPath(pathIn, path_components);
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]);
601 const char* sep = "";
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++];
609 casePath += path_components[idx++];
613 // Convert case of all components that exist.
614 bool converting = true;
615 for (; idx < path_components.size(); idx++) {
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) {
627 std::string test_str = casePath;
628 test_str += path_components[idx];
630 bool found_in_cache = false;
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;
639 if (!found_in_cache) {
640 WIN32_FIND_DATAW findData;
642 ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData);
643 if (INVALID_HANDLE_VALUE != hFind) {
644 auto case_file_name = Encoding::ToNarrow(findData.cFileName);
646 SystemToolsStatics->FindFileMap.emplace(test_str,
649 path_components[idx] = std::move(case_file_name);
658 casePath += path_components[idx];
663 std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p)
665 std::string casePath;
667 auto it = SystemToolsStatics->PathCaseMap.find(p);
668 if (it != SystemToolsStatics->PathCaseMap.end()) {
669 casePath = it->second;
671 casePath = SystemToolsStatic::GetCasePathName(p, true);
672 SystemToolsStatics->PathCaseMap.emplace(p, casePath);
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)
682 size_t const old_size = path.size();
683 #if defined(_WIN32) && !defined(__CYGWIN__)
684 const char pathSep = ';';
686 const char pathSep = ':';
692 if (!SystemTools::GetEnv(env, pathEnv)) {
696 // A hack to make the below algorithm work.
697 if (!pathEnv.empty() && pathEnv.back() != pathSep) {
700 std::string::size_type start = 0;
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));
711 for (auto i = path.begin() + old_size; i != path.end(); ++i) {
712 SystemTools::ConvertToUnixSlashes(*i);
717 const char* SystemToolsStatic::GetEnvBuffered(const char* key)
720 if (SystemTools::GetEnv(key, env)) {
721 std::string& menv = SystemToolsStatics->EnvMap[key];
723 menv = std::move(env);
731 const char* SystemTools::GetEnv(const char* key)
734 return SystemToolsStatic::GetEnvBuffered(key);
740 const char* SystemTools::GetEnv(const std::string& key)
743 return SystemToolsStatic::GetEnvBuffered(key.c_str());
745 return getenv(key.c_str());
749 bool SystemTools::GetEnv(const char* key, std::string& result)
752 const std::wstring wkey = Encoding::ToWide(key);
753 const wchar_t* wv = _wgetenv(wkey.c_str());
755 result = Encoding::ToNarrow(wv);
759 const char* v = getenv(key);
768 bool SystemTools::GetEnv(const std::string& key, std::string& result)
770 return SystemTools::GetEnv(key.c_str(), result);
773 bool SystemTools::HasEnv(const char* key)
776 const std::wstring wkey = Encoding::ToWide(key);
777 const wchar_t* v = _wgetenv(wkey.c_str());
779 const char* v = getenv(key);
784 bool SystemTools::HasEnv(const std::string& key)
786 return SystemTools::HasEnv(key.c_str());
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)
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());
799 unsetenv(env.c_str());
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)
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;
814 char* buf = sz > sizeof(local_buf) ? (char*)malloc(sz) : local_buf;
818 strncpy(buf, env.c_str(), len);
820 if (putenv(buf) < 0 && errno != EINVAL) {
823 if (buf != local_buf) {
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. */
839 static kwsysEnvSet kwsysUnPutEnvSet;
841 static int kwsysUnPutEnv(std::string const& env)
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());
851 kwsysEnvSet::Free oldEnv(kwsysUnPutEnvSet.Release(newEnv));
852 kwsysUnPutEnvSet.insert(newEnv);
853 return _wputenv(newEnv);
857 /* Manipulate the "environ" global directly. */
858 static int kwsysUnPutEnv(const std::string& env)
860 size_t pos = env.find('=');
861 size_t const len = pos == std::string::npos ? env.size() : pos;
864 while (environ[in]) {
865 if (strlen(environ[in]) > len && environ[in][len] == '=' &&
866 strncmp(env.c_str(), environ[in], len) == 0) {
869 environ[out++] = environ[in++];
879 #if KWSYS_CXX_HAS_SETENV
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)
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;
890 return kwsysUnPutEnv(env) == 0;
894 bool SystemTools::UnPutEnv(const std::string& env)
896 return kwsysUnPutEnv(env) == 0;
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. */
908 # ifdef __INTEL_COMPILER
909 # pragma warning disable 444 /* base has non-virtual destructor */
912 class kwsysEnv : public kwsysEnvSet
917 for (iterator i = this->begin(); i != this->end(); ++i) {
919 const std::string s = Encoding::ToNarrow(*i);
924 free(const_cast<envchar*>(*i));
927 bool Put(const char* env)
930 const std::wstring wEnv = Encoding::ToWide(env);
931 wchar_t* newEnv = _wcsdup(wEnv.c_str());
933 char* newEnv = strdup(env);
935 Free oldEnv(this->Release(newEnv));
936 this->insert(newEnv);
938 return _wputenv(newEnv) == 0;
940 return putenv(newEnv) == 0;
943 bool UnPut(const char* env)
946 const std::wstring wEnv = Encoding::ToWide(env);
947 Free oldEnv(this->Release(wEnv.c_str()));
949 Free oldEnv(this->Release(env));
951 return kwsysUnPutEnv(env) == 0;
955 static kwsysEnv kwsysEnvInstance;
957 bool SystemTools::PutEnv(const std::string& env)
959 return kwsysEnvInstance.Put(env.c_str());
962 bool SystemTools::UnPutEnv(const std::string& env)
964 return kwsysEnvInstance.UnPut(env.c_str());
969 const char* SystemTools::GetExecutableExtension()
971 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__VMS)
978 FILE* SystemTools::Fopen(const std::string& file, const char* mode)
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'),
985 return _wfopen(Encoding::ToWindowsExtendedPath(file).c_str(),
986 trimmedMode.c_str());
988 return fopen(file.c_str(), mode);
992 Status SystemTools::MakeDirectory(const char* path, const mode_t* mode)
995 return Status::POSIX(EINVAL);
997 return SystemTools::MakeDirectory(std::string(path), mode);
1000 Status SystemTools::MakeDirectory(std::string const& path, const mode_t* mode)
1003 return Status::POSIX(EINVAL);
1005 if (SystemTools::PathExists(path)) {
1006 if (SystemTools::FileIsDirectory(path)) {
1007 return Status::Success();
1009 return Status::POSIX(EEXIST);
1011 std::string dir = path;
1012 SystemTools::ConvertToUnixSlashes(dir);
1014 std::string::size_type pos = 0;
1016 while ((pos = dir.find('/', pos)) != std::string::npos) {
1017 // all underlying functions use C strings, so temporarily
1018 // end the string here
1027 if (Mkdir(topdir, mode) != 0 && errno != EEXIST) {
1028 return Status::POSIX_errno();
1031 return Status::Success();
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)
1040 // do while hangs if replaceSize is 0
1041 if (replace.empty()) {
1045 SystemToolsStatic::ReplaceString(source, replace.c_str(), replace.size(),
1049 void SystemTools::ReplaceString(std::string& source, const char* replace,
1052 // do while hangs if replaceSize is 0
1057 SystemToolsStatic::ReplaceString(source, replace, strlen(replace),
1061 void SystemToolsStatic::ReplaceString(std::string& source, const char* replace,
1063 const std::string& with)
1065 const char* src = source.c_str();
1066 char* searchPos = const_cast<char*>(strstr(src, replace));
1068 // get out quick if string is not found
1073 // perform replacements until done
1074 char* orig = strdup(src);
1075 char* currentPos = orig;
1076 searchPos = searchPos - src + orig;
1078 // initialize the result
1079 source.erase(source.begin(), source.end());
1082 source += currentPos;
1083 currentPos = searchPos + replaceSize;
1086 searchPos = strstr(currentPos, replace);
1087 } while (searchPos);
1089 // copy any trailing text
1090 source += currentPos;
1094 #if defined(_WIN32) && !defined(__CYGWIN__)
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
1100 # define KWSYS_ST_KEY_WOW64_32KEY 0x0200
1101 # define KWSYS_ST_KEY_WOW64_64KEY 0x0100
1104 static bool hasPrefix(const std::string& s, const char* pattern,
1105 std::string::size_type spos)
1107 size_t plen = strlen(pattern);
1110 return s.compare(0, plen, pattern) == 0;
1113 static bool SystemToolsParseRegistryKey(const std::string& key,
1114 HKEY& primaryKey, std::wstring& second,
1115 std::string* valuename)
1117 size_t start = key.find('\\');
1118 if (start == std::string::npos) {
1122 size_t valuenamepos = key.find(';');
1123 if (valuenamepos != std::string::npos && valuename) {
1124 *valuename = key.substr(valuenamepos + 1);
1127 second = Encoding::ToWide(key.substr(start + 1, valuenamepos - start - 1));
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;
1144 static DWORD SystemToolsMakeRegistryMode(DWORD mode,
1145 SystemTools::KeyWOW64 view)
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) {
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;
1163 #if defined(_WIN32) && !defined(__CYGWIN__)
1164 bool SystemTools::GetRegistrySubKeys(const std::string& key,
1165 std::vector<std::string>& subkeys,
1168 HKEY primaryKey = HKEY_CURRENT_USER;
1169 std::wstring second;
1170 if (!SystemToolsParseRegistryKey(key, primaryKey, second, nullptr)) {
1175 if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1176 SystemToolsMakeRegistryMode(KEY_READ, view),
1177 &hKey) != ERROR_SUCCESS) {
1181 DWORD dwNameSize = sizeof(name) / sizeof(name[0]);
1184 while (RegEnumKeyW(hKey, i, name, dwNameSize) == ERROR_SUCCESS) {
1185 subkeys.push_back(Encoding::ToNarrow(name));
1195 bool SystemTools::GetRegistrySubKeys(const std::string&,
1196 std::vector<std::string>&, KeyWOW64)
1202 // Read a registry value.
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
1209 #if defined(_WIN32) && !defined(__CYGWIN__)
1210 bool SystemTools::ReadRegistryValue(const std::string& key, std::string& value,
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)) {
1222 if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1223 SystemToolsMakeRegistryMode(KEY_READ, view),
1224 &hKey) != ERROR_SUCCESS) {
1227 DWORD dwType, dwSize;
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);
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);
1251 bool SystemTools::ReadRegistryValue(const std::string&, std::string&, KeyWOW64)
1257 // Write a registry value.
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
1264 #if defined(_WIN32) && !defined(__CYGWIN__)
1265 bool SystemTools::WriteRegistryValue(const std::string& key,
1266 const std::string& value, KeyWOW64 view)
1268 HKEY primaryKey = HKEY_CURRENT_USER;
1269 std::wstring second;
1270 std::string valuename;
1271 if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
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) {
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))) ==
1295 bool SystemTools::WriteRegistryValue(const std::string&, const std::string&,
1302 // Delete a registry value.
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
1309 #if defined(_WIN32) && !defined(__CYGWIN__)
1310 bool SystemTools::DeleteRegistryValue(const std::string& key, KeyWOW64 view)
1312 HKEY primaryKey = HKEY_CURRENT_USER;
1313 std::wstring second;
1314 std::string valuename;
1315 if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
1320 if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1321 SystemToolsMakeRegistryMode(KEY_WRITE, view),
1322 &hKey) != ERROR_SUCCESS) {
1325 if (RegDeleteValue(hKey, (LPTSTR)valuename.c_str()) == ERROR_SUCCESS) {
1333 bool SystemTools::DeleteRegistryValue(const std::string&, KeyWOW64)
1339 bool SystemTools::SameFile(const std::string& file1, const std::string& file2)
1342 HANDLE hFile1, hFile2;
1345 CreateFileW(Encoding::ToWide(file1).c_str(), GENERIC_READ, FILE_SHARE_READ,
1346 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
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);
1354 if (hFile2 != INVALID_HANDLE_VALUE) {
1355 CloseHandle(hFile2);
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);
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) {
1387 bool SystemTools::PathExists(const std::string& path)
1393 return (GetFileAttributesW(Encoding::ToWindowsExtendedPath(path).c_str()) !=
1394 INVALID_FILE_ATTRIBUTES);
1397 return lstat(path.c_str(), &st) == 0;
1401 bool SystemTools::FileExists(const char* filename)
1406 return SystemTools::FileExists(std::string(filename));
1409 bool SystemTools::FileExists(const std::string& filename)
1411 if (filename.empty()) {
1415 const std::wstring path = Encoding::ToWindowsExtendedPath(filename);
1416 DWORD attr = GetFileAttributesW(path.c_str());
1417 if (attr == INVALID_FILE_ATTRIBUTES) {
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);
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);
1435 if (handle == INVALID_HANDLE_VALUE) {
1439 byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1440 DWORD bytesReturned = 0;
1442 if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
1443 MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
1445 CloseHandle(handle);
1449 CloseHandle(handle);
1451 PREPARSE_DATA_BUFFER data =
1452 reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
1454 // Assume that file exists if it is an execution alias.
1455 return data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK;
1458 CloseHandle(handle);
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;
1467 return access(filename.c_str(), R_OK) == 0;
1472 bool SystemTools::FileExists(const char* filename, bool isFile)
1477 return SystemTools::FileExists(std::string(filename), isFile);
1480 bool SystemTools::FileExists(const std::string& filename, bool isFile)
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);
1490 bool SystemTools::TestFileAccess(const char* filename,
1491 TestFilePermissions permissions)
1496 return SystemTools::TestFileAccess(std::string(filename), permissions);
1499 bool SystemTools::TestFileAccess(const std::string& filename,
1500 TestFilePermissions permissions)
1502 if (filename.empty()) {
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;
1513 return _waccess(Encoding::ToWindowsExtendedPath(filename).c_str(),
1516 return access(filename.c_str(), permissions) == 0;
1520 int SystemTools::Stat(const char* path, SystemTools::Stat_t* buf)
1526 return SystemTools::Stat(std::string(path), buf);
1529 int SystemTools::Stat(const std::string& path, SystemTools::Stat_t* buf)
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);
1542 return stat(path.c_str(), buf);
1546 Status SystemTools::Touch(std::string const& filename, bool create)
1548 if (!SystemTools::FileExists(filename)) {
1550 FILE* file = Fopen(filename, "a+b");
1553 return Status::Success();
1555 return Status::POSIX_errno();
1557 return Status::Success();
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);
1565 return Status::Windows_GetLastError();
1568 GetSystemTimeAsFileTime(&mtime);
1569 if (!SetFileTime(h, 0, 0, &mtime)) {
1570 Status status = Status::Windows_GetLastError();
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();
1581 // fall back to utimes
1582 if (utimes(filename.c_str(), nullptr) < 0) {
1583 return Status::POSIX_errno();
1586 return Status::Success();
1589 Status SystemTools::FileTimeCompare(std::string const& f1,
1590 std::string const& f2, int* result)
1592 // Default to same time.
1594 #if !defined(_WIN32) || defined(__CYGWIN__)
1595 // POSIX version. Use stat function to get file modification time.
1597 if (stat(f1.c_str(), &s1) != 0) {
1598 return Status::POSIX_errno();
1601 if (stat(f2.c_str(), &s2) != 0) {
1602 return Status::POSIX_errno();
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) {
1608 } else if (s1.st_mtim.tv_sec > s2.st_mtim.tv_sec) {
1610 } else if (s1.st_mtim.tv_nsec < s2.st_mtim.tv_nsec) {
1612 } else if (s1.st_mtim.tv_nsec > s2.st_mtim.tv_nsec) {
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) {
1619 } else if (s1.st_mtimespec.tv_sec > s2.st_mtimespec.tv_sec) {
1621 } else if (s1.st_mtimespec.tv_nsec < s2.st_mtimespec.tv_nsec) {
1623 } else if (s1.st_mtimespec.tv_nsec > s2.st_mtimespec.tv_nsec) {
1627 // Compare using 1 second resolution.
1628 if (s1.st_mtime < s2.st_mtime) {
1630 } else if (s1.st_mtime > s2.st_mtime) {
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();
1642 if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f2).c_str(),
1643 GetFileExInfoStandard, &f2d)) {
1644 return Status::Windows_GetLastError();
1647 // Compare the file times using resolution provided by system call.
1648 *result = (int)CompareFileTime(&f1d.ftLastWriteTime, &f2d.ftLastWriteTime);
1650 return Status::Success();
1653 // Return a capitalized string (i.e the first letter is uppercased, all other
1655 std::string SystemTools::Capitalized(const std::string& s)
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]));
1669 // Return capitalized words
1670 std::string SystemTools::CapitalizedWords(const std::string& 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]))))
1680 if (isalpha(s[i]) && (i == 0 || isspace(s[i - 1])))
1683 n[i] = static_cast<std::string::value_type>(toupper(s[i]));
1689 // Return uncapitalized words
1690 std::string SystemTools::UnCapitalizedWords(const std::string& 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]))))
1700 if (isalpha(s[i]) && (i == 0 || isspace(s[i - 1])))
1703 n[i] = static_cast<std::string::value_type>(tolower(s[i]));
1709 // only works for words with at least two letters
1710 std::string SystemTools::AddSpaceBetweenCapitalizedWords(const std::string& s)
1714 n.reserve(s.size());
1716 for (size_t i = 1; i < s.size(); i++) {
1717 if (isupper(s[i]) && !isspace(s[i - 1]) && !isupper(s[i - 1])) {
1726 char* SystemTools::AppendStrings(const char* str1, const char* str2)
1729 return SystemTools::DuplicateString(str2);
1732 return SystemTools::DuplicateString(str1);
1734 size_t len1 = strlen(str1);
1735 char* newstr = new char[len1 + strlen(str2) + 1];
1739 strcpy(newstr, str1);
1740 strcat(newstr + len1, str2);
1744 char* SystemTools::AppendStrings(const char* str1, const char* str2,
1748 return SystemTools::AppendStrings(str2, str3);
1751 return SystemTools::AppendStrings(str1, str3);
1754 return SystemTools::AppendStrings(str1, str2);
1757 size_t len1 = strlen(str1), len2 = strlen(str2);
1758 char* newstr = new char[len1 + len2 + strlen(str3) + 1];
1762 strcpy(newstr, str1);
1763 strcat(newstr + len1, str2);
1764 strcat(newstr + len1 + len2, str3);
1768 // Return a lower case string
1769 std::string SystemTools::LowerCase(const std::string& s)
1773 for (size_t i = 0; i < s.size(); i++) {
1774 n[i] = static_cast<std::string::value_type>(tolower(s[i]));
1779 // Return a lower case string
1780 std::string SystemTools::UpperCase(const std::string& s)
1784 for (size_t i = 0; i < s.size(); i++) {
1785 n[i] = static_cast<std::string::value_type>(toupper(s[i]));
1790 // Count char in string
1791 size_t SystemTools::CountChar(const char* str, char c)
1806 // Remove chars in string
1807 char* SystemTools::RemoveChars(const char* str, const char* toremove)
1812 char* clean_str = new char[strlen(str) + 1];
1813 char* ptr = clean_str;
1815 const char* str2 = toremove;
1816 while (*str2 && *str != *str2) {
1828 // Remove chars in string
1829 char* SystemTools::RemoveCharsButUpperHex(const char* str)
1834 char* clean_str = new char[strlen(str) + 1];
1835 char* ptr = clean_str;
1837 if ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'F')) {
1846 // Replace chars in string
1847 char* SystemTools::ReplaceChars(char* str, const char* toreplace,
1853 const char* ptr2 = toreplace;
1855 if (*ptr == *ptr2) {
1866 // Returns if string starts with another string
1867 bool SystemTools::StringStartsWith(const char* str1, const char* str2)
1869 if (!str1 || !str2) {
1872 size_t len1 = strlen(str1), len2 = strlen(str2);
1873 return len1 >= len2 && !strncmp(str1, str2, len2) ? true : false;
1876 // Returns if string starts with another string
1877 bool SystemTools::StringStartsWith(const std::string& str1, const char* str2)
1882 size_t len1 = str1.size(), len2 = strlen(str2);
1883 return len1 >= len2 && !strncmp(str1.c_str(), str2, len2) ? true : false;
1886 // Returns if string ends with another string
1887 bool SystemTools::StringEndsWith(const char* str1, const char* str2)
1889 if (!str1 || !str2) {
1892 size_t len1 = strlen(str1), len2 = strlen(str2);
1893 return len1 >= len2 && !strncmp(str1 + (len1 - len2), str2, len2) ? true
1897 // Returns if string ends with another string
1898 bool SystemTools::StringEndsWith(const std::string& str1, const char* str2)
1903 size_t len1 = str1.size(), len2 = strlen(str2);
1904 return len1 >= len2 && !strncmp(str1.c_str() + (len1 - len2), str2, len2)
1909 // Returns a pointer to the last occurrence of str2 in str1
1910 const char* SystemTools::FindLastString(const char* str1, const char* str2)
1912 if (!str1 || !str2) {
1916 size_t len1 = strlen(str1), len2 = strlen(str2);
1918 const char* ptr = str1 + len1 - len2;
1920 if (!strncmp(ptr, str2, len2)) {
1923 } while (ptr-- != str1);
1930 char* SystemTools::DuplicateString(const char* str)
1933 char* newstr = new char[strlen(str) + 1];
1934 return strcpy(newstr, str);
1939 // Return a cropped string
1940 std::string SystemTools::CropString(const std::string& s, size_t max_len)
1942 if (s.empty() || max_len == 0 || max_len >= s.size()) {
1949 size_t middle = max_len / 2;
1951 n.assign(s, 0, middle);
1952 n += s.substr(s.size() - (max_len - middle));
1957 n[middle - 1] = '.';
1959 n[middle + 1] = '.';
1967 std::vector<std::string> SystemTools::SplitString(const std::string& p,
1968 char sep, bool isPath)
1970 std::string path = p;
1971 std::vector<std::string> paths;
1975 if (isPath && path[0] == '/') {
1976 path.erase(path.begin());
1977 paths.emplace_back("/");
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));
1984 pos2 = path.find(sep, pos1 + 1);
1986 paths.push_back(path.substr(pos1, pos2 - pos1));
1991 int SystemTools::EstimateFormatLength(const char* format, va_list ap)
1997 // Quick-hack attempt at estimating the length of the string.
1998 // Should never under-estimate.
2000 // Start with the length of the format string itself.
2002 size_t length = strlen(format);
2004 // Increase the length for every argument in the format.
2006 const char* cur = format;
2008 if (*cur++ == '%') {
2009 // Skip "%%" since it doesn't correspond to a va_arg.
2011 while (!int(isalpha(*cur))) {
2016 // Check the length of the string.
2017 char* s = va_arg(ap, char*);
2019 length += strlen(s);
2025 // Assume the argument contributes no more than 64 characters.
2028 // Eat the argument.
2029 static_cast<void>(va_arg(ap, double));
2032 // Assume the argument contributes no more than 64 characters.
2035 // Eat the argument.
2036 static_cast<void>(va_arg(ap, int));
2041 // Move past the characters just tested.
2046 return static_cast<int>(length);
2049 std::string SystemTools::EscapeChars(const char* str,
2050 const char* chars_to_escape,
2055 if (!chars_to_escape || !*chars_to_escape) {
2058 n.reserve(strlen(str));
2060 const char* ptr = chars_to_escape;
2077 static void ConvertVMSToUnix(std::string& path)
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) {
2088 pathPart[pos] = '/';
2092 path = "/" + root + "/" + pathPart;
2097 // convert windows slashes to unix slashes
2098 void SystemTools::ConvertToUnixSlashes(std::string& path)
2104 const char* pathCString = path.c_str();
2105 bool hasDoubleSlash = false;
2107 ConvertVMSToUnix(path);
2109 const char* pos0 = pathCString;
2110 for (std::string::size_type pos = 0; *pos0; ++pos) {
2111 if (*pos0 == '\\') {
2115 // Also, reuse the loop to check for slash followed by another slash
2116 if (!hasDoubleSlash && *(pos0 + 1) == '/' && *(pos0 + 2) == '/') {
2118 // However, on windows if the first characters are both slashes,
2119 // then keep them that way, so that network paths can be handled.
2121 hasDoubleSlash = true;
2124 hasDoubleSlash = true;
2131 if (hasDoubleSlash) {
2132 SystemTools::ReplaceString(path, "//", "/");
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);
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];
2151 passwd* pw = getpwnam(path.c_str() + 1);
2154 path.replace(0, idx, pw->pw_dir);
2158 // remove trailing slash if the path is more than
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);
2171 std::wstring SystemTools::ConvertToWindowsExtendedPath(
2172 const std::string& source)
2174 return Encoding::ToWindowsExtendedPath(source);
2178 // change // to /, and escape any spaces in the path
2179 std::string SystemTools::ConvertToUnixOutputPath(const std::string& path)
2181 std::string ret = path;
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) {
2188 // escape spaces and () in the path
2189 if (ret.find_first_of(' ') != std::string::npos) {
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 != '\\') {
2205 std::string SystemTools::ConvertToOutputPath(const std::string& path)
2207 #if defined(_WIN32) && !defined(__CYGWIN__)
2208 return SystemTools::ConvertToWindowsOutputPath(path);
2210 return SystemTools::ConvertToUnixOutputPath(path);
2214 // remove double slashes not at the start
2215 std::string SystemTools::ConvertToWindowsOutputPath(const std::string& path)
2218 // make it big enough for all of path and double quotes
2219 ret.reserve(path.size() + 3);
2220 // put path into the string
2222 std::string::size_type pos = 0;
2223 // first convert all of the slashes
2224 while ((pos = ret.find('/', pos)) != std::string::npos) {
2228 // check for really small paths
2229 if (ret.size() < 2) {
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
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) {
2242 while ((pos = ret.find("\\\\", pos)) != std::string::npos) {
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, '\"');
2256 * Append the filename from the path source to the directory name dir.
2258 static std::string FileInDir(const std::string& source, const std::string& dir)
2260 std::string new_destination = dir;
2261 SystemTools::ConvertToUnixSlashes(new_destination);
2262 return new_destination + '/' + SystemTools::GetFilenameName(source);
2265 Status SystemTools::CopyFileIfDifferent(std::string const& source,
2266 std::string const& destination)
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);
2276 // source and destination are files so do a copy if they
2278 if (SystemTools::FilesDiffer(source, destination)) {
2279 return SystemTools::CopyFileAlways(source, destination);
2282 // at this point the files must be the same so return true
2283 return Status::Success();
2286 #define KWSYS_ST_BUFFER 4096
2288 bool SystemTools::FilesDiffer(const std::string& source,
2289 const std::string& destination)
2293 WIN32_FILE_ATTRIBUTE_DATA statSource;
2294 if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(source).c_str(),
2295 GetFileExInfoStandard, &statSource) == 0) {
2299 WIN32_FILE_ATTRIBUTE_DATA statDestination;
2300 if (GetFileAttributesExW(
2301 Encoding::ToWindowsExtendedPath(destination).c_str(),
2302 GetFileExInfoStandard, &statDestination) == 0) {
2306 if (statSource.nFileSizeHigh != statDestination.nFileSizeHigh ||
2307 statSource.nFileSizeLow != statDestination.nFileSizeLow) {
2311 if (statSource.nFileSizeHigh == 0 && statSource.nFileSizeLow == 0) {
2315 ((__int64)statSource.nFileSizeHigh << 32) + statSource.nFileSizeLow;
2319 struct stat statSource;
2320 if (stat(source.c_str(), &statSource) != 0) {
2324 struct stat statDestination;
2325 if (stat(destination.c_str(), &statDestination) != 0) {
2329 if (statSource.st_size != statDestination.st_size) {
2333 if (statSource.st_size == 0) {
2336 off_t nleft = statSource.st_size;
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));
2344 kwsys::ifstream finSource(source.c_str());
2345 kwsys::ifstream finDestination(destination.c_str());
2347 if (!finSource || !finDestination) {
2351 // Compare the files a block at a time.
2352 char source_buf[KWSYS_ST_BUFFER];
2353 char dest_buf[KWSYS_ST_BUFFER];
2355 // Read a block from each file.
2356 std::streamsize nnext = (nleft > KWSYS_ST_BUFFER)
2358 : static_cast<std::streamsize>(nleft);
2359 finSource.read(source_buf, nnext);
2360 finDestination.read(dest_buf, nnext);
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) {
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) {
2375 // Update the byte count remaining.
2379 // No differences found.
2383 bool SystemTools::TextFilesDiffer(const std::string& path1,
2384 const std::string& path2)
2386 kwsys::ifstream if1(path1.c_str());
2387 kwsys::ifstream if2(path2.c_str());
2393 std::string line1, line2;
2394 bool hasData1 = GetLineFromStream(if1, line1);
2395 bool hasData2 = GetLineFromStream(if2, line2);
2396 if (hasData1 != hasData2) {
2402 if (line1 != line2) {
2409 Status SystemTools::CopyFileContentBlockwise(std::string const& source,
2410 std::string const& destination)
2413 kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
2415 return Status::POSIX_errno();
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);
2424 kwsys::ofstream fout(destination.c_str(),
2425 std::ios::out | std::ios::trunc | std::ios::binary);
2427 return Status::POSIX_errno();
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.
2436 const int bufferSize = 4096;
2437 char buffer[bufferSize];
2439 fin.read(buffer, bufferSize);
2441 fout.write(buffer, fin.gcount());
2447 // Make sure the operating system has finished writing the file
2448 // before closing it. This will ensure the file is finished before
2456 return Status::POSIX_errno();
2459 return Status::Success();
2463 * Clone the source file to the destination file
2465 * If available, the Linux FICLONE ioctl is used to create a check
2466 * copy-on-write clone of the source file.
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
2474 Status SystemTools::CloneFileContent(std::string const& source,
2475 std::string const& destination)
2477 #if defined(__linux) && defined(FICLONE)
2478 int in = open(source.c_str(), O_RDONLY);
2480 return Status::POSIX_errno();
2483 SystemTools::RemoveFile(destination);
2486 open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
2488 Status status = Status::POSIX_errno();
2493 Status status = Status::Success();
2494 if (ioctl(out, FICLONE, in) < 0) {
2495 status = Status::POSIX_errno();
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();
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();
2515 // fall back to utimes
2516 if (utimes(destination.c_str(), nullptr) < 0) {
2517 return Status::POSIX_errno();
2520 return Status::Success();
2524 return Status::POSIX(ENOSYS);
2529 * Copy a file named by "source" to the file named by "destination".
2531 Status SystemTools::CopyFileAlways(std::string const& source,
2532 std::string const& destination)
2536 Status perms = SystemTools::GetPermissions(source, perm);
2537 std::string real_destination = destination;
2539 if (SystemTools::FileIsDirectory(source)) {
2540 status = SystemTools::MakeDirectory(destination);
2541 if (!status.IsSuccess()) {
2545 // If destination is a directory, try to create a file with the same
2546 // name as the source in that directory.
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);
2556 destination_dir = SystemTools::GetFilenamePath(destination);
2558 // If files are the same do not copy
2559 if (SystemTools::SameFile(source, real_destination)) {
2563 // Create destination directory
2564 if (!destination_dir.empty()) {
2565 status = SystemTools::MakeDirectory(destination_dir);
2566 if (!status.IsSuccess()) {
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);
2576 if (!status.IsSuccess()) {
2581 status = SystemTools::SetPermissions(real_destination, perm);
2586 Status SystemTools::CopyAFile(std::string const& source,
2587 std::string const& destination, bool always)
2590 return SystemTools::CopyFileAlways(source, destination);
2592 return SystemTools::CopyFileIfDifferent(source, destination);
2597 * Copy a directory content from "source" directory to the directory named by
2600 Status SystemTools::CopyADirectory(std::string const& source,
2601 std::string const& destination, bool always)
2605 status = dir.Load(source);
2606 if (!status.IsSuccess()) {
2609 status = SystemTools::MakeDirectory(destination);
2610 if (!status.IsSuccess()) {
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;
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()) {
2629 status = SystemTools::CopyAFile(fullPath, destination, always);
2630 if (!status.IsSuccess()) {
2640 // return size of file; also returns zero if no file exists
2641 unsigned long SystemTools::FileLength(const std::string& filename)
2643 unsigned long length = 0;
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
2651 length = ((__int64)fs.nFileSizeHigh << 32) + fs.nFileSizeLow;
2653 length = static_cast<unsigned long>(fs.nFileSizeLow);
2657 if (stat(filename.c_str(), &fs) == 0) {
2658 length = static_cast<unsigned long>(fs.st_size);
2664 int SystemTools::Strucmp(const char* l, const char* r)
2671 } while (lc == rc && lc);
2675 // return file's modified time
2676 long int SystemTools::ModifiedTime(const std::string& filename)
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);
2687 if (stat(filename.c_str(), &fs) == 0) {
2688 mt = static_cast<long int>(fs.st_mtime);
2694 // return file's creation time
2695 long int SystemTools::CreationTime(const std::string& filename)
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);
2706 if (stat(filename.c_str(), &fs) == 0) {
2707 ct = fs.st_ctime >= 0 ? static_cast<long int>(fs.st_ctime) : 0;
2713 std::string SystemTools::GetLastSystemError()
2719 Status SystemTools::RemoveFile(std::string const& source)
2722 std::wstring const& ws = Encoding::ToWindowsExtendedPath(source);
2723 if (DeleteFileW(ws.c_str())) {
2724 return Status::Success();
2726 DWORD err = GetLastError();
2727 if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
2728 return Status::Success();
2730 if (err != ERROR_ACCESS_DENIED) {
2731 return Status::Windows(err);
2733 /* The file may be read-only. Try adding write permission. */
2735 if (!SystemTools::GetPermissions(source, mode) ||
2736 !SystemTools::SetPermissions(source, S_IWRITE)) {
2738 return Status::Windows(err);
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();
2749 if (DeleteFileW(ws.c_str()) || GetLastError() == ERROR_FILE_NOT_FOUND ||
2750 GetLastError() == ERROR_PATH_NOT_FOUND) {
2751 return Status::Success();
2753 /* Try to restore the original permissions. */
2754 SystemTools::SetPermissions(source, mode);
2756 return Status::Windows(err);
2758 if (unlink(source.c_str()) != 0 && errno != ENOENT) {
2759 return Status::POSIX_errno();
2761 return Status::Success();
2765 Status SystemTools::RemoveADirectory(std::string const& source)
2767 // Add write permission to the directory so we can modify its
2768 // content to remove files and directories from it.
2770 if (SystemTools::GetPermissions(source, mode)) {
2771 #if defined(_WIN32) && !defined(__CYGWIN__)
2776 SystemTools::SetPermissions(source, mode);
2781 status = dir.Load(source);
2782 if (!status.IsSuccess()) {
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;
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()) {
2800 status = SystemTools::RemoveFile(fullPath);
2801 if (!status.IsSuccess()) {
2808 if (Rmdir(source) != 0) {
2809 status = Status::POSIX_errno();
2816 size_t SystemTools::GetMaximumFilePathLength()
2818 return KWSYS_SYSTEMTOOLS_MAXPATH;
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.
2826 std::string SystemToolsStatic::FindName(
2827 const std::string& name, const std::vector<std::string>& userPaths,
2828 bool no_system_path)
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);
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) {
2843 if (tryPath.empty() || tryPath.back() != '/') {
2847 if (SystemTools::FileExists(tryPath)) {
2851 // Couldn't find the file.
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.
2860 std::string SystemTools::FindFile(const std::string& name,
2861 const std::vector<std::string>& userPaths,
2862 bool no_system_path)
2864 std::string tryPath =
2865 SystemToolsStatic::FindName(name, userPaths, no_system_path);
2866 if (!tryPath.empty() && !SystemTools::FileIsDirectory(tryPath)) {
2867 return SystemTools::CollapseFullPath(tryPath);
2869 // Couldn't find the file.
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.
2878 std::string SystemTools::FindDirectory(
2879 const std::string& name, const std::vector<std::string>& userPaths,
2880 bool no_system_path)
2882 std::string tryPath =
2883 SystemToolsStatic::FindName(name, userPaths, no_system_path);
2884 if (!tryPath.empty() && SystemTools::FileIsDirectory(tryPath)) {
2885 return SystemTools::CollapseFullPath(tryPath);
2887 // Couldn't find the file.
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.
2896 std::string SystemTools::FindProgram(const char* nameIn,
2897 const std::vector<std::string>& userPaths,
2898 bool no_system_path)
2900 if (!nameIn || !*nameIn) {
2903 return SystemTools::FindProgram(std::string(nameIn), userPaths,
2907 std::string SystemTools::FindProgram(const std::string& name,
2908 const std::vector<std::string>& userPaths,
2909 bool no_system_path)
2911 std::string tryPath;
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
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");
2922 // first try with extensions if the os supports them
2923 for (std::string const& ext : extensions) {
2926 if (SystemTools::FileIsExecutable(tryPath)) {
2927 return SystemTools::CollapseFullPath(tryPath);
2933 // now try just the name
2934 if (SystemTools::FileIsExecutable(name)) {
2935 return SystemTools::CollapseFullPath(name);
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);
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() != '/') {
2953 for (std::string& p : path) {
2955 // Remove double quotes from the path on windows
2956 SystemTools::ReplaceString(p, "\"", "");
2958 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
2959 // first try with extensions
2960 for (std::string const& ext : extensions) {
2964 if (SystemTools::FileIsExecutable(tryPath)) {
2965 return SystemTools::CollapseFullPath(tryPath);
2969 // now try it without them
2972 if (SystemTools::FileIsExecutable(tryPath)) {
2973 return SystemTools::CollapseFullPath(tryPath);
2976 // Couldn't find the program.
2980 std::string SystemTools::FindProgram(const std::vector<std::string>& names,
2981 const std::vector<std::string>& path,
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()) {
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.
2999 std::string SystemTools::FindLibrary(const std::string& name,
3000 const std::vector<std::string>& userPaths)
3002 // See if the executable exists as written.
3003 if (SystemTools::FileExists(name, true)) {
3004 return SystemTools::CollapseFullPath(name);
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() != '/') {
3019 std::string tryPath;
3020 for (std::string const& p : path) {
3021 #if defined(__APPLE__)
3024 tryPath += ".framework";
3025 if (SystemTools::FileIsDirectory(tryPath)) {
3026 return SystemTools::CollapseFullPath(tryPath);
3029 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
3033 if (SystemTools::FileExists(tryPath, true)) {
3034 return SystemTools::CollapseFullPath(tryPath);
3041 if (SystemTools::FileExists(tryPath, true)) {
3042 return SystemTools::CollapseFullPath(tryPath);
3048 if (SystemTools::FileExists(tryPath, true)) {
3049 return SystemTools::CollapseFullPath(tryPath);
3055 if (SystemTools::FileExists(tryPath, true)) {
3056 return SystemTools::CollapseFullPath(tryPath);
3061 tryPath += ".dylib";
3062 if (SystemTools::FileExists(tryPath, true)) {
3063 return SystemTools::CollapseFullPath(tryPath);
3069 if (SystemTools::FileExists(tryPath, true)) {
3070 return SystemTools::CollapseFullPath(tryPath);
3075 // Couldn't find the library.
3079 std::string SystemTools::GetRealPath(const std::string& path,
3080 std::string* errorMessage)
3083 Realpath(path, ret, errorMessage);
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)
3092 size_t length = inName.size();
3093 const char* name = inName.c_str();
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;
3103 string_buffer.append(name, last);
3104 name = string_buffer.c_str();
3111 bool SystemTools::FileIsDirectory(const std::string& inName)
3113 if (inName.empty()) {
3117 char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3118 std::string string_buffer;
3119 const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
3121 // Now check the file node type.
3124 GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
3125 if (attr != INVALID_FILE_ATTRIBUTES) {
3126 return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
3129 if (stat(name, &fs) == 0) {
3130 return S_ISDIR(fs.st_mode);
3137 bool SystemTools::FileIsExecutable(const std::string& inName)
3140 char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3141 std::string string_buffer;
3142 const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
3144 GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
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);
3150 return !FileIsDirectory(inName) && TestFileAccess(inName, TEST_FILE_EXECUTE);
3155 bool SystemTools::FileIsSymlinkWithAttr(const std::wstring& path,
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) {
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,
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;
3181 reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0])->ReparseTag;
3182 return (reparseTag == IO_REPARSE_TAG_SYMLINK) ||
3183 (reparseTag == IO_REPARSE_TAG_MOUNT_POINT);
3192 bool SystemTools::FileIsSymlink(const std::string& name)
3195 std::wstring path = Encoding::ToWindowsExtendedPath(name);
3196 return FileIsSymlinkWithAttr(path, GetFileAttributesW(path.c_str()));
3199 if (lstat(name.c_str(), &fs) == 0) {
3200 return S_ISLNK(fs.st_mode);
3207 bool SystemTools::FileIsFIFO(const std::string& name)
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) {
3216 const DWORD type = GetFileType(hFile);
3218 return type == FILE_TYPE_PIPE;
3221 if (lstat(name.c_str(), &fs) == 0) {
3222 return S_ISFIFO(fs.st_mode);
3229 Status SystemTools::CreateSymlink(std::string const& origName,
3230 std::string const& newName)
3232 #if defined(_WIN32) && !defined(__CYGWIN__)
3234 if (FileIsDirectory(origName)) {
3235 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
3240 std::wstring origPath = Encoding::ToWindowsExtendedPath(origName);
3241 std::wstring newPath = Encoding::ToWindowsExtendedPath(newName);
3244 if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(),
3246 SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
3247 status = Status::Windows_GetLastError();
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();
3260 if (symlink(origName.c_str(), newName.c_str()) < 0) {
3261 return Status::POSIX_errno();
3263 return Status::Success();
3267 Status SystemTools::ReadSymlink(std::string const& newName,
3268 std::string& origName)
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();
3281 byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3282 DWORD bytesReturned = 0;
3284 if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3285 MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3287 status = Status::Windows_GetLastError();
3290 if (!status.IsSuccess()) {
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.
3311 substituteNameData = GetAppExecLink(data, destLen);
3312 if (substituteNameData == nullptr || destLen == 0) {
3313 return Status::Windows(ERROR_SYMLINK_NOT_SUPPORTED);
3315 substituteNameLength = static_cast<USHORT>(destLen);
3317 return Status::Windows(ERROR_REPARSE_TAG_MISMATCH);
3319 std::wstring substituteName(substituteNameData, substituteNameLength);
3320 origName = Encoding::ToNarrow(substituteName);
3322 char buf[KWSYS_SYSTEMTOOLS_MAXPATH + 1];
3323 int count = static_cast<int>(
3324 readlink(newName.c_str(), buf, KWSYS_SYSTEMTOOLS_MAXPATH));
3326 return Status::POSIX_errno();
3328 // Add null-terminator.
3332 return Status::Success();
3335 Status SystemTools::ChangeDirectory(std::string const& dir)
3337 if (Chdir(dir) < 0) {
3338 return Status::POSIX_errno();
3340 return Status::Success();
3343 std::string SystemTools::GetCurrentWorkingDirectory()
3346 const char* cwd = Getcwd(buf, 2048);
3350 SystemTools::ConvertToUnixSlashes(path);
3355 std::string SystemTools::GetProgramPath(const std::string& in_name)
3357 std::string dir, file;
3358 SystemTools::SplitProgramPath(in_name, dir, file);
3362 bool SystemTools::SplitProgramPath(const std::string& in_name,
3363 std::string& dir, std::string& file, bool)
3367 SystemTools::ConvertToUnixSlashes(dir);
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);
3379 if (!(dir.empty()) && !SystemTools::FileIsDirectory(dir)) {
3380 std::string oldDir = in_name;
3381 SystemTools::ConvertToUnixSlashes(oldDir);
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)
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)) {
3400 std::string intdir = ".";
3402 intdir = CMAKE_INTDIR;
3409 self += SystemTools::GetExecutableExtension();
3412 if (installPrefix) {
3413 if (!SystemTools::FileIsExecutable(self)) {
3414 failures.push_back(self);
3415 self = installPrefix;
3420 if (!SystemTools::FileIsExecutable(self)) {
3421 failures.push_back(self);
3422 std::ostringstream msg;
3423 msg << "Can not find the command line program ";
3429 msg << " argv[0] = \"" << argv0 << "\"\n";
3431 msg << " Attempted paths:\n";
3432 for (std::string const& ff : failures) {
3433 msg << " \"" << ff << "\"\n";
3435 errorMsg = msg.str();
3442 #if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
3443 void SystemTools::AddTranslationPath(const std::string& a,
3444 const std::string& b)
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
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() != '/') {
3462 if (!path_b.empty() && path_b.back() != '/') {
3465 if (!(path_a == path_b)) {
3466 SystemToolsStatics->TranslationMap.insert(
3467 SystemToolsStatic::StringMap::value_type(std::move(path_a),
3468 std::move(path_b)));
3474 void SystemTools::AddKeepPath(const std::string& dir)
3477 Realpath(SystemTools::CollapseFullPath(dir), cdir);
3478 SystemTools::AddTranslationPath(cdir, dir);
3481 void SystemTools::CheckTranslationPath(std::string& path)
3483 // Do not translate paths that are too short to have meaningful
3485 if (path.size() < 2) {
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).
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);
3503 // Remove the trailing slash we added before.
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)
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) {
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));
3525 } else if (!i->empty() && *i != cur) {
3526 out_components.emplace_back(std::move(*i));
3533 std::string CollapseFullPathImpl(std::string const& in_path,
3534 std::string const* in_base)
3536 // Collect the output path components.
3537 std::vector<std::string> out_components;
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());
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;
3549 // Use the given base path.
3550 SystemTools::SplitPath(*in_base, base_components);
3552 // Use the current working directory as a base path.
3553 std::string cwd = SystemTools::GetCurrentWorkingDirectory();
3554 SystemTools::SplitPath(cwd, base_components);
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());
3563 // Append input path components to the output path.
3564 SystemToolsAppendComponents(out_components, path_components.begin(),
3565 path_components.end());
3567 // Transform the path back to a string.
3568 std::string newPath = SystemTools::JoinPath(out_components);
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);
3585 SystemTools::CheckTranslationPath(newPath);
3588 newPath = SystemToolsStatics->GetActualCaseForPathCached(newPath);
3589 SystemTools::ConvertToUnixSlashes(newPath);
3591 // Return the reconstructed path.
3596 std::string SystemTools::CollapseFullPath(std::string const& in_path)
3598 return CollapseFullPathImpl(in_path, nullptr);
3601 std::string SystemTools::CollapseFullPath(std::string const& in_path,
3602 const char* in_base)
3605 return CollapseFullPathImpl(in_path, nullptr);
3607 std::string tmp_base = in_base;
3608 return CollapseFullPathImpl(in_path, &tmp_base);
3611 std::string SystemTools::CollapseFullPath(std::string const& in_path,
3612 std::string const& in_base)
3614 return CollapseFullPathImpl(in_path, &in_base);
3617 // compute the relative path from here to there
3618 std::string SystemTools::RelativePath(const std::string& local,
3619 const std::string& remote)
3621 if (!SystemTools::FileIsFullPath(local)) {
3624 if (!SystemTools::FileIsFullPath(remote)) {
3628 std::string l = SystemTools::CollapseFullPath(local);
3629 std::string r = SystemTools::CollapseFullPath(remote);
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
3647 localSplit[sameCount] == remoteSplit[sameCount]
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] = "";
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) {
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) {
3672 finalPath.emplace_back("../");
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) {
3679 finalPath.push_back(rp);
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 += '/';
3691 return relativePath;
3694 std::string SystemTools::GetActualCaseForPath(const std::string& p)
3697 return SystemToolsStatic::GetCasePathName(p, false);
3703 const char* SystemTools::SplitPathRootComponent(const std::string& p,
3706 // Identify the root component.
3707 const char* c = p.c_str();
3708 if ((c[0] == '/' && c[1] == '/') || (c[0] == '\\' && c[1] == '\\')) {
3714 } else if (c[0] == '/' || c[0] == '\\') {
3715 // Unix path (or Windows path w/out drive letter).
3720 } else if (c[0] && c[1] == ':' && (c[2] == '/' || c[2] == '\\')) {
3727 } else if (c[0] && c[1] == ':') {
3728 // Path relative to a windows drive working directory.
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:
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"
3747 while (c[n] && c[n] != '/') {
3765 // Return the remaining path.
3769 void SystemTools::SplitPath(const std::string& p,
3770 std::vector<std::string>& components,
3771 bool expand_home_dir)
3776 // Identify the root component.
3779 c = SystemTools::SplitPathRootComponent(p, &root);
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))
3789 SystemTools::GetEnv("HOME", homedir);
3791 #ifdef HAVE_GETPWNAM
3792 else if (passwd* pw = getpwnam(root.c_str() + 1)) {
3794 homedir = pw->pw_dir;
3798 if (!homedir.empty() &&
3799 (homedir.back() == '/' || homedir.back() == '\\')) {
3800 homedir.resize(homedir.size() - 1);
3802 SystemTools::SplitPath(homedir, components);
3804 components.push_back(root);
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);
3819 // Save the last component unless there were no components.
3821 components.emplace_back(first, last);
3825 std::string SystemTools::JoinPath(const std::vector<std::string>& components)
3827 return SystemTools::JoinPath(components.begin(), components.end());
3830 std::string SystemTools::JoinPath(
3831 std::vector<std::string>::const_iterator first,
3832 std::vector<std::string>::const_iterator last)
3834 // Construct result in a single string.
3837 for (auto i = first; i != last; ++i) {
3838 len += 1 + i->size();
3840 result.reserve(len);
3842 // The first two components do not add a slash.
3843 if (first != last) {
3844 result.append(*first++);
3846 if (first != last) {
3847 result.append(*first++);
3850 // All remaining components are always separated with a slash.
3851 while (first != last) {
3852 result.push_back('/');
3853 result.append((*first++));
3856 // Return the concatenated result.
3860 bool SystemTools::ComparePath(const std::string& c1, const std::string& c2)
3862 #if defined(_WIN32) || defined(__APPLE__)
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;
3868 return SystemTools::Strucmp(c1.c_str(), c2.c_str()) == 0;
3875 bool SystemTools::Split(const std::string& str,
3876 std::vector<std::string>& lines, char separator)
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));
3887 // String ends in a separator, remove the character.
3888 lines.push_back(data.substr(lpos, rpos - lpos));
3895 bool SystemTools::Split(const std::string& str,
3896 std::vector<std::string>& lines)
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));
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));
3911 // Line ends in a "\n", remove the character.
3912 lines.push_back(data.substr(lpos, rpos - lpos));
3919 std::string SystemTools::Join(const std::vector<std::string>& list,
3920 const std::string& separator)
3927 size_t total_size = separator.size() * (list.size() - 1);
3928 for (const std::string& string : list) {
3929 total_size += string.size();
3932 result.reserve(total_size);
3933 bool needs_separator = false;
3934 for (const std::string& string : list) {
3935 if (needs_separator) {
3936 result += separator;
3939 needs_separator = true;
3946 * Return path of a full filename (no trailing slashes).
3947 * Warning: returned path is converted to Unix slashes format.
3949 std::string SystemTools::GetFilenamePath(const std::string& filename)
3951 std::string fn = filename;
3952 SystemTools::ConvertToUnixSlashes(fn);
3954 std::string::size_type slash_pos = fn.rfind('/');
3955 if (slash_pos == 0) {
3958 if (slash_pos == 2 && fn[1] == ':') {
3959 // keep the / after a drive letter
3963 if (slash_pos == std::string::npos) {
3966 fn.resize(slash_pos);
3971 * Return file name of a full filename (i.e. file name without path).
3973 std::string SystemTools::GetFilenameName(const std::string& filename)
3975 #if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
3976 const char* separators = "/\\";
3978 char separators = '/';
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);
3989 * Return file extension of a full filename (dot included).
3990 * Warning: this is the longest extension (for example: .tar.gz)
3992 std::string SystemTools::GetFilenameExtension(const std::string& filename)
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);
4005 * Return file extension of a full filename (dot included).
4006 * Warning: this is the shortest extension (for example: .gz of .tar.gz)
4008 std::string SystemTools::GetFilenameLastExtension(const std::string& filename)
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);
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)
4024 std::string SystemTools::GetFilenameWithoutExtension(
4025 const std::string& filename)
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);
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
4040 std::string SystemTools::GetFilenameWithoutLastExtension(
4041 const std::string& filename)
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);
4051 bool SystemTools::FileHasSignature(const char* filename, const char* signature,
4054 if (!filename || !signature) {
4058 FILE* fp = Fopen(filename, "rb");
4063 fseek(fp, offset, SEEK_SET);
4066 size_t signature_len = strlen(signature);
4067 char* buffer = new char[signature_len];
4069 if (fread(buffer, 1, signature_len, fp) == signature_len) {
4070 res = (!strncmp(buffer, signature, signature_len) ? true : false);
4079 SystemTools::FileTypeEnum SystemTools::DetectFileType(const char* filename,
4080 unsigned long length,
4083 if (!filename || percent_bin < 0) {
4084 return SystemTools::FileTypeUnknown;
4087 if (SystemTools::FileIsDirectory(filename)) {
4088 return SystemTools::FileTypeUnknown;
4091 FILE* fp = Fopen(filename, "rb");
4093 return SystemTools::FileTypeUnknown;
4096 // Allocate buffer and read bytes
4098 auto* buffer = new unsigned char[length];
4099 size_t read_length = fread(buffer, 1, length, fp);
4101 if (read_length == 0) {
4103 return SystemTools::FileTypeUnknown;
4106 // Loop over contents and count
4108 size_t text_count = 0;
4110 const unsigned char* ptr = buffer;
4111 const unsigned char* buffer_end = buffer + read_length;
4113 while (ptr != buffer_end) {
4114 if ((*ptr >= 0x20 && *ptr <= 0x7F) || *ptr == '\n' || *ptr == '\r' ||
4123 double current_percent_bin = (static_cast<double>(read_length - text_count) /
4124 static_cast<double>(read_length));
4126 if (current_percent_bin >= percent_bin) {
4127 return SystemTools::FileTypeBinary;
4130 return SystemTools::FileTypeText;
4133 bool SystemTools::LocateFileInDir(const char* filename, const char* dir,
4134 std::string& filename_found,
4135 int try_filename_dirs)
4137 if (!filename || !dir) {
4141 // Get the basename of 'filename'
4143 std::string filename_base = SystemTools::GetFilenameName(filename);
4145 // Check if 'dir' is really a directory
4146 // If win32 and matches something like C:, accept it as a dir
4148 std::string real_dir;
4149 if (!SystemTools::FileIsDirectory(dir)) {
4151 size_t dir_len = strlen(dir);
4152 if (dir_len < 2 || dir[dir_len - 1] != ':') {
4154 real_dir = SystemTools::GetFilenamePath(dir);
4155 dir = real_dir.c_str();
4161 // Try to find the file in 'dir'
4164 if (!filename_base.empty() && dir) {
4165 size_t dir_len = strlen(dir);
4167 (dir_len && dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\');
4169 std::string temp = dir;
4173 temp += filename_base;
4175 if (SystemTools::FileExists(temp)) {
4177 filename_found = temp;
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.
4185 else if (try_filename_dirs) {
4186 std::string filename_dir(filename);
4187 std::string filename_dir_base;
4188 std::string filename_dir_bases;
4190 filename_dir = SystemTools::GetFilenamePath(filename_dir);
4191 filename_dir_base = SystemTools::GetFilenameName(filename_dir);
4193 if (filename_dir_base.empty() || filename_dir_base.back() == ':')
4195 if (filename_dir_base.empty())
4201 filename_dir_bases = filename_dir_base + "/" + filename_dir_bases;
4207 temp += filename_dir_bases;
4209 res = SystemTools::LocateFileInDir(filename_base.c_str(), temp.c_str(),
4212 } while (!res && !filename_dir_base.empty());
4219 bool SystemTools::FileIsFullPath(const std::string& in_name)
4221 return SystemToolsStatic::FileIsFullPath(in_name.c_str(), in_name.size());
4224 bool SystemTools::FileIsFullPath(const char* in_name)
4226 return SystemToolsStatic::FileIsFullPath(
4227 in_name, in_name[0] ? (in_name[1] ? 2 : 1) : 0);
4230 bool SystemToolsStatic::FileIsFullPath(const char* in_name, size_t len)
4232 #if defined(_WIN32) && !defined(__CYGWIN__)
4233 // On Windows, the name must be at least two characters long.
4237 if (in_name[1] == ':') {
4240 if (in_name[0] == '\\') {
4244 // On UNIX, the name must be at least one character long.
4249 #if !defined(_WIN32)
4250 if (in_name[0] == '~') {
4254 // On UNIX, the name must begin in a '/'.
4255 // On Windows, if the name begins in a '/', then it is a full
4257 if (in_name[0] == '/') {
4263 Status SystemTools::GetShortPath(std::string const& path,
4264 std::string& shortPath)
4266 #if defined(_WIN32) && !defined(__CYGWIN__)
4267 std::string tempPath = path; // create a buffer
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);
4275 std::wstring wtempPath = Encoding::ToWide(tempPath);
4276 DWORD ret = GetShortPathNameW(wtempPath.c_str(), nullptr, 0);
4277 std::vector<wchar_t> buffer(ret);
4279 ret = GetShortPathNameW(wtempPath.c_str(), &buffer[0],
4280 static_cast<DWORD>(buffer.size()));
4284 return Status::Windows_GetLastError();
4286 shortPath = Encoding::ToNarrow(&buffer[0]);
4287 return Status::Success();
4291 return Status::Success();
4295 std::string SystemTools::GetCurrentDateTime(const char* format)
4300 strftime(buf, sizeof(buf), format, localtime(&t));
4301 return std::string(buf);
4304 std::string SystemTools::MakeCidentifier(const std::string& s)
4307 if (str.find_first_of("0123456789") == 0) {
4311 std::string permited_chars("_"
4312 "abcdefghijklmnopqrstuvwxyz"
4313 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
4315 std::string::size_type pos = 0;
4316 while ((pos = str.find_first_not_of(permited_chars, pos)) !=
4317 std::string::npos) {
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 */)
4330 // Start with an empty line.
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...)
4339 *has_newline = false;
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);
4352 // if we read too much then truncate the buffer
4353 if (sizeLimit != std::string::npos && line.size() > sizeLimit) {
4354 line.resize(sizeLimit);
4358 // Return the results.
4360 *has_newline = !is.eof();
4365 int SystemTools::GetTerminalWidth()
4368 #ifdef HAVE_TTY_INFO
4370 std::string columns; /* Unix98 environment variable */
4371 if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0 && ws.ws_row > 0) {
4374 if (!isatty(STDOUT_FILENO)) {
4377 if (SystemTools::GetEnv("COLUMNS", columns) && !columns.empty()) {
4380 t = strtol(columns.c_str(), &endptr, 0);
4381 if (endptr && !*endptr && (t > 0) && (t < 1000)) {
4382 width = static_cast<int>(t);
4392 Status SystemTools::GetPermissions(const char* file, mode_t& mode)
4395 return Status::POSIX(EINVAL);
4397 return SystemTools::GetPermissions(std::string(file), mode);
4400 Status SystemTools::GetPermissions(std::string const& file, mode_t& mode)
4404 GetFileAttributesW(Encoding::ToWindowsExtendedPath(file).c_str());
4405 if (attr == INVALID_FILE_ATTRIBUTES) {
4406 return Status::Windows_GetLastError();
4408 if ((attr & FILE_ATTRIBUTE_READONLY) != 0) {
4409 mode = (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4411 mode = (_S_IWRITE | (_S_IWRITE >> 3) | (_S_IWRITE >> 6)) |
4412 (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4414 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
4415 mode |= S_IFDIR | (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4419 size_t dotPos = file.rfind('.');
4420 const char* ext = dotPos == std::string::npos ? 0 : (file.c_str() + dotPos);
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));
4428 if (stat(file.c_str(), &st) < 0) {
4429 return Status::POSIX_errno();
4433 return Status::Success();
4436 Status SystemTools::SetPermissions(const char* file, mode_t mode,
4440 return Status::POSIX(EINVAL);
4442 return SystemTools::SetPermissions(std::string(file), mode, honor_umask);
4445 Status SystemTools::SetPermissions(std::string const& file, mode_t mode,
4448 if (!SystemTools::PathExists(file)) {
4449 return Status::POSIX(ENOENT);
4452 mode_t currentMask = umask(0);
4454 mode &= ~currentMask;
4457 if (_wchmod(Encoding::ToWindowsExtendedPath(file).c_str(), mode) < 0)
4459 if (chmod(file.c_str(), mode) < 0)
4462 return Status::POSIX_errno();
4465 return Status::Success();
4468 std::string SystemTools::GetParentDirectory(const std::string& fileOrDir)
4470 return SystemTools::GetFilenamePath(fileOrDir);
4473 bool SystemTools::IsSubDirectory(const std::string& cSubdir,
4474 const std::string& cDir)
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()) {
4486 bool isRootPath = dir.back() == '/'; // like "/" or "C:/"
4487 size_t expectedSlashPosition = isRootPath ? dir.size() - 1u : dir.size();
4488 if (subdir[expectedSlashPosition] != '/') {
4491 subdir.resize(dir.size());
4492 return SystemTools::ComparePath(subdir, dir);
4495 void SystemTools::Delay(unsigned int msec)
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.
4509 usleep((msec % 1000) * 1000);
4511 usleep(msec * 1000);
4516 std::string SystemTools::GetOperatingSystemNameAndVersion()
4523 OSVERSIONINFOEXA osvi;
4524 BOOL bOsVersionInfoEx;
4526 ZeroMemory(&osvi, sizeof(osvi));
4527 osvi.dwOSVersionInfoSize = sizeof(osvi);
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"
4537 # pragma warning(disable : 4996)
4540 bOsVersionInfoEx = GetVersionExA((OSVERSIONINFOA*)&osvi);
4541 if (!bOsVersionInfoEx) {
4544 # ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4546 # pragma clang diagnostic pop
4548 # pragma warning(pop)
4552 switch (osvi.dwPlatformId) {
4553 // Test for the Windows NT product family.
4555 case VER_PLATFORM_WIN32_NT:
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";
4562 res += "Microsoft Windows Server 2016 family";
4566 if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) {
4567 if (osvi.wProductType == VER_NT_WORKSTATION) {
4568 res += "Microsoft Windows 8.1";
4570 res += "Microsoft Windows Server 2012 R2 family";
4574 if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) {
4575 if (osvi.wProductType == VER_NT_WORKSTATION) {
4576 res += "Microsoft Windows 8";
4578 res += "Microsoft Windows Server 2012 family";
4582 if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) {
4583 if (osvi.wProductType == VER_NT_WORKSTATION) {
4584 res += "Microsoft Windows 7";
4586 res += "Microsoft Windows Server 2008 R2 family";
4590 if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) {
4591 if (osvi.wProductType == VER_NT_WORKSTATION) {
4592 res += "Microsoft Windows Vista";
4594 res += "Microsoft Windows Server 2008 family";
4598 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4599 res += "Microsoft Windows Server 2003 family";
4602 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
4603 res += "Microsoft Windows XP";
4606 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4607 res += "Microsoft Windows 2000";
4610 if (osvi.dwMajorVersion <= 4) {
4611 res += "Microsoft Windows NT";
4614 // Test for specific product on Windows NT 4.0 SP6 and later.
4616 if (bOsVersionInfoEx) {
4617 // Test for the workstation type.
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";
4626 res += " Professional";
4631 // Test for the server type.
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";
4642 res += " Standard Edition";
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";
4656 else if (osvi.dwMajorVersion <= 4) // Windows NT 4.0
4658 if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4659 res += " Server 4.0, Enterprise Edition";
4661 res += " Server 4.0";
4667 // Test for specific product on Windows NT 4.0 SP5 and earlier
4672 wchar_t szProductType[BUFSIZE];
4673 DWORD dwBufLen = BUFSIZE;
4677 RegOpenKeyExW(HKEY_LOCAL_MACHINE,
4678 L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
4679 0, KEY_QUERY_VALUE, &hKey);
4680 if (lRet != ERROR_SUCCESS) {
4684 lRet = RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr,
4685 (LPBYTE)szProductType, &dwBufLen);
4687 if ((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE)) {
4693 if (lstrcmpiW(L"WINNT", szProductType) == 0) {
4694 res += " Workstation";
4696 if (lstrcmpiW(L"LANMANNT", szProductType) == 0) {
4699 if (lstrcmpiW(L"SERVERNT", szProductType) == 0) {
4700 res += " Advanced Server";
4704 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMajorVersion);
4707 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMinorVersion);
4711 // Display service pack (if any) and build number.
4713 if (osvi.dwMajorVersion == 4 &&
4714 lstrcmpiA(osvi.szCSDVersion, "Service Pack 6") == 0) {
4718 // Test for SP6 versus SP6a.
4720 lRet = RegOpenKeyExW(
4722 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
4723 0, KEY_QUERY_VALUE, &hKey);
4725 if (lRet == ERROR_SUCCESS) {
4726 res += " Service Pack 6a (Build ";
4727 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4730 } else // Windows NT 4.0 prior to SP6a
4733 res += osvi.szCSDVersion;
4735 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4741 } else // Windows NT 3.51 and earlier or Windows 2000 and later
4744 res += osvi.szCSDVersion;
4746 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4753 // Test for the Windows 95 product family.
4755 case VER_PLATFORM_WIN32_WINDOWS:
4757 if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) {
4758 res += "Microsoft Windows 95";
4759 if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') {
4764 if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) {
4765 res += "Microsoft Windows 98";
4766 if (osvi.szCSDVersion[1] == 'A') {
4771 if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) {
4772 res += "Microsoft Windows Millennium Edition";
4776 case VER_PLATFORM_WIN32s:
4778 res += "Microsoft Win32s";
4786 bool SystemTools::ParseURLProtocol(const std::string& URL,
4787 std::string& protocol,
4788 std::string& dataglom, bool decode)
4790 // match 0 entire url
4792 // match 2 dataglom following protocol://
4793 kwsys::RegularExpression urlRe(VTK_URL_PROTOCOL_REGEX);
4795 if (!urlRe.find(URL))
4798 protocol = urlRe.match(1);
4799 dataglom = urlRe.match(2);
4802 dataglom = DecodeURL(dataglom);
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)
4813 kwsys::RegularExpression urlRe(VTK_URL_REGEX);
4814 if (!urlRe.find(URL))
4819 // match 2 mangled user
4821 // match 4 mangled password
4824 // match 7 mangled port
4826 // match 9 database name
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);
4836 username = DecodeURL(username);
4837 password = DecodeURL(password);
4838 hostname = DecodeURL(hostname);
4839 dataport = DecodeURL(dataport);
4840 database = DecodeURL(database);
4846 // ----------------------------------------------------------------------
4847 std::string SystemTools::DecodeURL(const std::string& url)
4849 kwsys::RegularExpression urlByteRe(VTK_URL_BYTE_REGEX);
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));
4863 // ----------------------------------------------------------------------
4864 // Do NOT initialize. Default initialization to zero is necessary.
4865 static unsigned int SystemToolsManagerCount;
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.
4873 SystemToolsManager::SystemToolsManager()
4875 if (++SystemToolsManagerCount == 1) {
4876 SystemTools::ClassInitialize();
4880 SystemToolsManager::~SystemToolsManager()
4882 if (--SystemToolsManagerCount == 0) {
4883 SystemTools::ClassFinalize();
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)
4896 i = decc$feature_get_index(name);
4897 return i >= 0 && (decc$feature_set_value(i, 1, value) >= 0 || errno == 0);
4901 void SystemTools::ClassInitialize()
4904 SetVMSFeature("DECC$FILENAME_UNIX_ONLY", 1);
4907 // Create statics singleton instance
4908 SystemToolsStatics = new SystemToolsStatic;
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/");
4918 // If the current working directory is a logical path then keep the
4920 std::string pwd_str;
4921 if (SystemTools::GetEnv("PWD", pwd_str)) {
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
4927 std::string cwd_changed;
4928 std::string pwd_changed;
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;
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);
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);
4956 void SystemTools::ClassFinalize()
4958 delete SystemToolsStatics;
4961 } // namespace KWSYS_NAMESPACE
4963 #if defined(_MSC_VER) && defined(_DEBUG)
4964 # include <crtdbg.h>
4966 # include <stdlib.h>
4967 namespace KWSYS_NAMESPACE {
4969 static int SystemToolsDebugReport(int, char* message, int* ret)
4972 // Pretend user clicked on Retry button in popup.
4975 fprintf(stderr, "%s", message);
4977 return 1; // no further reporting required
4980 void SystemTools::EnableMSVCDebugHook()
4982 if (SystemTools::HasEnv("DART_TEST_FROM_DART") ||
4983 SystemTools::HasEnv("DASHBOARD_TEST_FROM_CTEST")) {
4984 _CrtSetReportHook(SystemToolsDebugReport);
4988 } // namespace KWSYS_NAMESPACE
4990 namespace KWSYS_NAMESPACE {
4991 void SystemTools::EnableMSVCDebugHook()
4994 } // namespace KWSYS_NAMESPACE