]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/strconv.cpp
made DoTestConversion() work with strings containing NULs
[wxWidgets.git] / src / common / strconv.cpp
index 3508ac1c8b0fc9d95751d977a09274c2aaa0c394..9d8481349fe497d54ab99c1cd36b12d22c2ae746 100644 (file)
@@ -408,7 +408,7 @@ size_t wxMBConvUTF7::MB2WC(wchar_t *buf, const char *psz, size_t n) const
 {
     size_t len = 0;
 
-    while (*psz && ((!buf) || (len < n)))
+    while ( *psz && (!buf || (len < n)) )
     {
         unsigned char cc = *psz++;
         if (cc != '+')
@@ -426,20 +426,19 @@ size_t wxMBConvUTF7::MB2WC(wchar_t *buf, const char *psz, size_t n) const
             len++;
             psz++;
         }
-        else
+        else // start of BASE64 encoded string
         {
-            // BASE64 encoded string
-            bool lsb;
-            unsigned char c;
+            bool lsb, ok;
             unsigned int d, l;
-            for (lsb = false, d = 0, l = 0;
-                (cc = utf7unb64[(unsigned char)*psz]) != 0xff; psz++)
+            for ( ok = lsb = false, d = 0, l = 0;
+                  (cc = utf7unb64[(unsigned char)*psz]) != 0xff;
+                  psz++ )
             {
                 d <<= 6;
                 d += cc;
                 for (l += 6; l >= 8; lsb = !lsb)
                 {
-                    c = (unsigned char)((d >> (l -= 8)) % 256);
+                    unsigned char c = (unsigned char)((d >> (l -= 8)) % 256);
                     if (lsb)
                     {
                         if (buf)
@@ -447,16 +446,29 @@ size_t wxMBConvUTF7::MB2WC(wchar_t *buf, const char *psz, size_t n) const
                         len ++;
                     }
                     else
+                    {
                         if (buf)
                             *buf = (wchar_t)(c << 8);
+                    }
+
+                    ok = true;
                 }
             }
+
+            if ( !ok )
+            {
+                // in valid UTF7 we should have valid characters after '+'
+                return (size_t)-1;
+            }
+
             if (*psz == '-')
                 psz++;
         }
     }
-    if (buf && (len < n))
-        *buf = 0;
+
+    if ( buf && (len < n) )
+        *buf = '\0';
+
     return len;
 }
 
@@ -850,20 +862,24 @@ size_t wxMBConvUTF16straight::WC2MB(char *buf, const wchar_t *psz, size_t n) con
 // swap 16bit MB to 16bit String
 size_t wxMBConvUTF16swap::MB2WC(wchar_t *buf, const char *psz, size_t n) const
 {
-    size_t len=0;
+    size_t len = 0;
 
-    while (*(wxUint16*)psz && (!buf || len < n))
+    // UTF16 string must be terminated by 2 NULs as single NULs may occur
+    // inside the string
+    while ( (psz[0] || psz[1]) && (!buf || len < n) )
     {
-        if (buf)
+        if ( buf )
         {
             ((char *)buf)[0] = psz[1];
             ((char *)buf)[1] = psz[0];
             buf++;
         }
         len++;
-        psz += sizeof(wxUint16);
+        psz += 2;
     }
-    if (buf && len<n)   *buf=0;
+
+    if ( buf && len < n )
+        *buf = L'\0';
 
     return len;
 }
@@ -1652,7 +1668,28 @@ public:
         // own wxMBConvUTF7 doesn't detect errors (e.g. lone "+" which is
         // explicitly ill-formed according to RFC 2152) neither so we don't
         // even have any fallback here...
-        int flags = m_CodePage == CP_UTF7 ? 0 : MB_ERR_INVALID_CHARS;
+        //
+        // Moreover, MB_ERR_INVALID_CHARS is only supported on Win 2K SP4 or
+        // Win XP or newer and if it is specified on older versions, conversion
+        // from CP_UTF8 (which can have flags only 0 or MB_ERR_INVALID_CHARS)
+        // fails. So we can only use the flag on newer Windows versions.
+        // Additionally, the flag is not supported by UTF7, symbol and CJK
+        // encodings. See here:
+        //     http://blogs.msdn.com/michkap/archive/2005/04/19/409566.aspx
+        //     http://msdn.microsoft.com/library/en-us/intl/unicode_17si.asp
+        int flags = 0;
+        if ( m_CodePage != CP_UTF7 && m_CodePage != CP_SYMBOL &&
+             m_CodePage < 50000 &&
+             IsAtLeastWin2kSP4() )
+        {
+            flags = MB_ERR_INVALID_CHARS;
+        }
+        else if ( m_CodePage == CP_UTF8 )
+        {
+            // Avoid round-trip in the special case of UTF-8 by using our
+            // own UTF-8 conversion code:
+            return wxMBConvUTF8().MB2WC(buf, psz, n);
+        }
 
         const size_t len = ::MultiByteToWideChar
                              (
@@ -1663,11 +1700,41 @@ public:
                                 buf,            // output string
                                 buf ? n : 0     // size of output buffer
                              );
+        if ( !len )
+        {
+            // function totally failed
+            return (size_t)-1;
+        }
+
+        // if we were really converting and didn't use MB_ERR_INVALID_CHARS,
+        // check if we succeeded, by doing a double trip:
+        if ( !flags && buf )
+        {
+            const size_t mbLen = strlen(psz);
+            wxCharBuffer mbBuf(mbLen);
+            if ( ::WideCharToMultiByte
+                   (
+                      m_CodePage,
+                      0,
+                      buf,
+                      -1,
+                      mbBuf.data(),
+                      mbLen + 1,        // size in bytes, not length
+                      NULL,
+                      NULL
+                   ) == 0 ||
+                  strcmp(mbBuf, psz) != 0 )
+            {
+                // we didn't obtain the same thing we started from, hence
+                // the conversion was lossy and we consider that it failed
+                return (size_t)-1;
+            }
+        }
 
         // note that it returns count of written chars for buf != NULL and size
         // of the needed buffer for buf == NULL so in either case the length of
         // the string (which never includes the terminating NUL) is one less
-        return len ? len - 1 : (size_t)-1;
+        return len 1;
     }
 
     size_t WC2MB(char *buf, const wchar_t *pwz, size_t n) const
@@ -1782,6 +1849,33 @@ private:
         return s_isWin98Or2k == 1;
     }
 
+    static bool IsAtLeastWin2kSP4()
+    {
+#ifdef __WXWINCE__
+        return false;
+#else
+        static int s_isAtLeastWin2kSP4 = -1;
+
+        if ( s_isAtLeastWin2kSP4 == -1 )
+        {
+            OSVERSIONINFOEX ver;
+
+            memset(&ver, 0, sizeof(ver));
+            ver.dwOSVersionInfoSize = sizeof(ver);
+            GetVersionEx((OSVERSIONINFO*)&ver);
+
+            s_isAtLeastWin2kSP4 =
+              ((ver.dwMajorVersion > 5) || // Vista+
+               (ver.dwMajorVersion == 5 && ver.dwMinorVersion > 0) || // XP/2003
+               (ver.dwMajorVersion == 5 && ver.dwMinorVersion == 0 &&
+               ver.wServicePackMajor >= 4)) // 2000 SP4+
+              ? 1 : 0;
+        }
+
+        return s_isAtLeastWin2kSP4 == 1;
+#endif
+    }
+
     long m_CodePage;
 };
 
@@ -2462,7 +2556,11 @@ wxCSConv::wxCSConv(const wxChar *charset)
         SetName(charset);
     }
 
+#if wxUSE_FONTMAP
+    m_encoding = wxFontMapperBase::GetEncodingFromName(charset);
+#else
     m_encoding = wxFONTENCODING_SYSTEM;
+#endif
 }
 
 wxCSConv::wxCSConv(wxFontEncoding encoding)
@@ -2542,7 +2640,8 @@ wxMBConv *wxCSConv::DoCreate() const
     // check for the special case of ASCII or ISO8859-1 charset: as we have
     // special knowledge of it anyhow, we don't need to create a special
     // conversion object
-    if ( m_encoding == wxFONTENCODING_ISO8859_1 )
+    if ( m_encoding == wxFONTENCODING_ISO8859_1 ||
+            m_encoding == wxFONTENCODING_DEFAULT )
     {
         // don't convert at all
         return NULL;