diff --git a/include/wx/wxchar.h b/include/wx/wxchar.h index 1068c3dd69..84e8ee28f2 100644 --- a/include/wx/wxchar.h +++ b/include/wx/wxchar.h @@ -982,13 +982,18 @@ WXDLLIMPEXP_BASE bool wxOKlibc(); /* for internal use */ #endif /* wxUSE_PRINTF_POS_PARAMS/!wxUSE_PRINTF_POS_PARAMS */ #ifndef wxSnprintf_ - /* no [v]snprintf(), cook our own */ + /* no snprintf(), cook our own */ WXDLLIMPEXP_BASE int wxSnprintf_(wxChar *buf, size_t len, const wxChar *format, ...) ATTRIBUTE_PRINTF_3; #endif #ifndef wxVsnprintf_ + /* no (suitable) vsnprintf(), cook our own */ WXDLLIMPEXP_BASE int wxVsnprintf_(wxChar *buf, size_t len, const wxChar *format, va_list argptr); + + #define wxUSE_WXVSNPRINTF 1 +#else + #define wxUSE_WXVSNPRINTF 0 #endif /* diff --git a/src/common/string.cpp b/src/common/string.cpp index 2a98946787..78008c79a7 100644 --- a/src/common/string.cpp +++ b/src/common/string.cpp @@ -1837,15 +1837,29 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr) // buffer were large enough (newer standards such as Unix98) if ( len < 0 ) { +#if wxUSE_WXVSNPRINTF + // we know that our own implementation of wxVsnprintf() returns -1 + // only for a format error - thus there's something wrong with + // the user's format string + return -1; +#else // assume that system version only returns error if not enough space // 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 + // 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 size = len + 1; +#endif } else // ok, there was enough space { diff --git a/src/common/wxchar.cpp b/src/common/wxchar.cpp index 962a705dcd..8176058d0d 100644 --- a/src/common/wxchar.cpp +++ b/src/common/wxchar.cpp @@ -167,6 +167,10 @@ bool WXDLLEXPORT wxOKlibc() #if !defined(wxVsnprintf_) +#if !wxUSE_WXVSNPRINTF + #error wxUSE_WXVSNPRINTF must be 1 if our wxVsnprintf_ is used +#endif + // wxUSE_STRUTILS says our wxVsnprintf_ implementation to use or not to // use wxStrlen and wxStrncpy functions over one-char processing loops. // @@ -1124,6 +1128,7 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, va_end(ap); // something failed while loading arguments from the variable list... + // (e.g. the user repeated twice the same positional argument) if (!ok) { buf[0] = 0; @@ -1143,7 +1148,7 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, if (lenCur == lenMax) { buf[lenMax - 1] = 0; - return -1; + return lenMax+1; // not enough space in the output buffer ! } // process this specifier directly in the output buffer @@ -1151,7 +1156,7 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, if (n == -1) { buf[lenMax-1] = wxT('\0'); // be sure to always NUL-terminate the string - return -1; // not enough space in the output buffer ! + return lenMax+1; // not enough space in the output buffer ! } lenCur += n; @@ -1171,7 +1176,7 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, if (buf[lenCur]) { buf[lenCur] = 0; - return -1; + return lenMax+1; // not enough space in the output buffer ! } wxASSERT(lenCur == wxStrlen(buf)); @@ -1182,7 +1187,13 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, #undef APPEND_STR #undef CHECK_PREC -#endif // !wxVsnprintfA +#else // wxVsnprintf_ is defined + +#if wxUSE_WXVSNPRINTF + #error wxUSE_WXVSNPRINTF must be 0 if our wxVsnprintf_ is not used +#endif + +#endif // !wxVsnprintf_ #if !defined(wxSnprintf_) int WXDLLEXPORT wxSnprintf_(wxChar *buf, size_t len, const wxChar *format, ...) diff --git a/tests/strings/vsnprintf.cpp b/tests/strings/vsnprintf.cpp index fc77afe626..4d913d1d39 100644 --- a/tests/strings/vsnprintf.cpp +++ b/tests/strings/vsnprintf.cpp @@ -31,35 +31,36 @@ // temporary buffers static wxChar buf[MAX_TEST_LEN]; +int r; // these macros makes it possible to write all tests without repeating a lot of times wxT() macro #define ASSERT_STR_EQUAL( a, b ) \ CPPUNIT_ASSERT_EQUAL( wxString(a), wxString(b) ); -#define CMP5(expected, x, y, z, w) \ - wxSnprintf(buf, MAX_TEST_LEN, wxT(x), y, z, w); \ - \ +#define CMP5(expected, x, y, z, w) \ + r=wxSnprintf(buf, MAX_TEST_LEN, wxT(x), y, z, w); \ + CPPUNIT_ASSERT( r > 0 ); \ ASSERT_STR_EQUAL( wxT(expected), buf ); #define CMP4(expected, x, y, z) \ - wxSnprintf(buf, MAX_TEST_LEN, wxT(x), y, z); \ - \ + r=wxSnprintf(buf, MAX_TEST_LEN, wxT(x), y, z); \ + CPPUNIT_ASSERT( r > 0 ); \ ASSERT_STR_EQUAL( wxT(expected), buf ); #define CMP3(expected, x, y) \ - wxSnprintf(buf, MAX_TEST_LEN, wxT(x), y); \ - \ + r=wxSnprintf(buf, MAX_TEST_LEN, wxT(x), y); \ + CPPUNIT_ASSERT( r > 0 ); \ ASSERT_STR_EQUAL( wxT(expected), buf ); #define CMP2(expected, x) \ - wxSnprintf(buf, MAX_TEST_LEN, wxT(x)); \ - \ + r=wxSnprintf(buf, MAX_TEST_LEN, wxT(x)); \ + CPPUNIT_ASSERT( r > 0 ); \ ASSERT_STR_EQUAL( wxT(expected), buf ); #define CMPTOSIZE(buffer, size, expected, fmt, x, y, z, w) \ - wxSnprintf(buffer, size, wxT(fmt), x, y, z, w); \ - \ + r=wxSnprintf(buffer, size, wxT(fmt), x, y, z, w); \ + CPPUNIT_ASSERT( r > 0 ); \ CPPUNIT_ASSERT_EQUAL( wxString(wxT(expected)).Left(size - 1), \ wxString(buffer) ) @@ -92,6 +93,7 @@ private: #endif CPPUNIT_TEST( BigToSmallBuffer ); + CPPUNIT_TEST( WrongFormatStrings ); CPPUNIT_TEST( Miscellaneous ); CPPUNIT_TEST_SUITE_END(); @@ -112,6 +114,7 @@ private: void Unicode(); void BigToSmallBuffer(); + void WrongFormatStrings(); void Miscellaneous(); void Misc(wxChar *buffer, int size); @@ -321,9 +324,12 @@ void VsnprintfTestCase::Misc(wxChar *buffer, int size) // value can be the number of characters required for the output buffer // (conforming to ISO C99; implemented in e.g. GNU libc >= 2.1), or // just a negative number, usually -1; (this is how e.g. MSVC's - // *printf() behaves). Fortunately, in all implementations, when the + // *printf() behaves). Luckily, in all implementations, when the // output buffer is too small, it's nonetheless filled up to its max // size. + // + // Note that in the second case (i.e. when we're using our own implementation), + // wxVsnprintf() will always return the number of characters which // test without positionals CMPTOSIZE(buffer, size, "123 444444444 - test - 555 -0.666", @@ -350,6 +356,31 @@ void VsnprintfTestCase::Misc(wxChar *buffer, int size) L"unicode!!", L'W', "ansi!!", 'w'); } +void VsnprintfTestCase::WrongFormatStrings() +{ + // test how wxVsnprintf() behaves with wrong format string: + +#if wxUSE_PRINTF_POS_PARAMS + + // two positionals with the same index: + r = wxSnprintf(buf, MAX_TEST_LEN, wxT("%1$s %1$s"), "hello"); + CPPUNIT_ASSERT(r == -1); + + // three positionals with the same index mixed with other pos args: + r = wxSnprintf(buf, MAX_TEST_LEN, wxT("%4$d %2$f %1$s %2$s %3$d"), "hello", "world", 3, 4); + CPPUNIT_ASSERT(r == -1); + + // a missing positional arg: + r = wxSnprintf(buf, MAX_TEST_LEN, wxT("%1$d %3$d"), 1, 2, 3); + CPPUNIT_ASSERT(r == -1); + + // positional and non-positionals in the same format string: + r = wxSnprintf(buf, MAX_TEST_LEN, wxT("%1$d %d %3$d"), 1, 2, 3); + CPPUNIT_ASSERT(r == -1); + +#endif // wxUSE_PRINTF_POS_PARAMS +} + void VsnprintfTestCase::BigToSmallBuffer() { wxChar buf[1024], buf2[16], buf3[4], buf4;