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);
540 static std::string GetActualCaseForPathCached(std::string const& path);
541 static const char* GetEnvBuffered(const char* key);
542 std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap;
543 std::map<std::string, std::string> EnvMap;
546 StringMap Cyg2Win32Map;
550 * Actual implementation of ReplaceString.
552 static void ReplaceString(std::string& source, const char* replace,
553 size_t replaceSize, const std::string& with);
556 * Actual implementation of FileIsFullPath.
558 static bool FileIsFullPath(const char*, size_t);
561 * Find a filename (file or directory) in the system PATH, with
562 * optional extra paths.
564 static std::string FindName(
565 const std::string& name,
566 const std::vector<std::string>& userPaths = std::vector<std::string>(),
567 bool no_system_path = false);
570 // Do NOT initialize. Default initialization to zero is necessary.
571 static SystemToolsStatic* SystemToolsStatics;
574 std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
576 std::string casePath;
578 // First check if the file is relative. We don't fix relative paths since the
579 // real case depends on the root directory and the given path fragment may
580 // have meaning elsewhere in the project.
581 if (!SystemTools::FileIsFullPath(pathIn)) {
582 // This looks unnecessary, but it allows for the return value optimization
583 // since all return paths return the same local variable.
588 std::vector<std::string> path_components;
589 SystemTools::SplitPath(pathIn, path_components);
591 // Start with root component.
592 std::vector<std::string>::size_type idx = 0;
593 casePath = path_components[idx++];
594 // make sure drive letter is always upper case
595 if (casePath.size() > 1 && casePath[1] == ':') {
596 casePath[0] = toupper(casePath[0]);
598 const char* sep = "";
600 // If network path, fill casePath with server/share so FindFirstFile
601 // will work after that. Maybe someday call other APIs to get
602 // actual case of servers and shares.
603 if (path_components.size() > 2 && path_components[0] == "//") {
604 casePath += path_components[idx++];
606 casePath += path_components[idx++];
610 // Convert case of all components that exist.
611 bool converting = true;
612 for (; idx < path_components.size(); idx++) {
617 // If path component contains wildcards, we skip matching
618 // because these filenames are not allowed on windows,
619 // and we do not want to match a different file.
620 if (path_components[idx].find('*') != std::string::npos ||
621 path_components[idx].find('?') != std::string::npos) {
624 std::string test_str = casePath;
625 test_str += path_components[idx];
626 WIN32_FIND_DATAW findData;
628 ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData);
629 if (INVALID_HANDLE_VALUE != hFind) {
630 path_components[idx] = Encoding::ToNarrow(findData.cFileName);
638 casePath += path_components[idx];
643 std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p)
645 // Check to see if actual case has already been called
646 // for this path, and the result is stored in the PathCaseMap
647 auto& pcm = SystemToolsStatics->PathCaseMap;
649 auto itr = pcm.find(p);
650 if (itr != pcm.end()) {
654 std::string casePath = SystemToolsStatic::GetCasePathName(p);
655 if (casePath.size() <= MAX_PATH) {
662 // adds the elements of the env variable path to the arg passed in
663 void SystemTools::GetPath(std::vector<std::string>& path, const char* env)
665 size_t const old_size = path.size();
666 #if defined(_WIN32) && !defined(__CYGWIN__)
667 const char pathSep = ';';
669 const char pathSep = ':';
675 if (!SystemTools::GetEnv(env, pathEnv)) {
679 // A hack to make the below algorithm work.
680 if (!pathEnv.empty() && pathEnv.back() != pathSep) {
683 std::string::size_type start = 0;
686 std::string::size_type endpos = pathEnv.find(pathSep, start);
687 if (endpos != std::string::npos) {
688 path.push_back(pathEnv.substr(start, endpos - start));
694 for (auto i = path.begin() + old_size; i != path.end(); ++i) {
695 SystemTools::ConvertToUnixSlashes(*i);
700 const char* SystemToolsStatic::GetEnvBuffered(const char* key)
703 if (SystemTools::GetEnv(key, env)) {
704 std::string& menv = SystemToolsStatics->EnvMap[key];
706 menv = std::move(env);
714 const char* SystemTools::GetEnv(const char* key)
717 return SystemToolsStatic::GetEnvBuffered(key);
723 const char* SystemTools::GetEnv(const std::string& key)
726 return SystemToolsStatic::GetEnvBuffered(key.c_str());
728 return getenv(key.c_str());
732 bool SystemTools::GetEnv(const char* key, std::string& result)
735 const std::wstring wkey = Encoding::ToWide(key);
736 const wchar_t* wv = _wgetenv(wkey.c_str());
738 result = Encoding::ToNarrow(wv);
742 const char* v = getenv(key);
751 bool SystemTools::GetEnv(const std::string& key, std::string& result)
753 return SystemTools::GetEnv(key.c_str(), result);
756 bool SystemTools::HasEnv(const char* key)
759 const std::wstring wkey = Encoding::ToWide(key);
760 const wchar_t* v = _wgetenv(wkey.c_str());
762 const char* v = getenv(key);
767 bool SystemTools::HasEnv(const std::string& key)
769 return SystemTools::HasEnv(key.c_str());
772 #if KWSYS_CXX_HAS_UNSETENV
773 /* unsetenv("A") removes A from the environment.
774 On older platforms it returns void instead of int. */
775 static int kwsysUnPutEnv(const std::string& env)
777 size_t pos = env.find('=');
778 if (pos != std::string::npos) {
779 std::string name = env.substr(0, pos);
780 unsetenv(name.c_str());
782 unsetenv(env.c_str());
787 #elif defined(__CYGWIN__) || defined(__GLIBC__)
788 /* putenv("A") removes A from the environment. It must not put the
789 memory in the environment because it does not have any "=" syntax. */
790 static int kwsysUnPutEnv(const std::string& env)
793 size_t pos = env.find('=');
794 size_t const len = pos == std::string::npos ? env.size() : pos;
795 size_t const sz = len + 1;
797 char* buf = sz > sizeof(local_buf) ? (char*)malloc(sz) : local_buf;
801 strncpy(buf, env.c_str(), len);
803 if (putenv(buf) < 0 && errno != EINVAL) {
806 if (buf != local_buf) {
816 #elif defined(_WIN32)
817 /* putenv("A=") places "A=" in the environment, which is as close to
818 removal as we can get with the putenv API. We have to leak the
819 most recent value placed in the environment for each variable name
820 on program exit in case exit routines access it. */
822 static kwsysEnvSet kwsysUnPutEnvSet;
824 static int kwsysUnPutEnv(std::string const& env)
826 std::wstring wEnv = Encoding::ToWide(env);
827 size_t const pos = wEnv.find('=');
828 size_t const len = pos == std::string::npos ? wEnv.size() : pos;
829 wEnv.resize(len + 1, L'=');
830 wchar_t* newEnv = _wcsdup(wEnv.c_str());
834 kwsysEnvSet::Free oldEnv(kwsysUnPutEnvSet.Release(newEnv));
835 kwsysUnPutEnvSet.insert(newEnv);
836 return _wputenv(newEnv);
840 /* Manipulate the "environ" global directly. */
841 static int kwsysUnPutEnv(const std::string& env)
843 size_t pos = env.find('=');
844 size_t const len = pos == std::string::npos ? env.size() : pos;
847 while (environ[in]) {
848 if (strlen(environ[in]) > len && environ[in][len] == '=' &&
849 strncmp(env.c_str(), environ[in], len) == 0) {
852 environ[out++] = environ[in++];
862 #if KWSYS_CXX_HAS_SETENV
864 /* setenv("A", "B", 1) will set A=B in the environment and makes its
865 own copies of the strings. */
866 bool SystemTools::PutEnv(const std::string& env)
868 size_t pos = env.find('=');
869 if (pos != std::string::npos) {
870 std::string name = env.substr(0, pos);
871 return setenv(name.c_str(), env.c_str() + pos + 1, 1) == 0;
873 return kwsysUnPutEnv(env) == 0;
877 bool SystemTools::UnPutEnv(const std::string& env)
879 return kwsysUnPutEnv(env) == 0;
884 /* putenv("A=B") will set A=B in the environment. Most putenv implementations
885 put their argument directly in the environment. They never free the memory
886 on program exit. Keep an active set of pointers to memory we allocate and
887 pass to putenv, one per environment key. At program exit remove any
888 environment values that may still reference memory we allocated. Then free
889 the memory. This will not affect any environment values we never set. */
891 # ifdef __INTEL_COMPILER
892 # pragma warning disable 444 /* base has non-virtual destructor */
895 class kwsysEnv : public kwsysEnvSet
900 for (iterator i = this->begin(); i != this->end(); ++i) {
902 const std::string s = Encoding::ToNarrow(*i);
907 free(const_cast<envchar*>(*i));
910 bool Put(const char* env)
913 const std::wstring wEnv = Encoding::ToWide(env);
914 wchar_t* newEnv = _wcsdup(wEnv.c_str());
916 char* newEnv = strdup(env);
918 Free oldEnv(this->Release(newEnv));
919 this->insert(newEnv);
921 return _wputenv(newEnv) == 0;
923 return putenv(newEnv) == 0;
926 bool UnPut(const char* env)
929 const std::wstring wEnv = Encoding::ToWide(env);
930 Free oldEnv(this->Release(wEnv.c_str()));
932 Free oldEnv(this->Release(env));
934 return kwsysUnPutEnv(env) == 0;
938 static kwsysEnv kwsysEnvInstance;
940 bool SystemTools::PutEnv(const std::string& env)
942 return kwsysEnvInstance.Put(env.c_str());
945 bool SystemTools::UnPutEnv(const std::string& env)
947 return kwsysEnvInstance.UnPut(env.c_str());
952 const char* SystemTools::GetExecutableExtension()
954 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__VMS)
961 FILE* SystemTools::Fopen(const std::string& file, const char* mode)
964 // Remove any 'e', which is supported on UNIX, but not Windows.
965 std::wstring trimmedMode = Encoding::ToWide(mode);
966 trimmedMode.erase(std::remove(trimmedMode.begin(), trimmedMode.end(), L'e'),
968 return _wfopen(Encoding::ToWindowsExtendedPath(file).c_str(),
969 trimmedMode.c_str());
971 return fopen(file.c_str(), mode);
975 Status SystemTools::MakeDirectory(const char* path, const mode_t* mode)
978 return Status::POSIX(EINVAL);
980 return SystemTools::MakeDirectory(std::string(path), mode);
983 Status SystemTools::MakeDirectory(std::string const& path, const mode_t* mode)
986 return Status::POSIX(EINVAL);
988 if (SystemTools::PathExists(path)) {
989 if (SystemTools::FileIsDirectory(path)) {
990 return Status::Success();
992 return Status::POSIX(EEXIST);
994 std::string dir = path;
995 SystemTools::ConvertToUnixSlashes(dir);
997 std::string::size_type pos = 0;
999 while ((pos = dir.find('/', pos)) != std::string::npos) {
1000 // all underlying functions use C strings, so temporarily
1001 // end the string here
1010 if (Mkdir(topdir, mode) != 0 && errno != EEXIST) {
1011 return Status::POSIX_errno();
1014 return Status::Success();
1017 // replace replace with with as many times as it shows up in source.
1018 // write the result into source.
1019 void SystemTools::ReplaceString(std::string& source,
1020 const std::string& replace,
1021 const std::string& with)
1023 // do while hangs if replaceSize is 0
1024 if (replace.empty()) {
1028 SystemToolsStatic::ReplaceString(source, replace.c_str(), replace.size(),
1032 void SystemTools::ReplaceString(std::string& source, const char* replace,
1035 // do while hangs if replaceSize is 0
1040 SystemToolsStatic::ReplaceString(source, replace, strlen(replace),
1044 void SystemToolsStatic::ReplaceString(std::string& source, const char* replace,
1046 const std::string& with)
1048 const char* src = source.c_str();
1049 char* searchPos = const_cast<char*>(strstr(src, replace));
1051 // get out quick if string is not found
1056 // perform replacements until done
1057 char* orig = strdup(src);
1058 char* currentPos = orig;
1059 searchPos = searchPos - src + orig;
1061 // initialize the result
1062 source.erase(source.begin(), source.end());
1065 source += currentPos;
1066 currentPos = searchPos + replaceSize;
1069 searchPos = strstr(currentPos, replace);
1070 } while (searchPos);
1072 // copy any trailing text
1073 source += currentPos;
1077 #if defined(_WIN32) && !defined(__CYGWIN__)
1079 # if defined(KEY_WOW64_32KEY) && defined(KEY_WOW64_64KEY)
1080 # define KWSYS_ST_KEY_WOW64_32KEY KEY_WOW64_32KEY
1081 # define KWSYS_ST_KEY_WOW64_64KEY KEY_WOW64_64KEY
1083 # define KWSYS_ST_KEY_WOW64_32KEY 0x0200
1084 # define KWSYS_ST_KEY_WOW64_64KEY 0x0100
1087 static bool hasPrefix(const std::string& s, const char* pattern,
1088 std::string::size_type spos)
1090 size_t plen = strlen(pattern);
1093 return s.compare(0, plen, pattern) == 0;
1096 static bool SystemToolsParseRegistryKey(const std::string& key,
1097 HKEY& primaryKey, std::wstring& second,
1098 std::string* valuename)
1100 size_t start = key.find('\\');
1101 if (start == std::string::npos) {
1105 size_t valuenamepos = key.find(';');
1106 if (valuenamepos != std::string::npos && valuename) {
1107 *valuename = key.substr(valuenamepos + 1);
1110 second = Encoding::ToWide(key.substr(start + 1, valuenamepos - start - 1));
1112 if (hasPrefix(key, "HKEY_CURRENT_USER", start)) {
1113 primaryKey = HKEY_CURRENT_USER;
1114 } else if (hasPrefix(key, "HKEY_CURRENT_CONFIG", start)) {
1115 primaryKey = HKEY_CURRENT_CONFIG;
1116 } else if (hasPrefix(key, "HKEY_CLASSES_ROOT", start)) {
1117 primaryKey = HKEY_CLASSES_ROOT;
1118 } else if (hasPrefix(key, "HKEY_LOCAL_MACHINE", start)) {
1119 primaryKey = HKEY_LOCAL_MACHINE;
1120 } else if (hasPrefix(key, "HKEY_USERS", start)) {
1121 primaryKey = HKEY_USERS;
1127 static DWORD SystemToolsMakeRegistryMode(DWORD mode,
1128 SystemTools::KeyWOW64 view)
1130 // only add the modes when on a system that supports Wow64.
1131 static FARPROC wow64p =
1132 GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process");
1133 if (wow64p == nullptr) {
1137 if (view == SystemTools::KeyWOW64_32) {
1138 return mode | KWSYS_ST_KEY_WOW64_32KEY;
1139 } else if (view == SystemTools::KeyWOW64_64) {
1140 return mode | KWSYS_ST_KEY_WOW64_64KEY;
1146 #if defined(_WIN32) && !defined(__CYGWIN__)
1147 bool SystemTools::GetRegistrySubKeys(const std::string& key,
1148 std::vector<std::string>& subkeys,
1151 HKEY primaryKey = HKEY_CURRENT_USER;
1152 std::wstring second;
1153 if (!SystemToolsParseRegistryKey(key, primaryKey, second, nullptr)) {
1158 if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1159 SystemToolsMakeRegistryMode(KEY_READ, view),
1160 &hKey) != ERROR_SUCCESS) {
1164 DWORD dwNameSize = sizeof(name) / sizeof(name[0]);
1167 while (RegEnumKeyW(hKey, i, name, dwNameSize) == ERROR_SUCCESS) {
1168 subkeys.push_back(Encoding::ToNarrow(name));
1178 bool SystemTools::GetRegistrySubKeys(const std::string&,
1179 std::vector<std::string>&, KeyWOW64)
1185 // Read a registry value.
1187 // HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath
1188 // => will return the data of the "default" value of the key
1189 // HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root
1190 // => will return the data of the "Root" value of the key
1192 #if defined(_WIN32) && !defined(__CYGWIN__)
1193 bool SystemTools::ReadRegistryValue(const std::string& key, std::string& value,
1196 bool valueset = false;
1197 HKEY primaryKey = HKEY_CURRENT_USER;
1198 std::wstring second;
1199 std::string valuename;
1200 if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
1205 if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1206 SystemToolsMakeRegistryMode(KEY_READ, view),
1207 &hKey) != ERROR_SUCCESS) {
1210 DWORD dwType, dwSize;
1213 if (RegQueryValueExW(hKey, Encoding::ToWide(valuename).c_str(), nullptr,
1214 &dwType, (BYTE*)data, &dwSize) == ERROR_SUCCESS) {
1215 if (dwType == REG_SZ) {
1216 value = Encoding::ToNarrow(data);
1218 } else if (dwType == REG_EXPAND_SZ) {
1219 wchar_t expanded[1024];
1220 DWORD dwExpandedSize = sizeof(expanded) / sizeof(expanded[0]);
1221 if (ExpandEnvironmentStringsW(data, expanded, dwExpandedSize)) {
1222 value = Encoding::ToNarrow(expanded);
1234 bool SystemTools::ReadRegistryValue(const std::string&, std::string&, KeyWOW64)
1240 // Write a registry value.
1242 // HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath
1243 // => will set the data of the "default" value of the key
1244 // HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root
1245 // => will set the data of the "Root" value of the key
1247 #if defined(_WIN32) && !defined(__CYGWIN__)
1248 bool SystemTools::WriteRegistryValue(const std::string& key,
1249 const std::string& value, KeyWOW64 view)
1251 HKEY primaryKey = HKEY_CURRENT_USER;
1252 std::wstring second;
1253 std::string valuename;
1254 if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
1260 wchar_t lpClass[] = L"";
1261 if (RegCreateKeyExW(primaryKey, second.c_str(), 0, lpClass,
1262 REG_OPTION_NON_VOLATILE,
1263 SystemToolsMakeRegistryMode(KEY_WRITE, view), nullptr,
1264 &hKey, &dwDummy) != ERROR_SUCCESS) {
1268 std::wstring wvalue = Encoding::ToWide(value);
1269 if (RegSetValueExW(hKey, Encoding::ToWide(valuename).c_str(), 0, REG_SZ,
1270 (CONST BYTE*)wvalue.c_str(),
1271 (DWORD)(sizeof(wchar_t) * (wvalue.size() + 1))) ==
1278 bool SystemTools::WriteRegistryValue(const std::string&, const std::string&,
1285 // Delete a registry value.
1287 // HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.1\InstallPath
1288 // => will delete the data of the "default" value of the key
1289 // HKEY_LOCAL_MACHINE\SOFTWARE\Scriptics\Tcl\8.4;Root
1290 // => will delete the data of the "Root" value of the key
1292 #if defined(_WIN32) && !defined(__CYGWIN__)
1293 bool SystemTools::DeleteRegistryValue(const std::string& key, KeyWOW64 view)
1295 HKEY primaryKey = HKEY_CURRENT_USER;
1296 std::wstring second;
1297 std::string valuename;
1298 if (!SystemToolsParseRegistryKey(key, primaryKey, second, &valuename)) {
1303 if (RegOpenKeyExW(primaryKey, second.c_str(), 0,
1304 SystemToolsMakeRegistryMode(KEY_WRITE, view),
1305 &hKey) != ERROR_SUCCESS) {
1308 if (RegDeleteValue(hKey, (LPTSTR)valuename.c_str()) == ERROR_SUCCESS) {
1316 bool SystemTools::DeleteRegistryValue(const std::string&, KeyWOW64)
1322 bool SystemTools::SameFile(const std::string& file1, const std::string& file2)
1325 HANDLE hFile1, hFile2;
1328 CreateFileW(Encoding::ToWide(file1).c_str(), GENERIC_READ, FILE_SHARE_READ,
1329 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1331 CreateFileW(Encoding::ToWide(file2).c_str(), GENERIC_READ, FILE_SHARE_READ,
1332 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1333 if (hFile1 == INVALID_HANDLE_VALUE || hFile2 == INVALID_HANDLE_VALUE) {
1334 if (hFile1 != INVALID_HANDLE_VALUE) {
1335 CloseHandle(hFile1);
1337 if (hFile2 != INVALID_HANDLE_VALUE) {
1338 CloseHandle(hFile2);
1343 BY_HANDLE_FILE_INFORMATION fiBuf1;
1344 BY_HANDLE_FILE_INFORMATION fiBuf2;
1345 GetFileInformationByHandle(hFile1, &fiBuf1);
1346 GetFileInformationByHandle(hFile2, &fiBuf2);
1347 CloseHandle(hFile1);
1348 CloseHandle(hFile2);
1349 return (fiBuf1.dwVolumeSerialNumber == fiBuf2.dwVolumeSerialNumber &&
1350 fiBuf1.nFileIndexHigh == fiBuf2.nFileIndexHigh &&
1351 fiBuf1.nFileIndexLow == fiBuf2.nFileIndexLow);
1353 struct stat fileStat1, fileStat2;
1354 if (stat(file1.c_str(), &fileStat1) == 0 &&
1355 stat(file2.c_str(), &fileStat2) == 0) {
1356 // see if the files are the same file
1357 // check the device inode and size
1358 if (memcmp(&fileStat2.st_dev, &fileStat1.st_dev,
1359 sizeof(fileStat1.st_dev)) == 0 &&
1360 memcmp(&fileStat2.st_ino, &fileStat1.st_ino,
1361 sizeof(fileStat1.st_ino)) == 0 &&
1362 fileStat2.st_size == fileStat1.st_size) {
1370 bool SystemTools::PathExists(const std::string& path)
1376 return (GetFileAttributesW(Encoding::ToWindowsExtendedPath(path).c_str()) !=
1377 INVALID_FILE_ATTRIBUTES);
1380 return lstat(path.c_str(), &st) == 0;
1384 bool SystemTools::FileExists(const char* filename)
1389 return SystemTools::FileExists(std::string(filename));
1392 bool SystemTools::FileExists(const std::string& filename)
1394 if (filename.empty()) {
1398 const std::wstring path = Encoding::ToWindowsExtendedPath(filename);
1399 DWORD attr = GetFileAttributesW(path.c_str());
1400 if (attr == INVALID_FILE_ATTRIBUTES) {
1404 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
1405 // Using 0 instead of GENERIC_READ as it allows reading of file attributes
1406 // even if we do not have permission to read the file itself
1407 HANDLE handle = CreateFileW(path.c_str(), 0, 0, nullptr, OPEN_EXISTING,
1408 FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1410 if (handle == INVALID_HANDLE_VALUE) {
1411 // A reparse point may be an execution alias (Windows Store app), which
1412 // is similar to a symlink but it cannot be opened as a regular file.
1413 // We must look at the reparse point data explicitly.
1414 handle = CreateFileW(
1415 path.c_str(), 0, 0, nullptr, OPEN_EXISTING,
1416 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
1418 if (handle == INVALID_HANDLE_VALUE) {
1422 byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1423 DWORD bytesReturned = 0;
1425 if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
1426 MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
1428 CloseHandle(handle);
1432 CloseHandle(handle);
1434 PREPARSE_DATA_BUFFER data =
1435 reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
1437 // Assume that file exists if it is an execution alias.
1438 return data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK;
1441 CloseHandle(handle);
1446 // SCO OpenServer 5.0.7/3.2's command has 711 permission.
1447 # if defined(_SCO_DS)
1448 return access(filename.c_str(), F_OK) == 0;
1450 return access(filename.c_str(), R_OK) == 0;
1455 bool SystemTools::FileExists(const char* filename, bool isFile)
1460 return SystemTools::FileExists(std::string(filename), isFile);
1463 bool SystemTools::FileExists(const std::string& filename, bool isFile)
1465 if (SystemTools::FileExists(filename)) {
1466 // If isFile is set return not FileIsDirectory,
1467 // so this will only be true if it is a file
1468 return !isFile || !SystemTools::FileIsDirectory(filename);
1473 bool SystemTools::TestFileAccess(const char* filename,
1474 TestFilePermissions permissions)
1479 return SystemTools::TestFileAccess(std::string(filename), permissions);
1482 bool SystemTools::TestFileAccess(const std::string& filename,
1483 TestFilePermissions permissions)
1485 if (filename.empty()) {
1488 #if defined(_WIN32) && !defined(__CYGWIN__)
1489 // If execute set, change to read permission (all files on Windows
1490 // are executable if they are readable). The CRT will always fail
1491 // if you pass an execute bit.
1492 if (permissions & TEST_FILE_EXECUTE) {
1493 permissions &= ~TEST_FILE_EXECUTE;
1494 permissions |= TEST_FILE_READ;
1496 return _waccess(Encoding::ToWindowsExtendedPath(filename).c_str(),
1499 return access(filename.c_str(), permissions) == 0;
1503 int SystemTools::Stat(const char* path, SystemTools::Stat_t* buf)
1509 return SystemTools::Stat(std::string(path), buf);
1512 int SystemTools::Stat(const std::string& path, SystemTools::Stat_t* buf)
1518 #if defined(_WIN32) && !defined(__CYGWIN__)
1519 // Ideally we should use Encoding::ToWindowsExtendedPath to support
1520 // long paths, but _wstat64 rejects paths with '?' in them, thinking
1521 // they are wildcards.
1522 std::wstring const& wpath = Encoding::ToWide(path);
1523 return _wstat64(wpath.c_str(), buf);
1525 return stat(path.c_str(), buf);
1529 Status SystemTools::Touch(std::string const& filename, bool create)
1531 if (!SystemTools::FileExists(filename)) {
1533 FILE* file = Fopen(filename, "a+b");
1536 return Status::Success();
1538 return Status::POSIX_errno();
1540 return Status::Success();
1543 #if defined(_WIN32) && !defined(__CYGWIN__)
1544 HANDLE h = CreateFileW(Encoding::ToWindowsExtendedPath(filename).c_str(),
1545 FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, 0,
1546 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
1548 return Status::Windows_GetLastError();
1551 GetSystemTimeAsFileTime(&mtime);
1552 if (!SetFileTime(h, 0, 0, &mtime)) {
1553 Status status = Status::Windows_GetLastError();
1558 #elif KWSYS_CXX_HAS_UTIMENSAT
1559 // utimensat is only available on newer Unixes and macOS 10.13+
1560 if (utimensat(AT_FDCWD, filename.c_str(), nullptr, 0) < 0) {
1561 return Status::POSIX_errno();
1564 // fall back to utimes
1565 if (utimes(filename.c_str(), nullptr) < 0) {
1566 return Status::POSIX_errno();
1569 return Status::Success();
1572 Status SystemTools::FileTimeCompare(std::string const& f1,
1573 std::string const& f2, int* result)
1575 // Default to same time.
1577 #if !defined(_WIN32) || defined(__CYGWIN__)
1578 // POSIX version. Use stat function to get file modification time.
1580 if (stat(f1.c_str(), &s1) != 0) {
1581 return Status::POSIX_errno();
1584 if (stat(f2.c_str(), &s2) != 0) {
1585 return Status::POSIX_errno();
1587 # if KWSYS_CXX_STAT_HAS_ST_MTIM
1588 // Compare using nanosecond resolution.
1589 if (s1.st_mtim.tv_sec < s2.st_mtim.tv_sec) {
1591 } else if (s1.st_mtim.tv_sec > s2.st_mtim.tv_sec) {
1593 } else if (s1.st_mtim.tv_nsec < s2.st_mtim.tv_nsec) {
1595 } else if (s1.st_mtim.tv_nsec > s2.st_mtim.tv_nsec) {
1598 # elif KWSYS_CXX_STAT_HAS_ST_MTIMESPEC
1599 // Compare using nanosecond resolution.
1600 if (s1.st_mtimespec.tv_sec < s2.st_mtimespec.tv_sec) {
1602 } else if (s1.st_mtimespec.tv_sec > s2.st_mtimespec.tv_sec) {
1604 } else if (s1.st_mtimespec.tv_nsec < s2.st_mtimespec.tv_nsec) {
1606 } else if (s1.st_mtimespec.tv_nsec > s2.st_mtimespec.tv_nsec) {
1610 // Compare using 1 second resolution.
1611 if (s1.st_mtime < s2.st_mtime) {
1613 } else if (s1.st_mtime > s2.st_mtime) {
1618 // Windows version. Get the modification time from extended file attributes.
1619 WIN32_FILE_ATTRIBUTE_DATA f1d;
1620 WIN32_FILE_ATTRIBUTE_DATA f2d;
1621 if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f1).c_str(),
1622 GetFileExInfoStandard, &f1d)) {
1623 return Status::Windows_GetLastError();
1625 if (!GetFileAttributesExW(Encoding::ToWindowsExtendedPath(f2).c_str(),
1626 GetFileExInfoStandard, &f2d)) {
1627 return Status::Windows_GetLastError();
1630 // Compare the file times using resolution provided by system call.
1631 *result = (int)CompareFileTime(&f1d.ftLastWriteTime, &f2d.ftLastWriteTime);
1633 return Status::Success();
1636 // Return a capitalized string (i.e the first letter is uppercased, all other
1638 std::string SystemTools::Capitalized(const std::string& s)
1645 n[0] = static_cast<std::string::value_type>(toupper(s[0]));
1646 for (size_t i = 1; i < s.size(); i++) {
1647 n[i] = static_cast<std::string::value_type>(tolower(s[i]));
1652 // Return capitalized words
1653 std::string SystemTools::CapitalizedWords(const std::string& s)
1656 for (size_t i = 0; i < s.size(); i++) {
1657 #if defined(_MSC_VER) && defined(_MT) && defined(_DEBUG)
1658 // MS has an assert that will fail if s[i] < 0; setting
1659 // LC_CTYPE using setlocale() does *not* help. Painful.
1660 if ((int)s[i] >= 0 && isalpha(s[i]) &&
1661 (i == 0 || ((int)s[i - 1] >= 0 && isspace(s[i - 1]))))
1663 if (isalpha(s[i]) && (i == 0 || isspace(s[i - 1])))
1666 n[i] = static_cast<std::string::value_type>(toupper(s[i]));
1672 // Return uncapitalized words
1673 std::string SystemTools::UnCapitalizedWords(const std::string& s)
1676 for (size_t i = 0; i < s.size(); i++) {
1677 #if defined(_MSC_VER) && defined(_MT) && defined(_DEBUG)
1678 // MS has an assert that will fail if s[i] < 0; setting
1679 // LC_CTYPE using setlocale() does *not* help. Painful.
1680 if ((int)s[i] >= 0 && isalpha(s[i]) &&
1681 (i == 0 || ((int)s[i - 1] >= 0 && isspace(s[i - 1]))))
1683 if (isalpha(s[i]) && (i == 0 || isspace(s[i - 1])))
1686 n[i] = static_cast<std::string::value_type>(tolower(s[i]));
1692 // only works for words with at least two letters
1693 std::string SystemTools::AddSpaceBetweenCapitalizedWords(const std::string& s)
1697 n.reserve(s.size());
1699 for (size_t i = 1; i < s.size(); i++) {
1700 if (isupper(s[i]) && !isspace(s[i - 1]) && !isupper(s[i - 1])) {
1709 char* SystemTools::AppendStrings(const char* str1, const char* str2)
1712 return SystemTools::DuplicateString(str2);
1715 return SystemTools::DuplicateString(str1);
1717 size_t len1 = strlen(str1);
1718 char* newstr = new char[len1 + strlen(str2) + 1];
1722 strcpy(newstr, str1);
1723 strcat(newstr + len1, str2);
1727 char* SystemTools::AppendStrings(const char* str1, const char* str2,
1731 return SystemTools::AppendStrings(str2, str3);
1734 return SystemTools::AppendStrings(str1, str3);
1737 return SystemTools::AppendStrings(str1, str2);
1740 size_t len1 = strlen(str1), len2 = strlen(str2);
1741 char* newstr = new char[len1 + len2 + strlen(str3) + 1];
1745 strcpy(newstr, str1);
1746 strcat(newstr + len1, str2);
1747 strcat(newstr + len1 + len2, str3);
1751 // Return a lower case string
1752 std::string SystemTools::LowerCase(const std::string& s)
1756 for (size_t i = 0; i < s.size(); i++) {
1757 n[i] = static_cast<std::string::value_type>(tolower(s[i]));
1762 // Return a lower case string
1763 std::string SystemTools::UpperCase(const std::string& s)
1767 for (size_t i = 0; i < s.size(); i++) {
1768 n[i] = static_cast<std::string::value_type>(toupper(s[i]));
1773 // Count char in string
1774 size_t SystemTools::CountChar(const char* str, char c)
1789 // Remove chars in string
1790 char* SystemTools::RemoveChars(const char* str, const char* toremove)
1795 char* clean_str = new char[strlen(str) + 1];
1796 char* ptr = clean_str;
1798 const char* str2 = toremove;
1799 while (*str2 && *str != *str2) {
1811 // Remove chars in string
1812 char* SystemTools::RemoveCharsButUpperHex(const char* str)
1817 char* clean_str = new char[strlen(str) + 1];
1818 char* ptr = clean_str;
1820 if ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'F')) {
1829 // Replace chars in string
1830 char* SystemTools::ReplaceChars(char* str, const char* toreplace,
1836 const char* ptr2 = toreplace;
1838 if (*ptr == *ptr2) {
1849 // Returns if string starts with another string
1850 bool SystemTools::StringStartsWith(const char* str1, const char* str2)
1852 if (!str1 || !str2) {
1855 size_t len1 = strlen(str1), len2 = strlen(str2);
1856 return len1 >= len2 && !strncmp(str1, str2, len2) ? true : false;
1859 // Returns if string starts with another string
1860 bool SystemTools::StringStartsWith(const std::string& str1, const char* str2)
1865 size_t len1 = str1.size(), len2 = strlen(str2);
1866 return len1 >= len2 && !strncmp(str1.c_str(), str2, len2) ? true : false;
1869 // Returns if string ends with another string
1870 bool SystemTools::StringEndsWith(const char* str1, const char* str2)
1872 if (!str1 || !str2) {
1875 size_t len1 = strlen(str1), len2 = strlen(str2);
1876 return len1 >= len2 && !strncmp(str1 + (len1 - len2), str2, len2) ? true
1880 // Returns if string ends with another string
1881 bool SystemTools::StringEndsWith(const std::string& str1, const char* str2)
1886 size_t len1 = str1.size(), len2 = strlen(str2);
1887 return len1 >= len2 && !strncmp(str1.c_str() + (len1 - len2), str2, len2)
1892 // Returns a pointer to the last occurrence of str2 in str1
1893 const char* SystemTools::FindLastString(const char* str1, const char* str2)
1895 if (!str1 || !str2) {
1899 size_t len1 = strlen(str1), len2 = strlen(str2);
1901 const char* ptr = str1 + len1 - len2;
1903 if (!strncmp(ptr, str2, len2)) {
1906 } while (ptr-- != str1);
1913 char* SystemTools::DuplicateString(const char* str)
1916 char* newstr = new char[strlen(str) + 1];
1917 return strcpy(newstr, str);
1922 // Return a cropped string
1923 std::string SystemTools::CropString(const std::string& s, size_t max_len)
1925 if (s.empty() || max_len == 0 || max_len >= s.size()) {
1932 size_t middle = max_len / 2;
1934 n.assign(s, 0, middle);
1935 n += s.substr(s.size() - (max_len - middle));
1940 n[middle - 1] = '.';
1942 n[middle + 1] = '.';
1950 std::vector<std::string> SystemTools::SplitString(const std::string& p,
1951 char sep, bool isPath)
1953 std::string path = p;
1954 std::vector<std::string> paths;
1958 if (isPath && path[0] == '/') {
1959 path.erase(path.begin());
1960 paths.emplace_back("/");
1962 std::string::size_type pos1 = 0;
1963 std::string::size_type pos2 = path.find(sep, pos1);
1964 while (pos2 != std::string::npos) {
1965 paths.push_back(path.substr(pos1, pos2 - pos1));
1967 pos2 = path.find(sep, pos1 + 1);
1969 paths.push_back(path.substr(pos1, pos2 - pos1));
1974 int SystemTools::EstimateFormatLength(const char* format, va_list ap)
1980 // Quick-hack attempt at estimating the length of the string.
1981 // Should never under-estimate.
1983 // Start with the length of the format string itself.
1985 size_t length = strlen(format);
1987 // Increase the length for every argument in the format.
1989 const char* cur = format;
1991 if (*cur++ == '%') {
1992 // Skip "%%" since it doesn't correspond to a va_arg.
1994 while (!int(isalpha(*cur))) {
1999 // Check the length of the string.
2000 char* s = va_arg(ap, char*);
2002 length += strlen(s);
2008 // Assume the argument contributes no more than 64 characters.
2011 // Eat the argument.
2012 static_cast<void>(va_arg(ap, double));
2015 // Assume the argument contributes no more than 64 characters.
2018 // Eat the argument.
2019 static_cast<void>(va_arg(ap, int));
2024 // Move past the characters just tested.
2029 return static_cast<int>(length);
2032 std::string SystemTools::EscapeChars(const char* str,
2033 const char* chars_to_escape,
2038 if (!chars_to_escape || !*chars_to_escape) {
2041 n.reserve(strlen(str));
2043 const char* ptr = chars_to_escape;
2060 static void ConvertVMSToUnix(std::string& path)
2062 std::string::size_type rootEnd = path.find(":[");
2063 std::string::size_type pathEnd = path.find("]");
2064 if (rootEnd != std::string::npos) {
2065 std::string root = path.substr(0, rootEnd);
2066 std::string pathPart = path.substr(rootEnd + 2, pathEnd - rootEnd - 2);
2067 const char* pathCString = pathPart.c_str();
2068 const char* pos0 = pathCString;
2069 for (std::string::size_type pos = 0; *pos0; ++pos) {
2071 pathPart[pos] = '/';
2075 path = "/" + root + "/" + pathPart;
2080 // convert windows slashes to unix slashes
2081 void SystemTools::ConvertToUnixSlashes(std::string& path)
2087 const char* pathCString = path.c_str();
2088 bool hasDoubleSlash = false;
2090 ConvertVMSToUnix(path);
2092 const char* pos0 = pathCString;
2093 for (std::string::size_type pos = 0; *pos0; ++pos) {
2094 if (*pos0 == '\\') {
2098 // Also, reuse the loop to check for slash followed by another slash
2099 if (!hasDoubleSlash && *(pos0 + 1) == '/' && *(pos0 + 2) == '/') {
2101 // However, on windows if the first characters are both slashes,
2102 // then keep them that way, so that network paths can be handled.
2104 hasDoubleSlash = true;
2107 hasDoubleSlash = true;
2114 if (hasDoubleSlash) {
2115 SystemTools::ReplaceString(path, "//", "/");
2119 // remove any trailing slash
2120 // if there is a tilda ~ then replace it with HOME
2121 pathCString = path.c_str();
2122 if (pathCString[0] == '~' &&
2123 (pathCString[1] == '/' || pathCString[1] == '\0')) {
2124 std::string homeEnv;
2125 if (SystemTools::GetEnv("HOME", homeEnv)) {
2126 path.replace(0, 1, homeEnv);
2129 #ifdef HAVE_GETPWNAM
2130 else if (pathCString[0] == '~') {
2131 std::string::size_type idx = path.find_first_of("/\0");
2132 char oldch = path[idx];
2134 passwd* pw = getpwnam(path.c_str() + 1);
2137 path.replace(0, idx, pw->pw_dir);
2141 // remove trailing slash if the path is more than
2143 pathCString = path.c_str();
2144 size_t size = path.size();
2145 if (size > 1 && path.back() == '/') {
2146 // if it is c:/ then do not remove the trailing slash
2147 if (!((size == 3 && pathCString[1] == ':'))) {
2148 path.resize(size - 1);
2154 std::wstring SystemTools::ConvertToWindowsExtendedPath(
2155 const std::string& source)
2157 return Encoding::ToWindowsExtendedPath(source);
2161 // change // to /, and escape any spaces in the path
2162 std::string SystemTools::ConvertToUnixOutputPath(const std::string& path)
2164 std::string ret = path;
2166 // remove // except at the beginning might be a cygwin drive
2167 std::string::size_type pos = 1;
2168 while ((pos = ret.find("//", pos)) != std::string::npos) {
2171 // escape spaces and () in the path
2172 if (ret.find_first_of(' ') != std::string::npos) {
2175 for (const char* ch = ret.c_str(); *ch != '\0'; ++ch) {
2176 // if it is already escaped then don't try to escape it again
2177 if ((*ch == ' ') && lastch != '\\') {
2188 std::string SystemTools::ConvertToOutputPath(const std::string& path)
2190 #if defined(_WIN32) && !defined(__CYGWIN__)
2191 return SystemTools::ConvertToWindowsOutputPath(path);
2193 return SystemTools::ConvertToUnixOutputPath(path);
2197 // remove double slashes not at the start
2198 std::string SystemTools::ConvertToWindowsOutputPath(const std::string& path)
2201 // make it big enough for all of path and double quotes
2202 ret.reserve(path.size() + 3);
2203 // put path into the string
2205 std::string::size_type pos = 0;
2206 // first convert all of the slashes
2207 while ((pos = ret.find('/', pos)) != std::string::npos) {
2211 // check for really small paths
2212 if (ret.size() < 2) {
2215 // now clean up a bit and remove double slashes
2216 // Only if it is not the first position in the path which is a network
2218 pos = 1; // start at position 1
2219 if (ret[0] == '\"') {
2220 pos = 2; // if the string is already quoted then start at 2
2221 if (ret.size() < 3) {
2225 while ((pos = ret.find("\\\\", pos)) != std::string::npos) {
2228 // now double quote the path if it has spaces in it
2229 // and is not already double quoted
2230 if (ret.find(' ') != std::string::npos && ret[0] != '\"') {
2231 ret.insert(static_cast<std::string::size_type>(0),
2232 static_cast<std::string::size_type>(1), '\"');
2233 ret.append(1, '\"');
2239 * Append the filename from the path source to the directory name dir.
2241 static std::string FileInDir(const std::string& source, const std::string& dir)
2243 std::string new_destination = dir;
2244 SystemTools::ConvertToUnixSlashes(new_destination);
2245 return new_destination + '/' + SystemTools::GetFilenameName(source);
2248 Status SystemTools::CopyFileIfDifferent(std::string const& source,
2249 std::string const& destination)
2251 // special check for a destination that is a directory
2252 // FilesDiffer does not handle file to directory compare
2253 if (SystemTools::FileIsDirectory(destination)) {
2254 const std::string new_destination = FileInDir(source, destination);
2255 if (!SystemTools::ComparePath(new_destination, destination)) {
2256 return SystemTools::CopyFileIfDifferent(source, new_destination);
2259 // source and destination are files so do a copy if they
2261 if (SystemTools::FilesDiffer(source, destination)) {
2262 return SystemTools::CopyFileAlways(source, destination);
2265 // at this point the files must be the same so return true
2266 return Status::Success();
2269 #define KWSYS_ST_BUFFER 4096
2271 bool SystemTools::FilesDiffer(const std::string& source,
2272 const std::string& destination)
2276 WIN32_FILE_ATTRIBUTE_DATA statSource;
2277 if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(source).c_str(),
2278 GetFileExInfoStandard, &statSource) == 0) {
2282 WIN32_FILE_ATTRIBUTE_DATA statDestination;
2283 if (GetFileAttributesExW(
2284 Encoding::ToWindowsExtendedPath(destination).c_str(),
2285 GetFileExInfoStandard, &statDestination) == 0) {
2289 if (statSource.nFileSizeHigh != statDestination.nFileSizeHigh ||
2290 statSource.nFileSizeLow != statDestination.nFileSizeLow) {
2294 if (statSource.nFileSizeHigh == 0 && statSource.nFileSizeLow == 0) {
2298 ((__int64)statSource.nFileSizeHigh << 32) + statSource.nFileSizeLow;
2302 struct stat statSource;
2303 if (stat(source.c_str(), &statSource) != 0) {
2307 struct stat statDestination;
2308 if (stat(destination.c_str(), &statDestination) != 0) {
2312 if (statSource.st_size != statDestination.st_size) {
2316 if (statSource.st_size == 0) {
2319 off_t nleft = statSource.st_size;
2323 kwsys::ifstream finSource(source.c_str(), (std::ios::binary | std::ios::in));
2324 kwsys::ifstream finDestination(destination.c_str(),
2325 (std::ios::binary | std::ios::in));
2327 kwsys::ifstream finSource(source.c_str());
2328 kwsys::ifstream finDestination(destination.c_str());
2330 if (!finSource || !finDestination) {
2334 // Compare the files a block at a time.
2335 char source_buf[KWSYS_ST_BUFFER];
2336 char dest_buf[KWSYS_ST_BUFFER];
2338 // Read a block from each file.
2339 std::streamsize nnext = (nleft > KWSYS_ST_BUFFER)
2341 : static_cast<std::streamsize>(nleft);
2342 finSource.read(source_buf, nnext);
2343 finDestination.read(dest_buf, nnext);
2345 // If either failed to read assume they are different.
2346 if (static_cast<std::streamsize>(finSource.gcount()) != nnext ||
2347 static_cast<std::streamsize>(finDestination.gcount()) != nnext) {
2351 // If this block differs the file differs.
2352 if (memcmp(static_cast<const void*>(source_buf),
2353 static_cast<const void*>(dest_buf),
2354 static_cast<size_t>(nnext)) != 0) {
2358 // Update the byte count remaining.
2362 // No differences found.
2366 bool SystemTools::TextFilesDiffer(const std::string& path1,
2367 const std::string& path2)
2369 kwsys::ifstream if1(path1.c_str());
2370 kwsys::ifstream if2(path2.c_str());
2376 std::string line1, line2;
2377 bool hasData1 = GetLineFromStream(if1, line1);
2378 bool hasData2 = GetLineFromStream(if2, line2);
2379 if (hasData1 != hasData2) {
2385 if (line1 != line2) {
2392 Status SystemTools::CopyFileContentBlockwise(std::string const& source,
2393 std::string const& destination)
2396 kwsys::ifstream fin(source.c_str(), std::ios::in | std::ios::binary);
2398 return Status::POSIX_errno();
2401 // try and remove the destination file so that read only destination files
2402 // can be written to.
2403 // If the remove fails continue so that files in read only directories
2404 // that do not allow file removal can be modified.
2405 SystemTools::RemoveFile(destination);
2407 kwsys::ofstream fout(destination.c_str(),
2408 std::ios::out | std::ios::trunc | std::ios::binary);
2410 return Status::POSIX_errno();
2413 // This copy loop is very sensitive on certain platforms with
2414 // slightly broken stream libraries (like HPUX). Normally, it is
2415 // incorrect to not check the error condition on the fin.read()
2416 // before using the data, but the fin.gcount() will be zero if an
2417 // error occurred. Therefore, the loop should be safe everywhere.
2419 const int bufferSize = 4096;
2420 char buffer[bufferSize];
2422 fin.read(buffer, bufferSize);
2424 fout.write(buffer, fin.gcount());
2430 // Make sure the operating system has finished writing the file
2431 // before closing it. This will ensure the file is finished before
2439 return Status::POSIX_errno();
2442 return Status::Success();
2446 * Clone the source file to the destination file
2448 * If available, the Linux FICLONE ioctl is used to create a check
2449 * copy-on-write clone of the source file.
2451 * The method returns false for the following cases:
2452 * - The code has not been compiled on Linux or the ioctl was unknown
2453 * - The source and destination is on different file systems
2454 * - The underlying filesystem does not support file cloning
2455 * - An unspecified error occurred
2457 Status SystemTools::CloneFileContent(std::string const& source,
2458 std::string const& destination)
2460 #if defined(__linux) && defined(FICLONE)
2461 int in = open(source.c_str(), O_RDONLY);
2463 return Status::POSIX_errno();
2466 SystemTools::RemoveFile(destination);
2469 open(destination.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
2471 Status status = Status::POSIX_errno();
2476 Status status = Status::Success();
2477 if (ioctl(out, FICLONE, in) < 0) {
2478 status = Status::POSIX_errno();
2484 #elif defined(__APPLE__) && \
2485 defined(KWSYS_SYSTEMTOOLS_HAVE_MACOS_COPYFILE_CLONE)
2486 // NOTE: we cannot use `clonefile` as the {a,c,m}time for the file needs to
2487 // be updated by `copy_file_if_different` and `copy_file`.
2488 if (copyfile(source.c_str(), destination.c_str(), nullptr,
2489 COPYFILE_METADATA | COPYFILE_CLONE) < 0) {
2490 return Status::POSIX_errno();
2492 # if KWSYS_CXX_HAS_UTIMENSAT
2493 // utimensat is only available on newer Unixes and macOS 10.13+
2494 if (utimensat(AT_FDCWD, destination.c_str(), nullptr, 0) < 0) {
2495 return Status::POSIX_errno();
2498 // fall back to utimes
2499 if (utimes(destination.c_str(), nullptr) < 0) {
2500 return Status::POSIX_errno();
2503 return Status::Success();
2507 return Status::POSIX(ENOSYS);
2512 * Copy a file named by "source" to the file named by "destination".
2514 Status SystemTools::CopyFileAlways(std::string const& source,
2515 std::string const& destination)
2519 Status perms = SystemTools::GetPermissions(source, perm);
2520 std::string real_destination = destination;
2522 if (SystemTools::FileIsDirectory(source)) {
2523 status = SystemTools::MakeDirectory(destination);
2524 if (!status.IsSuccess()) {
2528 // If destination is a directory, try to create a file with the same
2529 // name as the source in that directory.
2531 std::string destination_dir;
2532 if (SystemTools::FileIsDirectory(destination)) {
2533 destination_dir = real_destination;
2534 SystemTools::ConvertToUnixSlashes(real_destination);
2535 real_destination += '/';
2536 std::string source_name = source;
2537 real_destination += SystemTools::GetFilenameName(source_name);
2539 destination_dir = SystemTools::GetFilenamePath(destination);
2541 // If files are the same do not copy
2542 if (SystemTools::SameFile(source, real_destination)) {
2546 // Create destination directory
2547 if (!destination_dir.empty()) {
2548 status = SystemTools::MakeDirectory(destination_dir);
2549 if (!status.IsSuccess()) {
2554 status = SystemTools::CloneFileContent(source, real_destination);
2555 // if cloning did not succeed, fall back to blockwise copy
2556 if (!status.IsSuccess()) {
2557 status = SystemTools::CopyFileContentBlockwise(source, real_destination);
2559 if (!status.IsSuccess()) {
2564 status = SystemTools::SetPermissions(real_destination, perm);
2569 Status SystemTools::CopyAFile(std::string const& source,
2570 std::string const& destination, bool always)
2573 return SystemTools::CopyFileAlways(source, destination);
2575 return SystemTools::CopyFileIfDifferent(source, destination);
2580 * Copy a directory content from "source" directory to the directory named by
2583 Status SystemTools::CopyADirectory(std::string const& source,
2584 std::string const& destination, bool always)
2588 status = dir.Load(source);
2589 if (!status.IsSuccess()) {
2592 status = SystemTools::MakeDirectory(destination);
2593 if (!status.IsSuccess()) {
2597 for (size_t fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
2598 if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") != 0 &&
2599 strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..") != 0) {
2600 std::string fullPath = source;
2602 fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
2603 if (SystemTools::FileIsDirectory(fullPath)) {
2604 std::string fullDestPath = destination;
2605 fullDestPath += "/";
2606 fullDestPath += dir.GetFile(static_cast<unsigned long>(fileNum));
2607 status = SystemTools::CopyADirectory(fullPath, fullDestPath, always);
2608 if (!status.IsSuccess()) {
2612 status = SystemTools::CopyAFile(fullPath, destination, always);
2613 if (!status.IsSuccess()) {
2623 // return size of file; also returns zero if no file exists
2624 unsigned long SystemTools::FileLength(const std::string& filename)
2626 unsigned long length = 0;
2628 WIN32_FILE_ATTRIBUTE_DATA fs;
2629 if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2630 GetFileExInfoStandard, &fs) != 0) {
2631 /* To support the full 64-bit file size, use fs.nFileSizeHigh
2632 * and fs.nFileSizeLow to construct the 64 bit size
2634 length = ((__int64)fs.nFileSizeHigh << 32) + fs.nFileSizeLow;
2636 length = static_cast<unsigned long>(fs.nFileSizeLow);
2640 if (stat(filename.c_str(), &fs) == 0) {
2641 length = static_cast<unsigned long>(fs.st_size);
2647 int SystemTools::Strucmp(const char* l, const char* r)
2654 } while (lc == rc && lc);
2658 // return file's modified time
2659 long int SystemTools::ModifiedTime(const std::string& filename)
2663 WIN32_FILE_ATTRIBUTE_DATA fs;
2664 if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2665 GetFileExInfoStandard, &fs) != 0) {
2666 mt = windows_filetime_to_posix_time(fs.ftLastWriteTime);
2670 if (stat(filename.c_str(), &fs) == 0) {
2671 mt = static_cast<long int>(fs.st_mtime);
2677 // return file's creation time
2678 long int SystemTools::CreationTime(const std::string& filename)
2682 WIN32_FILE_ATTRIBUTE_DATA fs;
2683 if (GetFileAttributesExW(Encoding::ToWindowsExtendedPath(filename).c_str(),
2684 GetFileExInfoStandard, &fs) != 0) {
2685 ct = windows_filetime_to_posix_time(fs.ftCreationTime);
2689 if (stat(filename.c_str(), &fs) == 0) {
2690 ct = fs.st_ctime >= 0 ? static_cast<long int>(fs.st_ctime) : 0;
2696 std::string SystemTools::GetLastSystemError()
2702 Status SystemTools::RemoveFile(std::string const& source)
2705 std::wstring const& ws = Encoding::ToWindowsExtendedPath(source);
2706 if (DeleteFileW(ws.c_str())) {
2707 return Status::Success();
2709 DWORD err = GetLastError();
2710 if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
2711 return Status::Success();
2713 if (err != ERROR_ACCESS_DENIED) {
2714 return Status::Windows(err);
2716 /* The file may be read-only. Try adding write permission. */
2718 if (!SystemTools::GetPermissions(source, mode) ||
2719 !SystemTools::SetPermissions(source, S_IWRITE)) {
2721 return Status::Windows(err);
2724 const DWORD DIRECTORY_SOFT_LINK_ATTRS =
2725 FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT;
2726 DWORD attrs = GetFileAttributesW(ws.c_str());
2727 if (attrs != INVALID_FILE_ATTRIBUTES &&
2728 (attrs & DIRECTORY_SOFT_LINK_ATTRS) == DIRECTORY_SOFT_LINK_ATTRS &&
2729 RemoveDirectoryW(ws.c_str())) {
2730 return Status::Success();
2732 if (DeleteFileW(ws.c_str()) || GetLastError() == ERROR_FILE_NOT_FOUND ||
2733 GetLastError() == ERROR_PATH_NOT_FOUND) {
2734 return Status::Success();
2736 /* Try to restore the original permissions. */
2737 SystemTools::SetPermissions(source, mode);
2739 return Status::Windows(err);
2741 if (unlink(source.c_str()) != 0 && errno != ENOENT) {
2742 return Status::POSIX_errno();
2744 return Status::Success();
2748 Status SystemTools::RemoveADirectory(std::string const& source)
2750 // Add write permission to the directory so we can modify its
2751 // content to remove files and directories from it.
2753 if (SystemTools::GetPermissions(source, mode)) {
2754 #if defined(_WIN32) && !defined(__CYGWIN__)
2759 SystemTools::SetPermissions(source, mode);
2764 status = dir.Load(source);
2765 if (!status.IsSuccess()) {
2770 for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) {
2771 if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") != 0 &&
2772 strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..") != 0) {
2773 std::string fullPath = source;
2775 fullPath += dir.GetFile(static_cast<unsigned long>(fileNum));
2776 if (SystemTools::FileIsDirectory(fullPath) &&
2777 !SystemTools::FileIsSymlink(fullPath)) {
2778 status = SystemTools::RemoveADirectory(fullPath);
2779 if (!status.IsSuccess()) {
2783 status = SystemTools::RemoveFile(fullPath);
2784 if (!status.IsSuccess()) {
2791 if (Rmdir(source) != 0) {
2792 status = Status::POSIX_errno();
2799 size_t SystemTools::GetMaximumFilePathLength()
2801 return KWSYS_SYSTEMTOOLS_MAXPATH;
2805 * Find the file the given name. Searches the given path and then
2806 * the system search path. Returns the full path to the file if it is
2807 * found. Otherwise, the empty string is returned.
2809 std::string SystemToolsStatic::FindName(
2810 const std::string& name, const std::vector<std::string>& userPaths,
2811 bool no_system_path)
2813 // Add the system search path to our path first
2814 std::vector<std::string> path;
2815 if (!no_system_path) {
2816 SystemTools::GetPath(path, "CMAKE_FILE_PATH");
2817 SystemTools::GetPath(path);
2819 // now add the additional paths
2820 path.reserve(path.size() + userPaths.size());
2821 path.insert(path.end(), userPaths.begin(), userPaths.end());
2822 // now look for the file
2823 std::string tryPath;
2824 for (std::string const& p : path) {
2826 if (tryPath.empty() || tryPath.back() != '/') {
2830 if (SystemTools::FileExists(tryPath)) {
2834 // Couldn't find the file.
2839 * Find the file the given name. Searches the given path and then
2840 * the system search path. Returns the full path to the file if it is
2841 * found. Otherwise, the empty string is returned.
2843 std::string SystemTools::FindFile(const std::string& name,
2844 const std::vector<std::string>& userPaths,
2845 bool no_system_path)
2847 std::string tryPath =
2848 SystemToolsStatic::FindName(name, userPaths, no_system_path);
2849 if (!tryPath.empty() && !SystemTools::FileIsDirectory(tryPath)) {
2850 return SystemTools::CollapseFullPath(tryPath);
2852 // Couldn't find the file.
2857 * Find the directory the given name. Searches the given path and then
2858 * the system search path. Returns the full path to the directory if it is
2859 * found. Otherwise, the empty string is returned.
2861 std::string SystemTools::FindDirectory(
2862 const std::string& name, const std::vector<std::string>& userPaths,
2863 bool no_system_path)
2865 std::string tryPath =
2866 SystemToolsStatic::FindName(name, userPaths, no_system_path);
2867 if (!tryPath.empty() && SystemTools::FileIsDirectory(tryPath)) {
2868 return SystemTools::CollapseFullPath(tryPath);
2870 // Couldn't find the file.
2875 * Find the executable with the given name. Searches the given path and then
2876 * the system search path. Returns the full path to the executable if it is
2877 * found. Otherwise, the empty string is returned.
2879 std::string SystemTools::FindProgram(const char* nameIn,
2880 const std::vector<std::string>& userPaths,
2881 bool no_system_path)
2883 if (!nameIn || !*nameIn) {
2886 return SystemTools::FindProgram(std::string(nameIn), userPaths,
2890 std::string SystemTools::FindProgram(const std::string& name,
2891 const std::vector<std::string>& userPaths,
2892 bool no_system_path)
2894 std::string tryPath;
2896 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
2897 std::vector<std::string> extensions;
2898 // check to see if the name already has a .xxx at
2900 // on windows try .com then .exe
2901 if (name.size() <= 3 || name[name.size() - 4] != '.') {
2902 extensions.emplace_back(".com");
2903 extensions.emplace_back(".exe");
2905 // first try with extensions if the os supports them
2906 for (std::string const& ext : extensions) {
2909 if (SystemTools::FileIsExecutable(tryPath)) {
2910 return SystemTools::CollapseFullPath(tryPath);
2916 // now try just the name
2917 if (SystemTools::FileIsExecutable(name)) {
2918 return SystemTools::CollapseFullPath(name);
2920 // now construct the path
2921 std::vector<std::string> path;
2922 // Add the system search path to our path.
2923 if (!no_system_path) {
2924 SystemTools::GetPath(path);
2926 // now add the additional paths
2927 path.reserve(path.size() + userPaths.size());
2928 path.insert(path.end(), userPaths.begin(), userPaths.end());
2929 // Add a trailing slash to all paths to aid the search process.
2930 for (std::string& p : path) {
2931 if (p.empty() || p.back() != '/') {
2936 for (std::string& p : path) {
2938 // Remove double quotes from the path on windows
2939 SystemTools::ReplaceString(p, "\"", "");
2941 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
2942 // first try with extensions
2943 for (std::string const& ext : extensions) {
2947 if (SystemTools::FileIsExecutable(tryPath)) {
2948 return SystemTools::CollapseFullPath(tryPath);
2952 // now try it without them
2955 if (SystemTools::FileIsExecutable(tryPath)) {
2956 return SystemTools::CollapseFullPath(tryPath);
2959 // Couldn't find the program.
2963 std::string SystemTools::FindProgram(const std::vector<std::string>& names,
2964 const std::vector<std::string>& path,
2967 for (std::string const& name : names) {
2968 // Try to find the program.
2969 std::string result = SystemTools::FindProgram(name, path, noSystemPath);
2970 if (!result.empty()) {
2978 * Find the library with the given name. Searches the given path and then
2979 * the system search path. Returns the full path to the library if it is
2980 * found. Otherwise, the empty string is returned.
2982 std::string SystemTools::FindLibrary(const std::string& name,
2983 const std::vector<std::string>& userPaths)
2985 // See if the executable exists as written.
2986 if (SystemTools::FileExists(name, true)) {
2987 return SystemTools::CollapseFullPath(name);
2990 // Add the system search path to our path.
2991 std::vector<std::string> path;
2992 SystemTools::GetPath(path);
2993 // now add the additional paths
2994 path.reserve(path.size() + userPaths.size());
2995 path.insert(path.end(), userPaths.begin(), userPaths.end());
2996 // Add a trailing slash to all paths to aid the search process.
2997 for (std::string& p : path) {
2998 if (p.empty() || p.back() != '/') {
3002 std::string tryPath;
3003 for (std::string const& p : path) {
3004 #if defined(__APPLE__)
3007 tryPath += ".framework";
3008 if (SystemTools::FileIsDirectory(tryPath)) {
3009 return SystemTools::CollapseFullPath(tryPath);
3012 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
3016 if (SystemTools::FileExists(tryPath, true)) {
3017 return SystemTools::CollapseFullPath(tryPath);
3024 if (SystemTools::FileExists(tryPath, true)) {
3025 return SystemTools::CollapseFullPath(tryPath);
3031 if (SystemTools::FileExists(tryPath, true)) {
3032 return SystemTools::CollapseFullPath(tryPath);
3038 if (SystemTools::FileExists(tryPath, true)) {
3039 return SystemTools::CollapseFullPath(tryPath);
3044 tryPath += ".dylib";
3045 if (SystemTools::FileExists(tryPath, true)) {
3046 return SystemTools::CollapseFullPath(tryPath);
3052 if (SystemTools::FileExists(tryPath, true)) {
3053 return SystemTools::CollapseFullPath(tryPath);
3058 // Couldn't find the library.
3062 std::string SystemTools::GetRealPath(const std::string& path,
3063 std::string* errorMessage)
3066 Realpath(path, ret, errorMessage);
3070 bool SystemTools::FileIsDirectory(const std::string& inName)
3072 if (inName.empty()) {
3075 size_t length = inName.size();
3076 const char* name = inName.c_str();
3078 // Remove any trailing slash from the name except in a root component.
3079 char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
3080 std::string string_buffer;
3081 size_t last = length - 1;
3082 if (last > 0 && (name[last] == '/' || name[last] == '\\') &&
3083 strcmp(name, "/") != 0 && name[last - 1] != ':') {
3084 if (last < sizeof(local_buffer)) {
3085 memcpy(local_buffer, name, last);
3086 local_buffer[last] = '\0';
3087 name = local_buffer;
3089 string_buffer.append(name, last);
3090 name = string_buffer.c_str();
3094 // Now check the file node type.
3097 GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
3098 if (attr != INVALID_FILE_ATTRIBUTES) {
3099 return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
3102 if (stat(name, &fs) == 0) {
3103 return S_ISDIR(fs.st_mode);
3110 bool SystemTools::FileIsExecutable(const std::string& name)
3112 return !FileIsDirectory(name) && TestFileAccess(name, TEST_FILE_EXECUTE);
3116 bool SystemTools::FileIsSymlinkWithAttr(const std::wstring& path,
3119 if (attr != INVALID_FILE_ATTRIBUTES) {
3120 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
3121 // FILE_ATTRIBUTE_REPARSE_POINT means:
3122 // * a file or directory that has an associated reparse point, or
3123 // * a file that is a symbolic link.
3124 HANDLE hFile = CreateFileW(
3125 path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
3126 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3127 if (hFile == INVALID_HANDLE_VALUE) {
3130 byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3131 DWORD bytesReturned = 0;
3132 if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3133 MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3136 // Since FILE_ATTRIBUTE_REPARSE_POINT is set this file must be
3137 // a symbolic link if it is not a reparse point.
3138 return GetLastError() == ERROR_NOT_A_REPARSE_POINT;
3142 reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0])->ReparseTag;
3143 return (reparseTag == IO_REPARSE_TAG_SYMLINK) ||
3144 (reparseTag == IO_REPARSE_TAG_MOUNT_POINT);
3153 bool SystemTools::FileIsSymlink(const std::string& name)
3156 std::wstring path = Encoding::ToWindowsExtendedPath(name);
3157 return FileIsSymlinkWithAttr(path, GetFileAttributesW(path.c_str()));
3160 if (lstat(name.c_str(), &fs) == 0) {
3161 return S_ISLNK(fs.st_mode);
3168 bool SystemTools::FileIsFIFO(const std::string& name)
3172 CreateFileW(Encoding::ToWide(name).c_str(), GENERIC_READ, FILE_SHARE_READ,
3173 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3174 if (hFile == INVALID_HANDLE_VALUE) {
3177 const DWORD type = GetFileType(hFile);
3179 return type == FILE_TYPE_PIPE;
3182 if (lstat(name.c_str(), &fs) == 0) {
3183 return S_ISFIFO(fs.st_mode);
3190 Status SystemTools::CreateSymlink(std::string const& origName,
3191 std::string const& newName)
3193 #if defined(_WIN32) && !defined(__CYGWIN__)
3195 if (FileIsDirectory(origName)) {
3196 flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
3201 std::wstring origPath = Encoding::ToWindowsExtendedPath(origName);
3202 std::wstring newPath = Encoding::ToWindowsExtendedPath(newName);
3205 if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(),
3207 SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {
3208 status = Status::Windows_GetLastError();
3210 // Older Windows versions do not understand
3211 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
3212 if (status.GetWindows() == ERROR_INVALID_PARAMETER) {
3213 status = Status::Success();
3214 if (!CreateSymbolicLinkW(newPath.c_str(), origPath.c_str(), flags)) {
3215 status = Status::Windows_GetLastError();
3221 if (symlink(origName.c_str(), newName.c_str()) < 0) {
3222 return Status::POSIX_errno();
3224 return Status::Success();
3228 Status SystemTools::ReadSymlink(std::string const& newName,
3229 std::string& origName)
3231 #if defined(_WIN32) && !defined(__CYGWIN__)
3232 std::wstring newPath = Encoding::ToWindowsExtendedPath(newName);
3233 // FILE_ATTRIBUTE_REPARSE_POINT means:
3234 // * a file or directory that has an associated reparse point, or
3235 // * a file that is a symbolic link.
3236 HANDLE hFile = CreateFileW(
3237 newPath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
3238 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
3239 if (hFile == INVALID_HANDLE_VALUE) {
3240 return Status::Windows_GetLastError();
3242 byte buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
3243 DWORD bytesReturned = 0;
3245 if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, nullptr, 0, buffer,
3246 MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytesReturned,
3248 status = Status::Windows_GetLastError();
3251 if (!status.IsSuccess()) {
3254 PREPARSE_DATA_BUFFER data =
3255 reinterpret_cast<PREPARSE_DATA_BUFFER>(&buffer[0]);
3256 USHORT substituteNameLength;
3257 PCWSTR substituteNameData;
3258 if (data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
3259 substituteNameLength =
3260 data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
3261 substituteNameData = data->SymbolicLinkReparseBuffer.PathBuffer +
3262 data->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
3263 } else if (data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
3264 substituteNameLength =
3265 data->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
3266 substituteNameData = data->MountPointReparseBuffer.PathBuffer +
3267 data->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
3268 } else if (data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) {
3269 // The reparse buffer is a list of 0-terminated non-empty strings,
3270 // terminated by an empty string (0-0). We need the third string.
3272 substituteNameData = GetAppExecLink(data, destLen);
3273 if (substituteNameData == nullptr || destLen == 0) {
3274 return Status::Windows(ERROR_SYMLINK_NOT_SUPPORTED);
3276 substituteNameLength = static_cast<USHORT>(destLen);
3278 return Status::Windows(ERROR_REPARSE_TAG_MISMATCH);
3280 std::wstring substituteName(substituteNameData, substituteNameLength);
3281 origName = Encoding::ToNarrow(substituteName);
3283 char buf[KWSYS_SYSTEMTOOLS_MAXPATH + 1];
3284 int count = static_cast<int>(
3285 readlink(newName.c_str(), buf, KWSYS_SYSTEMTOOLS_MAXPATH));
3287 return Status::POSIX_errno();
3289 // Add null-terminator.
3293 return Status::Success();
3296 Status SystemTools::ChangeDirectory(std::string const& dir)
3298 if (Chdir(dir) < 0) {
3299 return Status::POSIX_errno();
3301 return Status::Success();
3304 std::string SystemTools::GetCurrentWorkingDirectory()
3307 const char* cwd = Getcwd(buf, 2048);
3311 SystemTools::ConvertToUnixSlashes(path);
3316 std::string SystemTools::GetProgramPath(const std::string& in_name)
3318 std::string dir, file;
3319 SystemTools::SplitProgramPath(in_name, dir, file);
3323 bool SystemTools::SplitProgramPath(const std::string& in_name,
3324 std::string& dir, std::string& file, bool)
3328 SystemTools::ConvertToUnixSlashes(dir);
3330 if (!SystemTools::FileIsDirectory(dir)) {
3331 std::string::size_type slashPos = dir.rfind('/');
3332 if (slashPos != std::string::npos) {
3333 file = dir.substr(slashPos + 1);
3334 dir.resize(slashPos);
3340 if (!(dir.empty()) && !SystemTools::FileIsDirectory(dir)) {
3341 std::string oldDir = in_name;
3342 SystemTools::ConvertToUnixSlashes(oldDir);
3349 bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut,
3350 std::string& errorMsg, const char* exeName,
3351 const char* buildDir,
3352 const char* installPrefix)
3354 std::vector<std::string> failures;
3355 std::string self = argv0 ? argv0 : "";
3356 failures.push_back(self);
3357 SystemTools::ConvertToUnixSlashes(self);
3358 self = SystemTools::FindProgram(self);
3359 if (!SystemTools::FileIsExecutable(self)) {
3361 std::string intdir = ".";
3363 intdir = CMAKE_INTDIR;
3370 self += SystemTools::GetExecutableExtension();
3373 if (installPrefix) {
3374 if (!SystemTools::FileIsExecutable(self)) {
3375 failures.push_back(self);
3376 self = installPrefix;
3381 if (!SystemTools::FileIsExecutable(self)) {
3382 failures.push_back(self);
3383 std::ostringstream msg;
3384 msg << "Can not find the command line program ";
3390 msg << " argv[0] = \"" << argv0 << "\"\n";
3392 msg << " Attempted paths:\n";
3393 for (std::string const& ff : failures) {
3394 msg << " \"" << ff << "\"\n";
3396 errorMsg = msg.str();
3403 #if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
3404 void SystemTools::AddTranslationPath(const std::string& a,
3405 const std::string& b)
3407 std::string path_a = a;
3408 std::string path_b = b;
3409 SystemTools::ConvertToUnixSlashes(path_a);
3410 SystemTools::ConvertToUnixSlashes(path_b);
3411 // First check this is a directory path, since we don't want the table to
3413 if (SystemTools::FileIsDirectory(path_a)) {
3414 // Make sure the path is a full path and does not contain no '..'
3415 // Ken--the following code is incorrect. .. can be in a valid path
3416 // for example /home/martink/MyHubba...Hubba/Src
3417 if (SystemTools::FileIsFullPath(path_b) &&
3418 path_b.find("..") == std::string::npos) {
3419 // Before inserting make sure path ends with '/'
3420 if (!path_a.empty() && path_a.back() != '/') {
3423 if (!path_b.empty() && path_b.back() != '/') {
3426 if (!(path_a == path_b)) {
3427 SystemToolsStatics->TranslationMap.insert(
3428 SystemToolsStatic::StringMap::value_type(std::move(path_a),
3429 std::move(path_b)));
3435 void SystemTools::AddKeepPath(const std::string& dir)
3438 Realpath(SystemTools::CollapseFullPath(dir), cdir);
3439 SystemTools::AddTranslationPath(cdir, dir);
3442 void SystemTools::CheckTranslationPath(std::string& path)
3444 // Do not translate paths that are too short to have meaningful
3446 if (path.size() < 2) {
3450 // Always add a trailing slash before translation. It does not
3451 // matter if this adds an extra slash, but we do not want to
3452 // translate part of a directory (like the foo part of foo-dir).
3455 // In case a file was specified we still have to go through this:
3456 // Now convert any path found in the table back to the one desired:
3457 for (auto const& pair : SystemToolsStatics->TranslationMap) {
3458 // We need to check of the path is a substring of the other path
3459 if (path.compare(0, pair.first.size(), pair.first) == 0) {
3460 path = path.replace(0, pair.first.size(), pair.second);
3464 // Remove the trailing slash we added before.
3469 static void SystemToolsAppendComponents(
3470 std::vector<std::string>& out_components,
3471 std::vector<std::string>::iterator first,
3472 std::vector<std::string>::iterator last)
3474 static const std::string up = "..";
3475 static const std::string cur = ".";
3476 for (std::vector<std::string>::const_iterator i = first; i != last; ++i) {
3478 // Remove the previous component if possible. Ignore ../ components
3479 // that try to go above the root. Keep ../ components if they are
3480 // at the beginning of a relative path (base path is relative).
3481 if (out_components.size() > 1 && out_components.back() != up) {
3482 out_components.resize(out_components.size() - 1);
3483 } else if (!out_components.empty() && out_components[0].empty()) {
3484 out_components.emplace_back(std::move(*i));
3486 } else if (!i->empty() && *i != cur) {
3487 out_components.emplace_back(std::move(*i));
3494 std::string CollapseFullPathImpl(std::string const& in_path,
3495 std::string const* in_base)
3497 // Collect the output path components.
3498 std::vector<std::string> out_components;
3500 // Split the input path components.
3501 std::vector<std::string> path_components;
3502 SystemTools::SplitPath(in_path, path_components);
3503 out_components.reserve(path_components.size());
3505 // If the input path is relative, start with a base path.
3506 if (path_components[0].empty()) {
3507 std::vector<std::string> base_components;
3510 // Use the given base path.
3511 SystemTools::SplitPath(*in_base, base_components);
3513 // Use the current working directory as a base path.
3514 std::string cwd = SystemTools::GetCurrentWorkingDirectory();
3515 SystemTools::SplitPath(cwd, base_components);
3518 // Append base path components to the output path.
3519 out_components.push_back(base_components[0]);
3520 SystemToolsAppendComponents(out_components, base_components.begin() + 1,
3521 base_components.end());
3524 // Append input path components to the output path.
3525 SystemToolsAppendComponents(out_components, path_components.begin(),
3526 path_components.end());
3528 // Transform the path back to a string.
3529 std::string newPath = SystemTools::JoinPath(out_components);
3531 #if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
3532 // Update the translation table with this potentially new path. I am not
3533 // sure why this line is here, it seems really questionable, but yet I
3534 // would put good money that if I remove it something will break, basically
3535 // from what I can see it created a mapping from the collapsed path, to be
3536 // replaced by the input path, which almost completely does the opposite of
3537 // this function, the only thing preventing this from happening a lot is
3538 // that if the in_path has a .. in it, then it is not added to the
3539 // translation table. So for most calls this either does nothing due to the
3540 // .. or it adds a translation between identical paths as nothing was
3541 // collapsed, so I am going to try to comment it out, and see what hits the
3542 // fan, hopefully quickly.
3543 // Commented out line below:
3544 // SystemTools::AddTranslationPath(newPath, in_path);
3546 SystemTools::CheckTranslationPath(newPath);
3549 newPath = SystemToolsStatics->GetActualCaseForPathCached(newPath);
3550 SystemTools::ConvertToUnixSlashes(newPath);
3552 // Return the reconstructed path.
3557 std::string SystemTools::CollapseFullPath(std::string const& in_path)
3559 return CollapseFullPathImpl(in_path, nullptr);
3562 std::string SystemTools::CollapseFullPath(std::string const& in_path,
3563 const char* in_base)
3566 return CollapseFullPathImpl(in_path, nullptr);
3568 std::string tmp_base = in_base;
3569 return CollapseFullPathImpl(in_path, &tmp_base);
3572 std::string SystemTools::CollapseFullPath(std::string const& in_path,
3573 std::string const& in_base)
3575 return CollapseFullPathImpl(in_path, &in_base);
3578 // compute the relative path from here to there
3579 std::string SystemTools::RelativePath(const std::string& local,
3580 const std::string& remote)
3582 if (!SystemTools::FileIsFullPath(local)) {
3585 if (!SystemTools::FileIsFullPath(remote)) {
3589 std::string l = SystemTools::CollapseFullPath(local);
3590 std::string r = SystemTools::CollapseFullPath(remote);
3592 // split up both paths into arrays of strings using / as a separator
3593 std::vector<std::string> localSplit = SystemTools::SplitString(l, '/', true);
3594 std::vector<std::string> remoteSplit =
3595 SystemTools::SplitString(r, '/', true);
3596 std::vector<std::string>
3597 commonPath; // store shared parts of path in this array
3598 std::vector<std::string> finalPath; // store the final relative path here
3599 // count up how many matching directory names there are from the start
3600 unsigned int sameCount = 0;
3601 while (((sameCount <= (localSplit.size() - 1)) &&
3602 (sameCount <= (remoteSplit.size() - 1))) &&
3603 // for Windows and Apple do a case insensitive string compare
3604 #if defined(_WIN32) || defined(__APPLE__)
3605 SystemTools::Strucmp(localSplit[sameCount].c_str(),
3606 remoteSplit[sameCount].c_str()) == 0
3608 localSplit[sameCount] == remoteSplit[sameCount]
3611 // put the common parts of the path into the commonPath array
3612 commonPath.push_back(localSplit[sameCount]);
3613 // erase the common parts of the path from the original path arrays
3614 localSplit[sameCount] = "";
3615 remoteSplit[sameCount] = "";
3619 // If there is nothing in common at all then just return the full
3620 // path. This is the case only on windows when the paths have
3621 // different drive letters. On unix two full paths always at least
3622 // have the root "/" in common so we will return a relative path
3623 // that passes through the root directory.
3624 if (sameCount == 0) {
3628 // for each entry that is not common in the local path
3629 // add a ../ to the finalpath array, this gets us out of the local
3630 // path into the remote dir
3631 for (std::string const& lp : localSplit) {
3633 finalPath.emplace_back("../");
3636 // for each entry that is not common in the remote path add it
3637 // to the final path.
3638 for (std::string const& rp : remoteSplit) {
3640 finalPath.push_back(rp);
3643 std::string relativePath; // result string
3644 // now turn the array of directories into a unix path by puttint /
3645 // between each entry that does not already have one
3646 for (std::string const& fp : finalPath) {
3647 if (!relativePath.empty() && relativePath.back() != '/') {
3648 relativePath += '/';
3652 return relativePath;
3655 std::string SystemTools::GetActualCaseForPath(const std::string& p)
3658 return SystemToolsStatic::GetCasePathName(p);
3664 const char* SystemTools::SplitPathRootComponent(const std::string& p,
3667 // Identify the root component.
3668 const char* c = p.c_str();
3669 if ((c[0] == '/' && c[1] == '/') || (c[0] == '\\' && c[1] == '\\')) {
3675 } else if (c[0] == '/' || c[0] == '\\') {
3676 // Unix path (or Windows path w/out drive letter).
3681 } else if (c[0] && c[1] == ':' && (c[2] == '/' || c[2] == '\\')) {
3688 } else if (c[0] && c[1] == ':') {
3689 // Path relative to a windows drive working directory.
3695 } else if (c[0] == '~') {
3696 // Home directory. The returned root should always have a
3697 // trailing slash so that appending components as
3698 // c[0]c[1]/c[2]/... works. The remaining path returned should
3699 // skip the first slash if it exists:
3701 // "~" : root = "~/" , return ""
3702 // "~/ : root = "~/" , return ""
3703 // "~/x : root = "~/" , return "x"
3704 // "~u" : root = "~u/", return ""
3705 // "~u/" : root = "~u/", return ""
3706 // "~u/x" : root = "~u/", return "x"
3708 while (c[n] && c[n] != '/') {
3726 // Return the remaining path.
3730 void SystemTools::SplitPath(const std::string& p,
3731 std::vector<std::string>& components,
3732 bool expand_home_dir)
3737 // Identify the root component.
3740 c = SystemTools::SplitPathRootComponent(p, &root);
3742 // Expand home directory references if requested.
3743 if (expand_home_dir && !root.empty() && root[0] == '~') {
3744 std::string homedir;
3745 root.resize(root.size() - 1);
3746 if (root.size() == 1) {
3747 #if defined(_WIN32) && !defined(__CYGWIN__)
3748 if (!SystemTools::GetEnv("USERPROFILE", homedir))
3750 SystemTools::GetEnv("HOME", homedir);
3752 #ifdef HAVE_GETPWNAM
3753 else if (passwd* pw = getpwnam(root.c_str() + 1)) {
3755 homedir = pw->pw_dir;
3759 if (!homedir.empty() &&
3760 (homedir.back() == '/' || homedir.back() == '\\')) {
3761 homedir.resize(homedir.size() - 1);
3763 SystemTools::SplitPath(homedir, components);
3765 components.push_back(root);
3769 // Parse the remaining components.
3770 const char* first = c;
3771 const char* last = first;
3772 for (; *last; ++last) {
3773 if (*last == '/' || *last == '\\') {
3774 // End of a component. Save it.
3775 components.emplace_back(first, last);
3780 // Save the last component unless there were no components.
3782 components.emplace_back(first, last);
3786 std::string SystemTools::JoinPath(const std::vector<std::string>& components)
3788 return SystemTools::JoinPath(components.begin(), components.end());
3791 std::string SystemTools::JoinPath(
3792 std::vector<std::string>::const_iterator first,
3793 std::vector<std::string>::const_iterator last)
3795 // Construct result in a single string.
3798 for (auto i = first; i != last; ++i) {
3799 len += 1 + i->size();
3801 result.reserve(len);
3803 // The first two components do not add a slash.
3804 if (first != last) {
3805 result.append(*first++);
3807 if (first != last) {
3808 result.append(*first++);
3811 // All remaining components are always separated with a slash.
3812 while (first != last) {
3813 result.push_back('/');
3814 result.append((*first++));
3817 // Return the concatenated result.
3821 bool SystemTools::ComparePath(const std::string& c1, const std::string& c2)
3823 #if defined(_WIN32) || defined(__APPLE__)
3825 return _stricmp(c1.c_str(), c2.c_str()) == 0;
3826 # elif defined(__APPLE__) || defined(__GNUC__)
3827 return strcasecmp(c1.c_str(), c2.c_str()) == 0;
3829 return SystemTools::Strucmp(c1.c_str(), c2.c_str()) == 0;
3836 bool SystemTools::Split(const std::string& str,
3837 std::vector<std::string>& lines, char separator)
3839 std::string data(str);
3840 std::string::size_type lpos = 0;
3841 while (lpos < data.length()) {
3842 std::string::size_type rpos = data.find_first_of(separator, lpos);
3843 if (rpos == std::string::npos) {
3844 // String ends at end of string without a separator.
3845 lines.push_back(data.substr(lpos));
3848 // String ends in a separator, remove the character.
3849 lines.push_back(data.substr(lpos, rpos - lpos));
3856 bool SystemTools::Split(const std::string& str,
3857 std::vector<std::string>& lines)
3859 std::string data(str);
3860 std::string::size_type lpos = 0;
3861 while (lpos < data.length()) {
3862 std::string::size_type rpos = data.find_first_of('\n', lpos);
3863 if (rpos == std::string::npos) {
3864 // Line ends at end of string without a newline.
3865 lines.push_back(data.substr(lpos));
3868 if ((rpos > lpos) && (data[rpos - 1] == '\r')) {
3869 // Line ends in a "\r\n" pair, remove both characters.
3870 lines.push_back(data.substr(lpos, (rpos - 1) - lpos));
3872 // Line ends in a "\n", remove the character.
3873 lines.push_back(data.substr(lpos, rpos - lpos));
3880 std::string SystemTools::Join(const std::vector<std::string>& list,
3881 const std::string& separator)
3888 size_t total_size = separator.size() * (list.size() - 1);
3889 for (const std::string& string : list) {
3890 total_size += string.size();
3893 result.reserve(total_size);
3894 bool needs_separator = false;
3895 for (const std::string& string : list) {
3896 if (needs_separator) {
3897 result += separator;
3900 needs_separator = true;
3907 * Return path of a full filename (no trailing slashes).
3908 * Warning: returned path is converted to Unix slashes format.
3910 std::string SystemTools::GetFilenamePath(const std::string& filename)
3912 std::string fn = filename;
3913 SystemTools::ConvertToUnixSlashes(fn);
3915 std::string::size_type slash_pos = fn.rfind('/');
3916 if (slash_pos == 0) {
3919 if (slash_pos == 2 && fn[1] == ':') {
3920 // keep the / after a drive letter
3924 if (slash_pos == std::string::npos) {
3927 fn.resize(slash_pos);
3932 * Return file name of a full filename (i.e. file name without path).
3934 std::string SystemTools::GetFilenameName(const std::string& filename)
3936 #if defined(_WIN32) || defined(KWSYS_SYSTEMTOOLS_SUPPORT_WINDOWS_SLASHES)
3937 const char* separators = "/\\";
3939 char separators = '/';
3941 std::string::size_type slash_pos = filename.find_last_of(separators);
3942 if (slash_pos != std::string::npos) {
3943 return filename.substr(slash_pos + 1);
3950 * Return file extension of a full filename (dot included).
3951 * Warning: this is the longest extension (for example: .tar.gz)
3953 std::string SystemTools::GetFilenameExtension(const std::string& filename)
3955 std::string name = SystemTools::GetFilenameName(filename);
3956 std::string::size_type dot_pos = name.find('.');
3957 if (dot_pos != std::string::npos) {
3958 name.erase(0, dot_pos);
3966 * Return file extension of a full filename (dot included).
3967 * Warning: this is the shortest extension (for example: .gz of .tar.gz)
3969 std::string SystemTools::GetFilenameLastExtension(const std::string& filename)
3971 std::string name = SystemTools::GetFilenameName(filename);
3972 std::string::size_type dot_pos = name.rfind('.');
3973 if (dot_pos != std::string::npos) {
3974 name.erase(0, dot_pos);
3982 * Return file name without extension of a full filename (i.e. without path).
3983 * Warning: it considers the longest extension (for example: .tar.gz)
3985 std::string SystemTools::GetFilenameWithoutExtension(
3986 const std::string& filename)
3988 std::string name = SystemTools::GetFilenameName(filename);
3989 std::string::size_type dot_pos = name.find('.');
3990 if (dot_pos != std::string::npos) {
3991 name.resize(dot_pos);
3997 * Return file name without extension of a full filename (i.e. without path).
3998 * Warning: it considers the last extension (for example: removes .gz
4001 std::string SystemTools::GetFilenameWithoutLastExtension(
4002 const std::string& filename)
4004 std::string name = SystemTools::GetFilenameName(filename);
4005 std::string::size_type dot_pos = name.rfind('.');
4006 if (dot_pos != std::string::npos) {
4007 name.resize(dot_pos);
4012 bool SystemTools::FileHasSignature(const char* filename, const char* signature,
4015 if (!filename || !signature) {
4019 FILE* fp = Fopen(filename, "rb");
4024 fseek(fp, offset, SEEK_SET);
4027 size_t signature_len = strlen(signature);
4028 char* buffer = new char[signature_len];
4030 if (fread(buffer, 1, signature_len, fp) == signature_len) {
4031 res = (!strncmp(buffer, signature, signature_len) ? true : false);
4040 SystemTools::FileTypeEnum SystemTools::DetectFileType(const char* filename,
4041 unsigned long length,
4044 if (!filename || percent_bin < 0) {
4045 return SystemTools::FileTypeUnknown;
4048 if (SystemTools::FileIsDirectory(filename)) {
4049 return SystemTools::FileTypeUnknown;
4052 FILE* fp = Fopen(filename, "rb");
4054 return SystemTools::FileTypeUnknown;
4057 // Allocate buffer and read bytes
4059 auto* buffer = new unsigned char[length];
4060 size_t read_length = fread(buffer, 1, length, fp);
4062 if (read_length == 0) {
4064 return SystemTools::FileTypeUnknown;
4067 // Loop over contents and count
4069 size_t text_count = 0;
4071 const unsigned char* ptr = buffer;
4072 const unsigned char* buffer_end = buffer + read_length;
4074 while (ptr != buffer_end) {
4075 if ((*ptr >= 0x20 && *ptr <= 0x7F) || *ptr == '\n' || *ptr == '\r' ||
4084 double current_percent_bin = (static_cast<double>(read_length - text_count) /
4085 static_cast<double>(read_length));
4087 if (current_percent_bin >= percent_bin) {
4088 return SystemTools::FileTypeBinary;
4091 return SystemTools::FileTypeText;
4094 bool SystemTools::LocateFileInDir(const char* filename, const char* dir,
4095 std::string& filename_found,
4096 int try_filename_dirs)
4098 if (!filename || !dir) {
4102 // Get the basename of 'filename'
4104 std::string filename_base = SystemTools::GetFilenameName(filename);
4106 // Check if 'dir' is really a directory
4107 // If win32 and matches something like C:, accept it as a dir
4109 std::string real_dir;
4110 if (!SystemTools::FileIsDirectory(dir)) {
4112 size_t dir_len = strlen(dir);
4113 if (dir_len < 2 || dir[dir_len - 1] != ':') {
4115 real_dir = SystemTools::GetFilenamePath(dir);
4116 dir = real_dir.c_str();
4122 // Try to find the file in 'dir'
4125 if (!filename_base.empty() && dir) {
4126 size_t dir_len = strlen(dir);
4128 (dir_len && dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\');
4130 std::string temp = dir;
4134 temp += filename_base;
4136 if (SystemTools::FileExists(temp)) {
4138 filename_found = temp;
4141 // If not found, we can try harder by appending part of the file to
4142 // to the directory to look inside.
4143 // Example: if we were looking for /foo/bar/yo.txt in /d1/d2, then
4144 // try to find yo.txt in /d1/d2/bar, then /d1/d2/foo/bar, etc.
4146 else if (try_filename_dirs) {
4147 std::string filename_dir(filename);
4148 std::string filename_dir_base;
4149 std::string filename_dir_bases;
4151 filename_dir = SystemTools::GetFilenamePath(filename_dir);
4152 filename_dir_base = SystemTools::GetFilenameName(filename_dir);
4154 if (filename_dir_base.empty() || filename_dir_base.back() == ':')
4156 if (filename_dir_base.empty())
4162 filename_dir_bases = filename_dir_base + "/" + filename_dir_bases;
4168 temp += filename_dir_bases;
4170 res = SystemTools::LocateFileInDir(filename_base.c_str(), temp.c_str(),
4173 } while (!res && !filename_dir_base.empty());
4180 bool SystemTools::FileIsFullPath(const std::string& in_name)
4182 return SystemToolsStatic::FileIsFullPath(in_name.c_str(), in_name.size());
4185 bool SystemTools::FileIsFullPath(const char* in_name)
4187 return SystemToolsStatic::FileIsFullPath(
4188 in_name, in_name[0] ? (in_name[1] ? 2 : 1) : 0);
4191 bool SystemToolsStatic::FileIsFullPath(const char* in_name, size_t len)
4193 #if defined(_WIN32) && !defined(__CYGWIN__)
4194 // On Windows, the name must be at least two characters long.
4198 if (in_name[1] == ':') {
4201 if (in_name[0] == '\\') {
4205 // On UNIX, the name must be at least one character long.
4210 #if !defined(_WIN32)
4211 if (in_name[0] == '~') {
4215 // On UNIX, the name must begin in a '/'.
4216 // On Windows, if the name begins in a '/', then it is a full
4218 if (in_name[0] == '/') {
4224 Status SystemTools::GetShortPath(std::string const& path,
4225 std::string& shortPath)
4227 #if defined(_WIN32) && !defined(__CYGWIN__)
4228 std::string tempPath = path; // create a buffer
4230 // if the path passed in has quotes around it, first remove the quotes
4231 if (!path.empty() && path[0] == '"' && path.back() == '"') {
4232 tempPath.resize(path.length() - 1);
4233 tempPath.erase(0, 1);
4236 std::wstring wtempPath = Encoding::ToWide(tempPath);
4237 DWORD ret = GetShortPathNameW(wtempPath.c_str(), nullptr, 0);
4238 std::vector<wchar_t> buffer(ret);
4240 ret = GetShortPathNameW(wtempPath.c_str(), &buffer[0],
4241 static_cast<DWORD>(buffer.size()));
4245 return Status::Windows_GetLastError();
4247 shortPath = Encoding::ToNarrow(&buffer[0]);
4248 return Status::Success();
4252 return Status::Success();
4256 std::string SystemTools::GetCurrentDateTime(const char* format)
4261 strftime(buf, sizeof(buf), format, localtime(&t));
4262 return std::string(buf);
4265 std::string SystemTools::MakeCidentifier(const std::string& s)
4268 if (str.find_first_of("0123456789") == 0) {
4272 std::string permited_chars("_"
4273 "abcdefghijklmnopqrstuvwxyz"
4274 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
4276 std::string::size_type pos = 0;
4277 while ((pos = str.find_first_not_of(permited_chars, pos)) !=
4278 std::string::npos) {
4284 // Convenience function around std::getline which removes a trailing carriage
4285 // return and can truncate the buffer as needed. Returns true
4286 // if any data were read before the end-of-file was reached.
4287 bool SystemTools::GetLineFromStream(
4288 std::istream& is, std::string& line, bool* has_newline /* = 0 */,
4289 std::string::size_type sizeLimit /* = std::string::npos */)
4291 // Start with an empty line.
4294 // Early short circuit return if stream is no good. Just return
4295 // false and the empty line. (Probably means caller tried to
4296 // create a file stream with a non-existent file name...)
4300 *has_newline = false;
4305 std::getline(is, line);
4306 bool haveData = !line.empty() || !is.eof();
4307 if (!line.empty()) {
4308 // Avoid storing a carriage return character.
4309 if (line.back() == '\r') {
4310 line.resize(line.size() - 1);
4313 // if we read too much then truncate the buffer
4314 if (sizeLimit != std::string::npos && line.size() > sizeLimit) {
4315 line.resize(sizeLimit);
4319 // Return the results.
4321 *has_newline = !is.eof();
4326 int SystemTools::GetTerminalWidth()
4329 #ifdef HAVE_TTY_INFO
4331 std::string columns; /* Unix98 environment variable */
4332 if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0 && ws.ws_row > 0) {
4335 if (!isatty(STDOUT_FILENO)) {
4338 if (SystemTools::GetEnv("COLUMNS", columns) && !columns.empty()) {
4341 t = strtol(columns.c_str(), &endptr, 0);
4342 if (endptr && !*endptr && (t > 0) && (t < 1000)) {
4343 width = static_cast<int>(t);
4353 Status SystemTools::GetPermissions(const char* file, mode_t& mode)
4356 return Status::POSIX(EINVAL);
4358 return SystemTools::GetPermissions(std::string(file), mode);
4361 Status SystemTools::GetPermissions(std::string const& file, mode_t& mode)
4365 GetFileAttributesW(Encoding::ToWindowsExtendedPath(file).c_str());
4366 if (attr == INVALID_FILE_ATTRIBUTES) {
4367 return Status::Windows_GetLastError();
4369 if ((attr & FILE_ATTRIBUTE_READONLY) != 0) {
4370 mode = (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4372 mode = (_S_IWRITE | (_S_IWRITE >> 3) | (_S_IWRITE >> 6)) |
4373 (_S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6));
4375 if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
4376 mode |= S_IFDIR | (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4380 size_t dotPos = file.rfind('.');
4381 const char* ext = dotPos == std::string::npos ? 0 : (file.c_str() + dotPos);
4383 (Strucmp(ext, ".exe") == 0 || Strucmp(ext, ".com") == 0 ||
4384 Strucmp(ext, ".cmd") == 0 || Strucmp(ext, ".bat") == 0)) {
4385 mode |= (_S_IEXEC | (_S_IEXEC >> 3) | (_S_IEXEC >> 6));
4389 if (stat(file.c_str(), &st) < 0) {
4390 return Status::POSIX_errno();
4394 return Status::Success();
4397 Status SystemTools::SetPermissions(const char* file, mode_t mode,
4401 return Status::POSIX(EINVAL);
4403 return SystemTools::SetPermissions(std::string(file), mode, honor_umask);
4406 Status SystemTools::SetPermissions(std::string const& file, mode_t mode,
4409 if (!SystemTools::PathExists(file)) {
4410 return Status::POSIX(ENOENT);
4413 mode_t currentMask = umask(0);
4415 mode &= ~currentMask;
4418 if (_wchmod(Encoding::ToWindowsExtendedPath(file).c_str(), mode) < 0)
4420 if (chmod(file.c_str(), mode) < 0)
4423 return Status::POSIX_errno();
4426 return Status::Success();
4429 std::string SystemTools::GetParentDirectory(const std::string& fileOrDir)
4431 return SystemTools::GetFilenamePath(fileOrDir);
4434 bool SystemTools::IsSubDirectory(const std::string& cSubdir,
4435 const std::string& cDir)
4440 std::string subdir = cSubdir;
4441 std::string dir = cDir;
4442 SystemTools::ConvertToUnixSlashes(subdir);
4443 SystemTools::ConvertToUnixSlashes(dir);
4444 if (subdir.size() <= dir.size() || dir.empty()) {
4447 bool isRootPath = dir.back() == '/'; // like "/" or "C:/"
4448 size_t expectedSlashPosition = isRootPath ? dir.size() - 1u : dir.size();
4449 if (subdir[expectedSlashPosition] != '/') {
4452 subdir.resize(dir.size());
4453 return SystemTools::ComparePath(subdir, dir);
4456 void SystemTools::Delay(unsigned int msec)
4461 // The sleep function gives 1 second resolution and the usleep
4462 // function gives 1e-6 second resolution but on some platforms has a
4463 // maximum sleep time of 1 second. This could be re-implemented to
4464 // use select with masked signals or pselect to mask signals
4465 // atomically. If select is given empty sets and zero as the max
4466 // file descriptor but a non-zero timeout it can be used to block
4467 // for a precise amount of time.
4470 usleep((msec % 1000) * 1000);
4472 usleep(msec * 1000);
4477 std::string SystemTools::GetOperatingSystemNameAndVersion()
4484 OSVERSIONINFOEXA osvi;
4485 BOOL bOsVersionInfoEx;
4487 ZeroMemory(&osvi, sizeof(osvi));
4488 osvi.dwOSVersionInfoSize = sizeof(osvi);
4490 # ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4491 # pragma warning(push)
4492 # ifdef __INTEL_COMPILER
4493 # pragma warning(disable : 1478)
4494 # elif defined __clang__
4495 # pragma clang diagnostic push
4496 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
4498 # pragma warning(disable : 4996)
4501 bOsVersionInfoEx = GetVersionExA((OSVERSIONINFOA*)&osvi);
4502 if (!bOsVersionInfoEx) {
4505 # ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
4507 # pragma clang diagnostic pop
4509 # pragma warning(pop)
4513 switch (osvi.dwPlatformId) {
4514 // Test for the Windows NT product family.
4516 case VER_PLATFORM_WIN32_NT:
4518 // Test for the specific product family.
4519 if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0) {
4520 if (osvi.wProductType == VER_NT_WORKSTATION) {
4521 res += "Microsoft Windows 10";
4523 res += "Microsoft Windows Server 2016 family";
4527 if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) {
4528 if (osvi.wProductType == VER_NT_WORKSTATION) {
4529 res += "Microsoft Windows 8.1";
4531 res += "Microsoft Windows Server 2012 R2 family";
4535 if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) {
4536 if (osvi.wProductType == VER_NT_WORKSTATION) {
4537 res += "Microsoft Windows 8";
4539 res += "Microsoft Windows Server 2012 family";
4543 if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) {
4544 if (osvi.wProductType == VER_NT_WORKSTATION) {
4545 res += "Microsoft Windows 7";
4547 res += "Microsoft Windows Server 2008 R2 family";
4551 if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) {
4552 if (osvi.wProductType == VER_NT_WORKSTATION) {
4553 res += "Microsoft Windows Vista";
4555 res += "Microsoft Windows Server 2008 family";
4559 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4560 res += "Microsoft Windows Server 2003 family";
4563 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
4564 res += "Microsoft Windows XP";
4567 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4568 res += "Microsoft Windows 2000";
4571 if (osvi.dwMajorVersion <= 4) {
4572 res += "Microsoft Windows NT";
4575 // Test for specific product on Windows NT 4.0 SP6 and later.
4577 if (bOsVersionInfoEx) {
4578 // Test for the workstation type.
4580 if (osvi.wProductType == VER_NT_WORKSTATION) {
4581 if (osvi.dwMajorVersion == 4) {
4582 res += " Workstation 4.0";
4583 } else if (osvi.dwMajorVersion == 5) {
4584 if (osvi.wSuiteMask & VER_SUITE_PERSONAL) {
4585 res += " Home Edition";
4587 res += " Professional";
4592 // Test for the server type.
4594 else if (osvi.wProductType == VER_NT_SERVER) {
4595 if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
4596 if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
4597 res += " Datacenter Edition";
4598 } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4599 res += " Enterprise Edition";
4600 } else if (osvi.wSuiteMask == VER_SUITE_BLADE) {
4601 res += " Web Edition";
4603 res += " Standard Edition";
4607 else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
4608 if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
4609 res += " Datacenter Server";
4610 } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4611 res += " Advanced Server";
4617 else if (osvi.dwMajorVersion <= 4) // Windows NT 4.0
4619 if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
4620 res += " Server 4.0, Enterprise Edition";
4622 res += " Server 4.0";
4628 // Test for specific product on Windows NT 4.0 SP5 and earlier
4633 wchar_t szProductType[BUFSIZE];
4634 DWORD dwBufLen = BUFSIZE;
4638 RegOpenKeyExW(HKEY_LOCAL_MACHINE,
4639 L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
4640 0, KEY_QUERY_VALUE, &hKey);
4641 if (lRet != ERROR_SUCCESS) {
4645 lRet = RegQueryValueExW(hKey, L"ProductType", nullptr, nullptr,
4646 (LPBYTE)szProductType, &dwBufLen);
4648 if ((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE)) {
4654 if (lstrcmpiW(L"WINNT", szProductType) == 0) {
4655 res += " Workstation";
4657 if (lstrcmpiW(L"LANMANNT", szProductType) == 0) {
4660 if (lstrcmpiW(L"SERVERNT", szProductType) == 0) {
4661 res += " Advanced Server";
4665 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMajorVersion);
4668 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwMinorVersion);
4672 // Display service pack (if any) and build number.
4674 if (osvi.dwMajorVersion == 4 &&
4675 lstrcmpiA(osvi.szCSDVersion, "Service Pack 6") == 0) {
4679 // Test for SP6 versus SP6a.
4681 lRet = RegOpenKeyExW(
4683 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
4684 0, KEY_QUERY_VALUE, &hKey);
4686 if (lRet == ERROR_SUCCESS) {
4687 res += " Service Pack 6a (Build ";
4688 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4691 } else // Windows NT 4.0 prior to SP6a
4694 res += osvi.szCSDVersion;
4696 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4702 } else // Windows NT 3.51 and earlier or Windows 2000 and later
4705 res += osvi.szCSDVersion;
4707 snprintf(buffer, sizeof(buffer), "%ld", osvi.dwBuildNumber & 0xFFFF);
4714 // Test for the Windows 95 product family.
4716 case VER_PLATFORM_WIN32_WINDOWS:
4718 if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) {
4719 res += "Microsoft Windows 95";
4720 if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') {
4725 if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) {
4726 res += "Microsoft Windows 98";
4727 if (osvi.szCSDVersion[1] == 'A') {
4732 if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) {
4733 res += "Microsoft Windows Millennium Edition";
4737 case VER_PLATFORM_WIN32s:
4739 res += "Microsoft Win32s";
4747 bool SystemTools::ParseURLProtocol(const std::string& URL,
4748 std::string& protocol,
4749 std::string& dataglom, bool decode)
4751 // match 0 entire url
4753 // match 2 dataglom following protocol://
4754 kwsys::RegularExpression urlRe(VTK_URL_PROTOCOL_REGEX);
4756 if (!urlRe.find(URL))
4759 protocol = urlRe.match(1);
4760 dataglom = urlRe.match(2);
4763 dataglom = DecodeURL(dataglom);
4769 bool SystemTools::ParseURL(const std::string& URL, std::string& protocol,
4770 std::string& username, std::string& password,
4771 std::string& hostname, std::string& dataport,
4772 std::string& database, bool decode)
4774 kwsys::RegularExpression urlRe(VTK_URL_REGEX);
4775 if (!urlRe.find(URL))
4780 // match 2 mangled user
4782 // match 4 mangled password
4785 // match 7 mangled port
4787 // match 9 database name
4789 protocol = urlRe.match(1);
4790 username = urlRe.match(3);
4791 password = urlRe.match(5);
4792 hostname = urlRe.match(6);
4793 dataport = urlRe.match(8);
4794 database = urlRe.match(9);
4797 username = DecodeURL(username);
4798 password = DecodeURL(password);
4799 hostname = DecodeURL(hostname);
4800 dataport = DecodeURL(dataport);
4801 database = DecodeURL(database);
4807 // ----------------------------------------------------------------------
4808 std::string SystemTools::DecodeURL(const std::string& url)
4810 kwsys::RegularExpression urlByteRe(VTK_URL_BYTE_REGEX);
4812 for (size_t i = 0; i < url.length(); i++) {
4813 if (urlByteRe.find(url.substr(i, 3))) {
4814 char bytes[] = { url[i + 1], url[i + 2], '\0' };
4815 ret += static_cast<char>(strtoul(bytes, nullptr, 16));
4824 // ----------------------------------------------------------------------
4825 // Do NOT initialize. Default initialization to zero is necessary.
4826 static unsigned int SystemToolsManagerCount;
4828 // SystemToolsManager manages the SystemTools singleton.
4829 // SystemToolsManager should be included in any translation unit
4830 // that will use SystemTools or that implements the singleton
4831 // pattern. It makes sure that the SystemTools singleton is created
4832 // before and destroyed after all other singletons in CMake.
4834 SystemToolsManager::SystemToolsManager()
4836 if (++SystemToolsManagerCount == 1) {
4837 SystemTools::ClassInitialize();
4841 SystemToolsManager::~SystemToolsManager()
4843 if (--SystemToolsManagerCount == 0) {
4844 SystemTools::ClassFinalize();
4849 // On VMS we configure the run time C library to be more UNIX like.
4850 // http://h71000.www7.hp.com/doc/732final/5763/5763pro_004.html
4851 extern "C" int decc$feature_get_index(char* name);
4852 extern "C" int decc$feature_set_value(int index, int mode, int value);
4853 static int SetVMSFeature(char* name, int value)
4857 i = decc$feature_get_index(name);
4858 return i >= 0 && (decc$feature_set_value(i, 1, value) >= 0 || errno == 0);
4862 void SystemTools::ClassInitialize()
4865 SetVMSFeature("DECC$FILENAME_UNIX_ONLY", 1);
4868 // Create statics singleton instance
4869 SystemToolsStatics = new SystemToolsStatic;
4871 #if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
4872 // Add some special translation paths for unix. These are not added
4873 // for windows because drive letters need to be maintained. Also,
4874 // there are not sym-links and mount points on windows anyway.
4875 # if !defined(_WIN32) || defined(__CYGWIN__)
4876 // The tmp path is frequently a logical path so always keep it:
4877 SystemTools::AddKeepPath("/tmp/");
4879 // If the current working directory is a logical path then keep the
4881 std::string pwd_str;
4882 if (SystemTools::GetEnv("PWD", pwd_str)) {
4884 if (const char* cwd = Getcwd(buf, 2048)) {
4885 // The current working directory may be a logical path. Find
4886 // the shortest logical path that still produces the correct
4888 std::string cwd_changed;
4889 std::string pwd_changed;
4891 // Test progressively shorter logical-to-physical mappings.
4892 std::string cwd_str = cwd;
4893 std::string pwd_path;
4894 Realpath(pwd_str, pwd_path);
4895 while (cwd_str == pwd_path && cwd_str != pwd_str) {
4896 // The current pair of paths is a working logical mapping.
4897 cwd_changed = cwd_str;
4898 pwd_changed = pwd_str;
4900 // Strip off one directory level and see if the logical
4901 // mapping still works.
4902 pwd_str = SystemTools::GetFilenamePath(pwd_str);
4903 cwd_str = SystemTools::GetFilenamePath(cwd_str);
4904 Realpath(pwd_str, pwd_path);
4907 // Add the translation to keep the logical path name.
4908 if (!cwd_changed.empty() && !pwd_changed.empty()) {
4909 SystemTools::AddTranslationPath(cwd_changed, pwd_changed);
4917 void SystemTools::ClassFinalize()
4919 delete SystemToolsStatics;
4922 } // namespace KWSYS_NAMESPACE
4924 #if defined(_MSC_VER) && defined(_DEBUG)
4925 # include <crtdbg.h>
4927 # include <stdlib.h>
4928 namespace KWSYS_NAMESPACE {
4930 static int SystemToolsDebugReport(int, char* message, int* ret)
4933 // Pretend user clicked on Retry button in popup.
4936 fprintf(stderr, "%s", message);
4938 return 1; // no further reporting required
4941 void SystemTools::EnableMSVCDebugHook()
4943 if (SystemTools::HasEnv("DART_TEST_FROM_DART") ||
4944 SystemTools::HasEnv("DASHBOARD_TEST_FROM_CTEST")) {
4945 _CrtSetReportHook(SystemToolsDebugReport);
4949 } // namespace KWSYS_NAMESPACE
4951 namespace KWSYS_NAMESPACE {
4952 void SystemTools::EnableMSVCDebugHook()
4955 } // namespace KWSYS_NAMESPACE