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:
Zachary Turner
2016-03-22 17:58:09 +00:00
parent 6feeb6554e
commit 190fadcdb2
29 changed files with 538 additions and 234 deletions

View File

@@ -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;
}