From bbb0ff36dbb57461c3086c6e729aa3acf7a559ec Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Sat, 26 Sep 2009 13:31:27 +0000
Subject: [PATCH] Fix another off-by-1 bug in wxMBConv::ToWChar().

When converting a fixed number of characters we need to take any NULs inside
the buffer being converted into account for our return value -- but this
wasn't done and converting 2 characters 'x' and '\0' returned only 1, even if
the length 2 was explicitly specified.

Fix this bug and add a unit test checking for it.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@62141 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 src/common/strconv.cpp    | 37 ++++++++++++++++++-------------------
 tests/strings/unicode.cpp | 15 ++++++++++++---
 2 files changed, 30 insertions(+), 22 deletions(-)

diff --git a/src/common/strconv.cpp b/src/common/strconv.cpp
index c0d9daf06b..7ad34c9ccf 100644
--- a/src/common/strconv.cpp
+++ b/src/common/strconv.cpp
@@ -216,7 +216,7 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen,
     // all the complication come from the fact that this function, for
     // historical reasons, must behave in 2 subtly different ways when it's
     // called with a fixed number of characters and when it's called for the
-    // entire NUL-terminated string: in the former case (srcEnd == NULL) we
+    // entire NUL-terminated string: in the former case (srcEnd != NULL) we
     // must count all characters we convert, NUL or not; but in the latter we
     // do not count the trailing NUL -- but still count all the NULs inside the
     // string
@@ -258,11 +258,13 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen,
         if ( !srcEnd )
         {
             // we convert just one chunk in this case as this is the entire
-            // string anyhow
+            // string anyhow (and we don't count the trailing NUL in this case)
             break;
         }
 
-        // advance the input pointer past the end of this chunk
+        // advance the input pointer past the end of this chunk: notice that we
+        // will always stop before srcEnd because we know that the chunk is
+        // always properly NUL-terminated
         while ( NotAllNULs(src, nulLen) )
         {
             // notice that we must skip over multiple bytes here as we suppose
@@ -272,23 +274,20 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen,
             src += nulLen;
         }
 
-        src += nulLen; // skipping over its terminator as well
+        // if the buffer ends before this NUL, we shouldn't count it in our
+        // output so skip the code below
+        if ( src == srcEnd )
+            break;
+
+        // do count this terminator as it's inside the buffer we convert
+        dstWritten++;
+        if ( dst )
+            dst++;
+
+        src += nulLen; // skip the terminator itself
 
-        // note that ">=" (and not just "==") is needed here as the terminator
-        // we skipped just above could be inside or just after the buffer
-        // delimited by srcEnd
         if ( src >= srcEnd )
             break;
-
-        // if we got here then this wasn't the last chunk in this string and
-        // hence we must count an extra char for L'\0' even when converting a
-        // fixed number of characters
-        if ( srcEnd )
-        {
-            dstWritten++;
-            if ( dst )
-                dst++;
-        }
     }
 
     return dstWritten;
@@ -333,7 +332,7 @@ wxMBConv::FromWChar(char *dst, size_t dstLen,
             return wxCONV_FAILED;
 
         dstWritten += lenChunk;
-        if ( src+lenChunk < srcEnd || isNulTerminated )
+        if ( src + lenChunk < srcEnd || isNulTerminated )
             dstWritten += lenNul;
 
         if ( dst )
@@ -345,7 +344,7 @@ wxMBConv::FromWChar(char *dst, size_t dstLen,
                 return wxCONV_FAILED;
 
             dst += lenChunk;
-            if ( src+lenChunk < srcEnd || isNulTerminated )
+            if ( src + lenChunk < srcEnd || isNulTerminated )
                 dst += lenNul;
         }
     }
diff --git a/tests/strings/unicode.cpp b/tests/strings/unicode.cpp
index 18bd0f286f..1a4e4c412b 100644
--- a/tests/strings/unicode.cpp
+++ b/tests/strings/unicode.cpp
@@ -139,7 +139,7 @@ private:
     CPPUNIT_TEST_SUITE( UnicodeTestCase );
         CPPUNIT_TEST( ToFromAscii );
         CPPUNIT_TEST( ConstructorsWithConversion );
-        CPPUNIT_TEST( ConversionEmpty );
+        CPPUNIT_TEST( ConversionFixed );
         CPPUNIT_TEST( ConversionWithNULs );
         CPPUNIT_TEST( ConversionUTF7 );
         CPPUNIT_TEST( ConversionUTF8 );
@@ -153,7 +153,7 @@ private:
 
     void ToFromAscii();
     void ConstructorsWithConversion();
-    void ConversionEmpty();
+    void ConversionFixed();
     void ConversionWithNULs();
     void ConversionUTF7();
     void ConversionUTF8();
@@ -238,7 +238,7 @@ void UnicodeTestCase::ConstructorsWithConversion()
     CPPUNIT_ASSERT( s5 != "SomethingElse" );
 }
 
-void UnicodeTestCase::ConversionEmpty()
+void UnicodeTestCase::ConversionFixed()
 {
     size_t len;
 
@@ -249,6 +249,15 @@ void UnicodeTestCase::ConversionEmpty()
 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
 
     CPPUNIT_ASSERT_EQUAL( 0, len );
+
+#if wxUSE_UNICODE
+    // check that when we convert a fixed number of characters we obtain the
+    // expected return value
+    CPPUNIT_ASSERT_EQUAL( 0, wxConvLibc.ToWChar(NULL, 0, "", 0) );
+    CPPUNIT_ASSERT_EQUAL( 1, wxConvLibc.ToWChar(NULL, 0, "x", 1) );
+    CPPUNIT_ASSERT_EQUAL( 2, wxConvLibc.ToWChar(NULL, 0, "x", 2) );
+    CPPUNIT_ASSERT_EQUAL( 2, wxConvLibc.ToWChar(NULL, 0, "xy", 2) );
+#endif // wxUSE_UNICODE
 }
 
 void UnicodeTestCase::ConversionWithNULs()
-- 
2.47.2