From bec6fa4ad7eb684253b4c80e477ac6b1690de645 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Wed, 13 Apr 2016 10:39:29 +0200
Subject: [PATCH] lib/icu/Converter: throw exception on error

---
 src/fs/Charset.cxx        |  9 +++++----
 src/lib/icu/Converter.cxx | 23 +++++++++++------------
 src/lib/icu/Converter.hxx |  4 ++--
 test/TestIcu.cxx          |  7 +++++--
 4 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx
index f91920819..a331bd064 100644
--- a/src/fs/Charset.cxx
+++ b/src/fs/Charset.cxx
@@ -114,11 +114,12 @@ PathToUTF8(PathTraitsFS::const_pointer_type path_fs)
 		return FixSeparators(path_fs);
 #ifdef HAVE_FS_CHARSET
 
-	const auto buffer = fs_converter->ToUTF8(path_fs);
-	if (buffer.IsNull())
+	try {
+		const auto buffer = fs_converter->ToUTF8(path_fs);
+		return FixSeparators(PathTraitsUTF8::string(buffer.c_str()));
+	} catch (const std::runtime_error &) {
 		return PathTraitsUTF8::string();
-
-	return FixSeparators(PathTraitsUTF8::string(buffer.c_str()));
+	}
 #endif
 #endif
 }
diff --git a/src/lib/icu/Converter.cxx b/src/lib/icu/Converter.cxx
index 815697821..1b638bbfd 100644
--- a/src/lib/icu/Converter.cxx
+++ b/src/lib/icu/Converter.cxx
@@ -90,8 +90,11 @@ DoConvert(iconv_t conv, const char *src)
 
 	size_t n = iconv(conv, &in, &in_left, &out, &out_left);
 
-	if (n == static_cast<size_t>(-1) || in_left > 0)
-		return nullptr;
+	if (n == static_cast<size_t>(-1))
+		throw MakeErrno("Charset conversion failed");
+
+	if (in_left > 0)
+		throw std::runtime_error("Charset conversion failed");
 
 	return AllocatedString<>::Duplicate(buffer, sizeof(buffer) - out_left);
 }
@@ -100,7 +103,7 @@ DoConvert(iconv_t conv, const char *src)
 
 AllocatedString<char>
 IcuConverter::ToUTF8(const char *s) const
-try {
+{
 #ifdef HAVE_ICU
 	const ScopeLock protect(mutex);
 
@@ -116,26 +119,23 @@ try {
 		       &source, source + strlen(source),
 		       nullptr, true, &code);
 	if (code != U_ZERO_ERROR)
-		return nullptr;
+		throw std::runtime_error(FormatString("Failed to convert to Unicode: %s",
+						      u_errorName(code)).c_str());
 
 	const size_t target_length = target - buffer;
 	return UCharToUTF8({buffer, target_length});
 #elif defined(HAVE_ICONV)
 	return DoConvert(to_utf8, s);
 #endif
-} catch (const std::runtime_error &) {
-	return nullptr;
 }
 
 AllocatedString<char>
 IcuConverter::FromUTF8(const char *s) const
-try {
+{
 #ifdef HAVE_ICU
 	const ScopeLock protect(mutex);
 
 	const auto u = UCharFromUTF8(s);
-	if (u.IsNull())
-		return nullptr;
 
 	ucnv_resetFromUnicode(converter);
 
@@ -149,15 +149,14 @@ try {
 			 nullptr, true, &code);
 
 	if (code != U_ZERO_ERROR)
-		return nullptr;
+		throw std::runtime_error(FormatString("Failed to convert from Unicode: %s",
+						      u_errorName(code)).c_str());
 
 	return AllocatedString<>::Duplicate(buffer, target);
 
 #elif defined(HAVE_ICONV)
 	return DoConvert(from_utf8, s);
 #endif
-} catch (const std::runtime_error &) {
-	return nullptr;
 }
 
 #endif
diff --git a/src/lib/icu/Converter.hxx b/src/lib/icu/Converter.hxx
index 59b1e5b5a..6e20c88f9 100644
--- a/src/lib/icu/Converter.hxx
+++ b/src/lib/icu/Converter.hxx
@@ -79,7 +79,7 @@ public:
 	/**
 	 * Convert the string to UTF-8.
 	 *
-	 * Returns AllocatedString::Null() on error.
+	 * Throws std::runtime_error on error.
 	 */
 	gcc_pure gcc_nonnull_all
 	AllocatedString<char> ToUTF8(const char *s) const;
@@ -87,7 +87,7 @@ public:
 	/**
 	 * Convert the string from UTF-8.
 	 *
-	 * Returns AllocatedString::Null() on error.
+	 * Throws std::runtime_error on error.
 	 */
 	gcc_pure gcc_nonnull_all
 	AllocatedString<char> FromUTF8(const char *s) const;
diff --git a/test/TestIcu.cxx b/test/TestIcu.cxx
index c4a015c4e..843b4c64e 100644
--- a/test/TestIcu.cxx
+++ b/test/TestIcu.cxx
@@ -54,8 +54,11 @@ public:
 		CPPUNIT_ASSERT(converter != nullptr);
 
 		for (const auto i : invalid_utf8) {
-			auto f = converter->FromUTF8(i);
-			CPPUNIT_ASSERT_EQUAL(true, f.IsNull());
+			try {
+				auto f = converter->FromUTF8(i);
+				CPPUNIT_FAIL("Exception expected");
+			} catch (const std::runtime_error &) {
+			}
 		}
 
 		for (const auto i : latin1_tests) {