#include <string.h>
#include <stdlib.h>
-#ifdef __SALFORDC__
- #include <clib.h>
-#endif
-
#include "wx/hashmap.h"
// string handling functions used by wxString:
// ---------------------------------------------------------------------------
// 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); }
#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;
}
// ---------------------------------------------------------------------------
}
#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<typename BufferType>
#else
// 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
// 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
}