#include "wx/defs.h"
#include "wx/string.h"
#include "wx/intl.h"
-#if wxUSE_THREADS
#include "wx/thread.h"
#endif
-#endif
#include <ctype.h>
#include <string.h>
// we want to find out if the current platform supports vsnprintf()-like
// function: for Unix this is done with configure, for Windows we test the
// compiler explicitly.
+//
+// FIXME currently, this is only for ANSI (!Unicode) strings, so we call this
+// function wxVsnprintfA (A for ANSI), should also find one for Unicode
+// strings in Unicode build
#ifdef __WXMSW__
- #ifdef __VISUALC__
- #define wxVsnprintf _vsnprintf
+ #if (defined(__VISUALC__) || defined(wxUSE_NORLANDER_HEADERS)) && !defined(__MINGW32__)
+ #define wxVsnprintfA _vsnprintf
#endif
#else // !Windows
#ifdef HAVE_VSNPRINTF
- #define wxVsnprintf vsnprintf
+ #define wxVsnprintfA vsnprintf
#endif
#endif // Windows/!Windows
-#ifndef wxVsnprintf
+#ifndef wxVsnprintfA
// in this case we'll use vsprintf() (which is ANSI and thus should be
// always available), but it's unsafe because it doesn't check for buffer
// size - so give a warning
- #define wxVsnprintf(buffer,len,format,argptr) vsprintf(buffer,format, argptr)
+ #define wxVsnprintfA(buf, len, format, arg) vsprintf(buf, format, arg)
#if defined(__VISUALC__)
#pragma message("Using sprintf() because no snprintf()-like function defined")
#elif defined(__GNUG__) && !defined(__UNIX__)
#warning "Using sprintf() because no snprintf()-like function defined"
#elif defined(__MWERKS__)
- #warning "Using sprintf() because no snprintf()-like function defined"
+ #warning "Using sprintf() because no snprintf()-like function defined"
#endif //compiler
#endif // no vsnprintf
#endif //std::string compatibility
+extern int WXDLLEXPORT wxVsnprintf(wxChar *buf, size_t len,
+ const wxChar *format, va_list argptr)
+{
+#if wxUSE_UNICODE
+ // FIXME should use wvsnprintf() or whatever if it's available
+ wxString s;
+ int iLen = s.PrintfV(format, argptr);
+ if ( iLen != -1 )
+ {
+ wxStrncpy(buf, s.c_str(), iLen);
+ }
+
+ return iLen;
+#else // ANSI
+ // vsnprintf() will not terminate the string with '\0' if there is not
+ // enough place, but we want the string to always be NUL terminated
+ int rc = wxVsnprintfA(buf, len - 1, format, argptr);
+ if ( rc == -1 )
+ {
+ buf[len] = 0;
+ }
+
+ return rc;
+#endif // Unicode/ANSI
+}
+
+extern int WXDLLEXPORT wxSnprintf(wxChar *buf, size_t len,
+ const wxChar *format, ...)
+{
+ va_list argptr;
+ va_start(argptr, format);
+
+ int iLen = wxVsnprintf(buf, len, format, argptr);
+
+ va_end(argptr);
+
+ return iLen;
+}
+
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
// allocates memory needed to store a C string of length nLen
void wxString::AllocBuffer(size_t nLen)
{
- wxASSERT( nLen > 0 ); //
- wxASSERT( nLen <= INT_MAX-1 ); // max size (enough room for 1 extra)
+ // allocating 0 sized buffer doesn't make sense, all empty strings should
+ // reuse g_strEmpty
+ wxASSERT( nLen > 0 );
+
+ // make sure that we don't overflow
+ wxASSERT( nLen < (INT_MAX / sizeof(wxChar)) -
+ (sizeof(wxStringData) + EXTRA_ALLOC + 1) );
STATISTICS_ADD(Length, nLen);
// must not share string and must have enough space
wxStringData* pData = GetStringData();
- if ( pData->IsShared() || (nLen > pData->nAllocLength) ) {
+ if ( pData->IsShared() || pData->IsEmpty() ) {
// can't work with old buffer, get new one
pData->Unlock();
AllocBuffer(nLen);
}
else {
- // update the string length
+ if ( nLen > pData->nAllocLength ) {
+ // realloc the buffer instead of calling malloc() again, this is more
+ // efficient
+ STATISTICS_ADD(Length, nLen);
+
+ nLen += EXTRA_ALLOC;
+
+ wxStringData *pDataOld = pData;
+ pData = (wxStringData*)
+ realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar));
+ if ( !pData ) {
+ // out of memory
+ free(pDataOld);
+
+ // FIXME we're going to crash...
+ return;
+ }
+
+ pData->nAllocLength = nLen;
+ m_pchData = pData->data();
+ }
+
+ // now we have enough space, just update the string length
pData->nDataLength = nLen;
}
else {
nLen += EXTRA_ALLOC;
+ wxStringData *pDataOld = pData;
wxStringData *p = (wxStringData *)
realloc(pData, sizeof(wxStringData) + (nLen + 1)*sizeof(wxChar));
if ( p == NULL ) {
- // @@@ what to do on memory error?
+ // don't leak memory
+ free(pDataOld);
+
+ // FIXME what to do on memory error?
return;
}
{
wxStringData *pData = GetStringData();
- // this variable is unused in release build, so avoid the compiler warning by
- // just not declaring it
+ // this variable is unused in release build, so avoid the compiler warning
+ // by just not declaring it
#ifdef __WXDEBUG__
void *p =
#endif
realloc(pData, sizeof(wxStringData) + (pData->nDataLength + 1)*sizeof(wxChar));
+ // we rely on a reasonable realloc() implementation here - so far I haven't
+ // seen any which wouldn't behave like this
+
wxASSERT( p != NULL ); // can't free memory?
wxASSERT( p == pData ); // we're decrementing the size - block shouldn't move!
}
return (psz == NULL) ? wxNOT_FOUND : psz - (const wxChar*) m_pchData;
}
+// ----------------------------------------------------------------------------
+// conversion to numbers
+// ----------------------------------------------------------------------------
+
+bool wxString::ToLong(long *val) const
+{
+ wxCHECK_MSG( val, FALSE, _T("NULL pointer in wxString::ToLong") );
+
+ const wxChar *start = c_str();
+ wxChar *end;
+ *val = wxStrtol(start, &end, 10);
+
+ // return TRUE only if scan was stopped by the terminating NUL and if the
+ // string was not empty to start with
+ return !*end && (end != start);
+}
+
+bool wxString::ToULong(unsigned long *val) const
+{
+ wxCHECK_MSG( val, FALSE, _T("NULL pointer in wxString::ToULong") );
+
+ const wxChar *start = c_str();
+ wxChar *end;
+ *val = wxStrtoul(start, &end, 10);
+
+ // return TRUE only if scan was stopped by the terminating NUL and if the
+ // string was not empty to start with
+ return !*end && (end != start);
+}
+
+bool wxString::ToDouble(double *val) const
+{
+ wxCHECK_MSG( val, FALSE, _T("NULL pointer in wxString::ToDouble") );
+
+ const wxChar *start = c_str();
+ wxChar *end;
+ *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
+ return !*end && (end != start);
+}
+
// ---------------------------------------------------------------------------
// stream-like operators
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// formatted output
// ---------------------------------------------------------------------------
+
+/* static */
+wxString wxString::Format(const wxChar *pszFormat, ...)
+{
+ va_list argptr;
+ va_start(argptr, pszFormat);
+
+ wxString s;
+ s.PrintfV(pszFormat, argptr);
+
+ va_end(argptr);
+
+ return s;
+}
+
+/* static */
+wxString wxString::FormatV(const wxChar *pszFormat, va_list argptr)
+{
+ wxString s;
+ s.Printf(pszFormat, argptr);
+ return s;
+}
+
int wxString::Printf(const wxChar *pszFormat, ...)
{
va_list argptr;
int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
{
- // static buffer to avoid dynamic memory allocation each time
- char s_szScratch[1024]; // using static buffer causes internal compiler err
-#if 0
-#if wxUSE_THREADS
- // protect the static buffer
- static wxCriticalSection critsect;
- wxCriticalSectionLocker lock(critsect);
-#endif
-#endif
-
#if wxUSE_EXPERIMENTAL_PRINTF
-// the new implementation
+ // the new implementation
+
+ // buffer to avoid dynamic memory allocation each time for small strings
+ char szScratch[1024];
Reinit();
for (size_t n = 0; pszFormat[n]; n++)
s_szFlags[flagofs] = '\0';
if (ilen == 0 ) {
int val = va_arg(argptr, int);
- ::sprintf(s_szScratch, s_szFlags, val);
+ ::sprintf(szScratch, s_szFlags, val);
}
else if (ilen == -1) {
short int val = va_arg(argptr, short int);
- ::sprintf(s_szScratch, s_szFlags, val);
+ ::sprintf(szScratch, s_szFlags, val);
}
else if (ilen == 1) {
long int val = va_arg(argptr, long int);
- ::sprintf(s_szScratch, s_szFlags, val);
+ ::sprintf(szScratch, s_szFlags, val);
}
else if (ilen == 2) {
#if SIZEOF_LONG_LONG
long long int val = va_arg(argptr, long long int);
- ::sprintf(s_szScratch, s_szFlags, val);
+ ::sprintf(szScratch, s_szFlags, val);
#else
long int val = va_arg(argptr, long int);
- ::sprintf(s_szScratch, s_szFlags, val);
+ ::sprintf(szScratch, s_szFlags, val);
#endif
}
else if (ilen == 3) {
size_t val = va_arg(argptr, size_t);
- ::sprintf(s_szScratch, s_szFlags, val);
+ ::sprintf(szScratch, s_szFlags, val);
}
- *this += wxString(s_szScratch);
+ *this += wxString(szScratch);
done = TRUE;
break;
case wxT('e'):
s_szFlags[flagofs] = '\0';
if (ilen == 2) {
long double val = va_arg(argptr, long double);
- ::sprintf(s_szScratch, s_szFlags, val);
+ ::sprintf(szScratch, s_szFlags, val);
} else {
double val = va_arg(argptr, double);
- ::sprintf(s_szScratch, s_szFlags, val);
+ ::sprintf(szScratch, s_szFlags, val);
}
- *this += wxString(s_szScratch);
+ *this += wxString(szScratch);
done = TRUE;
break;
case wxT('p'):
CHECK_PREC
s_szFlags[flagofs++] = pszFormat[n];
s_szFlags[flagofs] = '\0';
- ::sprintf(s_szScratch, s_szFlags, val);
- *this += wxString(s_szScratch);
+ ::sprintf(szScratch, s_szFlags, val);
+ *this += wxString(szScratch);
done = TRUE;
}
break;
} else *this += pszFormat[n];
#else
- // NB: wxVsnprintf() may return either less than the buffer size or -1 if there
- // is not enough place depending on implementation
- int iLen = wxVsnprintf(s_szScratch, WXSIZEOF(s_szScratch), pszFormat, argptr);
- char *buffer;
- if ( iLen < (int)WXSIZEOF(s_szScratch) ) {
- buffer = s_szScratch;
+ // buffer to avoid dynamic memory allocation each time for small strings
+ char szScratch[1024];
+
+ // NB: wxVsnprintf() may return either less than the buffer size or -1 if
+ // there is not enough place depending on implementation
+ int iLen = wxVsnprintfA(szScratch, WXSIZEOF(szScratch), pszFormat, argptr);
+ if ( iLen != -1 ) {
+ // the whole string is in szScratch
+ *this = szScratch;
}
else {
- int size = WXSIZEOF(s_szScratch) * 2;
- buffer = (char *)malloc(size);
- while ( buffer != NULL ) {
- iLen = wxVsnprintf(buffer, WXSIZEOF(s_szScratch), pszFormat, argptr);
- if ( iLen < size ) {
+ bool outOfMemory = FALSE;
+ int size = 2*WXSIZEOF(szScratch);
+ while ( !outOfMemory ) {
+ char *buf = GetWriteBuf(size);
+ if ( buf )
+ iLen = wxVsnprintfA(buf, size, pszFormat, argptr);
+ else
+ outOfMemory = TRUE;
+
+ UngetWriteBuf();
+
+ if ( iLen != -1 ) {
// ok, there was enough space
break;
}
// still not enough, double it again
- buffer = (char *)realloc(buffer, size *= 2);
+ size *= 2;
}
- if ( !buffer ) {
+ if ( outOfMemory ) {
// out of memory
return -1;
}
}
-
- wxString s(buffer);
- *this = s;
-
- if ( buffer != s_szScratch )
- free(buffer);
-#endif
+#endif // wxUSE_EXPERIMENTAL_PRINTF/!wxUSE_EXPERIMENTAL_PRINTF
return Len();
}
return npos;
}
-wxString wxString::substr(size_t nStart, size_t nLen) const
-{
- // npos means 'take all'
- if ( nLen == npos )
- nLen = 0;
-
- wxASSERT( nStart + nLen <= Len() );
-
- return wxString(c_str() + nStart, nLen == npos ? 0 : nLen);
-}
-
wxString& wxString::erase(size_t nStart, size_t nLen)
{
wxString strTmp(c_str(), nStart);
if ( src.m_nCount > ARRAY_DEFAULT_INITIAL_SIZE )
Alloc(src.m_nCount);
- // we can't just copy the pointers here because otherwise we would share
- // the strings with another array because strings are ref counted
-#if 0
- if ( m_nCount != 0 )
- memcpy(m_pItems, src.m_pItems, m_nCount*sizeof(wxChar *));
-#endif // 0
-
for ( size_t n = 0; n < src.m_nCount; n++ )
Add(src[n]);
-
- // if the other array is auto sorted too, we're already sorted, but
- // otherwise we should rearrange the items
- if ( m_autoSort && !src.m_autoSort )
- Sort();
}
// grow the array