From 65ff72cdf8d3fc8664893b55ca47fca284f34d87 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 25 Feb 2015 16:01:46 +0100 Subject: fs/Traits: enable _UNICODE on Windows Use wchar_t for everything on Windows. Solves a lot of filesystem charset problems. --- src/CommandLine.cxx | 13 +++++++++++++ src/PlaylistFile.cxx | 10 ++++++++++ src/fs/AllocatedPath.cxx | 2 +- src/fs/Charset.cxx | 44 +++++++++++++++++++++++++++++++++++++++++++- src/fs/NarrowPath.hxx | 20 ++++++++++++++++++++ src/fs/Traits.hxx | 4 ++++ src/net/SocketError.cxx | 25 ++++++++++++++++++++++--- 7 files changed, 113 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx index e87b1676f..1a55a29ea 100644 --- a/src/CommandLine.cxx +++ b/src/CommandLine.cxx @@ -35,6 +35,7 @@ #include "fs/Traits.hxx" #include "fs/FileSystem.hxx" #include "fs/StandardDirectory.hxx" +#include "util/Macros.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "util/OptionDef.hxx" @@ -337,7 +338,19 @@ parse_cmdline(int argc, char **argv, struct options *options, if (config_file != nullptr) { /* use specified configuration file */ +#ifdef _UNICODE + wchar_t buffer[MAX_PATH]; + auto result = MultiByteToWideChar(CP_ACP, 0, config_file, -1, + buffer, ARRAY_SIZE(buffer)); + if (result <= 0) { + error.SetLastError("MultiByteToWideChar() failed"); + return false; + } + + return ReadConfigFile(Path::FromFS(buffer), error); +#else return ReadConfigFile(Path::FromFS(config_file), error); +#endif } /* use default configuration file path */ diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index 791b2f2ed..1123f00ba 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -268,7 +268,17 @@ LoadPlaylistFile(const char *utf8path, Error &error) if (*s == 0 || *s == PLAYLIST_COMMENT) continue; +#ifdef _UNICODE + wchar_t buffer[MAX_PATH]; + auto result = MultiByteToWideChar(CP_ACP, 0, s, -1, + buffer, ARRAY_SIZE(buffer)); + if (result <= 0) + continue; + + const Path path = Path::FromFS(buffer); +#else const Path path = Path::FromFS(s); +#endif std::string uri_utf8; diff --git a/src/fs/AllocatedPath.cxx b/src/fs/AllocatedPath.cxx index 087cbf76a..8b03ed2f1 100644 --- a/src/fs/AllocatedPath.cxx +++ b/src/fs/AllocatedPath.cxx @@ -30,7 +30,7 @@ AllocatedPath::~AllocatedPath() {} AllocatedPath AllocatedPath::FromUTF8(const char *path_utf8) { -#ifdef HAVE_FS_CHARSET +#if defined(HAVE_FS_CHARSET) || defined(WIN32) return AllocatedPath(::PathFromUTF8(path_utf8)); #else return FromFS(path_utf8); diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx index bc6357297..b25615d42 100644 --- a/src/fs/Charset.cxx +++ b/src/fs/Charset.cxx @@ -25,6 +25,10 @@ #include "lib/icu/Converter.hxx" #include "util/Error.hxx" +#ifdef WIN32 +#include +#endif + #include #include @@ -95,6 +99,24 @@ PathToUTF8(PathTraitsFS::const_pointer path_fs) assert(path_fs != nullptr); #endif +#ifdef WIN32 + int length = WideCharToMultiByte(CP_UTF8, 0, path_fs, -1, nullptr, 0, + nullptr, nullptr); + if (length <= 0) + return PathTraitsUTF8::string(); + + char *buffer = new char[length]; + length = WideCharToMultiByte(CP_UTF8, 0, path_fs, -1, buffer, length, + nullptr, nullptr); + if (length <= 0) { + delete[] buffer; + return PathTraitsUTF8::string(); + } + + PathTraitsUTF8::string result(buffer); + delete[] buffer; + return FixSeparators(std::move(result)); +#else #ifdef HAVE_FS_CHARSET if (fs_converter == nullptr) #endif @@ -103,9 +125,10 @@ PathToUTF8(PathTraitsFS::const_pointer path_fs) return FixSeparators(fs_converter->ToUTF8(path_fs)); #endif +#endif } -#ifdef HAVE_FS_CHARSET +#if defined(HAVE_FS_CHARSET) || defined(WIN32) PathTraitsFS::string PathFromUTF8(PathTraitsUTF8::const_pointer path_utf8) @@ -115,10 +138,29 @@ PathFromUTF8(PathTraitsUTF8::const_pointer path_utf8) assert(path_utf8 != nullptr); #endif +#ifdef WIN32 + int length = MultiByteToWideChar(CP_UTF8, 0, path_utf8, -1, + nullptr, 0); + if (length <= 0) + return PathTraitsFS::string(); + + wchar_t *buffer = new wchar_t[length]; + length = MultiByteToWideChar(CP_UTF8, 0, path_utf8, -1, + buffer, length); + if (length <= 0) { + delete[] buffer; + return PathTraitsFS::string(); + } + + PathTraitsFS::string result(buffer); + delete[] buffer; + return std::move(result); +#else if (fs_converter == nullptr) return path_utf8; return fs_converter->FromUTF8(path_utf8); +#endif } #endif diff --git a/src/fs/NarrowPath.hxx b/src/fs/NarrowPath.hxx index 2088f9359..ad310cd5c 100644 --- a/src/fs/NarrowPath.hxx +++ b/src/fs/NarrowPath.hxx @@ -22,6 +22,11 @@ #include "check.h" #include "Path.hxx" +#include "util/Macros.hxx" + +#ifdef _UNICODE +#include +#endif /** * A path name that uses the regular (narrow) "char". This is used to @@ -32,10 +37,25 @@ class NarrowPath { typedef char value_type; typedef const char *const_pointer; +#ifdef _UNICODE + char value[PATH_MAX]; +#else const_pointer value; +#endif public: +#ifdef _UNICODE + explicit NarrowPath(Path _path) { + auto result = WideCharToMultiByte(CP_ACP, 0, + _path.c_str(), -1, + value, ARRAY_SIZE(value), + nullptr, nullptr); + if (result < 0) + value[0] = 0; + } +#else explicit NarrowPath(Path _path):value(_path.c_str()) {} +#endif operator const_pointer() const { return value; diff --git a/src/fs/Traits.hxx b/src/fs/Traits.hxx index cdd9b531d..b92330f60 100644 --- a/src/fs/Traits.hxx +++ b/src/fs/Traits.hxx @@ -43,7 +43,11 @@ * This class describes the nature of a native filesystem path. */ struct PathTraitsFS { +#ifdef WIN32 + typedef std::wstring string; +#else typedef std::string string; +#endif typedef string::traits_type char_traits; typedef char_traits::char_type value_type; typedef value_type *pointer; diff --git a/src/net/SocketError.cxx b/src/net/SocketError.cxx index c4c9d7a2c..efa969b1f 100644 --- a/src/net/SocketError.cxx +++ b/src/net/SocketError.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "SocketError.hxx" #include "util/Domain.hxx" +#include "util/Macros.hxx" #include @@ -29,13 +30,31 @@ const Domain socket_domain("socket"); SocketErrorMessage::SocketErrorMessage(socket_error_t code) { +#ifdef _UNICODE + wchar_t buffer[ARRAY_SIZE(msg)]; +#else + auto *buffer = msg; +#endif + DWORD nbytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, code, 0, - (LPSTR)msg, sizeof(msg), NULL); - if (nbytes == 0) + nullptr, code, 0, + buffer, ARRAY_SIZE(msg), nullptr); + if (nbytes == 0) { strcpy(msg, "Unknown error"); + return; + } + +#ifdef _UNICODE + auto length = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, + msg, ARRAY_SIZE(msg), + nullptr, nullptr); + if (length <= 0) { + strcpy(msg, "WideCharToMultiByte() error"); + return; + } +#endif } #else -- cgit v1.2.3