Unicode support on Win32.
Win32 API calls that are Unicode aware require wide character strings, but LLDB uses UTF8 everywhere. This patch does conversions wherever necessary when passing strings into and out of Win32 API calls. Patch by Cameron Differential Revision: http://reviews.llvm.org/D17107 Reviewed By: zturner, amccarth llvm-svn: 264074
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
|
||||
#include "lldb/Host/FileSystem.h"
|
||||
#include "lldb/Host/windows/AutoHandle.h"
|
||||
|
||||
#include "llvm/Support/ConvertUTF.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
|
||||
using namespace lldb_private;
|
||||
@@ -22,6 +24,8 @@ using namespace lldb_private;
|
||||
const char *
|
||||
FileSystem::DEV_NULL = "nul";
|
||||
|
||||
const char *FileSystem::PATH_CONVERSION_ERROR = "Error converting path between UTF-8 and native encoding";
|
||||
|
||||
FileSpec::PathSyntax
|
||||
FileSystem::GetNativePathSyntax()
|
||||
{
|
||||
@@ -47,25 +51,32 @@ Error
|
||||
FileSystem::DeleteDirectory(const FileSpec &file_spec, bool recurse)
|
||||
{
|
||||
Error error;
|
||||
std::wstring path_buffer;
|
||||
if (!llvm::ConvertUTF8toWide(file_spec.GetPath(), path_buffer))
|
||||
{
|
||||
error.SetErrorString(PATH_CONVERSION_ERROR);
|
||||
return error;
|
||||
}
|
||||
if (!recurse)
|
||||
{
|
||||
BOOL result = ::RemoveDirectory(file_spec.GetCString());
|
||||
BOOL result = ::RemoveDirectoryW(path_buffer.c_str());
|
||||
if (!result)
|
||||
error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
|
||||
}
|
||||
else
|
||||
{
|
||||
// SHFileOperation() accepts a list of paths, and so must be double-null-terminated to
|
||||
// indicate the end of the list.
|
||||
std::string path_buffer{file_spec.GetPath()};
|
||||
// indicate the end of the list. The first null terminator is there only in the backing
|
||||
// store but not the actual vector contents, and so we need to push twice.
|
||||
path_buffer.push_back(0);
|
||||
path_buffer.push_back(0);
|
||||
|
||||
SHFILEOPSTRUCT shfos = {0};
|
||||
SHFILEOPSTRUCTW shfos = {0};
|
||||
shfos.wFunc = FO_DELETE;
|
||||
shfos.pFrom = path_buffer.c_str();
|
||||
shfos.pFrom = (LPCWSTR)path_buffer.data();
|
||||
shfos.fFlags = FOF_NO_UI;
|
||||
|
||||
int result = ::SHFileOperation(&shfos);
|
||||
int result = ::SHFileOperationW(&shfos);
|
||||
// TODO(zturner): Correctly handle the intricacies of SHFileOperation return values.
|
||||
if (result != 0)
|
||||
error.SetErrorStringWithFormat("SHFileOperation failed");
|
||||
@@ -121,7 +132,10 @@ Error
|
||||
FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
|
||||
{
|
||||
Error error;
|
||||
if (!::CreateHardLink(src.GetCString(), dst.GetCString(), nullptr))
|
||||
std::wstring wsrc, wdst;
|
||||
if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
|
||||
error.SetErrorString(PATH_CONVERSION_ERROR);
|
||||
else if (!::CreateHardLinkW(wsrc.c_str(), wdst.c_str(), nullptr))
|
||||
error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
|
||||
return error;
|
||||
}
|
||||
@@ -129,13 +143,12 @@ FileSystem::Hardlink(const FileSpec &src, const FileSpec &dst)
|
||||
int
|
||||
FileSystem::GetHardlinkCount(const FileSpec &file_spec)
|
||||
{
|
||||
HANDLE file_handle = ::CreateFile(file_spec.GetCString(),
|
||||
FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ,
|
||||
nullptr,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
std::wstring path;
|
||||
if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path))
|
||||
return -1;
|
||||
|
||||
HANDLE file_handle = ::CreateFileW(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
|
||||
if (file_handle == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
@@ -152,7 +165,12 @@ Error
|
||||
FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
|
||||
{
|
||||
Error error;
|
||||
DWORD attrib = ::GetFileAttributes(dst.GetCString());
|
||||
std::wstring wsrc, wdst;
|
||||
if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc) || !llvm::ConvertUTF8toWide(dst.GetCString(), wdst))
|
||||
error.SetErrorString(PATH_CONVERSION_ERROR);
|
||||
if (error.Fail())
|
||||
return error;
|
||||
DWORD attrib = ::GetFileAttributesW(wdst.c_str());
|
||||
if (attrib == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
|
||||
@@ -160,7 +178,7 @@ FileSystem::Symlink(const FileSpec &src, const FileSpec &dst)
|
||||
}
|
||||
bool is_directory = !!(attrib & FILE_ATTRIBUTE_DIRECTORY);
|
||||
DWORD flag = is_directory ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
|
||||
BOOL result = ::CreateSymbolicLink(src.GetCString(), dst.GetCString(), flag);
|
||||
BOOL result = ::CreateSymbolicLinkW(wsrc.c_str(), wdst.c_str(), flag);
|
||||
if (!result)
|
||||
error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
|
||||
return error;
|
||||
@@ -170,7 +188,13 @@ Error
|
||||
FileSystem::Unlink(const FileSpec &file_spec)
|
||||
{
|
||||
Error error;
|
||||
BOOL result = ::DeleteFile(file_spec.GetCString());
|
||||
std::wstring path;
|
||||
if (!llvm::ConvertUTF8toWide(file_spec.GetCString(), path))
|
||||
{
|
||||
error.SetErrorString(PATH_CONVERSION_ERROR);
|
||||
return error;
|
||||
}
|
||||
BOOL result = ::DeleteFileW(path.c_str());
|
||||
if (!result)
|
||||
error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
|
||||
return error;
|
||||
@@ -180,23 +204,31 @@ Error
|
||||
FileSystem::Readlink(const FileSpec &src, FileSpec &dst)
|
||||
{
|
||||
Error error;
|
||||
HANDLE h = ::CreateFile(src.GetCString(), GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
||||
std::wstring wsrc;
|
||||
if (!llvm::ConvertUTF8toWide(src.GetCString(), wsrc))
|
||||
{
|
||||
error.SetErrorString(PATH_CONVERSION_ERROR);
|
||||
return error;
|
||||
}
|
||||
|
||||
HANDLE h = ::CreateFileW(wsrc.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_OPEN_REPARSE_POINT, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
|
||||
return error;
|
||||
}
|
||||
|
||||
char buf[PATH_MAX];
|
||||
std::vector<wchar_t> buf(PATH_MAX + 1);
|
||||
// Subtract 1 from the path length since this function does not add a null terminator.
|
||||
DWORD result = ::GetFinalPathNameByHandle(h, buf, sizeof(buf) - 1,
|
||||
FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
|
||||
DWORD result = ::GetFinalPathNameByHandleW(h, buf.data(), buf.size() - 1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS);
|
||||
std::string path;
|
||||
if (result == 0)
|
||||
error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
|
||||
else if (!llvm::convertWideToUTF8(buf.data(), path))
|
||||
error.SetErrorString(PATH_CONVERSION_ERROR);
|
||||
else
|
||||
dst.SetFile(buf, false);
|
||||
dst.SetFile(path, false);
|
||||
|
||||
::CloseHandle(h);
|
||||
return error;
|
||||
@@ -219,3 +251,43 @@ FileSystem::IsLocal(const FileSpec &spec)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE *
|
||||
FileSystem::Fopen(const char *path, const char *mode)
|
||||
{
|
||||
std::wstring wpath, wmode;
|
||||
if (!llvm::ConvertUTF8toWide(path, wpath))
|
||||
return nullptr;
|
||||
if (!llvm::ConvertUTF8toWide(mode, wmode))
|
||||
return nullptr;
|
||||
FILE *file;
|
||||
if (_wfopen_s(&file, wpath.c_str(), wmode.c_str()) != 0)
|
||||
return nullptr;
|
||||
return file;
|
||||
}
|
||||
|
||||
int
|
||||
FileSystem::Stat(const char *path, struct stat *stats)
|
||||
{
|
||||
std::wstring wpath;
|
||||
if (!llvm::ConvertUTF8toWide(path, wpath))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
int stat_result;
|
||||
#ifdef _USE_32BIT_TIME_T
|
||||
struct _stat32 file_stats;
|
||||
stat_result = ::_wstat32(wpath.c_str(), &file_stats);
|
||||
#else
|
||||
struct _stat64i32 file_stats;
|
||||
stat_result = ::_wstat64i32(wpath.c_str(), &file_stats);
|
||||
#endif
|
||||
if (stat_result == 0)
|
||||
{
|
||||
static_assert(sizeof(struct stat) == sizeof(file_stats),
|
||||
"stat and _stat32/_stat64i32 must have the same layout");
|
||||
*stats = *reinterpret_cast<struct stat *>(&file_stats);
|
||||
}
|
||||
return stat_result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user