]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/strconv.cpp
We need to update the scrollbar even if it's not shown, otherwise it can get stuck...
[wxWidgets.git] / src / common / strconv.cpp
index 88c3c6ca79a48a21f0e4261e11139539a187c5ff..ca7031528b076ec5d5a777a97aa661933a07f866 100644 (file)
@@ -70,6 +70,7 @@
 
 #ifdef HAVE_ICONV
     #include <iconv.h>
+    #include "wx/thread.h"
 #endif
 
 #include "wx/encconv.h"
@@ -217,14 +218,18 @@ const wxCharBuffer wxMBConv::cWC2MB(const wchar_t *pwz) const
     return buf;
 }
 
-size_t wxMBConv::MB2WC(wchar_t* szBuffer, const char* szString, 
-                       size_t outsize, size_t nStringLen) const
+const wxWCharBuffer wxMBConv::cMB2WC(const char *szString, size_t nStringLen, size_t* pOutSize) const
 {
+    wxASSERT(pOutSize != NULL);
+
     const char* szEnd = szString + nStringLen + 1;
     const char* szPos = szString;
     const char* szStart = szPos;
 
     size_t nActualLength = 0;
+    size_t nCurrentSize = nStringLen; //try normal size first (should never resize?)
+
+    wxWCharBuffer theBuffer(nCurrentSize);
 
     //Convert the string until the length() is reached, continuing the
     //loop every time a null character is reached
@@ -237,18 +242,31 @@ size_t wxMBConv::MB2WC(wchar_t* szBuffer, const char* szString,
 
         //Invalid conversion?
         if( nLen == (size_t)-1 )
-            return nLen;
+        {
+            *pOutSize = 0;
+            theBuffer.data()[0u] = wxT('\0');
+            return theBuffer;
+        }
+
 
         //Increase the actual length (+1 for current null character)
         nActualLength += nLen + 1;
 
-        //Only copy data in if buffer size is big enough
-        if (szBuffer != NULL &&
-            nActualLength <= outsize)
+        //if buffer too big, realloc the buffer
+        if (nActualLength > (nCurrentSize+1))
         {
-            //Convert the current (sub)string
-            if ( MB2WC(&szBuffer[szPos - szStart], szPos, nLen + 1) == (size_t)-1 )
-                return (size_t)-1;
+            wxWCharBuffer theNewBuffer(nCurrentSize << 1);
+            memcpy(theNewBuffer.data(), theBuffer.data(), nCurrentSize * sizeof(wchar_t));
+            theBuffer = theNewBuffer;
+            nCurrentSize <<= 1;
+        }
+
+        //Convert the current (sub)string
+        if ( MB2WC(&theBuffer.data()[szPos - szStart], szPos, nLen + 1) == (size_t)-1 )
+        {
+            *pOutSize = 0;
+            theBuffer.data()[0u] = wxT('\0');
+            return theBuffer;
         }
 
         //Increment to next (sub)string
@@ -258,17 +276,23 @@ size_t wxMBConv::MB2WC(wchar_t* szBuffer, const char* szString,
         szPos += strlen(szPos) + 1;
     }
 
-    return nActualLength - 1; //success - return actual length
+    //success - return actual length and the buffer
+    *pOutSize = nActualLength;
+    return theBuffer;  
 }
 
-size_t wxMBConv::WC2MB(char* szBuffer, const wchar_t* szString, 
-                       size_t outsize, size_t nStringLen) const
+const wxCharBuffer wxMBConv::cWC2MB(const wchar_t *szString, size_t nStringLen, size_t* pOutSize) const
 {
+    wxASSERT(pOutSize != NULL);
+
     const wchar_t* szEnd = szString + nStringLen + 1;
     const wchar_t* szPos = szString;
     const wchar_t* szStart = szPos;
 
     size_t nActualLength = 0;
+    size_t nCurrentSize = nStringLen << 2; //try * 4 first
+
+    wxCharBuffer theBuffer(nCurrentSize);
 
     //Convert the string until the length() is reached, continuing the
     //loop every time a null character is reached
@@ -281,18 +305,30 @@ size_t wxMBConv::WC2MB(char* szBuffer, const wchar_t* szString,
 
         //Invalid conversion?
         if( nLen == (size_t)-1 )
-            return nLen;
+        {
+            *pOutSize = 0;
+            theBuffer.data()[0u] = wxT('\0');
+            return theBuffer;
+        }
 
         //Increase the actual length (+1 for current null character)
         nActualLength += nLen + 1;
         
-        //Only copy data in if buffer size is big enough
-        if (szBuffer != NULL &&
-            nActualLength <= outsize)
+        //if buffer too big, realloc the buffer
+        if (nActualLength > (nCurrentSize+1))
         {
-            //Convert the current (sub)string
-            if(WC2MB(&szBuffer[szPos - szStart], szPos, nLen + 1) == (size_t)-1 )
-                return (size_t)-1;
+            wxCharBuffer theNewBuffer(nCurrentSize << 1);
+            memcpy(theNewBuffer.data(), theBuffer.data(), nCurrentSize);
+            theBuffer = theNewBuffer;
+            nCurrentSize <<= 1;
+        }
+
+        //Convert the current (sub)string
+        if(WC2MB(&theBuffer.data()[szPos - szStart], szPos, nLen + 1) == (size_t)-1 )
+        {
+            *pOutSize = 0;
+            theBuffer.data()[0u] = wxT('\0');
+            return theBuffer;
         }
 
         //Increment to next (sub)string
@@ -302,7 +338,9 @@ size_t wxMBConv::WC2MB(char* szBuffer, const wchar_t* szString,
         szPos += wxWcslen(szPos) + 1;
     }
 
-    return nActualLength - 1;  //success - return actual length
+    //success - return actual length and the buffer
+    *pOutSize = nActualLength;
+    return theBuffer;  
 }
 
 // ----------------------------------------------------------------------------
@@ -399,7 +437,7 @@ size_t wxMBConvUTF7::MB2WC(wchar_t *buf, const char *psz, size_t n) const
                 d += cc;
                 for (l += 6; l >= 8; lsb = !lsb)
                 {
-                    c = (d >> (l -= 8)) % 256;
+                    c = (unsigned char)((d >> (l -= 8)) % 256);
                     if (lsb)
                     {
                         if (buf)
@@ -408,7 +446,7 @@ size_t wxMBConvUTF7::MB2WC(wchar_t *buf, const char *psz, size_t n) const
                     }
                     else
                         if (buf)
-                            *buf = c << 8;
+                            *buf = (wchar_t)(c << 8);
                 }
             }
             if (*psz == '-')
@@ -473,7 +511,7 @@ size_t wxMBConvUTF7::WC2MB(char *buf, const wchar_t
             len++;
         }
 #ifndef WC_UTF16
-        else if (((wxUint16)cc) > 0xffff)
+        else if (((wxUint32)cc) > 0xffff)
            {
             // no surrogate pair generation (yet?)
             return (size_t)-1;
@@ -1111,12 +1149,13 @@ size_t wxMBConvUTF32swap::WC2MB(char *buf, const wchar_t *psz, size_t n) const
 
 #ifdef HAVE_ICONV
 
-// VS: glibc 2.1.3 is broken in that iconv() conversion to/from UCS4 fails with E2BIG
-//     if output buffer is _exactly_ as big as needed. Such case is (unless there's
-//     yet another bug in glibc) the only case when iconv() returns with (size_t)-1
-//     (which means error) and says there are 0 bytes left in the input buffer --
-//     when _real_ error occurs, bytes-left-in-input buffer is non-zero. Hence,
-//     this alternative test for iconv() failure.
+// VS: glibc 2.1.3 is broken in that iconv() conversion to/from UCS4 fails with
+//     E2BIG if output buffer is _exactly_ as big as needed. Such case is
+//     (unless there's yet another bug in glibc) the only case when iconv()
+//     returns with (size_t)-1 (which means error) and says there are 0 bytes
+//     left in the input buffer -- when _real_ error occurs,
+//     bytes-left-in-input buffer is non-zero. Hence, this alternative test for
+//     iconv() failure.
 //     [This bug does not appear in glibc 2.2.]
 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ <= 1
 #define ICONV_FAILED(cres, bufLeft) ((cres == (size_t)-1) && \
@@ -1148,6 +1187,10 @@ protected:
     // the other direction
     iconv_t m2w,
             w2m;
+#if wxUSE_THREADS
+    // guards access to m2w and w2m objects
+    wxMutex m_iconvMutex;
+#endif
 
 private:
     // the name (for iconv_open()) of a wide char charset -- if none is
@@ -1259,6 +1302,16 @@ wxMBConv_iconv::~wxMBConv_iconv()
 
 size_t wxMBConv_iconv::MB2WC(wchar_t *buf, const char *psz, size_t n) const
 {
+#if wxUSE_THREADS
+    // NB: iconv() is MT-safe, but each thread must use it's own iconv_t handle.
+    //     Unfortunately there is a couple of global wxCSConv objects such as
+    //     wxConvLocal that are used all over wx code, so we have to make sure
+    //     the handle is used by at most one thread at the time. Otherwise
+    //     only a few wx classes would be safe to use from non-main threads
+    //     as MB<->WC conversion would fail "randomly".
+    wxMutexLocker lock(wxConstCast(this, wxMBConv_iconv)->m_iconvMutex);
+#endif
     size_t inbuf = strlen(psz);
     size_t outbuf = n * SIZEOF_WCHAR_T;
     size_t res, cres;
@@ -1316,6 +1369,11 @@ size_t wxMBConv_iconv::MB2WC(wchar_t *buf, const char *psz, size_t n) const
 
 size_t wxMBConv_iconv::WC2MB(char *buf, const wchar_t *psz, size_t n) const
 {
+#if wxUSE_THREADS
+    // NB: explained in MB2WC
+    wxMutexLocker lock(wxConstCast(this, wxMBConv_iconv)->m_iconvMutex);
+#endif
+    
     size_t inbuf = wxWcslen(psz) * SIZEOF_WCHAR_T;
     size_t outbuf = n;
     size_t res, cres;
@@ -2158,7 +2216,10 @@ public:
     {
         size_t inbuf = strlen(psz);
         if (buf)
-            m2w.Convert(psz,buf);
+        {
+            if (!m2w.Convert(psz,buf))
+                return (size_t)-1;
+        }
         return inbuf;
     }
 
@@ -2166,7 +2227,10 @@ public:
     {
         const size_t inbuf = wxWcslen(psz);
         if (buf)
-            w2m.Convert(psz,buf);
+        {
+            if (!w2m.Convert(psz,buf))
+                return (size_t)-1;
+        }
 
         return inbuf;
     }