X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9a83f860948059b0273b5cc6d9e43fadad3ebfca..60d66be369d360e1528e4dd4bb65a909a8c6ac9a:/src/common/strconv.cpp diff --git a/src/common/strconv.cpp b/src/common/strconv.cpp index bf207f880b..73fe64d115 100644 --- a/src/common/strconv.cpp +++ b/src/common/strconv.cpp @@ -28,8 +28,6 @@ #include "wx/strconv.h" -#if wxUSE_WCHAR_T - #ifndef __WXWINCE__ #include #endif @@ -216,7 +214,7 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen, // all the complication come from the fact that this function, for // historical reasons, must behave in 2 subtly different ways when it's // called with a fixed number of characters and when it's called for the - // entire NUL-terminated string: in the former case (srcEnd == NULL) we + // entire NUL-terminated string: in the former case (srcEnd != NULL) we // must count all characters we convert, NUL or not; but in the latter we // do not count the trailing NUL -- but still count all the NULs inside the // string @@ -258,11 +256,13 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen, if ( !srcEnd ) { // we convert just one chunk in this case as this is the entire - // string anyhow + // string anyhow (and we don't count the trailing NUL in this case) break; } - // advance the input pointer past the end of this chunk + // advance the input pointer past the end of this chunk: notice that we + // will always stop before srcEnd because we know that the chunk is + // always properly NUL-terminated while ( NotAllNULs(src, nulLen) ) { // notice that we must skip over multiple bytes here as we suppose @@ -272,23 +272,20 @@ wxMBConv::ToWChar(wchar_t *dst, size_t dstLen, src += nulLen; } - src += nulLen; // skipping over its terminator as well + // if the buffer ends before this NUL, we shouldn't count it in our + // output so skip the code below + if ( src == srcEnd ) + break; + + // do count this terminator as it's inside the buffer we convert + dstWritten++; + if ( dst ) + dst++; + + src += nulLen; // skip the terminator itself - // note that ">=" (and not just "==") is needed here as the terminator - // we skipped just above could be inside or just after the buffer - // delimited by srcEnd if ( src >= srcEnd ) break; - - // if we got here then this wasn't the last chunk in this string and - // hence we must count an extra char for L'\0' even when converting a - // fixed number of characters - if ( srcEnd ) - { - dstWritten++; - if ( dst ) - dst++; - } } return dstWritten; @@ -324,16 +321,22 @@ wxMBConv::FromWChar(char *dst, size_t dstLen, const size_t lenNul = GetMBNulLen(); for ( const wchar_t * const srcEnd = src + srcLen; src < srcEnd; - src += wxWcslen(src) + 1 /* skip L'\0' too */ ) + src++ /* skip L'\0' too */ ) { // try to convert the current chunk size_t lenChunk = WC2MB(NULL, src, 0); - if ( lenChunk == wxCONV_FAILED ) return wxCONV_FAILED; dstWritten += lenChunk; - if ( src+lenChunk < srcEnd || isNulTerminated ) + + const wchar_t * const + chunkEnd = isNulTerminated ? srcEnd - 1 : src + wxWcslen(src); + + // our return value accounts for the trailing NUL(s), unlike that of + // WC2MB(), however don't do it for the last NUL we artificially added + // ourselves above + if ( chunkEnd < srcEnd ) dstWritten += lenNul; if ( dst ) @@ -341,13 +344,42 @@ wxMBConv::FromWChar(char *dst, size_t dstLen, if ( dstWritten > dstLen ) return wxCONV_FAILED; - if ( WC2MB(dst, src, lenChunk + lenNul) == wxCONV_FAILED ) + // if we know that there is enough space in the destination buffer + // (because we accounted for lenNul in dstWritten above), we can + // convert directly in place -- but otherwise we need another + // temporary buffer to ensure that we don't overwrite the output + wxCharBuffer dstBuf; + char *dstTmp; + if ( chunkEnd == srcEnd ) + { + dstBuf = wxCharBuffer(lenChunk + lenNul - 1); + dstTmp = dstBuf.data(); + } + else + { + dstTmp = dst; + } + + if ( WC2MB(dstTmp, src, lenChunk + lenNul) == wxCONV_FAILED ) return wxCONV_FAILED; + if ( dstTmp != dst ) + { + // copy everything up to but excluding the terminating NUL(s) + // into the real output buffer + memcpy(dst, dstTmp, lenChunk); + + // micro-optimization: if dstTmp != dst it means that chunkEnd + // == srcEnd and so we're done, no need to update anything below + break; + } + dst += lenChunk; - if ( src+lenChunk < srcEnd || isNulTerminated ) + if ( chunkEnd < srcEnd ) dst += lenNul; } + + src = chunkEnd; } return dstWritten; @@ -428,7 +460,6 @@ wxMBConv::cMB2WC(const char *inBuff, size_t inLen, size_t *outLen) const // 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); - wbuf.data()[dstLen] = L'\0'; if ( ToWChar(wbuf.data(), dstLen, inBuff, inLen) != wxCONV_FAILED ) { if ( outLen ) @@ -490,6 +521,42 @@ wxMBConv::cWC2MB(const wchar_t *inBuff, size_t inLen, size_t *outLen) const return wxCharBuffer(); } +const wxWCharBuffer wxMBConv::cMB2WC(const wxScopedCharBuffer& buf) const +{ + const size_t srcLen = buf.length(); + if ( srcLen ) + { + const size_t dstLen = ToWChar(NULL, 0, buf, srcLen); + if ( dstLen != wxCONV_FAILED ) + { + wxWCharBuffer wbuf(dstLen); + wbuf.data()[dstLen] = L'\0'; + if ( ToWChar(wbuf.data(), dstLen, buf, srcLen) != wxCONV_FAILED ) + return wbuf; + } + } + + return wxScopedWCharBuffer::CreateNonOwned(L"", 0); +} + +const wxCharBuffer wxMBConv::cWC2MB(const wxScopedWCharBuffer& wbuf) const +{ + const size_t srcLen = wbuf.length(); + if ( srcLen ) + { + const size_t dstLen = FromWChar(NULL, 0, wbuf, srcLen); + if ( dstLen != wxCONV_FAILED ) + { + wxCharBuffer buf(dstLen); + buf.data()[dstLen] = '\0'; + if ( FromWChar(buf.data(), dstLen, wbuf, srcLen) != wxCONV_FAILED ) + return buf; + } + } + + return wxScopedCharBuffer::CreateNonOwned("", 0); +} + // ---------------------------------------------------------------------------- // wxMBConvLibc // ---------------------------------------------------------------------------- @@ -927,7 +994,7 @@ wxMBConvStrictUTF8::ToWChar(wchar_t *dst, size_t dstLen, for ( const char *p = src; ; p++ ) { - if ( !(srcLen == wxNO_LEN ? *p : srcLen) ) + if ( (srcLen == wxNO_LEN ? !*p : !srcLen) ) { // all done successfully, just add the trailing NULL if we are not // using explicit length @@ -1047,7 +1114,7 @@ wxMBConvStrictUTF8::FromWChar(char *dst, size_t dstLen, for ( const wchar_t *wp = src; ; wp++ ) { - if ( !(srcLen == wxNO_LEN ? *wp : srcLen) ) + if ( (srcLen == wxNO_LEN ? !*wp : !srcLen) ) { // all done successfully, just add the trailing NULL if we are not // using explicit length @@ -1077,6 +1144,8 @@ wxMBConvStrictUTF8::FromWChar(char *dst, size_t dstLen, { // skip the next char too as we decoded a surrogate wp++; + if ( srcLen != wxNO_LEN ) + srcLen--; } #else // wchar_t is UTF-32 code = *wp & 0x7fffffff; @@ -1162,7 +1231,10 @@ size_t wxMBConvUTF8::ToWChar(wchar_t *buf, size_t n, size_t len = 0; - while ((srcLen == wxNO_LEN ? *psz : srcLen--) && ((!buf) || (len < n))) + // The length can be either given explicitly or computed implicitly for the + // NUL-terminated strings. + const bool isNulTerminated = srcLen == wxNO_LEN; + while ((isNulTerminated ? *psz : srcLen--) && ((!buf) || (len < n))) { const char *opsz = psz; bool invalid = false; @@ -1296,10 +1368,17 @@ size_t wxMBConvUTF8::ToWChar(wchar_t *buf, size_t n, } } - if (srcLen == wxNO_LEN && buf && (len < n)) - *buf = 0; + if ( isNulTerminated ) + { + // Add the trailing NUL in this case if we have a large enough buffer. + if ( buf && (len < n) ) + *buf = 0; + + // And count it in any case. + len++; + } - return len + 1; + return len; } static inline bool isoctal(wchar_t wch) @@ -1315,7 +1394,10 @@ size_t wxMBConvUTF8::FromWChar(char *buf, size_t n, size_t len = 0; - while ((srcLen == wxNO_LEN ? *psz : srcLen--) && ((!buf) || (len < n))) + // The length can be either given explicitly or computed implicitly for the + // NUL-terminated strings. + const bool isNulTerminated = srcLen == wxNO_LEN; + while ((isNulTerminated ? *psz : srcLen--) && ((!buf) || (len < n))) { wxUint32 cc; @@ -1383,10 +1465,17 @@ size_t wxMBConvUTF8::FromWChar(char *buf, size_t n, } } - if (srcLen == wxNO_LEN && buf && (len < n)) - *buf = 0; + if ( isNulTerminated ) + { + // Add the trailing NUL in this case if we have a large enough buffer. + if ( buf && (len < n) ) + *buf = 0; + + // And count it in any case. + len++; + } - return len + 1; + return len; } // ============================================================================ @@ -1577,7 +1666,7 @@ wxMBConvUTF16straight::FromWChar(char *dst, size_t dstLen, wxUint16 *outBuff = reinterpret_cast(dst); for ( size_t n = 0; n < srcLen; n++ ) { - wxUint16 cc[2]; + wxUint16 cc[2] = { 0 }; const size_t numChars = encode_utf16(*src++, cc); if ( numChars == wxCONV_FAILED ) return wxCONV_FAILED; @@ -1660,7 +1749,7 @@ wxMBConvUTF16swap::FromWChar(char *dst, size_t dstLen, wxUint16 *outBuff = reinterpret_cast(dst); for ( const wchar_t *srcEnd = src + srcLen; src < srcEnd; src++ ) { - wxUint16 cc[2]; + wxUint16 cc[2] = { 0 }; const size_t numChars = encode_utf16(*src, cc); if ( numChars == wxCONV_FAILED ) return wxCONV_FAILED; @@ -1744,7 +1833,7 @@ wxMBConvUTF32straight::ToWChar(wchar_t *dst, size_t dstLen, size_t outLen = 0; for ( size_t n = 0; n < inLen; n++ ) { - wxUint16 cc[2]; + wxUint16 cc[2] = { 0 }; const size_t numChars = encode_utf16(*inBuff++, cc); if ( numChars == wxCONV_FAILED ) return wxCONV_FAILED; @@ -1822,7 +1911,7 @@ wxMBConvUTF32swap::ToWChar(wchar_t *dst, size_t dstLen, size_t outLen = 0; for ( size_t n = 0; n < inLen; n++, inBuff++ ) { - wxUint16 cc[2]; + wxUint16 cc[2] = { 0 }; const size_t numChars = encode_utf16(wxUINT32_SWAP_ALWAYS(*inBuff), cc); if ( numChars == wxCONV_FAILED ) return wxCONV_FAILED; @@ -2045,7 +2134,7 @@ public: virtual wxMBConv *Clone() const { - wxMBConv_iconv *p = new wxMBConv_iconv(m_name.ToAscii()); + wxMBConv_iconv *p = new wxMBConv_iconv(m_name); p->m_minMBCharWidth = m_minMBCharWidth; return p; } @@ -2075,7 +2164,7 @@ private: // name of the encoding handled by this conversion - wxString m_name; + const char *m_name; // cached result of GetMBNulLen(); set to 0 meaning "unknown" // initially @@ -2099,7 +2188,7 @@ wxString wxMBConv_iconv::ms_wcCharsetName; bool wxMBConv_iconv::ms_wcNeedsSwap = false; wxMBConv_iconv::wxMBConv_iconv(const char *name) - : m_name(name) + : m_name(wxStrdup(name)) { m_minMBCharWidth = 0; @@ -2109,18 +2198,18 @@ wxMBConv_iconv::wxMBConv_iconv(const char *name) wxLogTrace(TRACE_STRCONV, wxT("Looking for wide char codeset:")); #if wxUSE_FONTMAP - const wxChar **names = wxFontMapperBase::GetAllEncodingNames(WC_ENC); + const wxChar *const *names = wxFontMapperBase::GetAllEncodingNames(WC_ENC); #else // !wxUSE_FONTMAP - static const wxChar *names_static[] = + static const wxChar *const names_static[] = { #if SIZEOF_WCHAR_T == 4 wxT("UCS-4"), -#elif SIZEOF_WCHAR_T = 2 +#elif SIZEOF_WCHAR_T == 2 wxT("UCS-2"), #endif NULL }; - const wxChar **names = names_static; + const wxChar *const *names = names_static; #endif // wxUSE_FONTMAP/!wxUSE_FONTMAP for ( ; *names && ms_wcCharsetName.empty(); ++names ) @@ -2216,6 +2305,8 @@ wxMBConv_iconv::wxMBConv_iconv(const char *name) wxMBConv_iconv::~wxMBConv_iconv() { + free(const_cast(m_name)); + if ( m2w != ICONV_T_INVALID ) iconv_close(m2w); if ( w2m != ICONV_T_INVALID ) @@ -2875,7 +2966,41 @@ void wxCSConv::Init() { m_name = NULL; m_convReal = NULL; - m_deferred = true; +} + +void wxCSConv::SetEncoding(wxFontEncoding encoding) +{ + switch ( encoding ) + { + case wxFONTENCODING_MAX: + case wxFONTENCODING_SYSTEM: + if ( m_name ) + { + // It's ok to not have encoding value if we have a name for it. + m_encoding = wxFONTENCODING_SYSTEM; + } + else // No name neither. + { + // Fall back to the system default encoding in this case (not + // sure how much sense does this make but this is how the old + // code used to behave). +#if wxUSE_INTL + m_encoding = wxLocale::GetSystemEncoding(); + if ( m_encoding == wxFONTENCODING_SYSTEM ) +#endif // wxUSE_INTL + m_encoding = wxFONTENCODING_ISO8859_1; + } + break; + + case wxFONTENCODING_DEFAULT: + // wxFONTENCODING_DEFAULT is same as US-ASCII in this context + m_encoding = wxFONTENCODING_ISO8859_1; + break; + + default: + // Just use the provided encoding. + m_encoding = encoding; + } } wxCSConv::wxCSConv(const wxString& charset) @@ -2888,20 +3013,12 @@ wxCSConv::wxCSConv(const wxString& charset) } #if wxUSE_FONTMAP - m_encoding = wxFontMapperBase::GetEncodingFromName(charset); - if ( m_encoding == wxFONTENCODING_MAX ) - { - // set to unknown/invalid value - m_encoding = wxFONTENCODING_SYSTEM; - } - else if ( m_encoding == wxFONTENCODING_DEFAULT ) - { - // wxFONTENCODING_DEFAULT is same as US-ASCII in this context - m_encoding = wxFONTENCODING_ISO8859_1; - } + SetEncoding(wxFontMapperBase::GetEncodingFromName(charset)); #else - m_encoding = wxFONTENCODING_SYSTEM; + SetEncoding(wxFONTENCODING_SYSTEM); #endif + + m_convReal = DoCreate(); } wxCSConv::wxCSConv(wxFontEncoding encoding) @@ -2915,7 +3032,9 @@ wxCSConv::wxCSConv(wxFontEncoding encoding) Init(); - m_encoding = encoding; + SetEncoding(encoding); + + m_convReal = DoCreate(); } wxCSConv::~wxCSConv() @@ -2929,7 +3048,9 @@ wxCSConv::wxCSConv(const wxCSConv& conv) Init(); SetName(conv.m_name); - m_encoding = conv.m_encoding; + SetEncoding(conv.m_encoding); + + m_convReal = DoCreate(); } wxCSConv& wxCSConv::operator=(const wxCSConv& conv) @@ -2937,7 +3058,9 @@ wxCSConv& wxCSConv::operator=(const wxCSConv& conv) Clear(); SetName(conv.m_name); - m_encoding = conv.m_encoding; + SetEncoding(conv.m_encoding); + + m_convReal = DoCreate(); return *this; } @@ -2945,19 +3068,15 @@ wxCSConv& wxCSConv::operator=(const wxCSConv& conv) void wxCSConv::Clear() { free(m_name); - delete m_convReal; - m_name = NULL; - m_convReal = NULL; + + wxDELETE(m_convReal); } void wxCSConv::SetName(const char *charset) { - if (charset) - { + if ( charset ) m_name = wxStrdup(charset); - m_deferred = true; - } } #if wxUSE_FONTMAP @@ -2980,8 +3099,7 @@ wxMBConv *wxCSConv::DoCreate() const // check for the special case of ASCII or ISO8859-1 charset: as we have // special knowledge of it anyhow, we don't need to create a special // conversion object - if ( m_encoding == wxFONTENCODING_ISO8859_1 || - m_encoding == wxFONTENCODING_DEFAULT ) + if ( m_encoding == wxFONTENCODING_ISO8859_1 ) { // don't convert at all return NULL; @@ -3033,7 +3151,7 @@ wxMBConv *wxCSConv::DoCreate() const delete conv; } - const wxChar** names = wxFontMapperBase::GetAllEncodingNames(encoding); + const wxChar* const* names = wxFontMapperBase::GetAllEncodingNames(encoding); // 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 @@ -3156,33 +3274,8 @@ wxMBConv *wxCSConv::DoCreate() const return NULL; } -void wxCSConv::CreateConvIfNeeded() const -{ - if ( m_deferred ) - { - wxCSConv *self = (wxCSConv *)this; // const_cast - - // 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 ) - { -#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 @@ -3195,8 +3288,6 @@ bool wxCSConv::IsOk() const 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); @@ -3219,8 +3310,6 @@ size_t wxCSConv::ToWChar(wchar_t *dst, size_t dstLen, 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); @@ -3256,12 +3345,8 @@ size_t wxCSConv::FromWChar(char *dst, size_t dstLen, size_t wxCSConv::GetMBNulLen() const { - CreateConvIfNeeded(); - if ( m_convReal ) - { return m_convReal->GetMBNulLen(); - } // otherwise, we are ISO-8859-1 return 1; @@ -3270,12 +3355,8 @@ size_t wxCSConv::GetMBNulLen() const #if wxUSE_UNICODE_UTF8 bool wxCSConv::IsUTF8() const { - CreateConvIfNeeded(); - if ( m_convReal ) - { return m_convReal->IsUTF8(); - } // otherwise, we are ISO-8859-1 return false; @@ -3374,8 +3455,9 @@ 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? +// It is important to use this conversion object under Darwin as it ensures +// that Unicode strings are (re)composed correctly even though xnu kernel uses +// decomposed form internally (at least for the file names). static wxMBConv_cf wxConvMacUTF8DObj(wxFONTENCODING_UTF8); #endif @@ -3385,14 +3467,3 @@ WXDLLIMPEXP_DATA_BASE(wxMBConv *) wxConvFileName = #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, - wxConvLocal, - wxConvUTF8; - -#endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T