]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/strconv.cpp
reverted rev 1.26, workarounds for memchecking code no longer needed
[wxWidgets.git] / src / common / strconv.cpp
index 1987800ac2ee2087691cfeef991833118a0b06ab..c10d2e211b0c70ee887b4536d656362c18d18d4f 100644 (file)
@@ -40,6 +40,7 @@
 
 #ifdef __WXMSW__
     #include "wx/msw/private.h"
+    #include "wx/msw/missing.h"
 #endif
 
 #ifndef __WXWINCE__
 #include "wx/encconv.h"
 #include "wx/fontmap.h"
 
+#ifdef __WXMAC__
+#include "ATSUnicode.h"
+#include "TextCommon.h"
+#include "TextEncodingConverter.h"
+
+#include  "wx/mac/private.h"  // includes mac headers
+#endif
 // ----------------------------------------------------------------------------
 // macros
 // ----------------------------------------------------------------------------
@@ -111,7 +119,8 @@ static size_t encode_utf16(wxUint32 input, wxUint16 *output)
 {
     if (input<=0xffff)
     {
-        if (output) *output++ = (wxUint16) input;
+        if (output)
+            *output = (wxUint16) input;
         return 1;
     }
     else if (input>=0x110000)
@@ -123,7 +132,7 @@ static size_t encode_utf16(wxUint32 input, wxUint16 *output)
         if (output)
         {
             *output++ = (wxUint16) ((input >> 10)+0xd7c0);
-            *output++ = (wxUint16) ((input&0x3ff)+0xdc00);
+            *output = (wxUint16) ((input&0x3ff)+0xdc00);
         }
         return 2;
     }
@@ -293,7 +302,8 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const
                     return (size_t)-1;
                 }
 #ifdef WC_UTF16
-                size_t pa = encode_utf16(res, buf);
+                // cast is ok because wchar_t == wxUuint16 if WC_UTF16
+                size_t pa = encode_utf16(res, (wxUint16 *)buf);
                 if (pa == (size_t)-1)
                   return (size_t)-1;
                 if (buf)
@@ -320,7 +330,8 @@ size_t wxMBConvUTF8::WC2MB(char *buf, const wchar_t *psz, size_t n) const
     {
         wxUint32 cc;
 #ifdef WC_UTF16
-        size_t pa = decode_utf16(psz, cc);
+        // cast is ok for WC_UTF16
+        size_t pa = decode_utf16((const wxUint16 *)psz, cc);
         psz += (pa == (size_t)-1) ? 1 : pa;
 #else
         cc=(*psz++) & 0x7fffffff;
@@ -495,7 +506,7 @@ size_t wxMBConvUTF16straight::WC2MB(char *buf, const wchar_t *psz, size_t n) con
         if (buf)
         {
             *(wxUint16*)buf = cc[0];
-            buf += sizeof(wxUint16); 
+            buf += sizeof(wxUint16);
             if (pa > 1)
             {
                 *(wxUint16*)buf = cc[1];
@@ -631,7 +642,8 @@ size_t wxMBConvUTF32straight::WC2MB(char *buf, const wchar_t *psz, size_t n) con
     {
         wxUint32 cc;
 
-        size_t pa=decode_utf16(psz, cc);
+        // cast is ok for WC_UTF16
+        size_t pa = decode_utf16((const wxUint16 *)psz, cc);
         if (pa == (size_t)-1)
             return pa;
 
@@ -643,7 +655,9 @@ size_t wxMBConvUTF32straight::WC2MB(char *buf, const wchar_t *psz, size_t n) con
         len += sizeof(wxUint32);
         psz += pa;
     }
-    if (buf && len<=n-sizeof(wxUint32))   *(wxUint32*)buf=0;
+
+    if (buf && len<=n-sizeof(wxUint32))
+        *(wxUint32*)buf=0;
 
     return len;
 }
@@ -677,7 +691,9 @@ size_t wxMBConvUTF32swap::MB2WC(wchar_t *buf, const char *psz, size_t n) const
         len += pa;
         psz += sizeof(wxUint32);
     }
-    if (buf && len<n)   *buf=0;
+
+    if (buf && len<n)
+        *buf=0;
 
     return len;
 }
@@ -692,7 +708,8 @@ size_t wxMBConvUTF32swap::WC2MB(char *buf, const wchar_t *psz, size_t n) const
     {
         char cc[4];
 
-        size_t pa=decode_utf16(psz, *(wxUint32*)cc);
+        // cast is ok for WC_UTF16
+        size_t pa=decode_utf16((const wxUint16 *)psz, *(wxUint32*)cc);
         if (pa == (size_t)-1)
             return pa;
 
@@ -706,7 +723,9 @@ size_t wxMBConvUTF32swap::WC2MB(char *buf, const wchar_t *psz, size_t n) const
         len += sizeof(wxUint32);
         psz += pa;
     }
-    if (buf && len<=n-sizeof(wxUint32))   *(wxUint32*)buf=0;
+
+    if (buf && len<=n-sizeof(wxUint32))
+        *(wxUint32*)buf=0;
 
     return len;
 }
@@ -726,7 +745,9 @@ size_t wxMBConvUTF32straight::MB2WC(wchar_t *buf, const char *psz, size_t n) con
         len++;
         psz += sizeof(wxUint32);
     }
-    if (buf && len<n)   *buf=0;
+
+    if (buf && len<n)
+        *buf=0;
 
     return len;
 }
@@ -749,7 +770,8 @@ size_t wxMBConvUTF32straight::WC2MB(char *buf, const wchar_t *psz, size_t n) con
         psz++;
     }
 
-    if (buf && len<=n-sizeof(wxUint32))   *(wxUint32*)buf=0;
+    if (buf && len<=n-sizeof(wxUint32))
+        *(wxUint32*)buf=0;
 
     return len;
 }
@@ -773,7 +795,9 @@ size_t wxMBConvUTF32swap::MB2WC(wchar_t *buf, const char *psz, size_t n) const
         len++;
         psz += sizeof(wxUint32);
     }
-    if (buf && len<n)   *buf=0;
+
+    if (buf && len<n)
+        *buf=0;
 
     return len;
 }
@@ -796,7 +820,9 @@ size_t wxMBConvUTF32swap::WC2MB(char *buf, const wchar_t *psz, size_t n) const
         len += sizeof(wxUint32);
         psz++;
     }
-    if (buf && len<=n-sizeof(wxUint32))   *(wxUint32*)buf=0;
+
+    if (buf && len<=n-sizeof(wxUint32))
+        *(wxUint32*)buf=0;
 
     return len;
 }
@@ -1120,38 +1146,246 @@ public:
                                 buf ? n : 0     // size of output buffer
                              );
 
-        // note that it returns # of written chars for buf != NULL and *size*
-        // of the needed buffer for buf == NULL
-        return len ? (buf ? len : len - 1) : (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;
     }
 
-    size_t WC2MB(char *buf, const wchar_t *psz, size_t n) const
+    size_t WC2MB(char *buf, const wchar_t *pwz, size_t n) const
     {
+        /*
+            we have a problem here: by default, WideCharToMultiByte() may
+            replace characters unrepresentable in the target code page with bad
+            quality approximations such as turning "1/2" symbol (U+00BD) into
+            "1" for the code pages which don't have it and we, obviously, want
+            to avoid this at any price
+        
+            the trouble is that this function does it _silently_, i.e. it won't
+            even tell us whether it did or not... Win98/2000 and higher provide
+            WC_NO_BEST_FIT_CHARS but it doesn't work for the older systems and
+            we have to resort to a round trip, i.e. check that converting back
+            results in the same string -- this is, of course, expensive but
+            otherwise we simply can't be sure to not garble the data.
+         */
+
+        // determine if we can rely on WC_NO_BEST_FIT_CHARS: according to MSDN
+        // it doesn't work with CJK encodings (which we test for rather roughly
+        // here...) nor with UTF-7/8 nor, of course, with Windows versions not
+        // supporting it
+        BOOL usedDef wxDUMMY_INITIALIZE(false),
+             *pUsedDef;
+        int flags;
+        if ( CanUseNoBestFit() && m_CodePage < 50000 )
+        {
+            // it's our lucky day
+            flags = WC_NO_BEST_FIT_CHARS;
+            pUsedDef = &usedDef;
+        }
+        else // old system or unsupported encoding
+        {
+            flags = 0;
+            pUsedDef = NULL;
+        }
+
         const size_t len = ::WideCharToMultiByte
                              (
                                 m_CodePage,     // code page
-                                0,              // flags (none)
-                                psz,            // input string
+                                flags,          // either none or no best fit
+                                pwz,            // input string
                                 -1,             // it is (wide) NUL-terminated
                                 buf,            // output buffer
                                 buf ? n : 0,    // and its size
                                 NULL,           // default "replacement" char
-                                NULL            // [out] was it used?
+                                pUsedDef        // [out] was it used?
                              );
 
-        // see the comment above!
-        return len ? (buf ? len : len - 1) : (size_t)-1;
+        if ( !len )
+        {
+            // function totally failed
+            return (size_t)-1;
+        }
+
+        // if we were really converting, check if we succeeded
+        if ( buf )
+        {
+            if ( flags )
+            {
+                // check if the conversion failed, i.e. if any replacements
+                // were done
+                if ( usedDef )
+                    return (size_t)-1;
+            }
+            else // we must resort to double tripping...
+            {
+                wxWCharBuffer wcBuf(n);
+                if ( MB2WC(wcBuf.data(), buf, n) == (size_t)-1 ||
+                        wcscmp(wcBuf, pwz) != 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;
+                }
+            }
+        }
+
+        // see the comment above for the reason of "len - 1"
+        return len - 1;
     }
 
-    bool IsOk() const
-        { return m_CodePage != -1; }
+    bool IsOk() const { return m_CodePage != -1; }
+
+private:
+    static bool CanUseNoBestFit()
+    {
+        static int s_isWin98Or2k = -1;
+
+        if ( s_isWin98Or2k == -1 )
+        {
+            int verMaj, verMin;
+            switch ( wxGetOsVersion(&verMaj, &verMin) )
+            {
+                case wxWIN95:
+                    s_isWin98Or2k = verMaj >= 4 && verMin >= 10;
+                    break;
+
+                case wxWINDOWS_NT:
+                    s_isWin98Or2k = verMaj >= 5;
+                    break;
+
+                default:
+                    // unknown, be conseravtive by default
+                    s_isWin98Or2k = 0;
+            }
+
+            wxASSERT_MSG( s_isWin98Or2k != -1, _T("should be set above") );
+        }
+
+        return s_isWin98Or2k == 1;
+    }
 
-public:
     long m_CodePage;
 };
 
 #endif // wxHAVE_WIN32_MB2WC
 
+// ============================================================================
+// Mac conversion classes
+// ============================================================================
+
+#if defined(__WXMAC__) && defined(TARGET_CARBON)
+
+class wxMBConv_mac : public wxMBConv
+{
+public:
+    wxMBConv_mac()
+    {
+        Init(CFStringGetSystemEncoding()) ;
+    }
+
+    wxMBConv_mac(const wxChar* name)
+    {
+       Init( wxMacGetSystemEncFromFontEnc(wxFontMapper::Get()->CharsetToEncoding(name, FALSE) ) ) ;
+    }
+
+    wxMBConv_mac(wxFontEncoding encoding)
+    {
+       Init( wxMacGetSystemEncFromFontEnc(encoding) );
+    }
+    
+       ~wxMBConv_mac()
+       {
+           OSStatus status = noErr ;
+           status = TECDisposeConverter(m_MB2WC_converter);
+           status = TECDisposeConverter(m_WC2MB_converter);            
+       }
+       
+    
+       void Init( TextEncodingBase encoding)
+       {
+           OSStatus status = noErr ;
+               m_char_encoding = encoding ;
+#if SIZEOF_WCHAR_T == 4
+               m_unicode_encoding = CreateTextEncoding(kTextEncodingUnicodeDefault,0,kUnicode32BitFormat) ;
+#else
+               m_unicode_encoding = CreateTextEncoding(kTextEncodingUnicodeDefault,0,kUnicode16BitFormat) ;
+#endif         
+           status = TECCreateConverter(&m_MB2WC_converter,
+                                       m_char_encoding,
+                                       m_unicode_encoding);
+           status = TECCreateConverter(&m_WC2MB_converter,
+                                       m_unicode_encoding,
+                                       m_char_encoding);
+       }
+       
+    size_t MB2WC(wchar_t *buf, const char *psz, size_t n) const
+    {
+           OSStatus status = noErr ;
+           ByteCount byteOutLen ;
+           ByteCount byteInLen = strlen(psz) ;
+               wchar_t *tbuf = NULL ;
+               
+               if (buf == NULL)
+               {
+                       n = byteInLen ;
+                       tbuf = (wchar_t*) malloc( n * SIZEOF_WCHAR_T) ;
+               }
+       
+           ByteCount byteBufferLen = n * SIZEOF_WCHAR_T ; 
+           status = TECConvertText(m_MB2WC_converter, (ConstTextPtr) psz , byteInLen, &byteInLen,
+             (TextPtr) (buf ? buf : tbuf) , byteBufferLen, &byteOutLen);
+
+               if ( buf == NULL )
+                       free(tbuf) ;
+
+               size_t res = byteOutLen / SIZEOF_WCHAR_T ;
+        if ( buf  && res < n)
+            buf[res] = 0;
+
+               return res ;
+    }
+
+    size_t WC2MB(char *buf, const wchar_t *psz, size_t n) const
+    {          
+           OSStatus status = noErr ;
+           ByteCount byteOutLen ;
+           ByteCount byteInLen = wxWcslen(psz) * SIZEOF_WCHAR_T ;
+
+               char *tbuf = NULL ;
+               
+               if (buf == NULL)
+               {
+                       // worst case
+                       n = byteInLen * 2 ;
+                       tbuf = (char*) malloc( n ) ;
+               }
+
+           ByteCount byteBufferLen = n ;
+           status = TECConvertText(m_WC2MB_converter, (ConstTextPtr) psz , byteInLen, &byteInLen,
+              (TextPtr) ( buf ? buf : tbuf ) , byteBufferLen, &byteOutLen);
+
+               if ( buf == NULL )
+                       free(tbuf) ;
+
+               size_t res = byteOutLen ;
+        if ( buf  && res < n)
+            buf[res] = 0;
+
+               return res ;
+    }
+
+    bool IsOk() const
+        { return m_MB2WC_converter !=  NULL && m_WC2MB_converter != NULL  ; }
+
+private:
+       TECObjectRef m_MB2WC_converter ;
+       TECObjectRef m_WC2MB_converter ;
+       
+       TextEncodingBase m_char_encoding ;
+       TextEncodingBase m_unicode_encoding ;
+};
+
+#endif // defined(__WXMAC__) && defined(TARGET_CARBON)
 
 // ============================================================================
 // wxEncodingConverter based conversion classes
@@ -1230,37 +1464,21 @@ void wxCSConv::Init()
     m_deferred = true;
 }
 
-// find a valid value for the encoding
-void wxCSConv::SetEncoding()
-{
-#if wxUSE_INTL
-    m_encoding = wxLocale::GetSystemEncoding();
-#else
-    m_encoding = wxFONTENCODING_SYSTEM;
-#endif
-}
-
 wxCSConv::wxCSConv(const wxChar *charset)
 {
     Init();
 
     if ( charset )
     {
-        // not used
-        m_encoding = wxFONTENCODING_SYSTEM;
-
         SetName(charset);
     }
-    else // no charset specified
-    {
-        SetEncoding();
-    }
+
+    m_encoding = wxFONTENCODING_SYSTEM;
 }
 
 wxCSConv::wxCSConv(wxFontEncoding encoding)
 {
-    if ( encoding == wxFONTENCODING_MAX ||
-            encoding == wxFONTENCODING_DEFAULT )
+    if ( encoding == wxFONTENCODING_MAX || encoding == wxFONTENCODING_DEFAULT )
     {
         wxFAIL_MSG( _T("invalid encoding value in wxCSConv ctor") );
 
@@ -1269,14 +1487,7 @@ wxCSConv::wxCSConv(wxFontEncoding encoding)
 
     Init();
 
-    if ( encoding == wxFONTENCODING_SYSTEM )
-    {
-        SetEncoding();
-    }
-    else // have valid encoding, use it
-    {
-        m_encoding = encoding;
-    }
+    m_encoding = encoding;
 }
 
 wxCSConv::~wxCSConv()
@@ -1321,28 +1532,12 @@ void wxCSConv::SetName(const wxChar *charset)
     }
 }
 
-static inline bool DoesntNeedConv(wxFontEncoding enc)
-{
-    return enc == wxFONTENCODING_DEFAULT ||
-            enc == wxFONTENCODING_SYSTEM ||
-             enc == wxFONTENCODING_ISO8859_1;
-}
-
 wxMBConv *wxCSConv::DoCreate() const
 {
-#if wxUSE_FONTMAP
-    wxFontMapper * const fontMapper = wxFontMapper::Get();
-
-    wxFontEncoding encFromName = m_name ? fontMapper->CharsetToEncoding(m_name)
-                                        : wxFONTENCODING_SYSTEM;
-#endif // wxUSE_FONTMAP
-
-    // check for the special case of ASCII charset
-    if ( (!m_name && DoesntNeedConv(m_encoding))
-#if wxUSE_FONTMAP
-            || (m_name && DoesntNeedConv(encFromName))
-#endif // wxUSE_FONTMAP
-       )
+    // 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 )
     {
         // don't convert at all
         return NULL;
@@ -1358,9 +1553,18 @@ wxMBConv *wxCSConv::DoCreate() const
 
     // step (1)
 #ifdef HAVE_ICONV
+#if !wxUSE_FONTMAP
     if ( m_name )
+#endif // !wxUSE_FONTMAP
     {
-        wxMBConv_iconv *conv = new wxMBConv_iconv(m_name);
+        wxString name(m_name);
+
+#if wxUSE_FONTMAP
+        if ( name.empty() )
+            name = wxFontMapper::Get()->GetEncodingName(m_encoding);
+#endif // wxUSE_FONTMAP
+
+        wxMBConv_iconv *conv = new wxMBConv_iconv(name);
         if ( conv->IsOk() )
             return conv;
 
@@ -1378,12 +1582,30 @@ wxMBConv *wxCSConv::DoCreate() const
         delete conv;
     }
 #endif // wxHAVE_WIN32_MB2WC
-
+#if defined(__WXMAC__) 
+    {
+       if ( m_name || ( m_encoding < wxFONTENCODING_UTF16BE ) )
+       {
+                       
+               wxMBConv_mac *conv = m_name ? new wxMBConv_mac(m_name)
+                                           : new wxMBConv_mac(m_encoding);
+               if ( conv->IsOk() )
+                   return conv;
+
+               delete conv;
+       }
+    }
+#endif
     // step (2)
     wxFontEncoding enc = m_encoding;
 #if wxUSE_FONTMAP
-    if ( enc == wxFONTENCODING_SYSTEM )
-        enc = encFromName;
+    if ( enc == wxFONTENCODING_SYSTEM && m_name )
+    {
+        // use "false" to suppress interactive dialogs -- we can be called from
+        // anywhere and popping up a dialog from here is the last thing we want to
+        // do
+        enc = wxFontMapper::Get()->CharsetToEncoding(m_name, false);
+    }
 #endif // wxUSE_FONTMAP
 
     switch ( enc )
@@ -1394,18 +1616,12 @@ wxMBConv *wxCSConv::DoCreate() const
         case wxFONTENCODING_UTF8:
              return new wxMBConvUTF8;
 
-        case wxFONTENCODING_UTF16:
-             return new wxMBConvUTF16;
-
         case wxFONTENCODING_UTF16BE:
              return new wxMBConvUTF16BE;
 
         case wxFONTENCODING_UTF16LE:
              return new wxMBConvUTF16LE;
 
-        case wxFONTENCODING_UTF32:
-             return new wxMBConvUTF32;
-
         case wxFONTENCODING_UTF32BE:
              return new wxMBConvUTF32BE;
 
@@ -1429,8 +1645,19 @@ wxMBConv *wxCSConv::DoCreate() const
     }
 #endif // wxUSE_FONTMAP
 
-    wxLogError(_("Cannot convert from the charset '%s'!"),
-               m_name ? m_name
+    // NB: This is a hack to prevent deadlock. What could otherwise happen
+    //     in Unicode build: wxConvLocal creation ends up being here
+    //     because of some failure and logs the error. But wxLog will try to
+    //     attach timestamp, for which it will need wxConvLocal (to convert
+    //     time to char* and then wchar_t*), but that fails, tries to log
+    //     error, but wxLog has a (already locked) critical section that
+    //     guards static buffer.
+    static bool alreadyLoggingError = false;
+    if (!alreadyLoggingError)
+    {
+        alreadyLoggingError = true;
+        wxLogError(_("Cannot convert from the charset '%s'!"),
+                   m_name ? m_name
                       :
 #if wxUSE_FONTMAP
                          wxFontMapper::GetEncodingDescription(m_encoding).c_str()
@@ -1438,6 +1665,8 @@ wxMBConv *wxCSConv::DoCreate() const
                          wxString::Format(_("encoding %s"), m_encoding).c_str()
 #endif // wxUSE_FONTMAP/!wxUSE_FONTMAP
               );
+        alreadyLoggingError = false;
+    }
 
     return NULL;
 }
@@ -1447,6 +1676,16 @@ void wxCSConv::CreateConvIfNeeded() const
     if ( m_deferred )
     {
         wxCSConv *self = (wxCSConv *)this; // const_cast
+
+#if wxUSE_INTL
+        // if we don't have neither the name nor the encoding, use the default
+        // encoding for this system
+        if ( !m_name && m_encoding == wxFONTENCODING_SYSTEM )
+        {
+            self->m_name = wxStrdup(wxLocale::GetSystemEncodingName());
+        }
+#endif // wxUSE_INTL
+
         self->m_convReal = DoCreate();
         self->m_deferred = false;
     }
@@ -1483,7 +1722,19 @@ size_t wxCSConv::WC2MB(char *buf, const wchar_t *psz, size_t n) const
     if (buf)
     {
         for (size_t c = 0; c <= len; c++)
-            buf[c] = (psz[c] > 0xff) ? '?' : psz[c];
+        {
+            if (psz[c] > 0xFF)
+                return (size_t)-1;
+            buf[c] = psz[c];
+        }
+    }
+    else
+    {
+        for (size_t c = 0; c <= len; c++)
+        {
+            if (psz[c] > 0xFF)
+                return (size_t)-1;
+        }
     }
 
     return len;
@@ -1496,7 +1747,7 @@ size_t wxCSConv::WC2MB(char *buf, const wchar_t *psz, size_t n) const
 #ifdef __WINDOWS__
     static wxMBConv_win32 wxConvLibcObj;
 #else
-    static wxMBConvSystem wxConvLibcObj;
+    static wxMBConvLibc wxConvLibcObj;
 #endif
 
 static wxCSConv wxConvLocalObj(wxFONTENCODING_SYSTEM);