X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/44d12026b8577bc687cb00802bbb9edf4d5204d3..02bcd285fac7124a41292d905609220005f51087:/src/common/strconv.cpp diff --git a/src/common/strconv.cpp b/src/common/strconv.cpp index e3ef168ba3..63de22405c 100644 --- a/src/common/strconv.cpp +++ b/src/common/strconv.cpp @@ -20,10 +20,6 @@ // headers // ---------------------------------------------------------------------------- -#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) - #pragma implementation "strconv.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -40,11 +36,8 @@ #if wxUSE_WCHAR_T -#ifdef __WXMSW__ - #include "wx/msw/private.h" -#endif - #ifdef __WINDOWS__ + #include "wx/msw/private.h" #include "wx/msw/missing.h" #endif @@ -55,18 +48,11 @@ #include #include #include -#ifdef HAVE_LANGINFO_H - #include -#endif #if defined(__WIN32__) && !defined(__WXMICROWIN__) #define wxHAVE_WIN32_MB2WC #endif // __WIN32__ but !__WXMICROWIN__ -// ---------------------------------------------------------------------------- -// headers -// ---------------------------------------------------------------------------- - #ifdef __SALFORDC__ #include #endif @@ -81,40 +67,16 @@ #include "wx/utils.h" #ifdef __WXMAC__ +#ifndef __DARWIN__ #include #include #include +#endif #include "wx/mac/private.h" // includes mac headers #endif -// ---------------------------------------------------------------------------- -// macros -// ---------------------------------------------------------------------------- - -#define BSWAP_UCS4(str, len) { unsigned _c; for (_c=0; _c=0xdfff)) + else if ((input[1]<0xdc00) || (input[1]>0xdfff)) { output = *input; return (size_t)-1; @@ -273,9 +235,9 @@ const wxWCharBuffer wxMBConv::cMB2WC(const char *szString, size_t nStringLen, si } //Increment to next (sub)string - //Note that we have to use strlen here instead of nLen - //here because XX2XX gives us the size of the output buffer, - //not neccessarly the length of the string + //Note that we have to use strlen instead of nLen here + //because XX2XX gives us the size of the output buffer, + //which is not necessarily the length of the string szPos += strlen(szPos) + 1; } @@ -335,9 +297,9 @@ const wxCharBuffer wxMBConv::cWC2MB(const wchar_t *szString, size_t nStringLen, } //Increment to next (sub)string - //Note that we have to use wxWcslen here instead of nLen - //here because XX2XX gives us the size of the output buffer, - //not neccessarly the length of the string + //Note that we have to use wxWcslen instead of nLen here + //because XX2XX gives us the size of the output buffer, + //which is not necessarily the length of the string szPos += wxWcslen(szPos) + 1; } @@ -360,45 +322,39 @@ size_t wxMBConvLibc::WC2MB(char *buf, const wchar_t *psz, size_t n) const return wxWC2MB(buf, psz, n); } +#ifdef __UNIX__ + // ---------------------------------------------------------------------------- -// wxConvBrokenFileNames is made for GTK2 in Unicode mode when -// files are accidentally written in an encoding which is not -// the system encoding. Typically, the system encoding will be -// UTF8 but there might be files stored in ISO8859-1 on disk. +// wxConvBrokenFileNames // ---------------------------------------------------------------------------- -class wxConvBrokenFileNames: public wxMBConvLibc +wxConvBrokenFileNames::wxConvBrokenFileNames(const wxChar *charset) { -public: - wxConvBrokenFileNames() : m_utf8conv(wxMBConvUTF8::MAP_INVALID_UTF8_TO_OCTAL) { } - virtual size_t MB2WC(wchar_t *outputBuf, const char *psz, size_t outputSize) const; - virtual size_t WC2MB(char *outputBuf, const wchar_t *psz, size_t outputSize) const; - inline bool UseUTF8() const; -private: - wxMBConvUTF8 m_utf8conv; -}; - -bool wxConvBrokenFileNames::UseUTF8() const -{ - return wxLocale::GetSystemEncoding() == wxFONTENCODING_UTF8; + if ( !charset || wxStricmp(charset, _T("UTF-8")) == 0 + || wxStricmp(charset, _T("UTF8")) == 0 ) + m_conv = new wxMBConvUTF8(wxMBConvUTF8::MAP_INVALID_UTF8_TO_OCTAL); + else + m_conv = new wxCSConv(charset); } -size_t wxConvBrokenFileNames::MB2WC(wchar_t *outputBuf, const char *psz, size_t outputSize) const +size_t +wxConvBrokenFileNames::MB2WC(wchar_t *outputBuf, + const char *psz, + size_t outputSize) const { - if (UseUTF8()) - return m_utf8conv.MB2WC( outputBuf, psz, outputSize ); - else - return wxMBConvLibc::MB2WC( outputBuf, psz, outputSize ); + return m_conv->MB2WC( outputBuf, psz, outputSize ); } -size_t wxConvBrokenFileNames::WC2MB(char *outputBuf, const wchar_t *psz, size_t outputSize) const +size_t +wxConvBrokenFileNames::WC2MB(char *outputBuf, + const wchar_t *psz, + size_t outputSize) const { - if (UseUTF8()) - return m_utf8conv.WC2MB( outputBuf, psz, outputSize ); - else - return wxMBConvLibc::WC2MB( outputBuf, psz, outputSize ); + return m_conv->WC2MB( outputBuf, psz, outputSize ); } +#endif + // ---------------------------------------------------------------------------- // UTF-7 // ---------------------------------------------------------------------------- @@ -553,7 +509,7 @@ size_t wxMBConvUTF7::WC2MB(char *buf, const wchar_t *psz, size_t n) const } #ifndef WC_UTF16 else if (((wxUint32)cc) > 0xffff) - { + { // no surrogate pair generation (yet?) return (size_t)-1; } @@ -633,6 +589,15 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const 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 { @@ -721,11 +686,11 @@ size_t wxMBConvUTF8::MB2WC(wchar_t *buf, const char *psz, size_t n) const { if ( buf && len + 3 < n ) { - unsigned char n = *opsz; + unsigned char on = *opsz; *buf++ = L'\\'; - *buf++ = L'0' + n / 0100; - *buf++ = L'0' + (n % 0100) / 010; - *buf++ = L'0' + n % 010; + *buf++ = (wchar_t)( L'0' + on / 0100 ); + *buf++ = (wchar_t)( L'0' + (on % 0100) / 010 ); + *buf++ = (wchar_t)( L'0' + on % 010 ); } opsz++; len += 4; @@ -770,15 +735,23 @@ size_t wxMBConvUTF8::WC2MB(char *buf, const wchar_t *psz, size_t n) const *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'); + *buf++ = (char) ((psz[0] - L'0')*0100 + + (psz[1] - L'0')*010 + + (psz[2] - L'0')); } psz += 3; @@ -1303,6 +1276,19 @@ size_t wxMBConvUTF32swap::WC2MB(char *buf, const wchar_t *psz, size_t n) const #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 // ---------------------------------------------------------------------------- @@ -1317,7 +1303,7 @@ public: virtual size_t WC2MB(char *buf, const wchar_t *psz, size_t n) const; bool IsOk() const - { return (m2w != (iconv_t)-1) && (w2m != (iconv_t)-1); } + { return (m2w != ICONV_T_INVALID) && (w2m != ICONV_T_INVALID); } protected: // the iconv handlers used to translate from multibyte to wide char and in @@ -1332,108 +1318,140 @@ protected: private: // the name (for iconv_open()) of a wide char charset -- if none is // available on this machine, it will remain NULL - static const char *ms_wcCharsetName; + 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; }; -const char *wxMBConv_iconv::ms_wcCharsetName = NULL; +// 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) { - // Do it the hard way - char cname[100]; - for (size_t i = 0; i < wxStrlen(name)+1; i++) - cname[i] = (char) name[i]; + // 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 == NULL) + if ( ms_wcCharsetName.empty() ) { - ms_wcNeedsSwap = false; - - // try charset with explicit bytesex info (e.g. "UCS-4LE"): - ms_wcCharsetName = WC_NAME_BEST; - m2w = iconv_open(ms_wcCharsetName, cname); - - if (m2w == (iconv_t)-1) +#if wxUSE_FONTMAP + const wxChar **names = wxFontMapperBase::GetAllEncodingNames(WC_ENC); +#else // !wxUSE_FONTMAP + static const wxChar *names[] = { - // try charset w/o bytesex info (e.g. "UCS4") - // and check for bytesex ourselves: - ms_wcCharsetName = WC_NAME; - m2w = iconv_open(ms_wcCharsetName, cname); +#if SIZEOF_WCHAR_T == 4 + _T("UCS-4"), +#elif SIZEOF_WCHAR_T = 2 + _T("UCS-2"), +#endif + NULL + }; +#endif // wxUSE_FONTMAP/!wxUSE_FONTMAP - // last bet, try if it knows WCHAR_T pseudo-charset - if (m2w == (iconv_t)-1) + for ( ; *names; ++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 + + m2w = iconv_open(nameXE.ToAscii(), cname); + if ( m2w == ICONV_T_INVALID ) { - ms_wcCharsetName = "WCHAR_T"; - m2w = iconv_open(ms_wcCharsetName, cname); - } + // try charset w/o bytesex info (e.g. "UCS4") + m2w = iconv_open(nameCS.ToAscii(), cname); - if (m2w != (iconv_t)-1) - { - 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)) + // and check for bytesex ourselves: + if ( m2w != ICONV_T_INVALID ) { - ms_wcCharsetName = NULL; - wxLogLastError(wxT("iconv")); - wxLogError(_("Conversion to charset '%s' doesn't work."), name); - } - else - { - ms_wcNeedsSwap = wbuf[0] != (wchar_t)buf[0]; + 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 + else // use charset not requiring byte swapping { - ms_wcCharsetName = NULL; - - // VS: we must not output an error here, since wxWidgets will safely - // fall back to using wxEncodingConverter. - wxLogTrace(wxT("strconv"), wxT("Impossible to convert to/from charset '%s' with iconv, falling back to wxEncodingConverter."), name); - //wxLogError( + ms_wcCharsetName = nameXE; } } - wxLogTrace(wxT("strconv"), wxT("wchar_t charset is '%s', needs swap: %i"), ms_wcCharsetName, ms_wcNeedsSwap); + + wxLogTrace(TRACE_STRCONV, + wxT("iconv wchar_t charset is \"%s\"%s"), + ms_wcCharsetName.empty() ? _T("") + : ms_wcCharsetName.c_str(), + ms_wcNeedsSwap ? _T(" (needs swap)") + : _T("")); } else // we already have ms_wcCharsetName { - m2w = iconv_open(ms_wcCharsetName, cname); + m2w = iconv_open(ms_wcCharsetName.ToAscii(), cname); } - // NB: don't ever pass NULL to iconv_open(), it may crash! - if ( ms_wcCharsetName ) + if ( ms_wcCharsetName.empty() ) { - w2m = iconv_open( cname, ms_wcCharsetName); + w2m = ICONV_T_INVALID; } else { - w2m = (iconv_t)-1; + 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)-1 ) + if ( m2w != ICONV_T_INVALID ) iconv_close(m2w); - if ( w2m != (iconv_t)-1 ) + if ( w2m != ICONV_T_INVALID ) iconv_close(w2m); } @@ -1467,7 +1485,8 @@ size_t wxMBConv_iconv::MB2WC(wchar_t *buf, const char *psz, size_t n) const if (ms_wcNeedsSwap) { // convert to native endianness - WC_BSWAP(buf /* _not_ bufPtr */, res) + for ( unsigned i = 0; i < res; i++ ) + buf[n] = WC_BSWAP(buf[i]); } // NB: iconv was given only strlen(psz) characters on input, and so @@ -1497,7 +1516,7 @@ size_t wxMBConv_iconv::MB2WC(wchar_t *buf, const char *psz, size_t n) const if (ICONV_FAILED(cres, inbuf)) { //VS: it is ok if iconv fails, hence trace only - wxLogTrace(wxT("strconv"), wxT("iconv failed: %s"), wxSysErrorMsg(wxSysErrorCode())); + wxLogTrace(TRACE_STRCONV, wxT("iconv failed: %s"), wxSysErrorMsg(wxSysErrorCode())); return (size_t)-1; } @@ -1511,7 +1530,8 @@ size_t wxMBConv_iconv::WC2MB(char *buf, const wchar_t *psz, size_t n) const wxMutexLocker lock(wxConstCast(this, wxMBConv_iconv)->m_iconvMutex); #endif - size_t inbuf = wxWcslen(psz) * SIZEOF_WCHAR_T; + size_t inlen = wxWcslen(psz); + size_t inbuf = inlen * SIZEOF_WCHAR_T; size_t outbuf = n; size_t res, cres; @@ -1520,13 +1540,13 @@ size_t wxMBConv_iconv::WC2MB(char *buf, const wchar_t *psz, size_t n) const if (ms_wcNeedsSwap) { // need to copy to temp buffer to switch endianness - // this absolutely doesn't rock! - // (no, doing WC_BSWAP twice on the original buffer won't help, as it + // (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+1)*SIZEOF_WCHAR_T); - memcpy(tmpbuf,psz,(inbuf+1)*SIZEOF_WCHAR_T); - WC_BSWAP(tmpbuf, inbuf) - psz=tmpbuf; + 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) @@ -1565,7 +1585,7 @@ size_t wxMBConv_iconv::WC2MB(char *buf, const wchar_t *psz, size_t n) const if (ICONV_FAILED(cres, inbuf)) { //VS: it is ok if iconv fails, hence trace only - wxLogTrace(wxT("strconv"), wxT("iconv failed: %s"), wxSysErrorMsg(wxSysErrorCode())); + wxLogTrace(TRACE_STRCONV, wxT("iconv failed: %s"), wxSysErrorMsg(wxSysErrorCode())); return (size_t)-1; } @@ -2108,7 +2128,7 @@ public: UniChar* szUniBuffer = (UniChar*) szUnConv; #if SIZEOF_WCHAR_T == 4 - wxMBConvUTF16BE converter ; + wxMBConvUTF16 converter ; nBufSize = converter.WC2MB( NULL , szUnConv , 0 ); szUniBuffer = new UniChar[ (nBufSize / sizeof(UniChar)) + 1] ; converter.WC2MB( (char*) szUniBuffer , szUnConv, nBufSize + sizeof(UniChar)) ; @@ -2244,7 +2264,7 @@ public: // 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 ; - wxMBConvUTF16BE converter ; + wxMBConvUTF16 converter ; res = converter.MB2WC( (buf ? buf : tbuf) , (const char*)ubuf , n ) ; free( ubuf ) ; #else @@ -2277,7 +2297,7 @@ public: ByteCount byteBufferLen = n ; UniChar* ubuf = NULL ; #if SIZEOF_WCHAR_T == 4 - wxMBConvUTF16BE converter ; + wxMBConvUTF16 converter ; size_t unicharlen = converter.WC2MB( NULL , psz , 0 ) ; byteInLen = unicharlen ; ubuf = (UniChar*) malloc( byteInLen + 2 ) ; @@ -2398,6 +2418,18 @@ public: DECLARE_NO_COPY_CLASS(wxMBConv_wxwin) }; +// make the constructors available for unit testing +WXDLLIMPEXP_BASE wxMBConv* new_wxMBConv_wxwin( const wxChar* name ) +{ + wxMBConv_wxwin* result = new wxMBConv_wxwin( name ); + if ( !result->IsOk() ) + { + delete result; + return 0; + } + return result; +} + #endif // wxUSE_FONTMAP // ============================================================================ @@ -2479,8 +2511,24 @@ void wxCSConv::SetName(const wxChar *charset) } } +#if wxUSE_FONTMAP +#include "wx/hashmap.h" + +WX_DECLARE_HASH_MAP( wxFontEncoding, wxString, wxIntegerHash, wxIntegerEqual, + wxEncodingNameCache ); + +static wxEncodingNameCache gs_nameCache; +#endif + wxMBConv *wxCSConv::DoCreate() const { +#if wxUSE_FONTMAP + wxLogTrace(TRACE_STRCONV, + wxT("creating conversion for %s"), + (m_name ? m_name + : wxFontMapperBase::GetEncodingName(m_encoding).c_str())); +#endif // wxUSE_FONTMAP + // check for the special case of ASCII or ISO8859-1 charset: as we have // special knowledge of it anyhow, we don't need to create a special // conversion object @@ -2505,17 +2553,53 @@ wxMBConv *wxCSConv::DoCreate() const #endif // !wxUSE_FONTMAP { wxString name(m_name); + wxFontEncoding encoding(m_encoding); + + if ( !name.empty() ) + { + wxMBConv_iconv *conv = new wxMBConv_iconv(name); + if ( conv->IsOk() ) + return conv; + + delete conv; #if wxUSE_FONTMAP - if ( name.empty() ) - name = wxFontMapperBase::Get()->GetEncodingName(m_encoding); + encoding = + wxFontMapperBase::Get()->CharsetToEncoding(name, false); #endif // wxUSE_FONTMAP + } +#if wxUSE_FONTMAP + { + const wxEncodingNameCache::iterator it = gs_nameCache.find(encoding); + if ( it != gs_nameCache.end() ) + { + if ( it->second.empty() ) + return NULL; - wxMBConv_iconv *conv = new wxMBConv_iconv(name); - if ( conv->IsOk() ) - return conv; + wxMBConv_iconv *conv = new wxMBConv_iconv(it->second); + if ( conv->IsOk() ) + return conv; - delete conv; + delete conv; + } + + const wxChar** names = wxFontMapperBase::GetAllEncodingNames(encoding); + + for ( ; *names; ++names ) + { + wxMBConv_iconv *conv = new wxMBConv_iconv(*names); + if ( conv->IsOk() ) + { + gs_nameCache[encoding] = *names; + return conv; + } + + delete conv; + } + + gs_nameCache[encoding] = _T(""); // cache the failure + } +#endif // wxUSE_FONTMAP } #endif // HAVE_ICONV @@ -2731,7 +2815,6 @@ static wxCSConv wxConvLocalObj(wxFONTENCODING_SYSTEM); static wxCSConv wxConvISO8859_1Obj(wxFONTENCODING_ISO8859_1); static wxMBConvUTF7 wxConvUTF7Obj; static wxMBConvUTF8 wxConvUTF8Obj; -static wxConvBrokenFileNames wxConvBrokenFileNamesObj; WXDLLIMPEXP_DATA_BASE(wxMBConv&) wxConvLibc = wxConvLibcObj; WXDLLIMPEXP_DATA_BASE(wxCSConv&) wxConvLocal = wxConvLocalObj; @@ -2742,8 +2825,6 @@ WXDLLIMPEXP_DATA_BASE(wxMBConv *) wxConvCurrent = &wxConvLibcObj; WXDLLIMPEXP_DATA_BASE(wxMBConv *) wxConvFileName = & #ifdef __WXOSX__ wxConvUTF8Obj; -#elif __WXGTK20__ - wxConvBrokenFileNamesObj; #else wxConvLibcObj; #endif