// headers
// ----------------------------------------------------------------------------
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
- #pragma implementation "strconv.h"
-#endif
-
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#define wxHAVE_WIN32_MB2WC
#endif // __WIN32__ but !__WXMICROWIN__
-// ----------------------------------------------------------------------------
-// headers
-// ----------------------------------------------------------------------------
-
#ifdef __SALFORDC__
#include <clib.h>
#endif
#include "wx/mac/private.h" // includes mac headers
#endif
-// ----------------------------------------------------------------------------
-// macros
-// ----------------------------------------------------------------------------
-#define BSWAP_UCS4(str, len) { unsigned _c; for (_c=0; _c<len; _c++) str[_c]=wxUINT32_SWAP_ALWAYS(str[_c]); }
-#define BSWAP_UTF16(str, len) { unsigned _c; for (_c=0; _c<len; _c++) str[_c]=wxUINT16_SWAP_ALWAYS(str[_c]); }
-
-#if SIZEOF_WCHAR_T == 4
- #define WC_NAME "UCS4"
- #define WC_BSWAP BSWAP_UCS4
- #ifdef WORDS_BIGENDIAN
- #define WC_NAME_BEST "UCS-4BE"
- #else
- #define WC_NAME_BEST "UCS-4LE"
- #endif
-#elif SIZEOF_WCHAR_T == 2
- #define WC_NAME "UTF16"
- #define WC_BSWAP BSWAP_UTF16
- #define WC_UTF16
- #ifdef WORDS_BIGENDIAN
- #define WC_NAME_BEST "UTF-16BE"
- #else
- #define WC_NAME_BEST "UTF-16LE"
- #endif
-#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
+#define TRACE_STRCONV _T("strconv")
// ============================================================================
// implementation
#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
// ----------------------------------------------------------------------------
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
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
return result;
}
-const char *wxMBConv_iconv::ms_wcCharsetName = NULL;
+wxString wxMBConv_iconv::ms_wcCharsetName;
bool wxMBConv_iconv::ms_wcNeedsSwap = false;
wxMBConv_iconv::wxMBConv_iconv(const wxChar *name)
{
// iconv operates with chars, not wxChars, but luckily it uses only ASCII
// names for the charsets
- const wxString cname = wxString::ToAscii(name);
+ 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 name(*names);
+
+ // first try charset with explicit bytesex info (e.g. "UCS-4LE"):
+ wxString nameXE(name);
+ #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(name.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."),
+ name.c_str());
+ }
+ else // ok, can convert to this encoding, remember it
+ {
+ ms_wcCharsetName = name;
+ 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("<none>")
+ : 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);
}
if (ms_wcNeedsSwap)
{
// convert to native endianness
- WC_BSWAP(buf /* _not_ bufPtr */, res)
+ for ( unsigned n = 0; n < res; n++ )
+ buf[n] = WC_BSWAP(buf[n]);
}
// NB: iconv was given only strlen(psz) characters on input, and so
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;
}
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;
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 n = 0; n < inlen; n++ )
+ tmpbuf[n] = WC_BSWAP(psz[n]);
+ tmpbuf[inlen] = L'\0';
+ psz = tmpbuf;
}
if (buf)
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;
}
}
}
+#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
#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::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