Fix wxVsnprintf-using code on both trunk and 2.8 and document how native
functions are supposed to behave as well as how some of them do. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/WX_2_8_BRANCH@49251 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -1805,12 +1805,58 @@ int wxString::Printf(const wxChar *pszFormat, ...)
|
|||||||
return iLen;
|
return iLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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 that are defined anywhere are by an
|
||||||
|
addendum indicating that EILSEQ should be set for bad input characters and
|
||||||
|
EINVALID for bad arguments such as a NULL buffer pointer. It would appear
|
||||||
|
that setting EOVERFLOW is not documented anywhere and has only been at
|
||||||
|
this time observed on Darwin.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
|
int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
|
||||||
{
|
{
|
||||||
int size = 1024;
|
int size = 1024;
|
||||||
|
|
||||||
for ( ;; )
|
for ( ;; )
|
||||||
{
|
{
|
||||||
|
// Allocate 1 more character than we tell wxVsnprintf about
|
||||||
|
// just in case it is buggy.
|
||||||
|
// FIXME: I have a feeling that the underlying function was not buggy
|
||||||
|
// and I suspect it was to fix the buf[size] = '\0' line below
|
||||||
wxStringBuffer tmp(*this, size + 1);
|
wxStringBuffer tmp(*this, size + 1);
|
||||||
wxChar *buf = tmp;
|
wxChar *buf = tmp;
|
||||||
|
|
||||||
@@ -1824,12 +1870,19 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
|
|||||||
// only a copy
|
// only a copy
|
||||||
va_list argptrcopy;
|
va_list argptrcopy;
|
||||||
wxVaCopy(argptrcopy, argptr);
|
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, pszFormat, argptrcopy);
|
int len = wxVsnprintf(buf, size, pszFormat, argptrcopy);
|
||||||
va_end(argptrcopy);
|
va_end(argptrcopy);
|
||||||
|
|
||||||
// some implementations of vsnprintf() don't NUL terminate
|
// some implementations of vsnprintf() don't NUL terminate
|
||||||
// the string if there is not enough space for it so
|
// the string if there is not enough space for it so
|
||||||
// always do it manually
|
// 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] = _T('\0');
|
||||||
|
|
||||||
// vsnprintf() may return either -1 (traditional Unix behaviour) or the
|
// vsnprintf() may return either -1 (traditional Unix behaviour) or the
|
||||||
@@ -1843,9 +1896,17 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
|
|||||||
// the user's format string
|
// the user's format string
|
||||||
return -1;
|
return -1;
|
||||||
#else // assume that system version only returns error if not enough space
|
#else // assume that system version only returns error if not enough space
|
||||||
|
#ifndef __WXWINCE__
|
||||||
|
if( (errno == 0) || (errno == EOVERFLOW) )
|
||||||
// still not enough, as we don't know how much we need, double the
|
// still not enough, as we don't know how much we need, double the
|
||||||
// current size of the buffer
|
// current size of the buffer
|
||||||
|
size *= 2;
|
||||||
|
else
|
||||||
|
// If errno was set to something else, assume hard failure.
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
size *= 2;
|
size *= 2;
|
||||||
|
#endif
|
||||||
#endif // wxUSE_WXVSNPRINTF/!wxUSE_WXVSNPRINTF
|
#endif // wxUSE_WXVSNPRINTF/!wxUSE_WXVSNPRINTF
|
||||||
}
|
}
|
||||||
else if ( len >= size )
|
else if ( len >= size )
|
||||||
@@ -1858,6 +1919,11 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
|
|||||||
#else
|
#else
|
||||||
// some vsnprintf() implementations NUL-terminate the buffer and
|
// some vsnprintf() implementations NUL-terminate the buffer and
|
||||||
// some don't in len == size case, to be safe always add 1
|
// 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;
|
size = len + 1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user