X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0d8b0f94c0d80f9cfedc69059f4f20c76fd55384..e9863f4ea1ab957e59b421d36e6199c7b472476d:/src/common/string.cpp diff --git a/src/common/string.cpp b/src/common/string.cpp index fbfe6cb6b5..c3bc9331cb 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -35,10 +35,6 @@ #include #include -#ifdef __SALFORDC__ - #include -#endif - #include "wx/hashmap.h" // string handling functions used by wxString: @@ -72,11 +68,10 @@ const size_t wxString::npos = (size_t) -1; wxSTD ostream& operator<<(wxSTD ostream& os, const wxCStrData& str) { -// FIXME-UTF8: always, not only if wxUSE_UNICODE -#if wxUSE_UNICODE && !defined(__BORLANDC__) - return os << (const wchar_t*)str.AsWCharBuf(); +#if wxUSE_UNICODE && !wxUSE_UNICODE_UTF8 + return os << (const char *)str.AsCharBuf(); #else - return os << (const char*)str.AsCharBuf(); + return os << str.AsInternal(); #endif } @@ -97,6 +92,25 @@ wxSTD ostream& operator<<(wxSTD ostream& os, const wxWCharBuffer& str) } #endif +#if wxUSE_UNICODE && defined(HAVE_WOSTREAM) + +wxSTD wostream& operator<<(wxSTD wostream& wos, const wxString& str) +{ + return wos << str.wc_str(); +} + +wxSTD wostream& operator<<(wxSTD wostream& wos, const wxCStrData& str) +{ + return wos << str.AsWChar(); +} + +wxSTD wostream& operator<<(wxSTD wostream& wos, const wxWCharBuffer& str) +{ + return wos << str.data(); +} + +#endif // wxUSE_UNICODE && defined(HAVE_WOSTREAM) + #endif // wxUSE_STD_IOSTREAM // =========================================================================== @@ -355,6 +369,9 @@ wxString::SubstrBufFromMB wxString::ConvertStr(const char *psz, size_t nLength, // UTF-8 sequence and psz may be invalid: if ( wxStringOperations::IsValidUtf8String(psz, 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); } // else: do the roundtrip through wchar_t* @@ -1222,34 +1239,27 @@ size_t wxString::Replace(const wxString& strOld, size_t uiCount = 0; // count of replacements made - size_t uiOldLen = strOld.length(); - size_t uiNewLen = strNew.length(); + const size_t uiOldLen = strOld.m_impl.length(); + const size_t uiNewLen = strNew.m_impl.length(); - size_t dwPos = 0; - - while ( (*this)[dwPos] != wxT('\0') ) + for ( size_t dwPos = 0; dwPos < m_impl.length(); ) { - //DO NOT USE STRSTR HERE - //this string can contain embedded null characters, - //so strstr will function incorrectly - dwPos = find(strOld, dwPos); + dwPos = m_impl.find(strOld.m_impl, dwPos); if ( dwPos == npos ) - break; // exit the loop - else - { - //replace this occurance of the old string with the new one - replace(dwPos, uiOldLen, strNew, uiNewLen); + break; - //move up pos past the string that was replaced - dwPos += uiNewLen; + // replace this occurance of the old string with the new one + m_impl.replace(dwPos, uiOldLen, strNew.m_impl); - //increase replace count - ++uiCount; + // move up pos past the string that was replaced + dwPos += uiNewLen; - // stop now? - if ( !bReplaceAll ) - break; // exit the loop - } + // increase replace count + ++uiCount; + + // stop after the first one? + if ( !bReplaceAll ) + break; } return uiCount; @@ -1329,9 +1339,9 @@ wxString& wxString::MakeLower() // --------------------------------------------------------------------------- // some compilers (VC++ 6.0 not to name them) return true for a call to -// isspace('ê') in the C locale which seems to be broken to me, but we have to -// live with this by checking that the character is a 7 bit one - even if this -// may fail to detect some spaces (I don't know if Unicode doesn't have +// isspace('\xEA') in the C locale which seems to be broken to me, but we have +// to live with this by checking that the character is a 7 bit one - even if +// this may fail to detect some spaces (I don't know if Unicode doesn't have // space-like symbols somewhere except in the first 128 chars), it is arguably // still better than trimming away accented letters inline int wxSafeIsspace(wxChar ch) { return (ch < 127) && wxIsspace(ch); } @@ -1426,61 +1436,62 @@ int wxString::Find(wxUniChar ch, bool bFromEnd) const #define DO_IF_NOT_WINCE(x) #endif -#define WX_STRING_TO_INT_TYPE(val, base, func) \ - wxCHECK_MSG( val, false, _T("NULL output pointer") ); \ +#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") ); \ \ DO_IF_NOT_WINCE( errno = 0; ) \ \ const wxStringCharType *start = wx_str(); \ wxStringCharType *end; \ - *val = func(start, &end, base); \ + 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: */ \ - return !*end && (end != start) \ - DO_IF_NOT_WINCE( && (errno != ERANGE) ) + if ( *end || end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) \ + return false; \ + *out = val; \ + return true -bool wxString::ToLong(long *val, int base) const +bool wxString::ToLong(long *pVal, int base) const { - WX_STRING_TO_INT_TYPE(val, base, wxStrtol); + WX_STRING_TO_INT_TYPE(pVal, base, wxStrtol, long); } -bool wxString::ToULong(unsigned long *val, int base) const +bool wxString::ToULong(unsigned long *pVal, int base) const { - WX_STRING_TO_INT_TYPE(val, base, wxStrtoul); + WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoul, unsigned long); } -bool wxString::ToLongLong(wxLongLong_t *val, int base) const +bool wxString::ToLongLong(wxLongLong_t *pVal, int base) const { - WX_STRING_TO_INT_TYPE(val, base, wxStrtoll); + WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoll, wxLongLong_t); } -bool wxString::ToULongLong(wxULongLong_t *val, int base) const +bool wxString::ToULongLong(wxULongLong_t *pVal, int base) const { - WX_STRING_TO_INT_TYPE(val, base, wxStrtoull); + WX_STRING_TO_INT_TYPE(pVal, base, wxStrtoull, wxULongLong_t); } -bool wxString::ToDouble(double *val) const +bool wxString::ToDouble(double *pVal) const { - wxCHECK_MSG( val, false, _T("NULL pointer in wxString::ToDouble") ); + wxCHECK_MSG( pVal, false, _T("NULL output pointer") ); -#ifndef __WXWINCE__ - errno = 0; -#endif + DO_IF_NOT_WINCE( errno = 0; ) const wxChar *start = c_str(); wxChar *end; - *val = wxStrtod(start, &end); + double val = wxStrtod(start, &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 - return !*end && (end != start) -#ifndef __WXWINCE__ - && (errno != ERANGE) -#endif - ; + if ( *end || end == start DO_IF_NOT_WINCE(|| errno == ERANGE) ) + return false; + + *pVal = val; + + return true; } // --------------------------------------------------------------------------- @@ -1572,6 +1583,60 @@ int wxString::DoPrintfUtf8(const char *format, ...) } #endif // wxUSE_UNICODE_UTF8 +/* + Uses wxVsnprintf and places the result into the this string. + + In ANSI build, wxVsnprintf is effectively vsnprintf but in Unicode build + it is vswprintf. Due to a discrepancy between vsnprintf and vswprintf in + the ISO C99 (and thus SUSv3) standard the return value for the case of + an undersized buffer is inconsistent. For conforming vsnprintf + implementations the function must return the number of characters that + would have been printed had the buffer been large enough. For conforming + vswprintf implementations the function must return a negative number + and set errno. + + What vswprintf sets errno to is undefined but Darwin seems to set it to + EOVERFLOW. The only expected errno are EILSEQ and EINVAL. Both of + those are defined in the standard and backed up by several conformance + statements. Note that ENOMEM mentioned in the manual page does not + apply to swprintf, only wprintf and fwprintf. + + Official manual page: + http://www.opengroup.org/onlinepubs/009695399/functions/swprintf.html + + Some conformance statements (AIX, Solaris): + http://www.opengroup.org/csq/view.mhtml?RID=ibm%2FSD1%2F3 + http://www.theopengroup.org/csq/view.mhtml?norationale=1&noreferences=1&RID=Fujitsu%2FSE2%2F10 + + 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 + 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 + 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 + 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. + Fortunately, that can be detected at compile-time. + + On top of all that, ISO C99 explicitly defines snprintf to write a null + character to the last position of the specified buffer. That would be at + 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. + + Glibc 2.6 almost follows the C99 behavior except vswprintf never sets + errno even when it fails. However, it only seems to ever fail due + to an undersized buffer. +*/ #if wxUSE_UNICODE_UTF8 template #else @@ -1609,12 +1674,19 @@ static int DoStringPrintfV(wxString& str, // only a copy va_list argptrcopy; wxVaCopy(argptrcopy, argptr); + +#ifndef __WXWINCE__ + // Set errno to 0 to make it determinate if wxVsnprintf fails to set it. + errno = 0; +#endif int len = wxVsnprintf(buf, size, format, argptrcopy); va_end(argptrcopy); // some implementations of vsnprintf() don't NUL terminate // the string if there is not enough space for it so // 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'); // vsnprintf() may return either -1 (traditional Unix behaviour) or the @@ -1636,19 +1708,33 @@ static int DoStringPrintfV(wxString& str, // assume it only returns error if there is not enough space, but // as we don't know how much we need, double the current size of // the buffer - size *= 2; +#ifndef __WXWINCE__ + if( (errno == EILSEQ) || (errno == EINVAL) ) + // If errno was set to one of the two well-known hard errors + // then fail immediately to avoid an infinite loop. + return -1; + else +#endif // __WXWINCE__ + // still not enough, as we don't know how much we need, double the + // current size of the buffer + size *= 2; #endif // wxUSE_WXVSNPRINTF/!wxUSE_WXVSNPRINTF } else if ( len >= size ) { #if wxUSE_WXVSNPRINTF - // we know that our own implementation of wxVsnprintf() returns + // we know that our own implementation of wxVsnprintf() returns // size+1 when there's not enough space but that's not the size // of the required buffer! size *= 2; // so we just double the current size of the buffer #else // some vsnprintf() implementations NUL-terminate the buffer and // some don't in len == size case, to be safe always add 1 + // FIXME: I don't quite understand this comment. The vsnprintf + // function is specifically defined to return the number of + // characters printed not including the null terminator. + // So OF COURSE you need to add 1 to get the right buffer size. + // The following line is definitely correct, no question. size = len + 1; #endif }