X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/6689960c3cea91383a8ce78de01cb30d1ddc54ef..a2d0722f48aeda2992e1214ada10c741a57ce223:/src/common/string.cpp diff --git a/src/common/string.cpp b/src/common/string.cpp index 56af51ae22..96c9c526f4 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 @@ -36,6 +38,16 @@ #include #include "wx/hashmap.h" +#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 @@ -50,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 @@ -103,7 +119,7 @@ static wxStrCacheInitializer gs_stringCacheInit; // gdb seems to be unable to display thread-local variables correctly, at least // not my 6.4.98 version under amd64, so provide this debugging helper to do it -#ifdef __WXDEBUG__ +#if wxDEBUG_LEVEL >= 2 struct wxStrCacheDumper { @@ -128,7 +144,7 @@ struct wxStrCacheDumper void wxDumpStrCache() { wxStrCacheDumper::ShowAll(); } -#endif // __WXDEBUG__ +#endif // wxDEBUG_LEVEL >= 2 #ifdef wxPROFILE_STRING_CACHE @@ -180,7 +196,7 @@ static wxStrCacheStatsDumper s_showCacheStats; wxSTD ostream& operator<<(wxSTD ostream& os, const wxCStrData& str) { #if wxUSE_UNICODE && !wxUSE_UNICODE_UTF8 - const wxCharBuffer buf(str.AsCharBuf()); + const wxScopedCharBuffer buf(str.AsCharBuf()); if ( !buf ) os.clear(wxSTD ios_base::failbit); else @@ -197,13 +213,13 @@ wxSTD ostream& operator<<(wxSTD ostream& os, const wxString& str) return os << str.c_str(); } -wxSTD ostream& operator<<(wxSTD ostream& os, const wxCharBuffer& str) +wxSTD ostream& operator<<(wxSTD ostream& os, const wxScopedCharBuffer& str) { return os << str.data(); } #ifndef __BORLANDC__ -wxSTD ostream& operator<<(wxSTD ostream& os, const wxWCharBuffer& str) +wxSTD ostream& operator<<(wxSTD ostream& os, const wxScopedWCharBuffer& str) { return os << str.data(); } @@ -221,7 +237,7 @@ wxSTD wostream& operator<<(wxSTD wostream& wos, const wxCStrData& str) return wos << str.AsWChar(); } -wxSTD wostream& operator<<(wxSTD wostream& wos, const wxWCharBuffer& str) +wxSTD wostream& operator<<(wxSTD wostream& wos, const wxScopedWCharBuffer& str) { return wos << str.data(); } @@ -359,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. - wxCharBuffer 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: - wxWCharBuffer 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 // =========================================================================== @@ -463,15 +390,15 @@ wxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength, { // anything to do? if ( !psz || nLength == 0 ) - return SubstrBufFromMB(L"", 0); + return SubstrBufFromMB(wxWCharBuffer(L""), 0); if ( nLength == npos ) nLength = wxNO_LEN; size_t wcLen; - wxWCharBuffer wcBuf(conv.cMB2WC(psz, nLength, &wcLen)); + wxScopedWCharBuffer wcBuf(conv.cMB2WC(psz, nLength, &wcLen)); if ( !wcLen ) - return SubstrBufFromMB(_T(""), 0); + return SubstrBufFromMB(wxWCharBuffer(L""), 0); else return SubstrBufFromMB(wcBuf, wcLen); } @@ -484,7 +411,7 @@ wxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength, { // anything to do? if ( !psz || nLength == 0 ) - return SubstrBufFromMB("", 0); + return SubstrBufFromMB(wxCharBuffer(""), 0); // if psz is already in UTF-8, we don't have to do the roundtrip to // wchar_t* and back: @@ -497,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* } @@ -507,14 +435,14 @@ wxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength, // first convert to wide string: size_t wcLen; - wxWCharBuffer wcBuf(conv.cMB2WC(psz, nLength, &wcLen)); + wxScopedWCharBuffer wcBuf(conv.cMB2WC(psz, nLength, &wcLen)); if ( !wcLen ) - return SubstrBufFromMB("", 0); + return SubstrBufFromMB(wxCharBuffer(""), 0); // 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; } @@ -527,71 +455,111 @@ wxString::SubstrBufFromWC wxString::ConvertStr(const wchar_t *pwz, size_t nLengt { // anything to do? if ( !pwz || nLength == 0 ) - return SubstrBufFromWC("", 0); + return SubstrBufFromWC(wxCharBuffer(""), 0); if ( nLength == npos ) nLength = wxNO_LEN; size_t mbLen; - wxCharBuffer mbBuf(conv.cWC2MB(pwz, nLength, &mbLen)); + wxScopedCharBuffer mbBuf(conv.cWC2MB(pwz, nLength, &mbLen)); if ( !mbLen ) - return SubstrBufFromWC("", 0); + return SubstrBufFromWC(wxCharBuffer(""), 0); else return SubstrBufFromWC(mbBuf, mbLen); } #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 wxCharBuffer 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 wxWCharBuffer 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 wxCharBuffer 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 wxCharBuffer::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; - wxWCharBuffer 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 wxWCharBuffer 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() @@ -682,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; @@ -698,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; @@ -714,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; @@ -730,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; @@ -948,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 ) @@ -962,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 ) @@ -995,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; @@ -1019,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; @@ -1035,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 ) @@ -1057,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; @@ -1115,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(); @@ -1139,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 } @@ -1168,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; } @@ -1188,13 +1210,13 @@ 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)); } -const wxCharBuffer wxString::ToAscii() const +const wxScopedCharBuffer wxString::ToAscii() const { // this will allocate enough space for the terminating NUL too wxCharBuffer buffer(length()); @@ -1243,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; @@ -1293,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; } @@ -1320,7 +1342,7 @@ 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; } @@ -1365,7 +1387,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(); @@ -1397,30 +1419,62 @@ size_t wxString::Replace(const wxString& strOld, break; } } - else // general case + else if ( !bReplaceAll) + { + size_t pos = m_impl.find(strOld, 0); + if ( pos != npos ) + { + m_impl.replace(pos, strOld.m_impl.length(), strNew.m_impl); + uiCount = 1; + } + } + else // replace all occurrences { const size_t uiOldLen = strOld.m_impl.length(); const size_t uiNewLen = strNew.m_impl.length(); - for ( size_t pos = 0; ; ) + // first scan the string to find all positions at which the replacement + // should be made + wxVector replacePositions; + + size_t pos; + for ( pos = m_impl.find(strOld.m_impl, 0); + pos != npos; + pos = m_impl.find(strOld.m_impl, pos + uiOldLen)) { - pos = m_impl.find(strOld.m_impl, pos); - if ( pos == npos ) - break; + replacePositions.push_back(pos); + ++uiCount; + } - // replace this occurrence of the old string with the new one - m_impl.replace(pos, uiOldLen, strNew.m_impl); + if ( !uiCount ) + return 0; - // move up pos past the string that was replaced - pos += uiNewLen; + // allocate enough memory for the whole new string + wxString tmp; + tmp.m_impl.reserve(m_impl.length() + uiCount*(uiNewLen - uiOldLen)); - // increase replace count - uiCount++; + // copy this string to tmp doing replacements on the fly + size_t replNum = 0; + for ( pos = 0; replNum < uiCount; replNum++ ) + { + const size_t nextReplPos = replacePositions[replNum]; - // stop after the first one? - if ( !bReplaceAll ) - break; + if ( pos != nextReplPos ) + { + tmp.m_impl.append(m_impl, pos, nextReplPos - pos); + } + + tmp.m_impl.append(strNew.m_impl); + pos = nextReplPos + uiOldLen; + } + + if ( pos != m_impl.length() ) + { + // append the rest of the string unchanged + tmp.m_impl.append(m_impl, pos, m_impl.length() - pos); } + + swap(tmp); } return uiCount; @@ -1455,7 +1509,7 @@ bool wxString::IsNumber() const const_iterator i = begin(); - if ( *i == _T('-') || *i == _T('+') ) + if ( *i == wxT('-') || *i == wxT('+') ) ++i; for ( ; i != end(); ++i ) @@ -1611,62 +1665,183 @@ int wxString::Find(wxUniChar ch, bool bFromEnd) const #define DO_IF_NOT_WINCE(x) #endif -#define WX_STRING_TO_INT_TYPE(out, base, func, T) \ - wxCHECK_MSG( out, false, _T("NULL output pointer") ); \ - wxASSERT_MSG( !base || (base > 1 && base <= 36), _T("invalid base") ); \ - \ +#define WX_STRING_TO_X_TYPE_START \ + wxCHECK_MSG( pVal, false, wxT("NULL output pointer") ); \ DO_IF_NOT_WINCE( errno = 0; ) \ - \ const wxStringCharType *start = wx_str(); \ - wxStringCharType *end; \ - T val = func(start, &end, base); \ - \ - /* 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) ) \ + 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 \ + if ( end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) \ return false; \ - *out = val; \ - return true + *pVal = val; \ + return !*end; bool wxString::ToLong(long *pVal, int base) const { - WX_STRING_TO_INT_TYPE(pVal, base, wxStrtol, long); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); + + WX_STRING_TO_X_TYPE_START + long val = wxStrtol(start, &end, base); + WX_STRING_TO_X_TYPE_END } bool wxString::ToULong(unsigned long *pVal, int base) const { - WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoul, unsigned long); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); + + WX_STRING_TO_X_TYPE_START + unsigned long val = wxStrtoul(start, &end, base); + WX_STRING_TO_X_TYPE_END } bool wxString::ToLongLong(wxLongLong_t *pVal, int base) const { - WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoll, wxLongLong_t); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); + + WX_STRING_TO_X_TYPE_START + wxLongLong_t val = wxStrtoll(start, &end, base); + WX_STRING_TO_X_TYPE_END } bool wxString::ToULongLong(wxULongLong_t *pVal, int base) const { - WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoull, wxULongLong_t); + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); + + WX_STRING_TO_X_TYPE_START + wxULongLong_t val = wxStrtoull(start, &end, base); + WX_STRING_TO_X_TYPE_END } bool wxString::ToDouble(double *pVal) const { - wxCHECK_MSG( pVal, false, _T("NULL output pointer") ); + WX_STRING_TO_X_TYPE_START + double val = wxStrtod(start, &end); + WX_STRING_TO_X_TYPE_END +} - DO_IF_NOT_WINCE( errno = 0; ) +#if wxUSE_XLOCALE - const wxChar *start = c_str(); - wxChar *end; - double val = wxStrtod(start, &end); +bool wxString::ToCLong(long *pVal, int base) const +{ + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); - // 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) ) - return false; + WX_STRING_TO_X_TYPE_START +#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); +#endif + WX_STRING_TO_X_TYPE_END +} - *pVal = val; +bool wxString::ToCULong(unsigned long *pVal, int base) const +{ + wxASSERT_MSG( !base || (base > 1 && base <= 36), wxT("invalid base") ); - return true; + WX_STRING_TO_X_TYPE_START +#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); +#endif + WX_STRING_TO_X_TYPE_END +} + +bool wxString::ToCDouble(double *pVal) const +{ + WX_STRING_TO_X_TYPE_START +#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); +#endif + WX_STRING_TO_X_TYPE_END +} + +#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::FromCDouble(double val) +{ +#if wxUSE_STD_IOSTREAM && wxUSE_STD_STRING + // We assume that we can use the ostream and not wstream for numbers. + wxSTD ostringstream os; + 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); +#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 } // --------------------------------------------------------------------------- @@ -1862,7 +2037,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 @@ -1968,31 +2143,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: @@ -2001,7 +2176,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()); @@ -2010,8 +2185,8 @@ bool wxString::Matches(const wxString& mask) const // FIXME-UTF8: implement using iterators, remove #if #if wxUSE_UNICODE_UTF8 - wxWCharBuffer maskBuf = mask.wc_str(); - wxWCharBuffer txtBuf = wc_str(); + const wxScopedWCharBuffer maskBuf = mask.wc_str(); + const wxScopedWCharBuffer txtBuf = wc_str(); const wxChar *pszMask = maskBuf.data(); const wxChar *pszTxt = txtBuf.data(); #else