diff options
Diffstat (limited to 'src/fs/Charset.cxx')
-rw-r--r-- | src/fs/Charset.cxx | 163 |
1 files changed, 83 insertions, 80 deletions
diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx index c634c9340..b25615d42 100644 --- a/src/fs/Charset.cxx +++ b/src/fs/Charset.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,12 +21,12 @@ #include "Charset.hxx" #include "Domain.hxx" #include "Limits.hxx" -#include "system/FatalError.hxx" #include "Log.hxx" -#include "Traits.hxx" +#include "lib/icu/Converter.hxx" +#include "util/Error.hxx" -#ifdef HAVE_GLIB -#include <glib.h> +#ifdef WIN32 +#include <windows.h> #endif #include <algorithm> @@ -34,130 +34,133 @@ #include <assert.h> #include <string.h> -#ifdef HAVE_GLIB - -/** - * Maximal number of bytes required to represent path name in UTF-8 - * (including nul-terminator). - * This value is a rought estimate of upper bound. - * It's based on path name limit in bytes (MPD_PATH_MAX) - * and assumption that some weird encoding could represent some UTF-8 4 byte - * sequences with single byte. - */ -static constexpr size_t MPD_PATH_MAX_UTF8 = (MPD_PATH_MAX - 1) * 4 + 1; +#ifdef HAVE_FS_CHARSET static std::string fs_charset; -gcc_pure -static bool -IsSupportedCharset(const char *charset) -{ - /* convert a space to check if the charset is valid */ - char *test = g_convert(" ", 1, charset, "UTF-8", nullptr, nullptr, nullptr); - if (test == nullptr) - return false; +static IcuConverter *fs_converter; - g_free(test); - return true; -} - -void -SetFSCharset(const char *charset) +bool +SetFSCharset(const char *charset, Error &error) { assert(charset != nullptr); + assert(fs_converter == nullptr); - if (!IsSupportedCharset(charset)) - FormatFatalError("invalid filesystem charset: %s", charset); - - fs_charset = charset; + fs_converter = IcuConverter::Create(charset, error); + if (fs_converter == nullptr) + return false; FormatDebug(path_domain, "SetFSCharset: fs charset is: %s", fs_charset.c_str()); + return true; } #endif +void +DeinitFSCharset() +{ +#ifdef HAVE_ICU_CONVERTER + delete fs_converter; + fs_converter = nullptr; +#endif +} + const char * GetFSCharset() { -#ifdef HAVE_GLIB +#ifdef HAVE_FS_CHARSET return fs_charset.empty() ? "UTF-8" : fs_charset.c_str(); #else return "UTF-8"; #endif } -static inline void FixSeparators(std::string &s) +static inline PathTraitsUTF8::string && +FixSeparators(PathTraitsUTF8::string &&s) { -#ifdef WIN32 // For whatever reason GCC can't convert constexpr to value reference. // This leads to link errors when passing separators directly. - auto from = PathTraitsFS::SEPARATOR; auto to = PathTraitsUTF8::SEPARATOR; - std::replace(s.begin(), s.end(), from, to); -#else - (void)s; -#endif + decltype(to) from = PathTraitsFS::SEPARATOR; + + if (from != to) + /* convert backslash to slash on WIN32 */ + std::replace(s.begin(), s.end(), from, to); + + return std::move(s); } -std::string -PathToUTF8(const char *path_fs) +PathTraitsUTF8::string +PathToUTF8(PathTraitsFS::const_pointer path_fs) { #if !CLANG_CHECK_VERSION(3,6) /* disabled on clang due to -Wtautological-pointer-compare */ assert(path_fs != nullptr); #endif -#ifdef HAVE_GLIB - if (fs_charset.empty()) { -#endif - auto result = std::string(path_fs); - FixSeparators(result); - return result; -#ifdef HAVE_GLIB +#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(); } - GIConv conv = g_iconv_open("utf-8", fs_charset.c_str()); - if (conv == reinterpret_cast<GIConv>(-1)) - return std::string(); - - // g_iconv() does not need nul-terminator, - // std::string could be created without it too. - char path_utf8[MPD_PATH_MAX_UTF8 - 1]; - char *in = const_cast<char *>(path_fs); - char *out = path_utf8; - size_t in_left = strlen(path_fs); - size_t out_left = sizeof(path_utf8); - - size_t ret = g_iconv(conv, &in, &in_left, &out, &out_left); - - g_iconv_close(conv); - - if (ret == static_cast<size_t>(-1) || in_left > 0) - return std::string(); + PathTraitsUTF8::string result(buffer); + delete[] buffer; + return FixSeparators(std::move(result)); +#else +#ifdef HAVE_FS_CHARSET + if (fs_converter == nullptr) +#endif + return FixSeparators(path_fs); +#ifdef HAVE_FS_CHARSET - auto result_path = std::string(path_utf8, sizeof(path_utf8) - out_left); - FixSeparators(result_path); - return result_path; + return FixSeparators(fs_converter->ToUTF8(path_fs)); +#endif #endif } -#ifdef HAVE_GLIB +#if defined(HAVE_FS_CHARSET) || defined(WIN32) -char * -PathFromUTF8(const char *path_utf8) +PathTraitsFS::string +PathFromUTF8(PathTraitsUTF8::const_pointer path_utf8) { #if !CLANG_CHECK_VERSION(3,6) /* disabled on clang due to -Wtautological-pointer-compare */ assert(path_utf8 != nullptr); #endif - if (fs_charset.empty()) - return g_strdup(path_utf8); +#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 g_convert(path_utf8, -1, - fs_charset.c_str(), "utf-8", - nullptr, nullptr, nullptr); + return fs_converter->FromUTF8(path_utf8); +#endif } #endif |