mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Code extracted from:
https://gitlab.kitware.com/utils/kwsys.git
at commit 615be4e105d29bc22811fd7fdd890cfa636776df (master).
Upstream Shortlog
-----------------
Brad King (3):
f5716ce5 Encoding: Update comment describing ToWindowsExtendedPath
597af96a Encoding: Avoid repeat work in ToWindowsExtendedPath on paths < 512 chars
27b76f56 Encoding: Reduce allocations in ToWindowsExtendedPath
320 lines
8.7 KiB
C++
320 lines
8.7 KiB
C++
/* 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 <algorithm>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
#ifdef _MSC_VER
|
|
# pragma warning(disable : 4786)
|
|
#endif
|
|
|
|
// Windows API.
|
|
#if defined(_WIN32)
|
|
# include <windows.h>
|
|
|
|
# include <shellapi.h>
|
|
#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<std::string> av1(ac);
|
|
std::vector<char const*> 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<int>(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<size_t>((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<size_t>((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<size_t>(-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<size_t>(-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
|