diff options
author | Remko Tronçon <git@el-tramo.be> | 2013-05-15 19:44:37 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2013-05-17 19:00:55 (GMT) |
commit | de39ce6ec44647cee92853e2928cf5475af992e6 (patch) | |
tree | 1f86dd075900aeeb9b91cdd120d71052458ca6ff /Swiften/Base | |
parent | 927d62cc54c8a5087dba6b61afa9ad30dc528a23 (diff) | |
download | swift-de39ce6ec44647cee92853e2928cf5475af992e6.zip swift-de39ce6ec44647cee92853e2928cf5475af992e6.tar.bz2 |
Fixed unicode path handling.
- Use boost::filesystem::path consistently for referring to files.
- Use boost::filesystem streams for I/O, such that paths are always handled
correctly.
- Use stringToPath and pathToString for conversion between strings and
boost::filesystem::path, to ensure we have consistent unicode handling
across platforms and environments. The default constructor and string
conversion uses platform-dependent encoding, depending on the global
locale set in the application, which causes problems. So, unless you are
in platform dependent code, the default constructor and string() function
should not be used. When constructing paths from other paths (e.g. using
operator/), also use stringToPath (instead of string arguments) if the path
can contain unicode characters.
Change-Id: If286bd9e71c8414afc0b24ba67e26ab7608ef6ea
Diffstat (limited to 'Swiften/Base')
-rw-r--r-- | Swiften/Base/ByteArray.cpp | 6 | ||||
-rw-r--r-- | Swiften/Base/ByteArray.h | 3 | ||||
-rw-r--r-- | Swiften/Base/Path.cpp | 28 | ||||
-rw-r--r-- | Swiften/Base/Path.h | 27 | ||||
-rw-r--r-- | Swiften/Base/Paths.cpp | 7 | ||||
-rw-r--r-- | Swiften/Base/SConscript | 1 | ||||
-rw-r--r-- | Swiften/Base/String.cpp | 39 | ||||
-rw-r--r-- | Swiften/Base/String.h | 7 | ||||
-rw-r--r-- | Swiften/Base/UnitTest/PathTest.cpp | 36 | ||||
-rw-r--r-- | Swiften/Base/UnitTest/StringTest.cpp | 15 |
10 files changed, 161 insertions, 8 deletions
diff --git a/Swiften/Base/ByteArray.cpp b/Swiften/Base/ByteArray.cpp index d6b5c97..466db5f 100644 --- a/Swiften/Base/ByteArray.cpp +++ b/Swiften/Base/ByteArray.cpp @@ -7,14 +7,14 @@ #include <Swiften/Base/ByteArray.h> #include <boost/numeric/conversion/cast.hpp> -#include <fstream> +#include <boost/filesystem/fstream.hpp> namespace Swift { static const int BUFFER_SIZE = 4096; -void readByteArrayFromFile(ByteArray& data, const std::string& file) { - std::ifstream input(file.c_str(), std::ios_base::in|std::ios_base::binary); +void readByteArrayFromFile(ByteArray& data, const boost::filesystem::path& file) { + boost::filesystem::ifstream input(file, std::ios_base::in|std::ios_base::binary); while (input.good()) { size_t oldSize = data.size(); data.resize(oldSize + BUFFER_SIZE); diff --git a/Swiften/Base/ByteArray.h b/Swiften/Base/ByteArray.h index 8688aab..133b75f 100644 --- a/Swiften/Base/ByteArray.h +++ b/Swiften/Base/ByteArray.h @@ -10,6 +10,7 @@ #include <string> #include <Swiften/Base/API.h> +#include <boost/filesystem/path.hpp> namespace Swift { typedef std::vector<unsigned char> ByteArray; @@ -41,6 +42,6 @@ namespace Swift { SWIFTEN_API std::string byteArrayToString(const ByteArray& b); - SWIFTEN_API void readByteArrayFromFile(ByteArray&, const std::string& file); + SWIFTEN_API void readByteArrayFromFile(ByteArray&, const boost::filesystem::path& file); } diff --git a/Swiften/Base/Path.cpp b/Swiften/Base/Path.cpp new file mode 100644 index 0000000..2a49676 --- /dev/null +++ b/Swiften/Base/Path.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Swiften/Base/Path.h> + +#include <Swiften/Base/Platform.h> +#include <Swiften/Base/String.h> + +using namespace Swift; + +boost::filesystem::path Swift::stringToPath(const std::string& path) { +#ifdef SWIFTEN_PLATFORM_WINDOWS + return boost::filesystem::path(convertStringToWString(path)); +#else + return boost::filesystem::path(path); +#endif +} + +std::string Swift::pathToString(const boost::filesystem::path& path) { +#ifdef SWIFTEN_PLATFORM_WINDOWS + return convertWStringToString(path.native()); +#else + return path.native(); +#endif +} diff --git a/Swiften/Base/Path.h b/Swiften/Base/Path.h new file mode 100644 index 0000000..ea99be9 --- /dev/null +++ b/Swiften/Base/Path.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Base/API.h> +#include <boost/filesystem/path.hpp> +#include <string> + +namespace Swift { + /** + * Creates a path for the given UTF-8 encoded string. + * This works independently of global locale settings. + */ + SWIFTEN_API boost::filesystem::path stringToPath(const std::string&); + + /** + * Returns the UTF-8 representation of the given path + * This works independently of global locale settings. + */ + SWIFTEN_API std::string pathToString(const boost::filesystem::path&); +} + + diff --git a/Swiften/Base/Paths.cpp b/Swiften/Base/Paths.cpp index d434cc1..8ad1159 100644 --- a/Swiften/Base/Paths.cpp +++ b/Swiften/Base/Paths.cpp @@ -36,10 +36,11 @@ boost::filesystem::path Paths::getExecutablePath() { return boost::filesystem::path(std::string(reinterpret_cast<const char*>(vecptr(path)), path.size()).c_str()).parent_path(); } #elif defined(SWIFTEN_PLATFORM_WINDOWS) - ByteArray data; + std::vector<wchar_t> data; data.resize(2048); - GetModuleFileName(NULL, reinterpret_cast<char*>(vecptr(data)), data.size()); - return boost::filesystem::path(std::string(reinterpret_cast<const char*>(vecptr(data)), data.size()).c_str()).parent_path(); + GetModuleFileNameW(NULL, vecptr(data), data.size()); + return boost::filesystem::path( + std::wstring(vecptr(data), data.size())).parent_path(); #endif return boost::filesystem::path(); } diff --git a/Swiften/Base/SConscript b/Swiften/Base/SConscript index b56db8c..4956fba 100644 --- a/Swiften/Base/SConscript +++ b/Swiften/Base/SConscript @@ -7,6 +7,7 @@ objects = swiften_env.SwiftenObject([ "SafeAllocator.cpp", "Error.cpp", "Log.cpp", + "Path.cpp", "Paths.cpp", "String.cpp", "IDGenerator.cpp", diff --git a/Swiften/Base/String.cpp b/Swiften/Base/String.cpp index 242b8e5..40ea2e1 100644 --- a/Swiften/Base/String.cpp +++ b/Swiften/Base/String.cpp @@ -1,15 +1,21 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ +#include <Swiften/Base/Platform.h> + #include <cassert> #include <algorithm> #include <sstream> #include <iomanip> +#ifdef SWIFTEN_PLATFORM_WINDOWS +#include <windows.h> +#endif #include <Swiften/Base/String.h> +#include <Swiften/Base/ByteArray.h> namespace Swift { @@ -113,4 +119,35 @@ int String::convertHexStringToInt(const std::string& s) { return h; } + +#ifdef SWIFTEN_PLATFORM_WINDOWS +std::string convertWStringToString(const std::wstring& s) { + int utf8Size = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, NULL, 0, NULL, NULL); + if (utf8Size < 0) { + throw std::runtime_error("Conversion error"); + } + std::vector<char> utf8Data(utf8Size); + int result = WideCharToMultiByte( + CP_UTF8, 0, s.c_str(), -1, vecptr(utf8Data), utf8Data.size(), NULL, NULL); + if (result < 0) { + throw std::runtime_error("Conversion error"); + } + return std::string(vecptr(utf8Data), utf8Size-1 /* trailing 0 character */); +} + +std::wstring convertStringToWString(const std::string& s) { + int utf16Size = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, NULL, 0); + if (utf16Size < 0) { + throw std::runtime_error("Conversion error"); + } + std::vector<wchar_t> utf16Data(utf16Size); + int result = MultiByteToWideChar( + CP_UTF8, 0, s.c_str(), -1, vecptr(utf16Data), utf16Data.size()); + if (result < 0) { + throw std::runtime_error("Conversion error"); + } + return std::wstring(vecptr(utf16Data), utf16Size-1 /* trailing 0 character */); +} +#endif + } diff --git a/Swiften/Base/String.h b/Swiften/Base/String.h index 5ce6c3d..5a5642e 100644 --- a/Swiften/Base/String.h +++ b/Swiften/Base/String.h @@ -11,6 +11,7 @@ #include <sstream> #include <Swiften/Base/API.h> +#include <Swiften/Base/Platform.h> #define SWIFTEN_STRING_TO_CFSTRING(a) \ CFStringCreateWithBytes(NULL, reinterpret_cast<const UInt8*>(a.c_str()), a.size(), kCFStringEncodingUTF8, false) @@ -32,8 +33,14 @@ namespace Swift { std::string convertIntToHexString(int h); int convertHexStringToInt(const std::string& s); + } +#ifdef SWIFTEN_PLATFORM_WINDOWS + SWIFTEN_API std::string convertWStringToString(const std::wstring& s); + SWIFTEN_API std::wstring convertStringToWString(const std::string& s); +#endif + class SWIFTEN_API makeString { public: template <typename T> makeString& operator<<(T const& v) { diff --git a/Swiften/Base/UnitTest/PathTest.cpp b/Swiften/Base/UnitTest/PathTest.cpp new file mode 100644 index 0000000..f5f99e7 --- /dev/null +++ b/Swiften/Base/UnitTest/PathTest.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/Base/Path.h> +#include <Swiften/Base/Platform.h> + +using namespace Swift; + +class PathTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(PathTest); + CPPUNIT_TEST(testStringToPath); + CPPUNIT_TEST(testPathToString); + CPPUNIT_TEST_SUITE_END(); + + public: + void testStringToPath() { +#ifdef SWIFTEN_PLATFORM_WINDOWS + CPPUNIT_ASSERT(std::wstring(L"tron\xe7on") == stringToPath("tron\xc3\xa7on").native()); +#else + CPPUNIT_ASSERT_EQUAL(std::string("tron\xc3\xa7on"), stringToPath("tron\xc3\xa7on").native()); +#endif + } + + void testPathToString() { + CPPUNIT_ASSERT_EQUAL(std::string("tron\xc3\xa7on"), pathToString(stringToPath("tron\xc3\xa7on"))); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(PathTest); + diff --git a/Swiften/Base/UnitTest/StringTest.cpp b/Swiften/Base/UnitTest/StringTest.cpp index b29f331..ffca98a 100644 --- a/Swiften/Base/UnitTest/StringTest.cpp +++ b/Swiften/Base/UnitTest/StringTest.cpp @@ -9,6 +9,7 @@ #include <string> #include <Swiften/Base/String.h> +#include <Swiften/Base/Platform.h> using namespace Swift; @@ -24,6 +25,10 @@ class StringTest : public CppUnit::TestFixture { CPPUNIT_TEST(testReplaceAll_ConsecutiveChars); CPPUNIT_TEST(testReplaceAll_MatchingReplace); CPPUNIT_TEST(testSplit); +#ifdef SWIFTEN_PLATFORM_WINDOWS + CPPUNIT_TEST(testConvertWStringToString); + CPPUNIT_TEST(testConvertStringToWString); +#endif CPPUNIT_TEST_SUITE_END(); public: @@ -109,6 +114,16 @@ class StringTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(std::string("def"), result[1]); CPPUNIT_ASSERT_EQUAL(std::string("ghi"), result[2]); } + +#ifdef SWIFTEN_PLATFORM_WINDOWS + void testConvertWStringToString() { + CPPUNIT_ASSERT_EQUAL(std::string("tron\xc3\xa7on"), convertWStringToString(std::wstring(L"tron\xe7on"))); + } + + void testConvertStringToWString() { + CPPUNIT_ASSERT(std::wstring(L"tron\xe7on") == convertStringToWString(std::string("tron\xc3\xa7on"))); + } +#endif }; CPPUNIT_TEST_SUITE_REGISTRATION(StringTest); |