+ while (*psz && ((!buf) || (len < n)))
+ {
+ const char *opsz = psz;
+ bool invalid = false;
+ unsigned char cc = *psz++, fc = cc;
+ unsigned cnt;
+ for (cnt = 0; fc & 0x80; cnt++)
+ fc <<= 1;
+
+ if (!cnt)
+ {
+ // plain ASCII char
+ if (buf)
+ *buf++ = cc;
+ len++;
+
+ // escape the escape character for octal escapes
+ if ((m_options & MAP_INVALID_UTF8_TO_OCTAL)
+ && cc == '\\' && (!buf || len < n))
+ {
+ if (buf)
+ *buf++ = cc;
+ len++;
+ }
+ }
+ else
+ {
+ cnt--;
+ if (!cnt)
+ {
+ // invalid UTF-8 sequence
+ invalid = true;
+ }
+ else
+ {
+ unsigned ocnt = cnt - 1;
+ wxUint32 res = cc & (0x3f >> cnt);
+ while (cnt--)
+ {
+ cc = *psz;
+ if ((cc & 0xC0) != 0x80)
+ {
+ // invalid UTF-8 sequence
+ invalid = true;
+ break;
+ }
+
+ psz++;
+ res = (res << 6) | (cc & 0x3f);
+ }
+
+ if (invalid || res <= utf8_max[ocnt])
+ {
+ // illegal UTF-8 encoding
+ invalid = true;
+ }
+ else if ((m_options & MAP_INVALID_UTF8_TO_PUA) &&
+ res >= wxUnicodePUA && res < wxUnicodePUAEnd)
+ {
+ // if one of our PUA characters turns up externally
+ // it must also be treated as an illegal sequence
+ // (a bit like you have to escape an escape character)
+ invalid = true;
+ }
+ else
+ {
+#ifdef WC_UTF16
+ // cast is ok because wchar_t == wxUuint16 if WC_UTF16
+ size_t pa = encode_utf16(res, (wxUint16 *)buf);
+ if (pa == wxCONV_FAILED)
+ {
+ invalid = true;
+ }
+ else
+ {
+ if (buf)
+ buf += pa;
+ len += pa;
+ }
+#else // !WC_UTF16
+ if (buf)
+ *buf++ = (wchar_t)res;
+ len++;
+#endif // WC_UTF16/!WC_UTF16
+ }
+ }
+
+ if (invalid)
+ {
+ if (m_options & MAP_INVALID_UTF8_TO_PUA)
+ {
+ while (opsz < psz && (!buf || len < n))
+ {
+#ifdef WC_UTF16
+ // cast is ok because wchar_t == wxUuint16 if WC_UTF16
+ size_t pa = encode_utf16((unsigned char)*opsz + wxUnicodePUA, (wxUint16 *)buf);
+ wxASSERT(pa != wxCONV_FAILED);
+ if (buf)
+ buf += pa;
+ opsz++;
+ len += pa;
+#else
+ if (buf)
+ *buf++ = (wchar_t)(wxUnicodePUA + (unsigned char)*opsz);
+ opsz++;
+ len++;
+#endif
+ }
+ }
+ else if (m_options & MAP_INVALID_UTF8_TO_OCTAL)
+ {
+ while (opsz < psz && (!buf || len < n))
+ {
+ if ( buf && len + 3 < n )
+ {
+ unsigned char on = *opsz;
+ *buf++ = L'\\';
+ *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;
+ }
+ }
+ else // MAP_INVALID_UTF8_NOT
+ {
+ return wxCONV_FAILED;
+ }
+ }
+ }
+ }
+
+ if (buf && (len < n))
+ *buf = 0;
+
+ return len;
+}
+
+static inline bool isoctal(wchar_t wch)
+{
+ return L'0' <= wch && wch <= L'7';
+}
+
+size_t wxMBConvUTF8::WC2MB(char *buf, const wchar_t *psz, size_t n) const
+{
+ size_t len = 0;
+
+ while (*psz && ((!buf) || (len < n)))
+ {
+ wxUint32 cc;
+
+#ifdef WC_UTF16
+ // cast is ok for WC_UTF16
+ size_t pa = decode_utf16((const wxUint16 *)psz, cc);
+ psz += (pa == wxCONV_FAILED) ? 1 : pa;
+#else
+ cc = (*psz++) & 0x7fffffff;
+#endif
+
+ if ( (m_options & MAP_INVALID_UTF8_TO_PUA)
+ && cc >= wxUnicodePUA && cc < wxUnicodePUAEnd )
+ {
+ if (buf)
+ *buf++ = (char)(cc - wxUnicodePUA);
+ len++;
+ }
+ else if ( (m_options & MAP_INVALID_UTF8_TO_OCTAL)
+ && cc == L'\\' && psz[0] == L'\\' )
+ {
+ if (buf)
+ *buf++ = (char)cc;
+ psz++;
+ len++;
+ }
+ else if ( (m_options & MAP_INVALID_UTF8_TO_OCTAL) &&
+ cc == L'\\' &&
+ isoctal(psz[0]) && isoctal(psz[1]) && isoctal(psz[2]) )
+ {
+ if (buf)
+ {
+ *buf++ = (char) ((psz[0] - L'0') * 0100 +
+ (psz[1] - L'0') * 010 +
+ (psz[2] - L'0'));
+ }
+
+ psz += 3;
+ len++;
+ }
+ else
+ {
+ unsigned cnt;
+ for (cnt = 0; cc > utf8_max[cnt]; cnt++)
+ {
+ }
+
+ if (!cnt)
+ {
+ // plain ASCII char
+ if (buf)
+ *buf++ = (char) cc;
+ len++;
+ }
+ else
+ {
+ len += cnt + 1;
+ if (buf)
+ {
+ *buf++ = (char) ((-128 >> cnt) | ((cc >> (cnt * 6)) & (0x3f >> cnt)));
+ while (cnt--)
+ *buf++ = (char) (0x80 | ((cc >> (cnt * 6)) & 0x3f));
+ }
+ }
+ }
+ }
+
+ if (buf && (len < n))
+ *buf = 0;
+
+ return len;
+}
+
+// ============================================================================
+// UTF-16
+// ============================================================================
+
+#ifdef WORDS_BIGENDIAN
+ #define wxMBConvUTF16straight wxMBConvUTF16BE
+ #define wxMBConvUTF16swap wxMBConvUTF16LE
+#else
+ #define wxMBConvUTF16swap wxMBConvUTF16BE
+ #define wxMBConvUTF16straight wxMBConvUTF16LE
+#endif
+
+/* static */
+size_t wxMBConvUTF16Base::GetLength(const char *src, size_t srcLen)
+{
+ if ( srcLen == wxNO_LEN )
+ {
+ // count the number of bytes in input, including the trailing NULs
+ const wxUint16 *inBuff = wx_reinterpret_cast(const wxUint16 *, src);
+ for ( srcLen = 1; *inBuff++; srcLen++ )
+ ;
+
+ srcLen *= BYTES_PER_CHAR;
+ }
+ else // we already have the length
+ {
+ // we can only convert an entire number of UTF-16 characters
+ if ( srcLen % BYTES_PER_CHAR )
+ return wxCONV_FAILED;
+ }
+
+ return srcLen;
+}
+
+// case when in-memory representation is UTF-16 too
+#ifdef WC_UTF16
+
+// ----------------------------------------------------------------------------
+// conversions without endianness change
+// ----------------------------------------------------------------------------
+
+size_t
+wxMBConvUTF16straight::ToWChar(wchar_t *dst, size_t dstLen,
+ const char *src, size_t srcLen) const
+{
+ // set up the scene for using memcpy() (which is presumably more efficient
+ // than copying the bytes one by one)
+ srcLen = GetLength(src, srcLen);
+ if ( srcLen == wxNO_LEN )
+ return wxCONV_FAILED;
+
+ const size_t inLen = srcLen / BYTES_PER_CHAR;
+ if ( dst )
+ {
+ if ( dstLen < inLen )
+ return wxCONV_FAILED;
+
+ memcpy(dst, src, srcLen);
+ }
+
+ return inLen;
+}
+
+size_t
+wxMBConvUTF16straight::FromWChar(char *dst, size_t dstLen,
+ const wchar_t *src, size_t srcLen) const
+{
+ if ( srcLen == wxNO_LEN )
+ srcLen = wxWcslen(src) + 1;
+
+ srcLen *= BYTES_PER_CHAR;
+
+ if ( dst )
+ {
+ if ( dstLen < srcLen )
+ return wxCONV_FAILED;
+
+ memcpy(dst, src, srcLen);
+ }
+
+ return srcLen;
+}
+
+// ----------------------------------------------------------------------------
+// endian-reversing conversions
+// ----------------------------------------------------------------------------
+
+size_t
+wxMBConvUTF16swap::ToWChar(wchar_t *dst, size_t dstLen,
+ const char *src, size_t srcLen) const
+{
+ srcLen = GetLength(src, srcLen);
+ if ( srcLen == wxNO_LEN )
+ return wxCONV_FAILED;
+
+ srcLen /= BYTES_PER_CHAR;
+
+ if ( dst )
+ {
+ if ( dstLen < srcLen )
+ return wxCONV_FAILED;
+
+ const wxUint16 *inBuff = wx_reinterpret_cast(const wxUint16 *, src);
+ for ( size_t n = 0; n < srcLen; n++, inBuff++ )
+ {
+ *dst++ = wxUINT16_SWAP_ALWAYS(*inBuff);
+ }
+ }
+
+ return srcLen;
+}
+
+size_t
+wxMBConvUTF16swap::FromWChar(char *dst, size_t dstLen,
+ const wchar_t *src, size_t srcLen) const
+{
+ if ( srcLen == wxNO_LEN )
+ srcLen = wxWcslen(src) + 1;
+
+ srcLen *= BYTES_PER_CHAR;
+
+ if ( dst )
+ {
+ if ( dstLen < srcLen )
+ return wxCONV_FAILED;
+
+ wxUint16 *outBuff = wx_reinterpret_cast(wxUint16 *, dst);
+ for ( size_t n = 0; n < srcLen; n += BYTES_PER_CHAR, src++ )
+ {
+ *outBuff++ = wxUINT16_SWAP_ALWAYS(*src);
+ }
+ }
+
+ return srcLen;
+}
+
+#else // !WC_UTF16: wchar_t is UTF-32
+
+// ----------------------------------------------------------------------------
+// conversions without endianness change
+// ----------------------------------------------------------------------------
+
+size_t
+wxMBConvUTF16straight::ToWChar(wchar_t *dst, size_t dstLen,
+ const char *src, size_t srcLen) const
+{
+ srcLen = GetLength(src, srcLen);
+ if ( srcLen == wxNO_LEN )
+ return wxCONV_FAILED;
+
+ const size_t inLen = srcLen / BYTES_PER_CHAR;
+ if ( !dst )
+ {
+ // optimization: return maximal space which could be needed for this
+ // string even if the real size could be smaller if the buffer contains
+ // any surrogates
+ return inLen;
+ }
+
+ size_t outLen = 0;
+ const wxUint16 *inBuff = wx_reinterpret_cast(const wxUint16 *, src);
+ for ( const wxUint16 * const inEnd = inBuff + inLen; inBuff < inEnd; )
+ {
+ const wxUint32 ch = wxDecodeSurrogate(&inBuff);
+ if ( !inBuff )
+ return wxCONV_FAILED;
+
+ if ( ++outLen > dstLen )
+ return wxCONV_FAILED;
+
+ *dst++ = ch;
+ }
+
+
+ return outLen;
+}
+
+size_t
+wxMBConvUTF16straight::FromWChar(char *dst, size_t dstLen,
+ const wchar_t *src, size_t srcLen) const
+{
+ if ( srcLen == wxNO_LEN )
+ srcLen = wxWcslen(src) + 1;
+
+ size_t outLen = 0;
+ wxUint16 *outBuff = wx_reinterpret_cast(wxUint16 *, dst);
+ for ( size_t n = 0; n < srcLen; n++ )
+ {
+ wxUint16 cc[2];
+ const size_t numChars = encode_utf16(*src++, cc);
+ if ( numChars == wxCONV_FAILED )
+ return wxCONV_FAILED;
+
+ outLen += numChars * BYTES_PER_CHAR;
+ if ( outBuff )
+ {
+ if ( outLen > dstLen )
+ return wxCONV_FAILED;
+
+ *outBuff++ = cc[0];
+ if ( numChars == 2 )
+ {
+ // second character of a surrogate
+ *outBuff++ = cc[1];
+ }
+ }
+ }
+
+ return outLen;
+}
+
+// ----------------------------------------------------------------------------
+// endian-reversing conversions
+// ----------------------------------------------------------------------------
+
+size_t
+wxMBConvUTF16swap::ToWChar(wchar_t *dst, size_t dstLen,
+ const char *src, size_t srcLen) const
+{
+ srcLen = GetLength(src, srcLen);
+ if ( srcLen == wxNO_LEN )
+ return wxCONV_FAILED;
+
+ const size_t inLen = srcLen / BYTES_PER_CHAR;
+ if ( !dst )
+ {
+ // optimization: return maximal space which could be needed for this
+ // string even if the real size could be smaller if the buffer contains
+ // any surrogates
+ return inLen;
+ }
+
+ size_t outLen = 0;
+ const wxUint16 *inBuff = wx_reinterpret_cast(const wxUint16 *, src);
+ for ( const wxUint16 * const inEnd = inBuff + inLen; inBuff < inEnd; )
+ {
+ wxUint32 ch;
+ wxUint16 tmp[2];
+
+ tmp[0] = wxUINT16_SWAP_ALWAYS(*inBuff);
+ inBuff++;
+ tmp[1] = wxUINT16_SWAP_ALWAYS(*inBuff);
+
+ const size_t numChars = decode_utf16(tmp, ch);
+ if ( numChars == wxCONV_FAILED )
+ return wxCONV_FAILED;
+
+ if ( numChars == 2 )
+ inBuff++;
+
+ if ( ++outLen > dstLen )
+ return wxCONV_FAILED;
+
+ *dst++ = ch;
+ }
+
+
+ return outLen;
+}
+
+size_t
+wxMBConvUTF16swap::FromWChar(char *dst, size_t dstLen,
+ const wchar_t *src, size_t srcLen) const
+{
+ if ( srcLen == wxNO_LEN )
+ srcLen = wxWcslen(src) + 1;
+
+ size_t outLen = 0;
+ wxUint16 *outBuff = wx_reinterpret_cast(wxUint16 *, dst);
+ for ( const wchar_t *srcEnd = src + srcLen; src < srcEnd; src++ )
+ {
+ wxUint16 cc[2];
+ const size_t numChars = encode_utf16(*src, cc);
+ if ( numChars == wxCONV_FAILED )
+ return wxCONV_FAILED;
+
+ outLen += numChars * BYTES_PER_CHAR;
+ if ( outBuff )
+ {
+ if ( outLen > dstLen )
+ return wxCONV_FAILED;
+
+ *outBuff++ = wxUINT16_SWAP_ALWAYS(cc[0]);
+ if ( numChars == 2 )
+ {
+ // second character of a surrogate
+ *outBuff++ = wxUINT16_SWAP_ALWAYS(cc[1]);
+ }
+ }
+ }
+
+ return outLen;
+}
+
+#endif // WC_UTF16/!WC_UTF16
+
+
+// ============================================================================
+// UTF-32
+// ============================================================================
+
+#ifdef WORDS_BIGENDIAN
+ #define wxMBConvUTF32straight wxMBConvUTF32BE
+ #define wxMBConvUTF32swap wxMBConvUTF32LE
+#else
+ #define wxMBConvUTF32swap wxMBConvUTF32BE
+ #define wxMBConvUTF32straight wxMBConvUTF32LE
+#endif
+
+
+WXDLLIMPEXP_DATA_BASE(wxMBConvUTF32LE) wxConvUTF32LE;
+WXDLLIMPEXP_DATA_BASE(wxMBConvUTF32BE) wxConvUTF32BE;
+
+/* static */
+size_t wxMBConvUTF32Base::GetLength(const char *src, size_t srcLen)
+{
+ if ( srcLen == wxNO_LEN )
+ {
+ // count the number of bytes in input, including the trailing NULs
+ const wxUint32 *inBuff = wx_reinterpret_cast(const wxUint32 *, src);
+ for ( srcLen = 1; *inBuff++; srcLen++ )
+ ;
+
+ srcLen *= BYTES_PER_CHAR;
+ }
+ else // we already have the length
+ {
+ // we can only convert an entire number of UTF-32 characters
+ if ( srcLen % BYTES_PER_CHAR )
+ return wxCONV_FAILED;
+ }
+
+ return srcLen;
+}
+
+// case when in-memory representation is UTF-16
+#ifdef WC_UTF16
+
+// ----------------------------------------------------------------------------
+// conversions without endianness change
+// ----------------------------------------------------------------------------
+
+size_t
+wxMBConvUTF32straight::ToWChar(wchar_t *dst, size_t dstLen,
+ const char *src, size_t srcLen) const
+{
+ srcLen = GetLength(src, srcLen);
+ if ( srcLen == wxNO_LEN )
+ return wxCONV_FAILED;
+
+ const wxUint32 *inBuff = wx_reinterpret_cast(const wxUint32 *, src);
+ const size_t inLen = srcLen / BYTES_PER_CHAR;
+ size_t outLen = 0;
+ for ( size_t n = 0; n < inLen; n++ )
+ {
+ wxUint16 cc[2];
+ const size_t numChars = encode_utf16(*inBuff++, cc);
+ if ( numChars == wxCONV_FAILED )
+ return wxCONV_FAILED;
+
+ outLen += numChars;
+ if ( dst )
+ {
+ if ( outLen > dstLen )
+ return wxCONV_FAILED;
+
+ *dst++ = cc[0];
+ if ( numChars == 2 )
+ {
+ // second character of a surrogate
+ *dst++ = cc[1];
+ }
+ }
+ }
+
+ return outLen;
+}
+
+size_t
+wxMBConvUTF32straight::FromWChar(char *dst, size_t dstLen,
+ const wchar_t *src, size_t srcLen) const
+{
+ if ( srcLen == wxNO_LEN )
+ srcLen = wxWcslen(src) + 1;
+
+ if ( !dst )
+ {
+ // optimization: return maximal space which could be needed for this
+ // string instead of the exact amount which could be less if there are
+ // any surrogates in the input
+ //
+ // we consider that surrogates are rare enough to make it worthwhile to
+ // avoid running the loop below at the cost of slightly extra memory
+ // consumption
+ return srcLen * BYTES_PER_CHAR;
+ }
+
+ wxUint32 *outBuff = wx_reinterpret_cast(wxUint32 *, dst);
+ size_t outLen = 0;
+ for ( const wchar_t * const srcEnd = src + srcLen; src < srcEnd; )
+ {
+ const wxUint32 ch = wxDecodeSurrogate(&src);
+ if ( !src )
+ return wxCONV_FAILED;
+
+ outLen += BYTES_PER_CHAR;
+
+ if ( outLen > dstLen )
+ return wxCONV_FAILED;
+
+ *outBuff++ = ch;
+ }
+
+ return outLen;
+}
+
+// ----------------------------------------------------------------------------
+// endian-reversing conversions
+// ----------------------------------------------------------------------------
+
+size_t
+wxMBConvUTF32swap::ToWChar(wchar_t *dst, size_t dstLen,
+ const char *src, size_t srcLen) const
+{
+ srcLen = GetLength(src, srcLen);
+ if ( srcLen == wxNO_LEN )
+ return wxCONV_FAILED;
+
+ const wxUint32 *inBuff = wx_reinterpret_cast(const wxUint32 *, src);
+ const size_t inLen = srcLen / BYTES_PER_CHAR;
+ size_t outLen = 0;
+ for ( size_t n = 0; n < inLen; n++, inBuff++ )
+ {
+ wxUint16 cc[2];
+ const size_t numChars = encode_utf16(wxUINT32_SWAP_ALWAYS(*inBuff), cc);
+ if ( numChars == wxCONV_FAILED )
+ return wxCONV_FAILED;
+
+ outLen += numChars;
+ if ( dst )
+ {
+ if ( outLen > dstLen )
+ return wxCONV_FAILED;
+
+ *dst++ = cc[0];
+ if ( numChars == 2 )
+ {
+ // second character of a surrogate
+ *dst++ = cc[1];
+ }
+ }
+ }
+
+ return outLen;
+}
+
+size_t
+wxMBConvUTF32swap::FromWChar(char *dst, size_t dstLen,
+ const wchar_t *src, size_t srcLen) const
+{
+ if ( srcLen == wxNO_LEN )
+ srcLen = wxWcslen(src) + 1;
+
+ if ( !dst )
+ {
+ // optimization: return maximal space which could be needed for this
+ // string instead of the exact amount which could be less if there are
+ // any surrogates in the input
+ //
+ // we consider that surrogates are rare enough to make it worthwhile to
+ // avoid running the loop below at the cost of slightly extra memory
+ // consumption
+ return srcLen*BYTES_PER_CHAR;
+ }
+
+ wxUint32 *outBuff = wx_reinterpret_cast(wxUint32 *, dst);
+ size_t outLen = 0;
+ for ( const wchar_t * const srcEnd = src + srcLen; src < srcEnd; )
+ {
+ const wxUint32 ch = wxDecodeSurrogate(&src);
+ if ( !src )
+ return wxCONV_FAILED;
+
+ outLen += BYTES_PER_CHAR;
+
+ if ( outLen > dstLen )
+ return wxCONV_FAILED;
+
+ *outBuff++ = wxUINT32_SWAP_ALWAYS(ch);
+ }
+
+ return outLen;
+}
+
+#else // !WC_UTF16: wchar_t is UTF-32
+
+// ----------------------------------------------------------------------------
+// conversions without endianness change
+// ----------------------------------------------------------------------------
+
+size_t
+wxMBConvUTF32straight::ToWChar(wchar_t *dst, size_t dstLen,
+ const char *src, size_t srcLen) const
+{
+ // use memcpy() as it should be much faster than hand-written loop
+ srcLen = GetLength(src, srcLen);
+ if ( srcLen == wxNO_LEN )
+ return wxCONV_FAILED;
+
+ const size_t inLen = srcLen/BYTES_PER_CHAR;
+ if ( dst )
+ {
+ if ( dstLen < inLen )
+ return wxCONV_FAILED;
+
+ memcpy(dst, src, srcLen);
+ }
+
+ return inLen;
+}
+
+size_t
+wxMBConvUTF32straight::FromWChar(char *dst, size_t dstLen,
+ const wchar_t *src, size_t srcLen) const
+{
+ if ( srcLen == wxNO_LEN )
+ srcLen = wxWcslen(src) + 1;
+
+ srcLen *= BYTES_PER_CHAR;
+
+ if ( dst )
+ {
+ if ( dstLen < srcLen )
+ return wxCONV_FAILED;
+
+ memcpy(dst, src, srcLen);
+ }
+
+ return srcLen;
+}
+
+// ----------------------------------------------------------------------------
+// endian-reversing conversions
+// ----------------------------------------------------------------------------
+
+size_t
+wxMBConvUTF32swap::ToWChar(wchar_t *dst, size_t dstLen,
+ const char *src, size_t srcLen) const
+{
+ srcLen = GetLength(src, srcLen);
+ if ( srcLen == wxNO_LEN )
+ return wxCONV_FAILED;
+
+ srcLen /= BYTES_PER_CHAR;
+
+ if ( dst )
+ {
+ if ( dstLen < srcLen )
+ return wxCONV_FAILED;
+
+ const wxUint32 *inBuff = wx_reinterpret_cast(const wxUint32 *, src);
+ for ( size_t n = 0; n < srcLen; n++, inBuff++ )
+ {
+ *dst++ = wxUINT32_SWAP_ALWAYS(*inBuff);
+ }
+ }
+
+ return srcLen;
+}
+
+size_t
+wxMBConvUTF32swap::FromWChar(char *dst, size_t dstLen,
+ const wchar_t *src, size_t srcLen) const
+{
+ if ( srcLen == wxNO_LEN )
+ srcLen = wxWcslen(src) + 1;
+
+ srcLen *= BYTES_PER_CHAR;
+
+ if ( dst )
+ {
+ if ( dstLen < srcLen )
+ return wxCONV_FAILED;
+
+ wxUint32 *outBuff = wx_reinterpret_cast(wxUint32 *, dst);
+ for ( size_t n = 0; n < srcLen; n += BYTES_PER_CHAR, src++ )
+ {
+ *outBuff++ = wxUINT32_SWAP_ALWAYS(*src);
+ }
+ }
+
+ return srcLen;
+}
+
+#endif // WC_UTF16/!WC_UTF16
+
+
+// ============================================================================
+// The classes doing conversion using the iconv_xxx() functions
+// ============================================================================
+
+#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.
+// [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) && \
+ (errno != E2BIG || bufLeft != 0))
+#else
+#define ICONV_FAILED(cres, bufLeft) (cres == (size_t)-1)
+#endif
+
+#define ICONV_CHAR_CAST(x) ((ICONV_CONST char **)(x))
+
+#define ICONV_T_INVALID ((iconv_t)-1)
+
+#if SIZEOF_WCHAR_T == 4
+ #define WC_BSWAP wxUINT32_SWAP_ALWAYS
+ #define WC_ENC wxFONTENCODING_UTF32
+#elif SIZEOF_WCHAR_T == 2
+ #define WC_BSWAP wxUINT16_SWAP_ALWAYS
+ #define WC_ENC wxFONTENCODING_UTF16
+#else // sizeof(wchar_t) != 2 nor 4
+ // does this ever happen?
+ #error "Unknown sizeof(wchar_t): please report this to wx-dev@lists.wxwindows.org"
+#endif
+
+// ----------------------------------------------------------------------------
+// wxMBConv_iconv: encapsulates an iconv character set
+// ----------------------------------------------------------------------------
+
+class wxMBConv_iconv : public wxMBConv
+{
+public:
+ wxMBConv_iconv(const wxChar *name);
+ virtual ~wxMBConv_iconv();
+
+ virtual size_t MB2WC(wchar_t *buf, const char *psz, size_t n) const;
+ virtual size_t WC2MB(char *buf, const wchar_t *psz, size_t n) const;
+
+ // classify this encoding as explained in wxMBConv::GetMBNulLen() comment
+ virtual size_t GetMBNulLen() const;
+
+ virtual wxMBConv *Clone() const
+ {
+ wxMBConv_iconv *p = new wxMBConv_iconv(m_name);
+ p->m_minMBCharWidth = m_minMBCharWidth;
+ return p;
+ }
+
+ bool IsOk() const
+ { return (m2w != ICONV_T_INVALID) && (w2m != ICONV_T_INVALID); }
+
+protected:
+ // the iconv handlers used to translate from multibyte
+ // to wide char and in 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
+ // available on this machine, it will remain NULL
+ static wxString ms_wcCharsetName;
+
+ // true if the wide char encoding we use (i.e. ms_wcCharsetName) has
+ // different endian-ness than the native one
+ static bool ms_wcNeedsSwap;
+
+
+ // name of the encoding handled by this conversion
+ wxString m_name;
+
+ // cached result of GetMBNulLen(); set to 0 meaning "unknown"
+ // initially
+ size_t m_minMBCharWidth;
+};
+
+// make the constructor available for unit testing
+WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_iconv( const wxChar* name )
+{
+ wxMBConv_iconv* result = new wxMBConv_iconv( name );
+ if ( !result->IsOk() )
+ {
+ delete result;
+ return 0;
+ }
+
+ return result;
+}
+
+wxString wxMBConv_iconv::ms_wcCharsetName;
+bool wxMBConv_iconv::ms_wcNeedsSwap = false;
+
+wxMBConv_iconv::wxMBConv_iconv(const wxChar *name)
+ : m_name(name)
+{
+ m_minMBCharWidth = 0;
+
+ // iconv operates with chars, not wxChars, but luckily it uses only ASCII
+ // names for the charsets
+ const wxCharBuffer cname(wxString(name).ToAscii());
+
+ // 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
+ static const wxChar *names_static[] =
+ {
+#if SIZEOF_WCHAR_T == 4
+ _T("UCS-4"),
+#elif SIZEOF_WCHAR_T = 2
+ _T("UCS-2"),
+#endif
+ NULL
+ };
+ const wxChar **names = names_static;
+#endif // wxUSE_FONTMAP/!wxUSE_FONTMAP
+
+ for ( ; *names && ms_wcCharsetName.empty(); ++names )
+ {
+ const wxString nameCS(*names);
+
+ // first try charset with explicit bytesex info (e.g. "UCS-4LE"):
+ 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")
+ 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 )
+ {
+ char buf[2], *bufPtr;
+ wchar_t wbuf[2], *wbufPtr;
+ size_t insz, outsz;
+ size_t res;
+
+ buf[0] = 'A';
+ buf[1] = 0;
+ wbuf[0] = 0;
+ insz = 2;
+ outsz = SIZEOF_WCHAR_T * 2;
+ wbufPtr = wbuf;
+ bufPtr = buf;
+
+ res = iconv(
+ m2w, ICONV_CHAR_CAST(&bufPtr), &insz,
+ (char**)&wbufPtr, &outsz);
+
+ if (ICONV_FAILED(res, insz))
+ {
+ wxLogLastError(wxT("iconv"));
+ wxLogError(_("Conversion to charset '%s' doesn't work."),
+ nameCS.c_str());
+ }
+ else // ok, can convert to this encoding, remember it
+ {
+ ms_wcCharsetName = nameCS;
+ ms_wcNeedsSwap = wbuf[0] != (wchar_t)buf[0];
+ }
+ }
+ }
+ else // use charset not requiring byte swapping
+ {
+ ms_wcCharsetName = nameXE;
+ }
+ }
+
+ wxLogTrace(TRACE_STRCONV,
+ wxT("iconv wchar_t charset is \"%s\"%s"),
+ ms_wcCharsetName.empty() ? _T("<none>")
+ : ms_wcCharsetName.c_str(),
+ ms_wcNeedsSwap ? _T(" (needs swap)")
+ : _T(""));
+ }
+ else // we already have ms_wcCharsetName
+ {
+ m2w = iconv_open(ms_wcCharsetName.ToAscii(), cname);
+ }
+
+ if ( ms_wcCharsetName.empty() )
+ {
+ w2m = ICONV_T_INVALID;
+ }
+ else
+ {
+ w2m = iconv_open(cname, ms_wcCharsetName.ToAscii());
+ if ( w2m == ICONV_T_INVALID )
+ {
+ wxLogTrace(TRACE_STRCONV,
+ wxT("\"%s\" -> \"%s\" works but not the converse!?"),
+ ms_wcCharsetName.c_str(), cname.data());
+ }
+ }
+}
+
+wxMBConv_iconv::~wxMBConv_iconv()
+{
+ if ( m2w != ICONV_T_INVALID )
+ iconv_close(m2w);
+ if ( w2m != ICONV_T_INVALID )
+ iconv_close(w2m);
+}
+
+size_t wxMBConv_iconv::MB2WC(wchar_t *buf, const char *psz, size_t n) const
+{
+ // find the string length: notice that must be done differently for
+ // NUL-terminated strings and UTF-16/32 which are terminated with 2/4 NULs
+ size_t inbuf;
+ const size_t nulLen = GetMBNulLen();
+ switch ( nulLen )
+ {
+ default:
+ return wxCONV_FAILED;
+
+ case 1:
+ inbuf = strlen(psz); // arguably more optimized than our version
+ break;
+
+ case 2:
+ case 4:
+ // for UTF-16/32 not only we need to have 2/4 consecutive NULs but
+ // they also have to start at character boundary and not span two
+ // adjacent characters
+ const char *p;
+ for ( p = psz; NotAllNULs(p, nulLen); p += nulLen )
+ ;
+ inbuf = p - psz;
+ break;
+ }
+
+#if wxUSE_THREADS
+ // NB: iconv() is MT-safe, but each thread must use its own iconv_t handle.
+ // Unfortunately there are 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 // wxUSE_THREADS
+
+ size_t outbuf = n * SIZEOF_WCHAR_T;
+ size_t res, cres;
+ // VS: Use these instead of psz, buf because iconv() modifies its arguments:
+ wchar_t *bufPtr = buf;
+ const char *pszPtr = psz;
+
+ if (buf)
+ {
+ // have destination buffer, convert there
+ cres = iconv(m2w,
+ ICONV_CHAR_CAST(&pszPtr), &inbuf,
+ (char**)&bufPtr, &outbuf);
+ res = n - (outbuf / SIZEOF_WCHAR_T);
+
+ if (ms_wcNeedsSwap)
+ {
+ // convert to native endianness
+ for ( unsigned i = 0; i < res; i++ )
+ buf[n] = WC_BSWAP(buf[i]);
+ }
+
+ // NUL-terminate the string if there is any space left
+ if (res < n)
+ buf[res] = 0;
+ }
+ else
+ {
+ // no destination buffer... convert using temp buffer
+ // to calculate destination buffer requirement
+ wchar_t tbuf[8];
+ res = 0;
+
+ do
+ {
+ bufPtr = tbuf;
+ outbuf = 8 * SIZEOF_WCHAR_T;
+
+ cres = iconv(m2w,
+ ICONV_CHAR_CAST(&pszPtr), &inbuf,
+ (char**)&bufPtr, &outbuf );
+
+ res += 8 - (outbuf / SIZEOF_WCHAR_T);
+ }
+ while ((cres == (size_t)-1) && (errno == E2BIG));
+ }
+
+ 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 wxCONV_FAILED;
+ }
+
+ return res;
+}
+
+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 inlen = wxWcslen(psz);
+ size_t inbuf = inlen * SIZEOF_WCHAR_T;
+ size_t outbuf = n;
+ size_t res, cres;
+
+ wchar_t *tmpbuf = 0;
+
+ if (ms_wcNeedsSwap)
+ {
+ // need to copy to temp buffer to switch endianness
+ // (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 i = 0; i < inlen; i++ )
+ tmpbuf[n] = WC_BSWAP(psz[i]);
+
+ tmpbuf[inlen] = L'\0';
+ psz = tmpbuf;
+ }
+
+ if (buf)
+ {
+ // have destination buffer, convert there
+ cres = iconv( w2m, ICONV_CHAR_CAST(&psz), &inbuf, &buf, &outbuf );
+
+ res = n - outbuf;
+
+ // NB: iconv was given only wcslen(psz) characters on input, and so
+ // it couldn't convert the trailing zero. Let's do it ourselves
+ // if there's some room left for it in the output buffer.
+ if (res < n)
+ buf[0] = 0;
+ }
+ else
+ {
+ // no destination buffer: convert using temp buffer
+ // to calculate destination buffer requirement
+ char tbuf[16];
+ res = 0;
+ do
+ {
+ buf = tbuf;
+ outbuf = 16;
+
+ cres = iconv( w2m, ICONV_CHAR_CAST(&psz), &inbuf, &buf, &outbuf );
+
+ res += 16 - outbuf;
+ }
+ while ((cres == (size_t)-1) && (errno == E2BIG));
+ }
+
+ if (ms_wcNeedsSwap)
+ {
+ free(tmpbuf);
+ }
+
+ if (ICONV_FAILED(cres, inbuf))
+ {
+ wxLogTrace(TRACE_STRCONV, wxT("iconv failed: %s"), wxSysErrorMsg(wxSysErrorCode()));
+ return wxCONV_FAILED;
+ }
+
+ return res;
+}
+
+size_t wxMBConv_iconv::GetMBNulLen() const
+{
+ if ( m_minMBCharWidth == 0 )
+ {
+ wxMBConv_iconv * const self = wxConstCast(this, wxMBConv_iconv);
+
+#if wxUSE_THREADS
+ // NB: explained in MB2WC
+ wxMutexLocker lock(self->m_iconvMutex);
+#endif