X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/509da4510f4e8d99b0780460adf53188f72aa101..180b5b4827d08f1ac13bbb5eca03f775ab35a0b1:/src/common/strconv.cpp diff --git a/src/common/strconv.cpp b/src/common/strconv.cpp index 01e0dc358c..3e354f466b 100644 --- a/src/common/strconv.cpp +++ b/src/common/strconv.cpp @@ -12,35 +12,24 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -// ============================================================================ -// declarations -// ============================================================================ - -// ---------------------------------------------------------------------------- -// headers -// ---------------------------------------------------------------------------- - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ - #pragma hdrstop -#endif + #pragma hdrstop +#endif //__BORLANDC__ #ifndef WX_PRECOMP #include "wx/intl.h" #include "wx/log.h" -#endif // WX_PRECOMP + #include "wx/utils.h" + #include "wx/hashmap.h" +#endif #include "wx/strconv.h" #if wxUSE_WCHAR_T -#ifdef __WINDOWS__ - #include "wx/msw/private.h" - #include "wx/msw/missing.h" -#endif - #ifndef __WXWINCE__ #include #endif @@ -50,8 +39,10 @@ #include #if defined(__WIN32__) && !defined(__WXMICROWIN__) + #include "wx/msw/private.h" + #include "wx/msw/missing.h" #define wxHAVE_WIN32_MB2WC -#endif // __WIN32__ but !__WXMICROWIN__ +#endif #ifdef __SALFORDC__ #include @@ -64,24 +55,21 @@ #include "wx/encconv.h" #include "wx/fontmap.h" -#include "wx/utils.h" -#ifdef __WXMAC__ -#ifndef __DARWIN__ -#include -#include -#include -#endif +#ifdef __DARWIN__ +#include "wx/mac/corefoundation/private/strconv_cf.h" +#endif //def __DARWIN__ -#include "wx/mac/private.h" // includes mac headers -#endif #define TRACE_STRCONV _T("strconv") +// WC_UTF16 is defined only if sizeof(wchar_t) == 2, otherwise it's supposed to +// be 4 bytes #if SIZEOF_WCHAR_T == 2 #define WC_UTF16 #endif + // ============================================================================ // implementation // ============================================================================ @@ -96,44 +84,45 @@ static bool NotAllNULs(const char *p, size_t n) } // ---------------------------------------------------------------------------- -// UTF-16 en/decoding to/from UCS-4 +// UTF-16 en/decoding to/from UCS-4 with surrogates handling // ---------------------------------------------------------------------------- - static size_t encode_utf16(wxUint32 input, wxUint16 *output) { - if (input<=0xffff) + if (input <= 0xffff) { if (output) *output = (wxUint16) input; + return 1; } - else if (input>=0x110000) + else if (input >= 0x110000) { - return (size_t)-1; + return wxCONV_FAILED; } else { if (output) { - *output++ = (wxUint16) ((input >> 10)+0xd7c0); - *output = (wxUint16) ((input&0x3ff)+0xdc00); + *output++ = (wxUint16) ((input >> 10) + 0xd7c0); + *output = (wxUint16) ((input & 0x3ff) + 0xdc00); } + return 2; } } static size_t decode_utf16(const wxUint16* input, wxUint32& output) { - if ((*input<0xd800) || (*input>0xdfff)) + if ((*input < 0xd800) || (*input > 0xdfff)) { output = *input; return 1; } - else if ((input[1]<0xdc00) || (input[1]>0xdfff)) + else if ((input[1] < 0xdc00) || (input[1] > 0xdfff)) { output = *input; - return (size_t)-1; + return wxCONV_FAILED; } else { @@ -142,6 +131,29 @@ static size_t decode_utf16(const wxUint16* input, wxUint32& output) } } +#ifdef WC_UTF16 + typedef wchar_t wxDecodeSurrogate_t; +#else // !WC_UTF16 + typedef wxUint16 wxDecodeSurrogate_t; +#endif // WC_UTF16/!WC_UTF16 + +// returns the next UTF-32 character from the wchar_t buffer and advances the +// pointer to the character after this one +// +// if an invalid character is found, *pSrc is set to NULL, the caller must +// check for this +static wxUint32 wxDecodeSurrogate(const wxDecodeSurrogate_t **pSrc) +{ + wxUint32 out; + const size_t + n = decode_utf16(wx_reinterpret_cast(const wxUint16 *, *pSrc), out); + if ( n == wxCONV_FAILED ) + *pSrc = NULL; + else + *pSrc += n; + + return out; +} // ---------------------------------------------------------------------------- // wxMBConv @@ -162,7 +174,7 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen, size_t dstWritten = 0; // the number of NULs terminating this string - size_t nulLen wxDUMMY_INITIALIZE(0); + size_t nulLen = 0; // not really needed, but just to avoid warnings // if we were not given the input size we just have to assume that the // string is properly terminated as we have no way of knowing how long it @@ -170,7 +182,7 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen, // NULs at the end wxCharBuffer bufTmp; const char *srcEnd; - if ( srcLen != (size_t)-1 ) + if ( srcLen != wxNO_LEN ) { // we need to know how to find the end of this string nulLen = GetMBNulLen(); @@ -201,33 +213,25 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen, { // try to convert the current chunk size_t lenChunk = MB2WC(NULL, src, 0); - if ( lenChunk == 0 ) - { - // nothing left in the input string, conversion succeeded - break; - } - if ( lenChunk == wxCONV_FAILED ) return wxCONV_FAILED; - // if we already have a previous chunk, leave the NUL separating it - // from this one - if ( dstWritten ) - { - dstWritten++; - if ( dst ) - dst++; - } + lenChunk++; // for the L'\0' at the end of this chunk dstWritten += lenChunk; + if ( lenChunk == 1 ) + { + // nothing left in the input string, conversion succeeded + break; + } + if ( dst ) { if ( dstWritten > dstLen ) return wxCONV_FAILED; - lenChunk = MB2WC(dst, src, lenChunk + 1 /* for NUL */); - if ( lenChunk == wxCONV_FAILED ) + if ( MB2WC(dst, src, lenChunk) == wxCONV_FAILED ) return wxCONV_FAILED; dst += lenChunk; @@ -235,8 +239,8 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen, if ( !srcEnd ) { - // we convert the entire string in this cas, as we suppose that the - // string is NUL-terminated and so srcEnd is not used at all + // we convert just one chunk in this case as this is the entire + // string anyhow break; } @@ -275,7 +279,7 @@ wxMBConv::FromWChar(char *dst, size_t dstLen, // if we don't know its length we have no choice but to assume that it is, // indeed, properly terminated wxWCharBuffer bufTmp; - if ( srcLen == (size_t)-1 ) + if ( srcLen == wxNO_LEN ) { srcLen = wxWcslen(src) + 1; } @@ -283,7 +287,7 @@ wxMBConv::FromWChar(char *dst, size_t dstLen, { // make a copy in order to properly NUL-terminate the string bufTmp = wxWCharBuffer(srcLen); - memcpy(bufTmp.data(), src, srcLen*sizeof(wchar_t)); + memcpy(bufTmp.data(), src, srcLen * sizeof(wchar_t)); src = bufTmp; } @@ -316,9 +320,9 @@ wxMBConv::FromWChar(char *dst, size_t dstLen, return dstWritten; } -size_t wxMBConv::MB2WC(wchar_t *out, const char *in, size_t outLen) const +size_t wxMBConv::MB2WC(wchar_t *outBuff, const char *inBuff, size_t outLen) const { - size_t rc = ToWChar(out, outLen, in); + size_t rc = ToWChar(outBuff, outLen, inBuff); if ( rc != wxCONV_FAILED ) { // ToWChar() returns the buffer length, i.e. including the trailing @@ -329,9 +333,9 @@ size_t wxMBConv::MB2WC(wchar_t *out, const char *in, size_t outLen) const return rc; } -size_t wxMBConv::WC2MB(char *out, const wchar_t *in, size_t outLen) const +size_t wxMBConv::WC2MB(char *outBuff, const wchar_t *inBuff, size_t outLen) const { - size_t rc = FromWChar(out, outLen, in); + size_t rc = FromWChar(outBuff, outLen, inBuff); if ( rc != wxCONV_FAILED ) { rc -= GetMBNulLen(); @@ -350,14 +354,14 @@ const wxWCharBuffer wxMBConv::cMB2WC(const char *psz) const if ( psz ) { // calculate the length of the buffer needed first - const size_t nLen = MB2WC(NULL, psz, 0); + const size_t nLen = ToWChar(NULL, 0, psz); if ( nLen != wxCONV_FAILED ) { // now do the actual conversion - wxWCharBuffer buf(nLen /* +1 added implicitly */); + wxWCharBuffer buf(nLen - 1 /* +1 added implicitly */); // +1 for the trailing NULL - if ( MB2WC(buf.data(), psz, nLen + 1) != wxCONV_FAILED ) + if ( ToWChar(buf.data(), nLen, psz) != wxCONV_FAILED ) return buf; } } @@ -369,14 +373,11 @@ const wxCharBuffer wxMBConv::cWC2MB(const wchar_t *pwz) const { if ( pwz ) { - const size_t nLen = WC2MB(NULL, pwz, 0); + const size_t nLen = FromWChar(NULL, 0, pwz); if ( nLen != wxCONV_FAILED ) { - // extra space for trailing NUL(s) - static const size_t extraLen = GetMaxMBNulLen(); - - wxCharBuffer buf(nLen + extraLen - 1); - if ( WC2MB(buf.data(), pwz, nLen + extraLen) != wxCONV_FAILED ) + wxCharBuffer buf(nLen - 1); + if ( FromWChar(buf.data(), nLen, pwz) != wxCONV_FAILED ) return buf; } } @@ -385,16 +386,25 @@ const wxCharBuffer wxMBConv::cWC2MB(const wchar_t *pwz) const } const wxWCharBuffer -wxMBConv::cMB2WC(const char *in, size_t inLen, size_t *outLen) const +wxMBConv::cMB2WC(const char *inBuff, size_t inLen, size_t *outLen) const { - const size_t dstLen = ToWChar(NULL, 0, in, inLen); + const size_t dstLen = ToWChar(NULL, 0, inBuff, inLen); if ( dstLen != wxCONV_FAILED ) { + // notice that we allocate space for dstLen+1 wide characters here + // because we want the buffer to always be NUL-terminated, even if the + // input isn't (as otherwise the caller has no way to know its length) wxWCharBuffer wbuf(dstLen); - if ( ToWChar(wbuf.data(), dstLen, in, inLen) ) + wbuf.data()[dstLen - 1] = L'\0'; + if ( ToWChar(wbuf.data(), dstLen, inBuff, inLen) != wxCONV_FAILED ) { if ( outLen ) + { *outLen = dstLen; + if ( wbuf[dstLen - 1] == L'\0' ) + (*outLen)--; + } + return wbuf; } } @@ -406,16 +416,32 @@ wxMBConv::cMB2WC(const char *in, size_t inLen, size_t *outLen) const } const wxCharBuffer -wxMBConv::cWC2MB(const wchar_t *in, size_t inLen, size_t *outLen) const +wxMBConv::cWC2MB(const wchar_t *inBuff, size_t inLen, size_t *outLen) const { - const size_t dstLen = FromWChar(NULL, 0, in, inLen); + size_t dstLen = FromWChar(NULL, 0, inBuff, inLen); if ( dstLen != wxCONV_FAILED ) { - wxCharBuffer buf(dstLen); - if ( FromWChar(buf.data(), dstLen, in, inLen) ) + const size_t nulLen = GetMBNulLen(); + + // as above, ensure that the buffer is always NUL-terminated, even if + // the input is not + wxCharBuffer buf(dstLen + nulLen - 1); + memset(buf.data() + dstLen, 0, nulLen); + if ( FromWChar(buf.data(), dstLen, inBuff, inLen) != wxCONV_FAILED ) { if ( outLen ) + { *outLen = dstLen; + + if ( dstLen >= nulLen && + !NotAllNULs(buf.data() + dstLen - nulLen, nulLen) ) + { + // in this case the output is NUL-terminated and we're not + // supposed to count NUL + *outLen -= nulLen; + } + } + return buf; } } @@ -446,11 +472,11 @@ size_t wxMBConvLibc::WC2MB(char *buf, const wchar_t *psz, size_t n) const #ifdef __UNIX__ -wxConvBrokenFileNames::wxConvBrokenFileNames(const wxChar *charset) +wxConvBrokenFileNames::wxConvBrokenFileNames(const wxString& charset) { - if ( !charset || wxStricmp(charset, _T("UTF-8")) == 0 - || wxStricmp(charset, _T("UTF8")) == 0 ) - m_conv = new wxMBConvUTF8(wxMBConvUTF8::MAP_INVALID_UTF8_TO_OCTAL); + if ( wxStricmp(charset, _T("UTF-8")) == 0 || + wxStricmp(charset, _T("UTF8")) == 0 ) + m_conv = new wxMBConvUTF8(wxMBConvUTF8::MAP_INVALID_UTF8_TO_PUA); else m_conv = new wxCSConv(charset); } @@ -556,7 +582,7 @@ size_t wxMBConvUTF7::MB2WC(wchar_t *buf, const char *psz, size_t n) const if ( !ok ) { // in valid UTF7 we should have valid characters after '+' - return (size_t)-1; + return wxCONV_FAILED; } if (*psz == '-') @@ -617,19 +643,21 @@ size_t wxMBConvUTF7::WC2MB(char *buf, const wchar_t *psz, size_t n) const // plain ASCII char if (buf) *buf++ = (char)cc; + len++; } #ifndef WC_UTF16 else if (((wxUint32)cc) > 0xffff) { // no surrogate pair generation (yet?) - return (size_t)-1; + return wxCONV_FAILED; } #endif else { if (buf) *buf++ = '+'; + len++; if (cc != '+') { @@ -650,24 +678,30 @@ size_t wxMBConvUTF7::WC2MB(char *buf, const wchar_t *psz, size_t n) const len++; } } + cc = *psz; if (!(cc) || (cc < 0x80 && utf7encode[cc] < 1)) break; } + if (l != 0) { if (buf) *buf++ = utf7enb64[((d % 16) << (6 - l)) % 64]; + len++; } } + if (buf) *buf++ = '-'; len++; } } + if (buf && (len < n)) *buf = 0; + return len; } @@ -675,7 +709,7 @@ size_t wxMBConvUTF7::WC2MB(char *buf, const wchar_t *psz, size_t n) const // UTF-8 // ---------------------------------------------------------------------------- -static wxUint32 utf8_max[]= +static const wxUint32 utf8_max[]= { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff, 0xffffffff }; // boundaries of the private use area we use to (temporarily) remap invalid @@ -683,11 +717,287 @@ static wxUint32 utf8_max[]= const wxUint32 wxUnicodePUA = 0x100000; const wxUint32 wxUnicodePUAEnd = wxUnicodePUA + 256; -size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const +// this table gives the length of the UTF-8 encoding from its first character: +const unsigned char tableUtf8Lengths[256] = { + // single-byte sequences (ASCII): + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00..0F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10..1F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20..2F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30..3F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40..4F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50..5F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60..6F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70..7F + + // these are invalid: + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80..8F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90..9F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0..AF + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0..BF + 0, 0, // C0,C1 + + // two-byte sequences: + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C2..CF + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D0..DF + + // three-byte sequences: + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E0..EF + + // four-byte sequences: + 4, 4, 4, 4, 4, // F0..F4 + + // these are invalid again (5- or 6-byte + // sequences and sequences for code points + // above U+10FFFF, as restricted by RFC 3629): + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F5..FF +}; + +size_t +wxMBConvStrictUTF8::ToWChar(wchar_t *dst, size_t dstLen, + const char *src, size_t srcLen) const +{ + wchar_t *out = dstLen ? dst : NULL; + size_t written = 0; + + if ( srcLen == wxNO_LEN ) + srcLen = strlen(src) + 1; + + for ( const char *p = src; ; p++ ) + { + if ( !(srcLen == wxNO_LEN ? *p : srcLen) ) + { + // all done successfully, just add the trailing NULL if we are not + // using explicit length + if ( srcLen == wxNO_LEN ) + { + if ( out ) + { + if ( !dstLen ) + break; + + *out = L'\0'; + } + + written++; + } + + return written; + } + + if ( out && !dstLen-- ) + break; + + wxUint32 code; + unsigned char c = *p; + + if ( c < 0x80 ) + { + if ( srcLen == 0 ) // the test works for wxNO_LEN too + break; + + if ( srcLen != wxNO_LEN ) + srcLen--; + + code = c; + } + else + { + unsigned len = tableUtf8Lengths[c]; + if ( !len ) + break; + + if ( srcLen < len ) // the test works for wxNO_LEN too + break; + + if ( srcLen != wxNO_LEN ) + srcLen -= len; + + // Char. number range | UTF-8 octet sequence + // (hexadecimal) | (binary) + // ----------------------+---------------------------------------- + // 0000 0000 - 0000 007F | 0xxxxxxx + // 0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx + // 0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + // 0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + // Code point value is stored in bits marked with 'x', + // lowest-order bit of the value on the right side in the diagram + // above. (from RFC 3629) + + // mask to extract lead byte's value ('x' bits above), by sequence + // length: + static const unsigned char leadValueMask[] = { 0x7F, 0x1F, 0x0F, 0x07 }; + + // mask and value of lead byte's most significant bits, by length: + static const unsigned char leadMarkerMask[] = { 0x80, 0xE0, 0xF0, 0xF8 }; + static const unsigned char leadMarkerVal[] = { 0x00, 0xC0, 0xE0, 0xF0 }; + + len--; // it's more convenient to work with 0-based length here + + // extract the lead byte's value bits: + if ( (c & leadMarkerMask[len]) != leadMarkerVal[len] ) + break; + + code = c & leadValueMask[len]; + + // all remaining bytes, if any, are handled in the same way + // regardless of sequence's length: + for ( ; len; --len ) + { + c = *++p; + if ( (c & 0xC0) != 0x80 ) + return wxCONV_FAILED; + + code <<= 6; + code |= c & 0x3F; + } + } + +#ifdef WC_UTF16 + // cast is ok because wchar_t == wxUint16 if WC_UTF16 + if ( encode_utf16(code, (wxUint16 *)out) == 2 ) + { + if ( out ) + out++; + written++; + } +#else // !WC_UTF16 + if ( out ) + *out = code; +#endif // WC_UTF16/!WC_UTF16 + + if ( out ) + out++; + + written++; + } + + return wxCONV_FAILED; +} + +size_t +wxMBConvStrictUTF8::FromWChar(char *dst, size_t dstLen, + const wchar_t *src, size_t srcLen) const +{ + char *out = dstLen ? dst : NULL; + size_t written = 0; + + for ( const wchar_t *wp = src; ; wp++ ) + { + if ( !(srcLen == wxNO_LEN ? *wp : srcLen--) ) + { + // all done successfully, just add the trailing NULL if we are not + // using explicit length + if ( srcLen == wxNO_LEN ) + { + if ( out ) + { + if ( !dstLen ) + break; + + *out = '\0'; + } + + written++; + } + + return written; + } + + + wxUint32 code; +#ifdef WC_UTF16 + // cast is ok for WC_UTF16 + if ( decode_utf16((const wxUint16 *)wp, code) == 2 ) + { + // skip the next char too as we decoded a surrogate + wp++; + } +#else // wchar_t is UTF-32 + code = *wp & 0x7fffffff; +#endif + + unsigned len; + if ( code <= 0x7F ) + { + len = 1; + if ( out ) + { + if ( dstLen < len ) + break; + + out[0] = (char)code; + } + } + else if ( code <= 0x07FF ) + { + len = 2; + if ( out ) + { + if ( dstLen < len ) + break; + + // NB: this line takes 6 least significant bits, encodes them as + // 10xxxxxx and discards them so that the next byte can be encoded: + out[1] = 0x80 | (code & 0x3F); code >>= 6; + out[0] = 0xC0 | code; + } + } + else if ( code < 0xFFFF ) + { + len = 3; + if ( out ) + { + if ( dstLen < len ) + break; + + out[2] = 0x80 | (code & 0x3F); code >>= 6; + out[1] = 0x80 | (code & 0x3F); code >>= 6; + out[0] = 0xE0 | code; + } + } + else if ( code <= 0x10FFFF ) + { + len = 4; + if ( out ) + { + if ( dstLen < len ) + break; + + out[3] = 0x80 | (code & 0x3F); code >>= 6; + out[2] = 0x80 | (code & 0x3F); code >>= 6; + out[1] = 0x80 | (code & 0x3F); code >>= 6; + out[0] = 0xF0 | code; + } + } + else + { + wxFAIL_MSG( _T("trying to encode undefined Unicode character") ); + break; + } + + if ( out ) + { + out += len; + dstLen -= len; + } + + written += len; + } + + // we only get here if an error occurs during decoding + return wxCONV_FAILED; +} + +size_t wxMBConvUTF8::ToWChar(wchar_t *buf, size_t n, + const char *psz, size_t srcLen) const { + if ( m_options == MAP_INVALID_UTF8_NOT ) + return wxMBConvStrictUTF8::ToWChar(buf, n, psz, srcLen); + size_t len = 0; - while (*psz && ((!buf) || (len < n))) + while ((srcLen == wxNO_LEN ? *psz : srcLen--) && ((!buf) || (len < n))) { const char *opsz = psz; bool invalid = false; @@ -695,6 +1005,7 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const unsigned cnt; for (cnt = 0; fc & 0x80; cnt++) fc <<= 1; + if (!cnt) { // plain ASCII char @@ -732,9 +1043,11 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const invalid = true; break; } + psz++; res = (res << 6) | (cc & 0x3f); } + if (invalid || res <= utf8_max[ocnt]) { // illegal UTF-8 encoding @@ -751,9 +1064,9 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const else { #ifdef WC_UTF16 - // cast is ok because wchar_t == wxUuint16 if WC_UTF16 + // cast is ok because wchar_t == wxUint16 if WC_UTF16 size_t pa = encode_utf16(res, (wxUint16 *)buf); - if (pa == (size_t)-1) + if (pa == wxCONV_FAILED) { invalid = true; } @@ -770,6 +1083,7 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const #endif // WC_UTF16/!WC_UTF16 } } + if (invalid) { if (m_options & MAP_INVALID_UTF8_TO_PUA) @@ -779,7 +1093,7 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const #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 != (size_t)-1); + wxASSERT(pa != wxCONV_FAILED); if (buf) buf += pa; opsz++; @@ -804,20 +1118,23 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const *buf++ = (wchar_t)( L'0' + (on % 0100) / 010 ); *buf++ = (wchar_t)( L'0' + on % 010 ); } + opsz++; len += 4; } } else // MAP_INVALID_UTF8_NOT { - return (size_t)-1; + return wxCONV_FAILED; } } } } - if (buf && (len < n)) + + if (srcLen == wxNO_LEN && buf && (len < n)) *buf = 0; - return len; + + return len + 1; } static inline bool isoctal(wchar_t wch) @@ -825,19 +1142,24 @@ 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 wxMBConvUTF8::FromWChar(char *buf, size_t n, + const wchar_t *psz, size_t srcLen) const { + if ( m_options == MAP_INVALID_UTF8_NOT ) + return wxMBConvStrictUTF8::FromWChar(buf, n, psz, srcLen); + size_t len = 0; - while (*psz && ((!buf) || (len < n))) + while ((srcLen == wxNO_LEN ? *psz : srcLen--) && ((!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 == (size_t)-1) ? 1 : pa; + psz += (pa == wxCONV_FAILED) ? 1 : pa; #else - cc=(*psz++) & 0x7fffffff; + cc = (*psz++) & 0x7fffffff; #endif if ( (m_options & MAP_INVALID_UTF8_TO_PUA) @@ -861,8 +1183,8 @@ size_t wxMBConvUTF8::WC2MB(char *buf, const wchar_t *psz, size_t n) const { if (buf) { - *buf++ = (char) ((psz[0] - L'0')*0100 + - (psz[1] - L'0')*010 + + *buf++ = (char) ((psz[0] - L'0') * 0100 + + (psz[1] - L'0') * 010 + (psz[2] - L'0')); } @@ -872,7 +1194,10 @@ size_t wxMBConvUTF8::WC2MB(char *buf, const wchar_t *psz, size_t n) const else { unsigned cnt; - for (cnt = 0; cc > utf8_max[cnt]; cnt++) {} + for (cnt = 0; cc > utf8_max[cnt]; cnt++) + { + } + if (!cnt) { // plain ASCII char @@ -880,7 +1205,6 @@ size_t wxMBConvUTF8::WC2MB(char *buf, const wchar_t *psz, size_t n) const *buf++ = (char) cc; len++; } - else { len += cnt + 1; @@ -894,15 +1218,15 @@ size_t wxMBConvUTF8::WC2MB(char *buf, const wchar_t *psz, size_t n) const } } - if (buf && (len dstLen ) + return wxCONV_FAILED; -// copy 32bit String to 16bit MB -size_t wxMBConvUTF16straight::WC2MB(char *buf, const wchar_t *psz, size_t n) const -{ - size_t len=0; + *dst++ = ch; + } - while (*psz && (!buf || len < n)) - { - wxUint16 cc[2]; - size_t pa=encode_utf16(*psz, cc); - if (pa == (size_t)-1) - return pa; + return outLen; +} - if (buf) +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 ) { - *(wxUint16*)buf = cc[0]; - buf += sizeof(wxUint16); - if (pa > 1) + if ( outLen > dstLen ) + return wxCONV_FAILED; + + *outBuff++ = cc[0]; + if ( numChars == 2 ) { - *(wxUint16*)buf = cc[1]; - buf += sizeof(wxUint16); + // second character of a surrogate + *outBuff++ = cc[1]; } } - - len += pa*sizeof(wxUint16); - psz++; } - if (buf && len<=n-sizeof(wxUint16)) *(wxUint16*)buf=0; - return len; + return outLen; } +// ---------------------------------------------------------------------------- +// endian-reversing conversions +// ---------------------------------------------------------------------------- -// swap 16bit MB to 32bit String -size_t wxMBConvUTF16swap::MB2WC(wchar_t *buf, const char *psz, size_t n) const +size_t +wxMBConvUTF16swap::ToWChar(wchar_t *dst, size_t dstLen, + const char *src, size_t srcLen) const { - size_t len=0; + srcLen = GetLength(src, srcLen); + if ( srcLen == wxNO_LEN ) + return wxCONV_FAILED; - while (*(wxUint16*)psz && (!buf || len < n)) + const size_t inLen = srcLen / BYTES_PER_CHAR; + if ( !dst ) { - wxUint32 cc; - char tmp[4]; - tmp[0]=psz[1]; tmp[1]=psz[0]; - tmp[2]=psz[3]; tmp[3]=psz[2]; + // 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]; - size_t pa=decode_utf16((wxUint16*)tmp, cc); - if (pa == (size_t)-1) - return pa; + tmp[0] = wxUINT16_SWAP_ALWAYS(*inBuff); + inBuff++; + tmp[1] = wxUINT16_SWAP_ALWAYS(*inBuff); - if (buf) - *buf++ = (wchar_t)cc; + const size_t numChars = decode_utf16(tmp, ch); + if ( numChars == wxCONV_FAILED ) + return wxCONV_FAILED; + + if ( numChars == 2 ) + inBuff++; - len++; - psz += pa * sizeof(wxUint16); + if ( ++outLen > dstLen ) + return wxCONV_FAILED; + + *dst++ = ch; } - if (buf && len 1) + if ( outLen > dstLen ) + return wxCONV_FAILED; + + *outBuff++ = wxUINT16_SWAP_ALWAYS(cc[0]); + if ( numChars == 2 ) { - *buf++ = ((char*)cc)[3]; - *buf++ = ((char*)cc)[2]; + // second character of a surrogate + *outBuff++ = wxUINT16_SWAP_ALWAYS(cc[1]); } } - - len += pa*sizeof(wxUint16); - psz++; } - if (buf && len<=n-sizeof(wxUint16)) *(wxUint16*)buf=0; - return len; + return outLen; } -#endif // WC_UTF16 +#endif // WC_UTF16/!WC_UTF16 -// ---------------------------------------------------------------------------- +// ============================================================================ // UTF-32 -// ---------------------------------------------------------------------------- +// ============================================================================ #ifdef WORDS_BIGENDIAN -#define wxMBConvUTF32straight wxMBConvUTF32BE -#define wxMBConvUTF32swap wxMBConvUTF32LE + #define wxMBConvUTF32straight wxMBConvUTF32BE + #define wxMBConvUTF32swap wxMBConvUTF32LE #else -#define wxMBConvUTF32swap wxMBConvUTF32BE -#define wxMBConvUTF32straight wxMBConvUTF32LE + #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 -// copy 32bit MB to 16bit String -size_t wxMBConvUTF32straight::MB2WC(wchar_t *buf, const char *psz, size_t n) const +// ---------------------------------------------------------------------------- +// conversions without endianness change +// ---------------------------------------------------------------------------- + +size_t +wxMBConvUTF32straight::ToWChar(wchar_t *dst, size_t dstLen, + const char *src, size_t srcLen) const { - size_t len=0; + srcLen = GetLength(src, srcLen); + if ( srcLen == wxNO_LEN ) + return wxCONV_FAILED; - while (*(wxUint32*)psz && (!buf || len < n)) + 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; - size_t pa=encode_utf16(*(wxUint32*)psz, cc); - if (pa == (size_t)-1) - return pa; - - if (buf) + outLen += numChars; + if ( dst ) { - *buf++ = cc[0]; - if (pa > 1) - *buf++ = cc[1]; + if ( outLen > dstLen ) + return wxCONV_FAILED; + + *dst++ = cc[0]; + if ( numChars == 2 ) + { + // second character of a surrogate + *dst++ = cc[1]; + } } - len += pa; - psz += sizeof(wxUint32); } - if (buf && len dstLen ) + return wxCONV_FAILED; - return len; -} + *outBuff++ = ch; + } + return outLen; +} +// ---------------------------------------------------------------------------- +// endian-reversing conversions +// ---------------------------------------------------------------------------- -// swap 32bit MB to 16bit String -size_t wxMBConvUTF32swap::MB2WC(wchar_t *buf, const char *psz, size_t n) const +size_t +wxMBConvUTF32swap::ToWChar(wchar_t *dst, size_t dstLen, + const char *src, size_t srcLen) const { - size_t len=0; + srcLen = GetLength(src, srcLen); + if ( srcLen == wxNO_LEN ) + return wxCONV_FAILED; - while (*(wxUint32*)psz && (!buf || len < n)) + 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++ ) { - char tmp[4]; - tmp[0] = psz[3]; tmp[1] = psz[2]; - tmp[2] = psz[1]; tmp[3] = psz[0]; - - wxUint16 cc[2]; + const size_t numChars = encode_utf16(wxUINT32_SWAP_ALWAYS(*inBuff), cc); + if ( numChars == wxCONV_FAILED ) + return wxCONV_FAILED; - size_t pa=encode_utf16(*(wxUint32*)tmp, cc); - if (pa == (size_t)-1) - return pa; - - if (buf) + outLen += numChars; + if ( dst ) { - *buf++ = cc[0]; - if (pa > 1) - *buf++ = cc[1]; + if ( outLen > dstLen ) + return wxCONV_FAILED; + + *dst++ = cc[0]; + if ( numChars == 2 ) + { + // second character of a surrogate + *dst++ = cc[1]; + } } - len += pa; - psz += sizeof(wxUint32); } - if (buf && len dstLen ) + return wxCONV_FAILED; - return len; + *outBuff++ = wxUINT32_SWAP_ALWAYS(ch); + } + + return outLen; } -#else // WC_UTF16 +#else // !WC_UTF16: wchar_t is UTF-32 +// ---------------------------------------------------------------------------- +// conversions without endianness change +// ---------------------------------------------------------------------------- -// copy 32bit MB to 32bit String -size_t wxMBConvUTF32straight::MB2WC(wchar_t *buf, const char *psz, size_t n) const +size_t +wxMBConvUTF32straight::ToWChar(wchar_t *dst, size_t dstLen, + const char *src, size_t srcLen) const { - size_t len=0; + // use memcpy() as it should be much faster than hand-written loop + srcLen = GetLength(src, srcLen); + if ( srcLen == wxNO_LEN ) + return wxCONV_FAILED; - while (*(wxUint32*)psz && (!buf || len < n)) + const size_t inLen = srcLen/BYTES_PER_CHAR; + if ( dst ) { - if (buf) - *buf++ = (wchar_t)(*(wxUint32*)psz); - len++; - psz += sizeof(wxUint32); - } + if ( dstLen < inLen ) + return wxCONV_FAILED; - if (buf && lenm_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 + // 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; @@ -1446,13 +1907,17 @@ private: // 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 ) +WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_iconv( const char* name ) { wxMBConv_iconv* result = new wxMBConv_iconv( name ); if ( !result->IsOk() ) @@ -1460,20 +1925,18 @@ WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_iconv( const wxChar* name ) delete result; return 0; } + return result; } wxString wxMBConv_iconv::ms_wcCharsetName; bool wxMBConv_iconv::ms_wcNeedsSwap = false; -wxMBConv_iconv::wxMBConv_iconv(const wxChar *name) +wxMBConv_iconv::wxMBConv_iconv(const char *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() ) { @@ -1482,7 +1945,7 @@ wxMBConv_iconv::wxMBConv_iconv(const wxChar *name) #if wxUSE_FONTMAP const wxChar **names = wxFontMapperBase::GetAllEncodingNames(WC_ENC); #else // !wxUSE_FONTMAP - static const wxChar *names[] = + static const wxChar *names_static[] = { #if SIZEOF_WCHAR_T == 4 _T("UCS-4"), @@ -1491,6 +1954,7 @@ wxMBConv_iconv::wxMBConv_iconv(const wxChar *name) #endif NULL }; + const wxChar **names = names_static; #endif // wxUSE_FONTMAP/!wxUSE_FONTMAP for ( ; *names && ms_wcCharsetName.empty(); ++names ) @@ -1499,22 +1963,23 @@ wxMBConv_iconv::wxMBConv_iconv(const wxChar *name) // first try charset with explicit bytesex info (e.g. "UCS-4LE"): wxString nameXE(nameCS); - #ifdef WORDS_BIGENDIAN + +#ifdef WORDS_BIGENDIAN nameXE += _T("BE"); - #else // little endian +#else // little endian nameXE += _T("LE"); - #endif +#endif wxLogTrace(TRACE_STRCONV, _T(" trying charset \"%s\""), nameXE.c_str()); - m2w = iconv_open(nameXE.ToAscii(), cname); + m2w = iconv_open(nameXE.ToAscii(), name); 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); + m2w = iconv_open(nameCS.ToAscii(), name); // and check for bytesex ourselves: if ( m2w != ICONV_T_INVALID ) @@ -1532,8 +1997,9 @@ wxMBConv_iconv::wxMBConv_iconv(const wxChar *name) wbufPtr = wbuf; bufPtr = buf; - res = iconv(m2w, ICONV_CHAR_CAST(&bufPtr), &insz, - (char**)&wbufPtr, &outsz); + res = iconv( + m2w, ICONV_CHAR_CAST(&bufPtr), &insz, + (char**)&wbufPtr, &outsz); if (ICONV_FAILED(res, insz)) { @@ -1556,14 +2022,14 @@ wxMBConv_iconv::wxMBConv_iconv(const wxChar *name) wxLogTrace(TRACE_STRCONV, wxT("iconv wchar_t charset is \"%s\"%s"), - ms_wcCharsetName.empty() ? _T("") - : ms_wcCharsetName.c_str(), + ms_wcCharsetName.empty() ? wxString("") + : ms_wcCharsetName, ms_wcNeedsSwap ? _T(" (needs swap)") : _T("")); } else // we already have ms_wcCharsetName { - m2w = iconv_open(ms_wcCharsetName.ToAscii(), cname); + m2w = iconv_open(ms_wcCharsetName.ToAscii(), name); } if ( ms_wcCharsetName.empty() ) @@ -1572,12 +2038,12 @@ wxMBConv_iconv::wxMBConv_iconv(const wxChar *name) } else { - w2m = iconv_open(cname, ms_wcCharsetName.ToAscii()); + w2m = iconv_open(name, 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()); + ms_wcCharsetName.c_str(), name); } } } @@ -1599,7 +2065,7 @@ size_t wxMBConv_iconv::MB2WC(wchar_t *buf, const char *psz, size_t n) const switch ( nulLen ) { default: - return (size_t)-1; + return wxCONV_FAILED; case 1: inbuf = strlen(psz); // arguably more optimized than our version @@ -1618,8 +2084,8 @@ 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 + // 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 @@ -1627,7 +2093,6 @@ size_t wxMBConv_iconv::MB2WC(wchar_t *buf, const char *psz, size_t n) const 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: @@ -1659,23 +2124,26 @@ size_t wxMBConv_iconv::MB2WC(wchar_t *buf, const char *psz, size_t n) const // to calculate destination buffer requirement wchar_t tbuf[8]; res = 0; - do { + + do + { bufPtr = tbuf; - outbuf = 8*SIZEOF_WCHAR_T; + 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)); + 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 (size_t)-1; + return wxCONV_FAILED; } return res; @@ -1703,6 +2171,7 @@ size_t wxMBConv_iconv::WC2MB(char *buf, const wchar_t *psz, size_t n) const 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; } @@ -1712,7 +2181,7 @@ size_t wxMBConv_iconv::WC2MB(char *buf, const wchar_t *psz, size_t n) const // have destination buffer, convert there cres = iconv( w2m, ICONV_CHAR_CAST(&psz), &inbuf, &buf, &outbuf ); - res = n-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 @@ -1722,17 +2191,20 @@ size_t wxMBConv_iconv::WC2MB(char *buf, const wchar_t *psz, size_t n) const } else { - // no destination buffer... convert using temp buffer + // no destination buffer: convert using temp buffer // to calculate destination buffer requirement char tbuf[16]; res = 0; - do { - buf = tbuf; outbuf = 16; + 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)); + } + while ((cres == (size_t)-1) && (errno == E2BIG)); } if (ms_wcNeedsSwap) @@ -1743,7 +2215,7 @@ size_t wxMBConv_iconv::WC2MB(char *buf, const wchar_t *psz, size_t n) const if (ICONV_FAILED(cres, inbuf)) { wxLogTrace(TRACE_STRCONV, wxT("iconv failed: %s"), wxSysErrorMsg(wxSysErrorCode())); - return (size_t)-1; + return wxCONV_FAILED; } return res; @@ -1760,25 +2232,33 @@ size_t wxMBConv_iconv::GetMBNulLen() const wxMutexLocker lock(self->m_iconvMutex); #endif - wchar_t *wnul = L""; + const wchar_t *wnul = L""; char buf[8]; // should be enough for NUL in any encoding size_t inLen = sizeof(wchar_t), outLen = WXSIZEOF(buf); - char *in = (char *)wnul; - char *out = buf; - if ( iconv(w2m, ICONV_CHAR_CAST(&in), &inLen, &out, &outLen) == (size_t)-1 ) + char *inBuff = (char *)wnul; + char *outBuff = buf; + if ( iconv(w2m, ICONV_CHAR_CAST(&inBuff), &inLen, &outBuff, &outLen) == (size_t)-1 ) { self->m_minMBCharWidth = (size_t)-1; } else // ok { - self->m_minMBCharWidth = out - buf; + self->m_minMBCharWidth = outBuff - buf; } } return m_minMBCharWidth; } +#if wxUSE_UNICODE_UTF8 +bool wxMBConv_iconv::IsUTF8() const +{ + return wxStricmp(m_name, "UTF-8") == 0 || + wxStricmp(m_name, "UTF8") == 0; +} +#endif + #endif // HAVE_ICONV @@ -1790,7 +2270,7 @@ size_t wxMBConv_iconv::GetMBNulLen() const // from utils.cpp #if wxUSE_FONTMAP -extern WXDLLIMPEXP_BASE long wxCharsetToCodepage(const wxChar *charset); +extern WXDLLIMPEXP_BASE long wxCharsetToCodepage(const char *charset); extern WXDLLIMPEXP_BASE long wxEncodingToCodepage(wxFontEncoding encoding); #endif @@ -1803,8 +2283,15 @@ public: m_minMBCharWidth = 0; } + wxMBConv_win32(const wxMBConv_win32& conv) + : wxMBConv() + { + m_CodePage = conv.m_CodePage; + m_minMBCharWidth = conv.m_minMBCharWidth; + } + #if wxUSE_FONTMAP - wxMBConv_win32(const wxChar* name) + wxMBConv_win32(const char* name) { m_CodePage = wxCharsetToCodepage(name); m_minMBCharWidth = 0; @@ -1817,7 +2304,7 @@ public: } #endif // wxUSE_FONTMAP - size_t MB2WC(wchar_t *buf, const char *psz, size_t n) const + virtual size_t MB2WC(wchar_t *buf, const char *psz, size_t n) const { // note that we have to use MB_ERR_INVALID_CHARS flag as it without it // the behaviour is not compatible with the Unix version (using iconv) @@ -1825,34 +2312,26 @@ public: // wouldn't work if reading an incomplete MB char didn't result in an // error // - // note however that using MB_ERR_INVALID_CHARS with CP_UTF7 results in - // an error (tested under Windows Server 2003) and apparently it is - // done on purpose, i.e. the function accepts any input in this case - // and although I'd prefer to return error on ill-formed output, our - // 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... - // // 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: + // Win XP or newer and it is not supported for UTF-[78] so we always + // use our own conversions in this case. See // 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() ) + if ( m_CodePage == CP_UTF8 ) { - flags = MB_ERR_INVALID_CHARS; + return wxMBConvUTF8().MB2WC(buf, psz, n); } - else if ( m_CodePage == CP_UTF8 ) + + if ( m_CodePage == CP_UTF7 ) { - // Avoid round-trip in the special case of UTF-8 by using our - // own UTF-8 conversion code: - return wxMBConvUTF8().MB2WC(buf, psz, n); + return wxMBConvUTF7().MB2WC(buf, psz, n); + } + + int flags = 0; + if ( (m_CodePage < 50000 && m_CodePage != CP_SYMBOL) && + IsAtLeastWin2kSP4() ) + { + flags = MB_ERR_INVALID_CHARS; } const size_t len = ::MultiByteToWideChar @@ -1867,7 +2346,7 @@ public: if ( !len ) { // function totally failed - return (size_t)-1; + return wxCONV_FAILED; } // if we were really converting and didn't use MB_ERR_INVALID_CHARS, @@ -1891,7 +2370,7 @@ public: { // 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; + return wxCONV_FAILED; } } @@ -1901,7 +2380,7 @@ public: return len - 1; } - size_t WC2MB(char *buf, const wchar_t *pwz, size_t n) const + virtual size_t WC2MB(char *buf, const wchar_t *pwz, size_t n) const { /* we have a problem here: by default, WideCharToMultiByte() may @@ -1952,29 +2431,41 @@ public: if ( !len ) { // function totally failed - return (size_t)-1; + return wxCONV_FAILED; } - // if we were really converting, check if we succeeded - if ( buf ) + // we did something, check if we really succeeded + if ( flags ) + { + // check if the conversion failed, i.e. if any replacements + // were done + if ( usedDef ) + return wxCONV_FAILED; + } + else // we must resort to double tripping... { - if ( flags ) + // first we need to ensure that we really have the MB data: this is + // not the case if we're called with NULL buffer, in which case we + // need to do the conversion yet again + wxCharBuffer bufDef; + if ( !buf ) { - // check if the conversion failed, i.e. if any replacements - // were done - if ( usedDef ) - return (size_t)-1; + bufDef = wxCharBuffer(len); + buf = bufDef.data(); + if ( !::WideCharToMultiByte(m_CodePage, flags, pwz, -1, + buf, len, NULL, NULL) ) + return wxCONV_FAILED; } - else // we must resort to double tripping... + + if ( !n ) + n = wcslen(pwz); + wxWCharBuffer wcBuf(n); + if ( MB2WC(wcBuf.data(), buf, n + 1) == wxCONV_FAILED || + wcscmp(wcBuf, pwz) != 0 ) { - 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; - } + // we didn't obtain the same thing we started from, hence + // the conversion was lossy and we consider that it failed + return wxCONV_FAILED; } } @@ -2003,7 +2494,8 @@ public: { default: wxLogDebug(_T("Unexpected NUL length %d"), len); - // fall through + self->m_minMBCharWidth = (size_t)-1; + break; case 0: self->m_minMBCharWidth = (size_t)-1; @@ -2020,6 +2512,8 @@ public: return m_minMBCharWidth; } + virtual wxMBConv *Clone() const { return new wxMBConv_win32(*this); } + bool IsOk() const { return m_CodePage != -1; } private: @@ -2032,17 +2526,18 @@ private: int verMaj, verMin; switch ( wxGetOsVersion(&verMaj, &verMin) ) { - case wxWIN95: + case wxOS_WINDOWS_9X: s_isWin98Or2k = verMaj >= 4 && verMin >= 10; break; - case wxWINDOWS_NT: + case wxOS_WINDOWS_NT: s_isWin98Or2k = verMaj >= 5; break; default: - // unknown, be conseravtive by default + // unknown: be conservative by default s_isWin98Or2k = 0; + break; } wxASSERT_MSG( s_isWin98Or2k != -1, _T("should be set above") ); @@ -2089,576 +2584,6 @@ private: #endif // wxHAVE_WIN32_MB2WC -// ============================================================================ -// Cocoa conversion classes -// ============================================================================ - -#if defined(__WXCOCOA__) - -// RN: There is no UTF-32 support in either Core Foundation or -// Cocoa. Strangely enough, internally Core Foundation uses -// UTF 32 internally quite a bit - its just not public (yet). - -#include -#include - -CFStringEncoding wxCFStringEncFromFontEnc(wxFontEncoding encoding) -{ - CFStringEncoding enc = kCFStringEncodingInvalidId ; - if ( encoding == wxFONTENCODING_DEFAULT ) - { - enc = CFStringGetSystemEncoding(); - } - else switch( encoding) - { - case wxFONTENCODING_ISO8859_1 : - enc = kCFStringEncodingISOLatin1 ; - break ; - case wxFONTENCODING_ISO8859_2 : - enc = kCFStringEncodingISOLatin2; - break ; - case wxFONTENCODING_ISO8859_3 : - enc = kCFStringEncodingISOLatin3 ; - break ; - case wxFONTENCODING_ISO8859_4 : - enc = kCFStringEncodingISOLatin4; - break ; - case wxFONTENCODING_ISO8859_5 : - enc = kCFStringEncodingISOLatinCyrillic; - break ; - case wxFONTENCODING_ISO8859_6 : - enc = kCFStringEncodingISOLatinArabic; - break ; - case wxFONTENCODING_ISO8859_7 : - enc = kCFStringEncodingISOLatinGreek; - break ; - case wxFONTENCODING_ISO8859_8 : - enc = kCFStringEncodingISOLatinHebrew; - break ; - case wxFONTENCODING_ISO8859_9 : - enc = kCFStringEncodingISOLatin5; - break ; - case wxFONTENCODING_ISO8859_10 : - enc = kCFStringEncodingISOLatin6; - break ; - case wxFONTENCODING_ISO8859_11 : - enc = kCFStringEncodingISOLatinThai; - break ; - case wxFONTENCODING_ISO8859_13 : - enc = kCFStringEncodingISOLatin7; - break ; - case wxFONTENCODING_ISO8859_14 : - enc = kCFStringEncodingISOLatin8; - break ; - case wxFONTENCODING_ISO8859_15 : - enc = kCFStringEncodingISOLatin9; - break ; - - case wxFONTENCODING_KOI8 : - enc = kCFStringEncodingKOI8_R; - break ; - case wxFONTENCODING_ALTERNATIVE : // MS-DOS CP866 - enc = kCFStringEncodingDOSRussian; - break ; - -// case wxFONTENCODING_BULGARIAN : -// enc = ; -// break ; - - case wxFONTENCODING_CP437 : - enc =kCFStringEncodingDOSLatinUS ; - break ; - case wxFONTENCODING_CP850 : - enc = kCFStringEncodingDOSLatin1; - break ; - case wxFONTENCODING_CP852 : - enc = kCFStringEncodingDOSLatin2; - break ; - case wxFONTENCODING_CP855 : - enc = kCFStringEncodingDOSCyrillic; - break ; - case wxFONTENCODING_CP866 : - enc =kCFStringEncodingDOSRussian ; - break ; - case wxFONTENCODING_CP874 : - enc = kCFStringEncodingDOSThai; - break ; - case wxFONTENCODING_CP932 : - enc = kCFStringEncodingDOSJapanese; - break ; - case wxFONTENCODING_CP936 : - enc =kCFStringEncodingDOSChineseSimplif ; - break ; - case wxFONTENCODING_CP949 : - enc = kCFStringEncodingDOSKorean; - break ; - case wxFONTENCODING_CP950 : - enc = kCFStringEncodingDOSChineseTrad; - break ; - case wxFONTENCODING_CP1250 : - enc = kCFStringEncodingWindowsLatin2; - break ; - case wxFONTENCODING_CP1251 : - enc =kCFStringEncodingWindowsCyrillic ; - break ; - case wxFONTENCODING_CP1252 : - enc =kCFStringEncodingWindowsLatin1 ; - break ; - case wxFONTENCODING_CP1253 : - enc = kCFStringEncodingWindowsGreek; - break ; - case wxFONTENCODING_CP1254 : - enc = kCFStringEncodingWindowsLatin5; - break ; - case wxFONTENCODING_CP1255 : - enc =kCFStringEncodingWindowsHebrew ; - break ; - case wxFONTENCODING_CP1256 : - enc =kCFStringEncodingWindowsArabic ; - break ; - case wxFONTENCODING_CP1257 : - enc = kCFStringEncodingWindowsBalticRim; - break ; -// This only really encodes to UTF7 (if that) evidently -// case wxFONTENCODING_UTF7 : -// enc = kCFStringEncodingNonLossyASCII ; -// break ; - case wxFONTENCODING_UTF8 : - enc = kCFStringEncodingUTF8 ; - break ; - case wxFONTENCODING_EUC_JP : - enc = kCFStringEncodingEUC_JP; - break ; - case wxFONTENCODING_UTF16 : - enc = kCFStringEncodingUnicode ; - break ; - case wxFONTENCODING_MACROMAN : - enc = kCFStringEncodingMacRoman ; - break ; - case wxFONTENCODING_MACJAPANESE : - enc = kCFStringEncodingMacJapanese ; - break ; - case wxFONTENCODING_MACCHINESETRAD : - enc = kCFStringEncodingMacChineseTrad ; - break ; - case wxFONTENCODING_MACKOREAN : - enc = kCFStringEncodingMacKorean ; - break ; - case wxFONTENCODING_MACARABIC : - enc = kCFStringEncodingMacArabic ; - break ; - case wxFONTENCODING_MACHEBREW : - enc = kCFStringEncodingMacHebrew ; - break ; - case wxFONTENCODING_MACGREEK : - enc = kCFStringEncodingMacGreek ; - break ; - case wxFONTENCODING_MACCYRILLIC : - enc = kCFStringEncodingMacCyrillic ; - break ; - case wxFONTENCODING_MACDEVANAGARI : - enc = kCFStringEncodingMacDevanagari ; - break ; - case wxFONTENCODING_MACGURMUKHI : - enc = kCFStringEncodingMacGurmukhi ; - break ; - case wxFONTENCODING_MACGUJARATI : - enc = kCFStringEncodingMacGujarati ; - break ; - case wxFONTENCODING_MACORIYA : - enc = kCFStringEncodingMacOriya ; - break ; - case wxFONTENCODING_MACBENGALI : - enc = kCFStringEncodingMacBengali ; - break ; - case wxFONTENCODING_MACTAMIL : - enc = kCFStringEncodingMacTamil ; - break ; - case wxFONTENCODING_MACTELUGU : - enc = kCFStringEncodingMacTelugu ; - break ; - case wxFONTENCODING_MACKANNADA : - enc = kCFStringEncodingMacKannada ; - break ; - case wxFONTENCODING_MACMALAJALAM : - enc = kCFStringEncodingMacMalayalam ; - break ; - case wxFONTENCODING_MACSINHALESE : - enc = kCFStringEncodingMacSinhalese ; - break ; - case wxFONTENCODING_MACBURMESE : - enc = kCFStringEncodingMacBurmese ; - break ; - case wxFONTENCODING_MACKHMER : - enc = kCFStringEncodingMacKhmer ; - break ; - case wxFONTENCODING_MACTHAI : - enc = kCFStringEncodingMacThai ; - break ; - case wxFONTENCODING_MACLAOTIAN : - enc = kCFStringEncodingMacLaotian ; - break ; - case wxFONTENCODING_MACGEORGIAN : - enc = kCFStringEncodingMacGeorgian ; - break ; - case wxFONTENCODING_MACARMENIAN : - enc = kCFStringEncodingMacArmenian ; - break ; - case wxFONTENCODING_MACCHINESESIMP : - enc = kCFStringEncodingMacChineseSimp ; - break ; - case wxFONTENCODING_MACTIBETAN : - enc = kCFStringEncodingMacTibetan ; - break ; - case wxFONTENCODING_MACMONGOLIAN : - enc = kCFStringEncodingMacMongolian ; - break ; - case wxFONTENCODING_MACETHIOPIC : - enc = kCFStringEncodingMacEthiopic ; - break ; - case wxFONTENCODING_MACCENTRALEUR : - enc = kCFStringEncodingMacCentralEurRoman ; - break ; - case wxFONTENCODING_MACVIATNAMESE : - enc = kCFStringEncodingMacVietnamese ; - break ; - case wxFONTENCODING_MACARABICEXT : - enc = kCFStringEncodingMacExtArabic ; - break ; - case wxFONTENCODING_MACSYMBOL : - enc = kCFStringEncodingMacSymbol ; - break ; - case wxFONTENCODING_MACDINGBATS : - enc = kCFStringEncodingMacDingbats ; - break ; - case wxFONTENCODING_MACTURKISH : - enc = kCFStringEncodingMacTurkish ; - break ; - case wxFONTENCODING_MACCROATIAN : - enc = kCFStringEncodingMacCroatian ; - break ; - case wxFONTENCODING_MACICELANDIC : - enc = kCFStringEncodingMacIcelandic ; - break ; - case wxFONTENCODING_MACROMANIAN : - enc = kCFStringEncodingMacRomanian ; - break ; - case wxFONTENCODING_MACCELTIC : - enc = kCFStringEncodingMacCeltic ; - break ; - case wxFONTENCODING_MACGAELIC : - enc = kCFStringEncodingMacGaelic ; - break ; -// case wxFONTENCODING_MACKEYBOARD : -// enc = kCFStringEncodingMacKeyboardGlyphs ; -// break ; - default : - // because gcc is picky - break ; - } ; - return enc ; -} - -class wxMBConv_cocoa : public wxMBConv -{ -public: - wxMBConv_cocoa() - { - Init(CFStringGetSystemEncoding()) ; - } - -#if wxUSE_FONTMAP - wxMBConv_cocoa(const wxChar* name) - { - Init( wxCFStringEncFromFontEnc(wxFontMapperBase::Get()->CharsetToEncoding(name, false) ) ) ; - } -#endif - - wxMBConv_cocoa(wxFontEncoding encoding) - { - Init( wxCFStringEncFromFontEnc(encoding) ); - } - - ~wxMBConv_cocoa() - { - } - - void Init( CFStringEncoding encoding) - { - m_encoding = encoding ; - } - - size_t MB2WC(wchar_t * szOut, const char * szUnConv, size_t nOutSize) const - { - wxASSERT(szUnConv); - - CFStringRef theString = CFStringCreateWithBytes ( - NULL, //the allocator - (const UInt8*)szUnConv, - strlen(szUnConv), - m_encoding, - false //no BOM/external representation - ); - - wxASSERT(theString); - - size_t nOutLength = CFStringGetLength(theString); - - if (szOut == NULL) - { - CFRelease(theString); - return nOutLength; - } - - CFRange theRange = { 0, nOutSize }; - -#if SIZEOF_WCHAR_T == 4 - UniChar* szUniCharBuffer = new UniChar[nOutSize]; -#endif - - CFStringGetCharacters(theString, theRange, szUniCharBuffer); - - CFRelease(theString); - - szUniCharBuffer[nOutLength] = '\0' ; - -#if SIZEOF_WCHAR_T == 4 - wxMBConvUTF16 converter ; - converter.MB2WC(szOut, (const char*)szUniCharBuffer , nOutSize ) ; - delete[] szUniCharBuffer; -#endif - - return nOutLength; - } - - size_t WC2MB(char *szOut, const wchar_t *szUnConv, size_t nOutSize) const - { - wxASSERT(szUnConv); - - size_t nRealOutSize; - size_t nBufSize = wxWcslen(szUnConv); - UniChar* szUniBuffer = (UniChar*) szUnConv; - -#if SIZEOF_WCHAR_T == 4 - wxMBConvUTF16 converter ; - nBufSize = converter.WC2MB( NULL , szUnConv , 0 ); - szUniBuffer = new UniChar[ (nBufSize / sizeof(UniChar)) + 1] ; - converter.WC2MB( (char*) szUniBuffer , szUnConv, nBufSize + sizeof(UniChar)) ; - nBufSize /= sizeof(UniChar); -#endif - - CFStringRef theString = CFStringCreateWithCharactersNoCopy( - NULL, //allocator - szUniBuffer, - nBufSize, - kCFAllocatorNull //deallocator - we want to deallocate it ourselves - ); - - wxASSERT(theString); - - //Note that CER puts a BOM when converting to unicode - //so we check and use getchars instead in that case - if (m_encoding == kCFStringEncodingUnicode) - { - if (szOut != NULL) - CFStringGetCharacters(theString, CFRangeMake(0, nOutSize - 1), (UniChar*) szOut); - - nRealOutSize = CFStringGetLength(theString) + 1; - } - else - { - CFStringGetBytes( - theString, - CFRangeMake(0, CFStringGetLength(theString)), - m_encoding, - 0, //what to put in characters that can't be converted - - //0 tells CFString to return NULL if it meets such a character - false, //not an external representation - (UInt8*) szOut, - nOutSize, - (CFIndex*) &nRealOutSize - ); - } - - CFRelease(theString); - -#if SIZEOF_WCHAR_T == 4 - delete[] szUniBuffer; -#endif - - return nRealOutSize - 1; - } - - bool IsOk() const - { - return m_encoding != kCFStringEncodingInvalidId && - CFStringIsEncodingAvailable(m_encoding); - } - -private: - CFStringEncoding m_encoding ; -}; - -#endif // defined(__WXCOCOA__) - -// ============================================================================ -// Mac conversion classes -// ============================================================================ - -#if defined(__WXMAC__) && defined(TARGET_CARBON) - -class wxMBConv_mac : public wxMBConv -{ -public: - wxMBConv_mac() - { - Init(CFStringGetSystemEncoding()) ; - } - -#if wxUSE_FONTMAP - wxMBConv_mac(const wxChar* name) - { - Init( wxMacGetSystemEncFromFontEnc(wxFontMapperBase::Get()->CharsetToEncoding(name, false) ) ) ; - } -#endif - - 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 ; - m_unicode_encoding = CreateTextEncoding(kTextEncodingUnicodeDefault,0,kUnicode16BitFormat) ; - - 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 ; - UniChar* ubuf = NULL ; - size_t res = 0 ; - - if (buf == NULL) - { - //apple specs say at least 32 - n = wxMax( 32 , byteInLen ) ; - tbuf = (wchar_t*) malloc( n * SIZEOF_WCHAR_T) ; - } - ByteCount byteBufferLen = n * sizeof( UniChar ) ; -#if SIZEOF_WCHAR_T == 4 - ubuf = (UniChar*) malloc( byteBufferLen + 2 ) ; -#else - ubuf = (UniChar*) (buf ? buf : tbuf) ; -#endif - status = TECConvertText(m_MB2WC_converter, (ConstTextPtr) psz , byteInLen, &byteInLen, - (TextPtr) ubuf , byteBufferLen, &byteOutLen); -#if SIZEOF_WCHAR_T == 4 - // we have to terminate here, because n might be larger for the trailing zero, and if UniChar - // is not properly terminated we get random characters at the end - ubuf[byteOutLen / sizeof( UniChar ) ] = 0 ; - wxMBConvUTF16 converter ; - res = converter.MB2WC( (buf ? buf : tbuf) , (const char*)ubuf , n ) ; - free( ubuf ) ; -#else - res = byteOutLen / sizeof( UniChar ) ; -#endif - if ( buf == NULL ) - free(tbuf) ; - - 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) - { - //apple specs say at least 32 - n = wxMax( 32 , ((byteInLen / SIZEOF_WCHAR_T) * 8) + SIZEOF_WCHAR_T ); - tbuf = (char*) malloc( n ) ; - } - - ByteCount byteBufferLen = n ; - UniChar* ubuf = NULL ; -#if SIZEOF_WCHAR_T == 4 - wxMBConvUTF16 converter ; - size_t unicharlen = converter.WC2MB( NULL , psz , 0 ) ; - byteInLen = unicharlen ; - ubuf = (UniChar*) malloc( byteInLen + 2 ) ; - converter.WC2MB( (char*) ubuf , psz, unicharlen + 2 ) ; -#else - ubuf = (UniChar*) psz ; -#endif - status = TECConvertText(m_WC2MB_converter, (ConstTextPtr) ubuf , byteInLen, &byteInLen, - (TextPtr) (buf ? buf : tbuf) , byteBufferLen, &byteOutLen); -#if SIZEOF_WCHAR_T == 4 - free( ubuf ) ; -#endif - if ( buf == NULL ) - free(tbuf) ; - - size_t res = byteOutLen ; - if ( buf && res < n) - { - buf[res] = 0; - - //we need to double-trip to verify it didn't insert any ? in place - //of bogus characters - wxWCharBuffer wcBuf(n); - size_t pszlen = wxWcslen(psz); - if ( MB2WC(wcBuf.data(), buf, n) == (size_t)-1 || - wxWcslen(wcBuf) != pszlen || - memcmp(wcBuf, psz, pszlen * sizeof(wchar_t)) != 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; - } - } - - 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 @@ -2671,14 +2596,17 @@ class wxMBConv_wxwin : public wxMBConv private: void Init() { - m_ok = m2w.Init(m_enc, wxFONTENCODING_UNICODE) && + // Refuse to use broken wxEncodingConverter code for Mac-specific encodings. + // The wxMBConv_cf class does a better job. + m_ok = (m_enc < wxFONTENCODING_MACMIN || m_enc > wxFONTENCODING_MACMAX) && + m2w.Init(m_enc, wxFONTENCODING_UNICODE) && w2m.Init(wxFONTENCODING_UNICODE, m_enc); } public: // temporarily just use wxEncodingConverter stuff, // so that it works while a better implementation is built - wxMBConv_wxwin(const wxChar* name) + wxMBConv_wxwin(const char* name) { if (name) m_enc = wxFontMapperBase::Get()->CharsetToEncoding(name, false); @@ -2700,8 +2628,8 @@ public: size_t inbuf = strlen(psz); if (buf) { - if (!m2w.Convert(psz,buf)) - return (size_t)-1; + if (!m2w.Convert(psz, buf)) + return wxCONV_FAILED; } return inbuf; } @@ -2711,8 +2639,8 @@ public: const size_t inbuf = wxWcslen(psz); if (buf) { - if (!w2m.Convert(psz,buf)) - return (size_t)-1; + if (!w2m.Convert(psz, buf)) + return wxCONV_FAILED; } return inbuf; @@ -2735,6 +2663,8 @@ public: } } + virtual wxMBConv *Clone() const { return new wxMBConv_wxwin(m_enc); } + bool IsOk() const { return m_ok; } public: @@ -2749,7 +2679,7 @@ private: }; // make the constructors available for unit testing -WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_wxwin( const wxChar* name ) +WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_wxwin( const char* name ) { wxMBConv_wxwin* result = new wxMBConv_wxwin( name ); if ( !result->IsOk() ) @@ -2757,6 +2687,7 @@ WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_wxwin( const wxChar* name ) delete result; return 0; } + return result; } @@ -2773,13 +2704,13 @@ void wxCSConv::Init() m_deferred = true; } -wxCSConv::wxCSConv(const wxChar *charset) +wxCSConv::wxCSConv(const wxString& charset) { Init(); - if ( charset ) + if ( !charset.empty() ) { - SetName(charset); + SetName(charset.ToAscii()); } #if wxUSE_FONTMAP @@ -2836,7 +2767,7 @@ void wxCSConv::Clear() m_convReal = NULL; } -void wxCSConv::SetName(const wxChar *charset) +void wxCSConv::SetName(const char *charset) { if (charset) { @@ -2846,7 +2777,6 @@ void wxCSConv::SetName(const wxChar *charset) } #if wxUSE_FONTMAP -#include "wx/hashmap.h" WX_DECLARE_HASH_MAP( wxFontEncoding, wxString, wxIntegerHash, wxIntegerEqual, wxEncodingNameCache ); @@ -2860,7 +2790,7 @@ wxMBConv *wxCSConv::DoCreate() const wxLogTrace(TRACE_STRCONV, wxT("creating conversion for %s"), (m_name ? m_name - : wxFontMapperBase::GetEncodingName(m_encoding).c_str())); + : (const char*)wxFontMapperBase::GetEncodingName(m_encoding).mb_str())); #endif // wxUSE_FONTMAP // check for the special case of ASCII or ISO8859-1 charset: as we have @@ -2887,12 +2817,13 @@ wxMBConv *wxCSConv::DoCreate() const if ( m_name ) #endif // !wxUSE_FONTMAP { - wxString name(m_name); +#if wxUSE_FONTMAP wxFontEncoding encoding(m_encoding); +#endif - if ( !name.empty() ) + if ( m_name ) { - wxMBConv_iconv *conv = new wxMBConv_iconv(name); + wxMBConv_iconv *conv = new wxMBConv_iconv(m_name); if ( conv->IsOk() ) return conv; @@ -2900,7 +2831,7 @@ wxMBConv *wxCSConv::DoCreate() const #if wxUSE_FONTMAP encoding = - wxFontMapperBase::Get()->CharsetToEncoding(name, false); + wxFontMapperBase::Get()->CharsetToEncoding(m_name, false); #endif // wxUSE_FONTMAP } #if wxUSE_FONTMAP @@ -2911,7 +2842,7 @@ wxMBConv *wxCSConv::DoCreate() const if ( it->second.empty() ) return NULL; - wxMBConv_iconv *conv = new wxMBConv_iconv(it->second); + wxMBConv_iconv *conv = new wxMBConv_iconv(it->second.ToAscii()); if ( conv->IsOk() ) return conv; @@ -2919,20 +2850,30 @@ wxMBConv *wxCSConv::DoCreate() const } const wxChar** names = wxFontMapperBase::GetAllEncodingNames(encoding); - - for ( ; *names; ++names ) + // CS : in case this does not return valid names (eg for MacRoman) + // encoding got a 'failure' entry in the cache all the same, + // although it just has to be created using a different method, so + // only store failed iconv creation attempts (or perhaps we + // shoulnd't do this at all ?) + if ( names[0] != NULL ) { - wxMBConv_iconv *conv = new wxMBConv_iconv(*names); - if ( conv->IsOk() ) + for ( ; *names; ++names ) { - gs_nameCache[encoding] = *names; - return conv; + // FIXME-UTF8: wxFontMapperBase::GetAllEncodingNames() + // will need changes that will obsolete this + wxString name(*names); + wxMBConv_iconv *conv = new wxMBConv_iconv(name.ToAscii()); + if ( conv->IsOk() ) + { + gs_nameCache[encoding] = *names; + return conv; + } + + delete conv; } - delete conv; + gs_nameCache[encoding] = _T(""); // cache the failure } - - gs_nameCache[encoding] = _T(""); // cache the failure } #endif // wxUSE_FONTMAP } @@ -2952,44 +2893,28 @@ wxMBConv *wxCSConv::DoCreate() const #endif } #endif // wxHAVE_WIN32_MB2WC -#if defined(__WXMAC__) + +#ifdef __DARWIN__ { // leave UTF16 and UTF32 to the built-ins of wx if ( m_name || ( m_encoding < wxFONTENCODING_UTF16BE || ( m_encoding >= wxFONTENCODING_MACMIN && m_encoding <= wxFONTENCODING_MACMAX ) ) ) { - #if wxUSE_FONTMAP - wxMBConv_mac *conv = m_name ? new wxMBConv_mac(m_name) - : new wxMBConv_mac(m_encoding); + wxMBConv_cf *conv = m_name ? new wxMBConv_cf(m_name) + : new wxMBConv_cf(m_encoding); #else - wxMBConv_mac *conv = new wxMBConv_mac(m_encoding); + wxMBConv_cf *conv = new wxMBConv_cf(m_encoding); #endif - if ( conv->IsOk() ) - return conv; - delete conv; - } - } -#endif -#if defined(__WXCOCOA__) - { - if ( m_name || ( m_encoding <= wxFONTENCODING_UTF16 ) ) - { - -#if wxUSE_FONTMAP - wxMBConv_cocoa *conv = m_name ? new wxMBConv_cocoa(m_name) - : new wxMBConv_cocoa(m_encoding); -#else - wxMBConv_cocoa *conv = new wxMBConv_cocoa(m_encoding); -#endif if ( conv->IsOk() ) return conv; delete conv; } } -#endif +#endif // __DARWIN__ + // step (2) wxFontEncoding enc = m_encoding; #if wxUSE_FONTMAP @@ -3024,7 +2949,7 @@ wxMBConv *wxCSConv::DoCreate() const default: // nothing to do but put here to suppress gcc warnings - ; + break; } // step (3) @@ -3042,10 +2967,10 @@ wxMBConv *wxCSConv::DoCreate() const // 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. + // attach a timestamp, for which it will need wxConvLocal (to convert + // time to char* and then wchar_t*), but that fails, tries to log the + // error, but wxLog has an (already locked) critical section that + // guards the static buffer. static bool alreadyLoggingError = false; if (!alreadyLoggingError) { @@ -3054,11 +2979,12 @@ wxMBConv *wxCSConv::DoCreate() const m_name ? m_name : #if wxUSE_FONTMAP - wxFontMapperBase::GetEncodingDescription(m_encoding).c_str() + (const char*)wxFontMapperBase::GetEncodingDescription(m_encoding).ToAscii() #else // !wxUSE_FONTMAP - wxString::Format(_("encoding %s"), m_encoding).c_str() + (const char*)wxString::Format(_("encoding %i"), m_encoding).ToAscii() #endif // wxUSE_FONTMAP/!wxUSE_FONTMAP ); + alreadyLoggingError = false; } @@ -3071,20 +2997,60 @@ void wxCSConv::CreateConvIfNeeded() const { 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()); - } +#if wxUSE_INTL + self->m_encoding = wxLocale::GetSystemEncoding(); +#else + // fallback to some reasonable default: + self->m_encoding = wxFONTENCODING_ISO8859_1; #endif // wxUSE_INTL + } self->m_convReal = DoCreate(); self->m_deferred = false; } } +bool wxCSConv::IsOk() const +{ + CreateConvIfNeeded(); + + // special case: no convReal created for wxFONTENCODING_ISO8859_1 + if ( m_encoding == wxFONTENCODING_ISO8859_1 ) + return true; // always ok as we do it ourselves + + // m_convReal->IsOk() is called at its own creation, so we know it must + // be ok if m_convReal is non-NULL + return m_convReal != NULL; +} + +size_t wxCSConv::ToWChar(wchar_t *dst, size_t dstLen, + const char *src, size_t srcLen) const +{ + CreateConvIfNeeded(); + + if (m_convReal) + return m_convReal->ToWChar(dst, dstLen, src, srcLen); + + // latin-1 (direct) + return wxMBConv::ToWChar(dst, dstLen, src, srcLen); +} + +size_t wxCSConv::FromWChar(char *dst, size_t dstLen, + const wchar_t *src, size_t srcLen) const +{ + CreateConvIfNeeded(); + + if (m_convReal) + return m_convReal->FromWChar(dst, dstLen, src, srcLen); + + // latin-1 (direct) + return wxMBConv::FromWChar(dst, dstLen, src, srcLen); +} + size_t wxCSConv::MB2WC(wchar_t *buf, const char *psz, size_t n) const { CreateConvIfNeeded(); @@ -3118,7 +3084,8 @@ size_t wxCSConv::WC2MB(char *buf, const wchar_t *psz, size_t n) const for (size_t c = 0; c <= len; c++) { if (psz[c] > 0xFF) - return (size_t)-1; + return wxCONV_FAILED; + buf[c] = (char)psz[c]; } } @@ -3127,7 +3094,7 @@ size_t wxCSConv::WC2MB(char *buf, const wchar_t *psz, size_t n) const for (size_t c = 0; c <= len; c++) { if (psz[c] > 0xFF) - return (size_t)-1; + return wxCONV_FAILED; } } @@ -3143,42 +3110,125 @@ size_t wxCSConv::GetMBNulLen() const return m_convReal->GetMBNulLen(); } + // otherwise, we are ISO-8859-1 return 1; } +#if wxUSE_UNICODE_UTF8 +bool wxCSConv::IsUTF8() const +{ + CreateConvIfNeeded(); + + if ( m_convReal ) + { + return m_convReal->IsUTF8(); + } + + // otherwise, we are ISO-8859-1 + return false; +} +#endif + + +#if wxUSE_UNICODE + +wxWCharBuffer wxSafeConvertMB2WX(const char *s) +{ + if ( !s ) + return wxWCharBuffer(); + + wxWCharBuffer wbuf(wxConvLibc.cMB2WX(s)); + if ( !wbuf ) + wbuf = wxMBConvUTF8().cMB2WX(s); + if ( !wbuf ) + wbuf = wxConvISO8859_1.cMB2WX(s); + + return wbuf; +} + +wxCharBuffer wxSafeConvertWX2MB(const wchar_t *ws) +{ + if ( !ws ) + return wxCharBuffer(); + + wxCharBuffer buf(wxConvLibc.cWX2MB(ws)); + if ( !buf ) + buf = wxMBConvUTF8(wxMBConvUTF8::MAP_INVALID_UTF8_TO_OCTAL).cWX2MB(ws); + + return buf; +} + +#endif // wxUSE_UNICODE + // ---------------------------------------------------------------------------- // globals // ---------------------------------------------------------------------------- +// NB: The reason why we create converted objects in this convoluted way, +// using a factory function instead of global variable, is that they +// may be used at static initialization time (some of them are used by +// wxString ctors and there may be a global wxString object). In other +// words, possibly _before_ the converter global object would be +// initialized. + +#undef wxConvLibc +#undef wxConvUTF8 +#undef wxConvUTF7 +#undef wxConvLocal +#undef wxConvISO8859_1 + +#define WX_DEFINE_GLOBAL_CONV2(klass, impl_klass, name, ctor_args) \ + WXDLLIMPEXP_DATA_BASE(klass*) name##Ptr = NULL; \ + WXDLLIMPEXP_BASE klass* wxGet_##name##Ptr() \ + { \ + static impl_klass name##Obj ctor_args; \ + return &name##Obj; \ + } \ + /* this ensures that all global converter objects are created */ \ + /* by the time static initialization is done, i.e. before any */ \ + /* thread is launched: */ \ + static klass* gs_##name##instance = wxGet_##name##Ptr() + +#define WX_DEFINE_GLOBAL_CONV(klass, name, ctor_args) \ + WX_DEFINE_GLOBAL_CONV2(klass, klass, name, ctor_args) + #ifdef __WINDOWS__ - static wxMBConv_win32 wxConvLibcObj; -#elif defined(__WXMAC__) && !defined(__MACH__) - static wxMBConv_mac wxConvLibcObj ; + WX_DEFINE_GLOBAL_CONV2(wxMBConv, wxMBConv_win32, wxConvLibc, wxEMPTY_PARAMETER_VALUE); #else - static wxMBConvLibc wxConvLibcObj; + WX_DEFINE_GLOBAL_CONV2(wxMBConv, wxMBConvLibc, wxConvLibc, wxEMPTY_PARAMETER_VALUE); #endif -static wxCSConv wxConvLocalObj(wxFONTENCODING_SYSTEM); -static wxCSConv wxConvISO8859_1Obj(wxFONTENCODING_ISO8859_1); -static wxMBConvUTF7 wxConvUTF7Obj; -static wxMBConvUTF8 wxConvUTF8Obj; - -WXDLLIMPEXP_DATA_BASE(wxMBConv&) wxConvLibc = wxConvLibcObj; -WXDLLIMPEXP_DATA_BASE(wxCSConv&) wxConvLocal = wxConvLocalObj; -WXDLLIMPEXP_DATA_BASE(wxCSConv&) wxConvISO8859_1 = wxConvISO8859_1Obj; -WXDLLIMPEXP_DATA_BASE(wxMBConvUTF7&) wxConvUTF7 = wxConvUTF7Obj; -WXDLLIMPEXP_DATA_BASE(wxMBConvUTF8&) wxConvUTF8 = wxConvUTF8Obj; -WXDLLIMPEXP_DATA_BASE(wxMBConv *) wxConvCurrent = &wxConvLibcObj; -WXDLLIMPEXP_DATA_BASE(wxMBConv *) wxConvFileName = & -#ifdef __WXOSX__ - wxConvUTF8Obj; -#else - wxConvLibcObj; +// NB: we can't use wxEMPTY_PARAMETER_VALUE as final argument here because it's +// passed to WX_DEFINE_GLOBAL_CONV2 after a macro expansion and so still +// provokes an error message about "not enough macro parameters"; and we +// can't use "()" here as the name##Obj declaration would be parsed as a +// function declaration then, so use a semicolon and live with an extra +// empty statement (and hope that no compilers warns about this) +WX_DEFINE_GLOBAL_CONV(wxMBConvStrictUTF8, wxConvUTF8, ;); +WX_DEFINE_GLOBAL_CONV(wxMBConvUTF7, wxConvUTF7, ;); + +WX_DEFINE_GLOBAL_CONV(wxCSConv, wxConvLocal, (wxFONTENCODING_SYSTEM)); +WX_DEFINE_GLOBAL_CONV(wxCSConv, wxConvISO8859_1, (wxFONTENCODING_ISO8859_1)); + +WXDLLIMPEXP_DATA_BASE(wxMBConv *) wxConvCurrent = wxGet_wxConvLibcPtr(); +WXDLLIMPEXP_DATA_BASE(wxMBConv *) wxConvUI = wxGet_wxConvLocalPtr(); + +#ifdef __DARWIN__ +// The xnu kernel always communicates file paths in decomposed UTF-8. +// WARNING: Are we sure that CFString's conversion will cause decomposition? +static wxMBConv_cf wxConvMacUTF8DObj(wxFONTENCODING_UTF8); #endif +WXDLLIMPEXP_DATA_BASE(wxMBConv *) wxConvFileName = +#ifdef __DARWIN__ + &wxConvMacUTF8DObj; +#else // !__DARWIN__ + wxGet_wxConvLibcPtr(); +#endif // __DARWIN__/!__DARWIN__ #else // !wxUSE_WCHAR_T +// FIXME-UTF8: remove this, wxUSE_WCHAR_T is required now // stand-ins in absence of wchar_t WXDLLIMPEXP_DATA_BASE(wxMBConv) wxConvLibc, wxConvISO8859_1,