/////////////////////////////////////////////////////////////////////////////
-// Name: strconv.cpp
+// Name: src/common/strconv.cpp
// Purpose: Unicode conversion classes
// Author: Ove Kaaven, Robert Roebling, Vadim Zeitlin, Vaclav Slavik,
// Ryan Norton, Fredrik Roubert (UTF7)
// headers
// ----------------------------------------------------------------------------
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
- #pragma implementation "strconv.h"
-#endif
-
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#define TRACE_STRCONV _T("strconv")
+#if SIZEOF_WCHAR_T == 2
+ #define WC_UTF16
+#endif
+
// ============================================================================
// implementation
// ============================================================================
{
size_t len = 0;
- while (*psz && ((!buf) || (len < n)))
+ while ( *psz && (!buf || (len < n)) )
{
unsigned char cc = *psz++;
if (cc != '+')
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)
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;
}
{
// BASE64 encode string
unsigned int lsb, d, l;
- for (d = 0, l = 0;; psz++)
+ for (d = 0, l = 0; /*nothing*/; psz++)
{
for (lsb = 0; lsb < 2; lsb ++)
{
}
#else // !WC_UTF16
if (buf)
- *buf++ = res;
+ *buf++ = (wchar_t)res;
len++;
#endif // WC_UTF16/!WC_UTF16
}
len += pa;
#else
if (buf)
- *buf++ = wxUnicodePUA + (unsigned char)*opsz;
+ *buf++ = (wchar_t)(wxUnicodePUA + (unsigned char)*opsz);
opsz++;
len++;
#endif
{
if ( buf && len + 3 < n )
{
- unsigned char n = *opsz;
+ unsigned char on = *opsz;
*buf++ = L'\\';
- *buf++ = (wchar_t)( L'0' + n / 0100 );
- *buf++ = (wchar_t)( L'0' + (n % 0100) / 010 );
- *buf++ = (wchar_t)( L'0' + n % 010 );
+ *buf++ = (wchar_t)( L'0' + on / 0100 );
+ *buf++ = (wchar_t)( L'0' + (on % 0100) / 010 );
+ *buf++ = (wchar_t)( L'0' + on % 010 );
}
opsz++;
len += 4;
// 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;
}
return pa;
if (buf)
- *buf++ = cc;
+ *buf++ = (wchar_t)cc;
len++;
psz += pa * sizeof(wxUint16);
}
return pa;
if (buf)
- *buf++ = cc;
+ *buf++ = (wchar_t)cc;
len++;
psz += pa * sizeof(wxUint16);
while (*(wxUint32*)psz && (!buf || len < n))
{
if (buf)
- *buf++ = *(wxUint32*)psz;
+ *buf++ = (wchar_t)(*(wxUint32*)psz);
len++;
psz += sizeof(wxUint32);
}
// check for charset that represents wchar_t:
if ( ms_wcCharsetName.empty() )
{
+ wxLogTrace(TRACE_STRCONV, _T("Looking for wide char codeset:"));
+
#if wxUSE_FONTMAP
const wxChar **names = wxFontMapperBase::GetAllEncodingNames(WC_ENC);
#else // !wxUSE_FONTMAP
};
#endif // wxUSE_FONTMAP/!wxUSE_FONTMAP
- for ( ; *names; ++names )
+ for ( ; *names && ms_wcCharsetName.empty(); ++names )
{
- const wxString name(*names);
+ const wxString nameCS(*names);
// first try charset with explicit bytesex info (e.g. "UCS-4LE"):
- wxString nameXE(name);
+ wxString nameXE(nameCS);
#ifdef WORDS_BIGENDIAN
nameXE += _T("BE");
#else // little endian
nameXE += _T("LE");
#endif
+ wxLogTrace(TRACE_STRCONV, _T(" trying charset \"%s\""),
+ nameXE.c_str());
+
m2w = iconv_open(nameXE.ToAscii(), cname);
if ( m2w == ICONV_T_INVALID )
{
// try charset w/o bytesex info (e.g. "UCS4")
- m2w = iconv_open(name.ToAscii(), cname);
+ wxLogTrace(TRACE_STRCONV, _T(" trying charset \"%s\""),
+ nameCS.c_str());
+ m2w = iconv_open(nameCS.ToAscii(), cname);
// and check for bytesex ourselves:
if ( m2w != ICONV_T_INVALID )
{
wxLogLastError(wxT("iconv"));
wxLogError(_("Conversion to charset '%s' doesn't work."),
- name.c_str());
+ nameCS.c_str());
}
else // ok, can convert to this encoding, remember it
{
- ms_wcCharsetName = name;
+ ms_wcCharsetName = nameCS;
ms_wcNeedsSwap = wbuf[0] != (wchar_t)buf[0];
}
}
if (ms_wcNeedsSwap)
{
// convert to native endianness
- for ( unsigned n = 0; n < res; n++ )
- buf[n] = WC_BSWAP(buf[n]);
+ for ( unsigned i = 0; i < res; i++ )
+ buf[n] = WC_BSWAP(buf[i]);
}
// NB: iconv was given only strlen(psz) characters on input, and so
wxMutexLocker lock(wxConstCast(this, wxMBConv_iconv)->m_iconvMutex);
#endif
- size_t inbuf = wxWcslen(psz) * SIZEOF_WCHAR_T;
+ size_t inlen = wxWcslen(psz);
+ size_t inbuf = inlen * SIZEOF_WCHAR_T;
size_t outbuf = n;
size_t res, cres;
// (doing WC_BSWAP twice on the original buffer won't help, as it
// could be in read-only memory, or be accessed in some other thread)
tmpbuf = (wchar_t *)malloc(inbuf + SIZEOF_WCHAR_T);
- for ( size_t n = 0; n < inbuf; n++ )
- tmpbuf[n] = WC_BSWAP(psz[n]);
- tmpbuf[inbuf] = L'\0';
+ for ( size_t i = 0; i < inlen; i++ )
+ tmpbuf[n] = WC_BSWAP(psz[i]);
+ tmpbuf[inlen] = L'\0';
psz = tmpbuf;
}
if (ICONV_FAILED(cres, inbuf))
{
- //VS: it is ok if iconv fails, hence trace only
wxLogTrace(TRACE_STRCONV, wxT("iconv failed: %s"), wxSysErrorMsg(wxSysErrorCode()));
return (size_t)-1;
}
// 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
(
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
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;
};
SetName(charset);
}
+#if wxUSE_FONTMAP
+ m_encoding = wxFontMapperBase::GetEncodingFromName(charset);
+#else
m_encoding = wxFONTENCODING_SYSTEM;
+#endif
}
wxCSConv::wxCSConv(wxFontEncoding encoding)
// 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;
wxConvUTF8;
#endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
-
-