/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ #ifdef __osf__ # define _OSF_SOURCE # define _POSIX_C_SOURCE 199506L # define _XOPEN_SOURCE_EXTENDED #endif #include "kwsysPrivate.h" #include KWSYS_HEADER(Encoding.hxx) #include KWSYS_HEADER(Encoding.h) #include KWSYS_HEADER(String.h) // Work-around CMake dependency scanning limitation. This must // duplicate the above list of headers. #if 0 # include "Encoding.h.in" # include "Encoding.hxx.in" # include "String.h" #endif #include #include #include #include #ifdef _MSC_VER # pragma warning(disable : 4786) #endif // Windows API. #if defined(_WIN32) # include # include #endif namespace KWSYS_NAMESPACE { Encoding::CommandLineArguments Encoding::CommandLineArguments::Main( int argc, char const* const* argv) { #ifdef _WIN32 (void)argc; (void)argv; int ac; LPWSTR* w_av = CommandLineToArgvW(GetCommandLineW(), &ac); std::vector av1(ac); std::vector av2(ac); for (int i = 0; i < ac; i++) { av1[i] = ToNarrow(w_av[i]); av2[i] = av1[i].c_str(); } LocalFree(w_av); return CommandLineArguments(ac, &av2[0]); #else return CommandLineArguments(argc, argv); #endif } Encoding::CommandLineArguments::CommandLineArguments(int ac, char const* const* av) { this->argv_.resize(ac + 1); for (int i = 0; i < ac; i++) { this->argv_[i] = strdup(av[i]); } this->argv_[ac] = nullptr; } Encoding::CommandLineArguments::CommandLineArguments(int ac, wchar_t const* const* av) { this->argv_.resize(ac + 1); for (int i = 0; i < ac; i++) { this->argv_[i] = kwsysEncoding_DupToNarrow(av[i]); } this->argv_[ac] = nullptr; } Encoding::CommandLineArguments::~CommandLineArguments() { for (size_t i = 0; i < this->argv_.size(); i++) { free(argv_[i]); } } Encoding::CommandLineArguments::CommandLineArguments( CommandLineArguments const& other) { this->argv_.resize(other.argv_.size()); for (size_t i = 0; i < this->argv_.size(); i++) { this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : nullptr; } } Encoding::CommandLineArguments& Encoding::CommandLineArguments::operator=( CommandLineArguments const& other) { if (this != &other) { size_t i; for (i = 0; i < this->argv_.size(); i++) { free(this->argv_[i]); } this->argv_.resize(other.argv_.size()); for (i = 0; i < this->argv_.size(); i++) { this->argv_[i] = other.argv_[i] ? strdup(other.argv_[i]) : nullptr; } } return *this; } int Encoding::CommandLineArguments::argc() const { return static_cast(this->argv_.size() - 1); } char const* const* Encoding::CommandLineArguments::argv() const { return &this->argv_[0]; } std::wstring Encoding::ToWide(std::string const& str) { std::wstring wstr; #if defined(_WIN32) int const wlength = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), int(str.size()), nullptr, 0); if (wlength > 0) { wstr.resize(wlength); int r = MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), int(str.size()), &wstr[0], wlength); wstr.resize(static_cast((std::max)(0, r))); } #else size_t pos = 0; size_t nullPos = 0; do { if (pos < str.size() && str.at(pos) != '\0') { wstr += ToWide(str.c_str() + pos); } nullPos = str.find('\0', pos); if (nullPos != std::string::npos) { pos = nullPos + 1; wstr += wchar_t('\0'); } } while (nullPos != std::string::npos); #endif return wstr; } std::string Encoding::ToNarrow(std::wstring const& str) { std::string nstr; #if defined(_WIN32) int length = WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(), int(str.size()), nullptr, 0, nullptr, nullptr); if (length > 0) { nstr.resize(length); int r = WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.c_str(), int(str.size()), &nstr[0], length, nullptr, nullptr); nstr.resize(static_cast((std::max)(0, r))); } #else size_t pos = 0; size_t nullPos = 0; do { if (pos < str.size() && str.at(pos) != '\0') { nstr += ToNarrow(str.c_str() + pos); } nullPos = str.find(wchar_t('\0'), pos); if (nullPos != std::string::npos) { pos = nullPos + 1; nstr += '\0'; } } while (nullPos != std::string::npos); #endif return nstr; } std::wstring Encoding::ToWide(char const* cstr) { std::wstring wstr; size_t length = kwsysEncoding_mbstowcs(nullptr, cstr, 0); if (length == 0 || length == static_cast(-1)) { return wstr; } ++length; wstr.resize(length); length = kwsysEncoding_mbstowcs(&wstr[0], cstr, length); wstr.resize(length); return wstr; } std::string Encoding::ToNarrow(wchar_t const* wcstr) { std::string str; size_t length = kwsysEncoding_wcstombs(nullptr, wcstr, 0); if (length == 0 || length == static_cast(-1)) { return str; } ++length; str.resize(length); length = kwsysEncoding_wcstombs(&str[0], wcstr, length); str.resize(length); return str; } #if defined(_WIN32) // Convert local paths to UNC style paths: // * https://web.archive.org/web/20250329050419/ // https://cpp.arsenmk.com/2015/12/handling-long-paths-on-windows.html namespace { // UNC prefix for local paths. std::wstring const unc_local_prefix = L"\\\\?\\"; // UNC prefix for remote paths. std::wstring const unc_remote_prefix = L"\\\\?\\UNC\\"; // The longest UNC prefix replaces two leading backslashes. size_t const unc_max_space = unc_remote_prefix.length() - 2; } std::wstring Encoding::ToWindowsExtendedPath(std::string const& source) { return ToWindowsExtendedPath(ToWide(source)); } std::wstring Encoding::ToWindowsExtendedPath(char const* source) { return ToWindowsExtendedPath(ToWide(source)); } std::wstring Encoding::ToWindowsExtendedPath(std::wstring const& wsource) { // Resolve any relative paths std::wstring wfull; DWORD wfull_len; // Try with a stack-allocated buffer first. wchar_t local_buf[512]; size_t const local_buf_len = sizeof(local_buf) / sizeof(local_buf[0]); wfull_len = GetFullPathNameW(wsource.c_str(), local_buf_len, local_buf, nullptr); if (wfull_len <= local_buf_len) { // The stack-allocated buffer was large enough. It holds the result. // Reserve room for the prefix added below. wfull.reserve(wfull_len + unc_max_space); // Store the absolute path to be returned. wfull.assign(local_buf, wfull_len); } else { // The stack-allocated buffer was too small, but now we know how // much to allocate on the heap. // Some versions of GetFullPathNameW return a slightly too-small size. wfull_len += 3; // Reserve room for the prefix added below. wfull.reserve(wfull_len + unc_max_space); // Try again with a heap-allocated buffer. wfull.resize(wfull_len, L'\0'); wfull_len = GetFullPathNameW(wsource.c_str(), wfull_len, &wfull[0], nullptr); wfull.resize(wfull_len); } if (wfull_len >= 2 && kwsysString_isalpha(wfull[0]) && wfull[1] == L':') { /* C:\Foo\bar\FooBar.txt */ wfull.insert(0, unc_local_prefix); return wfull; } else if (wfull_len >= 2 && wfull[0] == L'\\' && wfull[1] == L'\\') { /* Starts with \\ */ if (wfull_len >= 4 && wfull[2] == L'?' && wfull[3] == L'\\') { /* Starts with \\?\ */ if (wfull_len >= 8 && wfull[4] == L'U' && wfull[5] == L'N' && wfull[6] == L'C' && wfull[7] == L'\\') { /* \\?\UNC\Foo\bar\FooBar.txt */ return wfull; } else if (wfull_len >= 6 && kwsysString_isalpha(wfull[4]) && wfull[5] == L':') { /* \\?\C:\Foo\bar\FooBar.txt */ return wfull; } else if (wfull_len >= 5) { /* \\?\Foo\bar\FooBar.txt */ wfull.replace(0, 4, unc_remote_prefix); return wfull; } } else if (wfull_len >= 4 && wfull[2] == L'.' && wfull[3] == L'\\') { /* Starts with \\.\ a device name */ if (wfull_len >= 6 && kwsysString_isalpha(wfull[4]) && wfull[5] == L':') { /* \\.\C:\Foo\bar\FooBar.txt */ wfull.replace(0, 4, unc_local_prefix); return wfull; } else if (wfull_len >= 5) { /* \\.\Foo\bar\ Device name is left unchanged */ return wfull; } } else if (wfull_len >= 3) { /* \\Foo\bar\FooBar.txt */ wfull.replace(0, 2, unc_remote_prefix); return wfull; } } // If this case has been reached, then the path is invalid. Leave it // unchanged return wsource; } #endif } // namespace KWSYS_NAMESPACE