X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/de4983f3236c2043479abb21857aca958a7b61f0..7198c3368055d88249a338eb33b21f051f674806:/src/common/string.cpp diff --git a/src/common/string.cpp b/src/common/string.cpp index 960b6d2d35..2978dcdca8 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -24,6 +24,8 @@ #ifndef WX_PRECOMP #include "wx/string.h" #include "wx/wxcrtvararg.h" + #include "wx/intl.h" + #include "wx/log.h" #endif #include @@ -39,6 +41,14 @@ #include "wx/vector.h" #include "wx/xlocale.h" +#ifdef __WXMSW__ + #include "wx/msw/wrapwin.h" +#endif // __WXMSW__ + +#if wxUSE_STD_IOSTREAM + #include +#endif + // string handling functions used by wxString: #if wxUSE_UNICODE_UTF8 #define wxStringMemcpy memcpy @@ -52,16 +62,20 @@ #define wxStringStrlen wxStrlen #endif -// ---------------------------------------------------------------------------- -// global variables -// ---------------------------------------------------------------------------- - +// define a function declared in wx/buffer.h here as we don't have buffer.cpp +// and don't want to add it just because of this simple function namespace wxPrivate { -static UntypedBufferData s_untypedNullData(NULL); +// wxXXXBuffer classes can be (implicitly) used during global statics +// initialization so wrap the status UntypedBufferData variable in a function +// to make it safe to access it even before all global statics are initialized +UntypedBufferData *GetUntypedNullData() +{ + static UntypedBufferData s_untypedNullData(NULL, 0); -UntypedBufferData * const untypedNullDataPtr = &s_untypedNullData; + return &s_untypedNullData; +} } // namespace wxPrivate @@ -361,95 +375,6 @@ wxString::~wxString() } #endif -#if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY -const char* wxCStrData::AsChar() const -{ -#if wxUSE_UNICODE_UTF8 - if ( wxLocaleIsUtf8 ) - return AsInternal(); -#endif - // under non-UTF8 locales, we have to convert the internal UTF-8 - // representation using wxConvLibc and cache the result - - wxString *str = wxConstCast(m_str, wxString); - - // convert the string: - // - // FIXME-UTF8: we'd like to do the conversion in the existing buffer (if we - // have it) but it's unfortunately not obvious to implement - // because we don't know how big buffer do we need for the - // given string length (in case of multibyte encodings, e.g. - // ISO-2022-JP or UTF-8 when internal representation is wchar_t) - // - // One idea would be to store more than just m_convertedToChar - // in wxString: then we could record the length of the string - // which was converted the last time and try to reuse the same - // buffer if the current length is not greater than it (this - // could still fail because string could have been modified in - // place but it would work most of the time, so we'd do it and - // only allocate the new buffer if in-place conversion returned - // an error). We could also store a bit saying if the string - // was modified since the last conversion (and update it in all - // operation modifying the string, of course) to avoid unneeded - // consequential conversions. But both of these ideas require - // adding more fields to wxString and require profiling results - // to be sure that we really gain enough from them to justify - // doing it. - wxScopedCharBuffer buf(str->mb_str()); - - // if it failed, return empty string and not NULL to avoid crashes in code - // written with either wxWidgets 2 wxString or std::string behaviour in - // mind: neither of them ever returns NULL and so we shouldn't neither - if ( !buf ) - return ""; - - if ( str->m_convertedToChar && - strlen(buf) == strlen(str->m_convertedToChar) ) - { - // keep the same buffer for as long as possible, so that several calls - // to c_str() in a row still work: - strcpy(str->m_convertedToChar, buf); - } - else - { - str->m_convertedToChar = buf.release(); - } - - // and keep it: - return str->m_convertedToChar + m_offset; -} -#endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY - -#if !wxUSE_UNICODE_WCHAR -const wchar_t* wxCStrData::AsWChar() const -{ - wxString *str = wxConstCast(m_str, wxString); - - // convert the string: - wxScopedWCharBuffer buf(str->wc_str()); - - // notice that here, unlike above in AsChar(), conversion can't fail as our - // internal UTF-8 is always well-formed -- or the string was corrupted and - // all bets are off anyhow - - // FIXME-UTF8: do the conversion in-place in the existing buffer - if ( str->m_convertedToWChar && - wxWcslen(buf) == wxWcslen(str->m_convertedToWChar) ) - { - // keep the same buffer for as long as possible, so that several calls - // to c_str() in a row still work: - memcpy(str->m_convertedToWChar, buf, sizeof(wchar_t) * wxWcslen(buf)); - } - else - { - str->m_convertedToWChar = buf.release(); - } - - // and keep it: - return str->m_convertedToWChar + m_offset; -} -#endif // !wxUSE_UNICODE_WCHAR - // =========================================================================== // wxString class core // =========================================================================== @@ -499,7 +424,8 @@ wxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength, // we must pass the real string length to SubstrBufFromMB ctor if ( nLength == npos ) nLength = psz ? strlen(psz) : 0; - return SubstrBufFromMB(wxCharBuffer::CreateNonOwned(psz), nLength); + return SubstrBufFromMB(wxScopedCharBuffer::CreateNonOwned(psz, nLength), + nLength); } // else: do the roundtrip through wchar_t* } @@ -516,7 +442,7 @@ wxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength, // and then to UTF-8: SubstrBufFromMB buf(ConvertStr(wcBuf, wcLen, wxMBConvStrictUTF8())); // widechar -> UTF-8 conversion isn't supposed to ever fail: - wxASSERT_MSG( buf.data, _T("conversion to UTF-8 failed") ); + wxASSERT_MSG( buf.data, wxT("conversion to UTF-8 failed") ); return buf; } @@ -543,60 +469,97 @@ wxString::SubstrBufFromWC wxString::ConvertStr(const wchar_t *pwz, size_t nLengt } #endif // wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE +// This std::string::c_str()-like method returns a wide char pointer to string +// contents. In wxUSE_UNICODE_WCHAR case it is trivial as it can simply return +// a pointer to the internal representation. Otherwise a conversion is required +// and it returns a temporary buffer. +// +// However for compatibility with c_str() and to avoid breaking existing code +// doing +// +// for ( const wchar_t *p = s.wc_str(); *p; p++ ) +// ... use *p... +// +// we actually need to ensure that the returned buffer is _not_ temporary and +// so we use wxString::m_convertedToWChar to store the returned data +#if !wxUSE_UNICODE_WCHAR -#if wxUSE_UNICODE_WCHAR - -//Convert wxString in Unicode mode to a multi-byte string -const wxScopedCharBuffer wxString::mb_str(const wxMBConv& conv) const +const wchar_t *wxString::AsWChar(const wxMBConv& conv) const { - return conv.cWC2MB(wx_str(), length() + 1 /* size, not length */, NULL); -} + const char * const strMB = m_impl.c_str(); + const size_t lenMB = m_impl.length(); -#elif wxUSE_UNICODE_UTF8 + // find out the size of the buffer needed + const size_t lenWC = conv.ToWChar(NULL, 0, strMB, lenMB); + if ( lenWC == wxCONV_FAILED ) + return NULL; -const wxScopedWCharBuffer wxString::wc_str() const -{ - return wxMBConvStrictUTF8().cMB2WC - ( - m_impl.c_str(), - m_impl.length() + 1, // size, not length - NULL - ); + // keep the same buffer if the string size didn't change: this is not only + // an optimization but also ensure that code which modifies string + // character by character (without changing its length) can continue to use + // the pointer returned by a previous wc_str() call even after changing the + // string + + // TODO-UTF8: we could check for ">" instead of "!=" here as this would + // allow to save on buffer reallocations but at the cost of + // consuming (even) more memory, we should benchmark this to + // determine if it's worth doing + if ( !m_convertedToWChar.m_str || lenWC != m_convertedToWChar.m_len ) + { + if ( !const_cast(this)->m_convertedToWChar.Extend(lenWC) ) + return NULL; + } + + // finally do convert + m_convertedToWChar.m_str[lenWC] = L'\0'; + if ( conv.ToWChar(m_convertedToWChar.m_str, lenWC, + strMB, lenMB) == wxCONV_FAILED ) + return NULL; + + return m_convertedToWChar.m_str; } -const wxScopedCharBuffer wxString::mb_str(const wxMBConv& conv) const +#endif // !wxUSE_UNICODE_WCHAR + + +// Same thing for mb_str() which returns a normal char pointer to string +// contents: this always requires converting it to the specified encoding in +// non-ANSI build except if we need to convert to UTF-8 and this is what we +// already use internally. +#if wxUSE_UNICODE + +const char *wxString::AsChar(const wxMBConv& conv) const { +#if wxUSE_UNICODE_UTF8 if ( conv.IsUTF8() ) - return wxScopedCharBuffer::CreateNonOwned(m_impl.c_str()); + return m_impl.c_str(); - // FIXME-UTF8: use wc_str() here once we have buffers with length + const wchar_t * const strWC = AsWChar(wxMBConvStrictUTF8()); + const size_t lenWC = m_convertedToWChar.m_len; +#else // wxUSE_UNICODE_WCHAR + const wchar_t * const strWC = m_impl.c_str(); + const size_t lenWC = m_impl.length(); +#endif // wxUSE_UNICODE_UTF8/wxUSE_UNICODE_WCHAR - size_t wcLen; - wxScopedWCharBuffer wcBuf - ( - wxMBConvStrictUTF8().cMB2WC - ( - m_impl.c_str(), - m_impl.length() + 1, // size - &wcLen - ) - ); - if ( !wcLen ) - return wxCharBuffer(""); + const size_t lenMB = conv.FromWChar(NULL, 0, strWC, lenWC); + if ( lenMB == wxCONV_FAILED ) + return NULL; - return conv.cWC2MB(wcBuf, wcLen+1, NULL); -} + if ( !m_convertedToChar.m_str || lenMB != m_convertedToChar.m_len ) + { + if ( !const_cast(this)->m_convertedToChar.Extend(lenMB) ) + return NULL; + } -#else // ANSI + m_convertedToChar.m_str[lenMB] = '\0'; + if ( conv.FromWChar(m_convertedToChar.m_str, lenMB, + strWC, lenWC) == wxCONV_FAILED ) + return NULL; -//Converts this string to a wide character string if unicode -//mode is not enabled and wxUSE_WCHAR_T is enabled -const wxScopedWCharBuffer wxString::wc_str(const wxMBConv& conv) const -{ - return conv.cMB2WC(wx_str(), length() + 1 /* size, not length */, NULL); + return m_convertedToChar.m_str; } -#endif // Unicode/ANSI +#endif // wxUSE_UNICODE // shrink to minimal size (releasing extra memory) bool wxString::Shrink() @@ -687,7 +650,7 @@ wxString operator+(const wxString& str, const char *psz) wxString s; if ( !s.Alloc(strlen(psz) + str.length()) ) { - wxFAIL_MSG( _T("out of memory in wxString::operator+") ); + wxFAIL_MSG( wxT("out of memory in wxString::operator+") ); } s += str; s += psz; @@ -703,7 +666,7 @@ wxString operator+(const wxString& str, const wchar_t *pwz) wxString s; if ( !s.Alloc(wxWcslen(pwz) + str.length()) ) { - wxFAIL_MSG( _T("out of memory in wxString::operator+") ); + wxFAIL_MSG( wxT("out of memory in wxString::operator+") ); } s += str; s += pwz; @@ -719,7 +682,7 @@ wxString operator+(const char *psz, const wxString& str) wxString s; if ( !s.Alloc(strlen(psz) + str.length()) ) { - wxFAIL_MSG( _T("out of memory in wxString::operator+") ); + wxFAIL_MSG( wxT("out of memory in wxString::operator+") ); } s = psz; s += str; @@ -735,7 +698,7 @@ wxString operator+(const wchar_t *pwz, const wxString& str) wxString s; if ( !s.Alloc(wxWcslen(pwz) + str.length()) ) { - wxFAIL_MSG( _T("out of memory in wxString::operator+") ); + wxFAIL_MSG( wxT("out of memory in wxString::operator+") ); } s = pwz; s += str; @@ -953,7 +916,7 @@ size_t wxString::find_first_not_of(const wxChar* sz, size_t nStart) const size_t wxString::find_first_of(const wxChar* sz, size_t nStart, size_t n) const { - wxASSERT_MSG( nStart <= length(), _T("invalid index") ); + wxASSERT_MSG( nStart <= length(), wxT("invalid index") ); size_t idx = nStart; for ( const_iterator i = begin() + nStart; i != end(); ++idx, ++i ) @@ -967,7 +930,7 @@ size_t wxString::find_first_of(const wxChar* sz, size_t nStart, size_t n) const size_t wxString::find_first_not_of(const wxChar* sz, size_t nStart, size_t n) const { - wxASSERT_MSG( nStart <= length(), _T("invalid index") ); + wxASSERT_MSG( nStart <= length(), wxT("invalid index") ); size_t idx = nStart; for ( const_iterator i = begin() + nStart; i != end(); ++idx, ++i ) @@ -1000,7 +963,7 @@ size_t wxString::find_last_of(const wxChar* sz, size_t nStart, size_t n) const } else { - wxASSERT_MSG( nStart <= len, _T("invalid index") ); + wxASSERT_MSG( nStart <= len, wxT("invalid index") ); } size_t idx = nStart; @@ -1024,7 +987,7 @@ size_t wxString::find_last_not_of(const wxChar* sz, size_t nStart, size_t n) con } else { - wxASSERT_MSG( nStart <= len, _T("invalid index") ); + wxASSERT_MSG( nStart <= len, wxT("invalid index") ); } size_t idx = nStart; @@ -1040,7 +1003,7 @@ size_t wxString::find_last_not_of(const wxChar* sz, size_t nStart, size_t n) con size_t wxString::find_first_not_of(wxUniChar ch, size_t nStart) const { - wxASSERT_MSG( nStart <= length(), _T("invalid index") ); + wxASSERT_MSG( nStart <= length(), wxT("invalid index") ); size_t idx = nStart; for ( const_iterator i = begin() + nStart; i != end(); ++idx, ++i ) @@ -1062,7 +1025,7 @@ size_t wxString::find_last_not_of(wxUniChar ch, size_t nStart) const } else { - wxASSERT_MSG( nStart <= len, _T("invalid index") ); + wxASSERT_MSG( nStart <= len, wxT("invalid index") ); } size_t idx = nStart; @@ -1120,9 +1083,65 @@ size_t wxString::find_last_not_of(const wxOtherCharType* sz, size_t nStart, int wxString::CmpNoCase(const wxString& s) const { -#if wxUSE_UNICODE_UTF8 - // FIXME-UTF8: use wxUniChar::ToLower/ToUpper once added +#if !wxUSE_UNICODE_UTF8 + // We compare NUL-delimited chunks of the strings inside the loop. We will + // do as many iterations as there are embedded NULs in the string, i.e. + // usually we will run it just once. + + typedef const wxStringImpl::value_type *pchar_type; + const pchar_type thisBegin = m_impl.c_str(); + const pchar_type thatBegin = s.m_impl.c_str(); + + const pchar_type thisEnd = thisBegin + m_impl.length(); + const pchar_type thatEnd = thatBegin + s.m_impl.length(); + + pchar_type thisCur = thisBegin; + pchar_type thatCur = thatBegin; + + int rc; + for ( ;; ) + { + // Compare until the next NUL, if the strings differ this is the final + // result. + rc = wxStricmp(thisCur, thatCur); + if ( rc ) + break; + + const size_t lenChunk = wxStrlen(thisCur); + thisCur += lenChunk; + thatCur += lenChunk; + // Skip all the NULs as wxStricmp() doesn't handle them. + for ( ; !*thisCur; thisCur++, thatCur++ ) + { + // Check if we exhausted either of the strings. + if ( thisCur == thisEnd ) + { + // This one is exhausted, is the other one too? + return thatCur == thatEnd ? 0 : -1; + } + + if ( thatCur == thatEnd ) + { + // Because of the test above we know that this one is not + // exhausted yet so it's greater than the other one that is. + return 1; + } + + if ( *thatCur ) + { + // Anything non-NUL is greater than NUL. + return -1; + } + } + } + + return rc; +#else // wxUSE_UNICODE_UTF8 + // CRT functions can't be used for case-insensitive comparison of UTF-8 + // strings so do it in the naive, simple and inefficient way. + + // FIXME-UTF8: use wxUniChar::ToLower/ToUpper once added const_iterator i1 = begin(); const_iterator end1 = end(); const_iterator i2 = s.begin(); @@ -1144,9 +1163,7 @@ int wxString::CmpNoCase(const wxString& s) const else if ( len1 > len2 ) return 1; return 0; -#else // wxUSE_UNICODE_WCHAR or ANSI - return wxStricmp(m_impl.c_str(), s.m_impl.c_str()); -#endif +#endif // !wxUSE_UNICODE_UTF8/wxUSE_UNICODE_UTF8 } @@ -1173,7 +1190,7 @@ wxString wxString::FromAscii(const char *ascii, size_t len) { unsigned char c = (unsigned char)*ascii++; wxASSERT_MSG( c < 0x80, - _T("Non-ASCII value passed to FromAscii().") ); + wxT("Non-ASCII value passed to FromAscii().") ); *dest++ = (wchar_t)c; } @@ -1193,7 +1210,7 @@ wxString wxString::FromAscii(char ascii) unsigned char c = (unsigned char)ascii; - wxASSERT_MSG( c < 0x80, _T("Non-ASCII value passed to FromAscii().") ); + wxASSERT_MSG( c < 0x80, wxT("Non-ASCII value passed to FromAscii().") ); // NB: the cast to wchar_t causes interpretation of 'ascii' as Latin1 value return wxString(wxUniChar((wchar_t)c)); @@ -1248,7 +1265,7 @@ wxString wxString::Mid(size_t nFirst, size_t nCount) const wxString dest(*this, nFirst, nCount); if ( dest.length() != nCount ) { - wxFAIL_MSG( _T("out of memory in wxString::Mid") ); + wxFAIL_MSG( wxT("out of memory in wxString::Mid") ); } return dest; @@ -1298,7 +1315,7 @@ wxString wxString::Right(size_t nCount) const wxString dest(*this, length() - nCount, nCount); if ( dest.length() != nCount ) { - wxFAIL_MSG( _T("out of memory in wxString::Right") ); + wxFAIL_MSG( wxT("out of memory in wxString::Right") ); } return dest; } @@ -1325,29 +1342,50 @@ wxString wxString::Left(size_t nCount) const wxString dest(*this, 0, nCount); if ( dest.length() != nCount ) { - wxFAIL_MSG( _T("out of memory in wxString::Left") ); + wxFAIL_MSG( wxT("out of memory in wxString::Left") ); } return dest; } // get all characters before the first occurrence of ch // (returns the whole string if ch not found) -wxString wxString::BeforeFirst(wxUniChar ch) const +wxString wxString::BeforeFirst(wxUniChar ch, wxString *rest) const { int iPos = Find(ch); if ( iPos == wxNOT_FOUND ) - iPos = length(); + { + iPos = length(); + if ( rest ) + rest->clear(); + } + else + { + if ( rest ) + rest->assign(*this, iPos + 1, npos); + } + return wxString(*this, 0, iPos); } /// get all characters before the last occurrence of ch /// (returns empty string if ch not found) -wxString wxString::BeforeLast(wxUniChar ch) const +wxString wxString::BeforeLast(wxUniChar ch, wxString *rest) const { wxString str; int iPos = Find(ch, true); - if ( iPos != wxNOT_FOUND && iPos != 0 ) - str = wxString(c_str(), iPos); + if ( iPos != wxNOT_FOUND ) + { + if ( iPos != 0 ) + str.assign(*this, 0, iPos); + + if ( rest ) + rest->assign(*this, iPos + 1, npos); + } + else + { + if ( rest ) + *rest = *this; + } return str; } @@ -1370,7 +1408,7 @@ size_t wxString::Replace(const wxString& strOld, { // if we tried to replace an empty string we'd enter an infinite loop below wxCHECK_MSG( !strOld.empty(), 0, - _T("wxString::Replace(): invalid parameter") ); + wxT("wxString::Replace(): invalid parameter") ); wxSTRING_INVALIDATE_CACHE(); @@ -1492,7 +1530,7 @@ bool wxString::IsNumber() const const_iterator i = begin(); - if ( *i == _T('-') || *i == _T('+') ) + if ( *i == wxT('-') || *i == wxT('+') ) ++i; for ( ; i != end(); ++i ) @@ -1649,23 +1687,23 @@ int wxString::Find(wxUniChar ch, bool bFromEnd) const #endif #define WX_STRING_TO_X_TYPE_START \ - wxCHECK_MSG( pVal, false, _T("NULL output pointer") ); \ + wxCHECK_MSG( pVal, false, wxT("NULL output pointer") ); \ DO_IF_NOT_WINCE( errno = 0; ) \ const wxStringCharType *start = wx_str(); \ wxStringCharType *end; +// notice that we return false without modifying the output parameter at all if +// nothing could be parsed but we do modify it and return false then if we did +// parse something successfully but not the entire string #define WX_STRING_TO_X_TYPE_END \ - /* return true only if scan was stopped by the terminating NUL and */ \ - /* if the string was not empty to start with and no under/overflow */ \ - /* occurred: */ \ - if ( *end || end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) \ + if ( end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) \ return false; \ *pVal = val; \ - return true; + return !*end; bool wxString::ToLong(long *pVal, int base) const { - wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); WX_STRING_TO_X_TYPE_START long val = wxStrtol(start, &end, base); @@ -1674,7 +1712,7 @@ bool wxString::ToLong(long *pVal, int base) const bool wxString::ToULong(unsigned long *pVal, int base) const { - wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); WX_STRING_TO_X_TYPE_START unsigned long val = wxStrtoul(start, &end, base); @@ -1683,7 +1721,7 @@ bool wxString::ToULong(unsigned long *pVal, int base) const bool wxString::ToLongLong(wxLongLong_t *pVal, int base) const { - wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); WX_STRING_TO_X_TYPE_START wxLongLong_t val = wxStrtoll(start, &end, base); @@ -1692,7 +1730,7 @@ bool wxString::ToLongLong(wxLongLong_t *pVal, int base) const bool wxString::ToULongLong(wxULongLong_t *pVal, int base) const { - wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); WX_STRING_TO_X_TYPE_START wxULongLong_t val = wxStrtoull(start, &end, base); @@ -1710,10 +1748,10 @@ bool wxString::ToDouble(double *pVal) const bool wxString::ToCLong(long *pVal, int base) const { - wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); WX_STRING_TO_X_TYPE_START -#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE +#if (wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE) && defined(wxHAS_XLOCALE_SUPPORT) long val = wxStrtol_lA(start, &end, base, wxCLocale); #else long val = wxStrtol_l(start, &end, base, wxCLocale); @@ -1723,10 +1761,10 @@ bool wxString::ToCLong(long *pVal, int base) const bool wxString::ToCULong(unsigned long *pVal, int base) const { - wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); WX_STRING_TO_X_TYPE_START -#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE +#if (wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE) && defined(wxHAS_XLOCALE_SUPPORT) unsigned long val = wxStrtoul_lA(start, &end, base, wxCLocale); #else unsigned long val = wxStrtoul_l(start, &end, base, wxCLocale); @@ -1737,7 +1775,7 @@ bool wxString::ToCULong(unsigned long *pVal, int base) const bool wxString::ToCDouble(double *pVal) const { WX_STRING_TO_X_TYPE_START -#if wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE +#if (wxUSE_UNICODE_UTF8 || !wxUSE_UNICODE) && defined(wxHAS_XLOCALE_SUPPORT) double val = wxStrtod_lA(start, &end, wxCLocale); #else double val = wxStrtod_l(start, &end, wxCLocale); @@ -1745,7 +1783,113 @@ bool wxString::ToCDouble(double *pVal) const WX_STRING_TO_X_TYPE_END } -#endif // wxUSE_XLOCALE +#else // wxUSE_XLOCALE + +// Provide implementation of these functions even when wxUSE_XLOCALE is +// disabled, we still need them in wxWidgets internal code. + +// For integers we just assume the current locale uses the same number +// representation as the C one as there is nothing else we can do. +bool wxString::ToCLong(long *pVal, int base) const +{ + return ToLong(pVal, base); +} + +bool wxString::ToCULong(unsigned long *pVal, int base) const +{ + return ToULong(pVal, base); +} + +// For floating point numbers we have to handle the problem of the decimal +// point which is different in different locales. +bool wxString::ToCDouble(double *pVal) const +{ + // Create a copy of this string using the decimal point instead of whatever + // separator the current locale uses. +#if wxUSE_INTL + wxString sep = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, + wxLOCALE_CAT_NUMBER); + if ( sep == "." ) + { + // We can avoid an unnecessary string copy in this case. + return ToDouble(pVal); + } +#else // !wxUSE_INTL + // We don't know what the current separator is so it might even be a point + // already, try to parse the string as a double: + if ( ToDouble(pVal) ) + { + // It must have been the point, nothing else to do. + return true; + } + + // Try to guess the separator, using the most common alternative value. + wxString sep(","); +#endif // wxUSE_INTL/!wxUSE_INTL + wxString cstr(*this); + cstr.Replace(".", sep); + + return cstr.ToDouble(pVal); +} + +#endif // wxUSE_XLOCALE/!wxUSE_XLOCALE + +// ---------------------------------------------------------------------------- +// number to string conversion +// ---------------------------------------------------------------------------- + +/* static */ +wxString wxString::FromDouble(double val, int precision) +{ + wxCHECK_MSG( precision >= -1, wxString(), "Invalid negative precision" ); + + wxString format; + if ( precision == -1 ) + { + format = "%g"; + } + else // Use fixed precision. + { + format.Printf("%%.%df", precision); + } + + return wxString::Format(format, val); +} + +/* static */ +wxString wxString::FromCDouble(double val, int precision) +{ + wxCHECK_MSG( precision >= -1, wxString(), "Invalid negative precision" ); + +#if wxUSE_STD_IOSTREAM && wxUSE_STD_STRING + // We assume that we can use the ostream and not wstream for numbers. + wxSTD ostringstream os; + if ( precision != -1 ) + { + os.precision(precision); + os.setf(std::ios::fixed, std::ios::floatfield); + } + + os << val; + return os.str(); +#else // !wxUSE_STD_IOSTREAM + // Can't use iostream locale support, fall back to the manual method + // instead. + wxString s = FromDouble(val, precision); +#if wxUSE_INTL + wxString sep = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, + wxLOCALE_CAT_NUMBER); +#else // !wxUSE_INTL + // As above, this is the most common alternative value. Notice that here it + // doesn't matter if we guess wrongly and the current separator is already + // ".": we'll just waste a call to Replace() in this case. + wxString sep(","); +#endif // wxUSE_INTL/!wxUSE_INTL + + s.Replace(sep, "."); + return s; +#endif // wxUSE_STD_IOSTREAM/!wxUSE_STD_IOSTREAM +} // --------------------------------------------------------------------------- // formatted output @@ -1864,16 +2008,16 @@ int wxString::DoPrintfUtf8(const char *format, ...) Since EILSEQ and EINVAL are rather common but EOVERFLOW is not and since EILSEQ and EINVAL are specifically defined to mean the error is other than an undersized buffer and no other errno are defined we treat those two - as meaning hard errors and everything else gets the old behavior which + as meaning hard errors and everything else gets the old behaviour which is to keep looping and increasing buffer size until the function succeeds. - In practice it's impossible to determine before compilation which behavior - may be used. The vswprintf function may have vsnprintf-like behavior or - vice-versa. Behavior detected on one release can theoretically change + In practice it's impossible to determine before compilation which behaviour + may be used. The vswprintf function may have vsnprintf-like behaviour or + vice-versa. Behaviour detected on one release can theoretically change with an updated release. Not to mention that configure testing for it would require the test to be run on the host system, not the build system which makes cross compilation difficult. Therefore, we make no assumptions - about behavior and try our best to handle every known case, including the + about behaviour and try our best to handle every known case, including the case where wxVsnprintf returns a negative number and fails to set errno. There is yet one more non-standard implementation and that is our own. @@ -1884,9 +2028,9 @@ int wxString::DoPrintfUtf8(const char *format, ...) at the given buffer size minus 1. It is supposed to do this even if it turns out that the buffer is sized too small. - Darwin (tested on 10.5) follows the C99 behavior exactly. + Darwin (tested on 10.5) follows the C99 behaviour exactly. - Glibc 2.6 almost follows the C99 behavior except vswprintf never sets + Glibc 2.6 almost follows the C99 behaviour except vswprintf never sets errno even when it fails. However, it only seems to ever fail due to an undersized buffer. */ @@ -1915,11 +2059,6 @@ static int DoStringPrintfV(wxString& str, if ( !buf ) { // out of memory - - // in UTF-8 build, leaving uninitialized junk in the buffer - // could result in invalid non-empty UTF-8 string, so just - // reset the string to empty on failure: - buf[0] = '\0'; return -1; } @@ -1940,7 +2079,7 @@ static int DoStringPrintfV(wxString& str, // always do it manually // FIXME: This really seems to be the wrong and would be an off-by-one // bug except the code above allocates an extra character. - buf[size] = _T('\0'); + buf[size] = wxT('\0'); // vsnprintf() may return either -1 (traditional Unix behaviour) or the // total number of characters which would have been written if the @@ -2046,31 +2185,31 @@ bool wxString::Matches(const wxString& mask) const wxString pattern; pattern.reserve(wxStrlen(pszMask)); - pattern += _T('^'); + pattern += wxT('^'); while ( *pszMask ) { switch ( *pszMask ) { - case _T('?'): - pattern += _T('.'); + case wxT('?'): + pattern += wxT('.'); break; - case _T('*'): - pattern += _T(".*"); + case wxT('*'): + pattern += wxT(".*"); break; - case _T('^'): - case _T('.'): - case _T('$'): - case _T('('): - case _T(')'): - case _T('|'): - case _T('+'): - case _T('\\'): + case wxT('^'): + case wxT('.'): + case wxT('$'): + case wxT('('): + case wxT(')'): + case wxT('|'): + case wxT('+'): + case wxT('\\'): // these characters are special in a RE, quote them // (however note that we don't quote '[' and ']' to allow // using them for Unix shell like matching) - pattern += _T('\\'); + pattern += wxT('\\'); // fall through default: @@ -2079,7 +2218,7 @@ bool wxString::Matches(const wxString& mask) const pszMask++; } - pattern += _T('$'); + pattern += wxT('$'); // and now use it return wxRegEx(pattern, wxRE_NOSUB | wxRE_EXTENDED).Matches(c_str());