wxSnprintf() and wxVsnprintf() added, documented and used in wxLog

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@4572 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
1999-11-15 15:49:59 +00:00
parent e2de943347
commit 378b05f7f8
6 changed files with 160 additions and 64 deletions

View File

@@ -412,6 +412,33 @@ should help to find the strings which were not yet translated. As this function
is used very often, an alternative syntax is provided: the \_() macro is is used very often, an alternative syntax is provided: the \_() macro is
defined as wxGetTranslation(). defined as wxGetTranslation().
\membersection{::wxSnprintf}\label{wxsnprintf}
\func{int}{wxSnprintf}{\param{wxChar *}{buf}, \param{size\_t }{len}, \param{const wxChar *}{format}, \param{}{...}}
This function replaces the dangerous standard function {\tt sprintf()} and is
like {\tt snprintf()} available on some platforms. The only difference with
sprintf() is that an additional argument - buffer size - is taken and the
buffer is never overflowed.
Returns the number of characters copied to the buffer or -1 if there is not
enough space.
\wxheading{See also:}
\helpref{wxVsnprintf}{wxvsnprintf},
\helpref{wxString::Printf}{wxstringprintf}
\membersection{::wxVsnprintf}\label{wxsnprintf}
\func{int}{wxVsnprintf}{\param{wxChar *}{buf}, \param{size\_t }{len}, \param{const wxChar *}{format}, \param{va\_list }{argptr}}
The same as \helpref{wxSnprintf}{wxsnprintf} but takes a {\tt va\_list}
argument instead of arbitrary number of parameters.
\wxheading{See also:}
\helpref{wxSnprintf}{wxsnprintf},
\helpref{wxString::PrintfV}{wxstringprintfv}
\section{Dialog functions}\label{dialogfunctions} \section{Dialog functions}\label{dialogfunctions}
Below are a number of convenience functions for getting input from the Below are a number of convenience functions for getting input from the

View File

@@ -144,6 +144,13 @@ and returns 0 for them and \helpref{Stricmp()}{Stricmp} is just a
platform-independent version of case-insensitive string comparison function platform-independent version of case-insensitive string comparison function
known either as stricmp() or strcasecmp() on different platforms. known either as stricmp() or strcasecmp() on different platforms.
The {\tt <wx/string.h>} header also defines \helpref{wxSnprintf}{wxsnprintf}
and \helpref{wxVsnprintf}{wxvsnprintf} functions which should be used instead
of the inherently dangerous standard {\tt sprintf()} and which use {\tt
snprintf()} instead which does buffer size checks whenever possible. Of
course, you may also use \helpref{wxString::Printf}{wxstringprintf} which is
also safe.
There is another class which might be useful when working with wxString: There is another class which might be useful when working with wxString:
\helpref{wxStringTokenizer}{wxstringtokenizer}. It is helpful when a string must \helpref{wxStringTokenizer}{wxstringtokenizer}. It is helpful when a string must
be broken into tokens and replaces the standard C library {\it be broken into tokens and replaces the standard C library {\it

View File

@@ -156,6 +156,15 @@ inline int WXDLLEXPORT Stricmp(const char *psz1, const char *psz2)
#endif // OS/compiler #endif // OS/compiler
} }
// wxSnprintf() is like snprintf() if it's available and sprintf() (always
// available, but dangerous!) if not
extern int WXDLLEXPORT wxSnprintf(wxChar *buf, size_t len,
const wxChar *format, ...);
// and wxVsnprintf() is like vsnprintf() or vsprintf()
extern int WXDLLEXPORT wxVsnprintf(wxChar *buf, size_t len,
const wxChar *format, va_list argptr);
// return an empty wxString // return an empty wxString
class WXDLLEXPORT wxString; // not yet defined class WXDLLEXPORT wxString; // not yet defined
inline const wxString& wxGetEmptyString() { return *(wxString *)&wxEmptyString; } inline const wxString& wxGetEmptyString() { return *(wxString *)&wxEmptyString; }

View File

@@ -28,8 +28,10 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// what to test? // what to test?
#define TEST_ARRAYS
#undef TEST_THREADS //#define TEST_ARRAYS
#define TEST_LOG
//#define TEST_THREADS
// ============================================================================ // ============================================================================
// implementation // implementation
@@ -162,6 +164,23 @@ int main(int argc, char **argv)
PrintArray("a3", a3); PrintArray("a3", a3);
#endif // TEST_ARRAYS #endif // TEST_ARRAYS
#ifdef TEST_LOG
wxString s;
for ( size_t n = 0; n < 8000; n++ )
{
s << (char)('A' + (n % 26));
}
wxString msg;
msg.Printf("A very very long message: '%s', the end!\n", s.c_str());
// this one shouldn't be truncated
printf(msg);
// but this one will because log functions use fixed size buffer
wxLogMessage("A very very long message 2: '%s', the end!\n", s.c_str());
#endif // TEST_LOG
#ifdef TEST_THREADS #ifdef TEST_THREADS
static const size_t nThreads = 3; static const size_t nThreads = 3;
MyThread *threads[nThreads]; MyThread *threads[nThreads];

View File

@@ -97,7 +97,7 @@ void wxLogGeneric(wxLogLevel level, const wxChar *szFormat, ...)
if ( wxLog::GetActiveTarget() != NULL ) { if ( wxLog::GetActiveTarget() != NULL ) {
va_list argptr; va_list argptr;
va_start(argptr, szFormat); va_start(argptr, szFormat);
wxVsprintf(s_szBuf, szFormat, argptr); wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);
va_end(argptr); va_end(argptr);
wxLog::OnLog(level, s_szBuf, time(NULL)); wxLog::OnLog(level, s_szBuf, time(NULL));
@@ -110,7 +110,7 @@ void wxLogGeneric(wxLogLevel level, const wxChar *szFormat, ...)
if ( wxLog::GetActiveTarget() != NULL ) { \ if ( wxLog::GetActiveTarget() != NULL ) { \
va_list argptr; \ va_list argptr; \
va_start(argptr, szFormat); \ va_start(argptr, szFormat); \
wxVsprintf(s_szBuf, szFormat, argptr); \ wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr); \
va_end(argptr); \ va_end(argptr); \
\ \
wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL)); \ wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL)); \
@@ -131,7 +131,7 @@ void wxLogVerbose(const wxChar *szFormat, ...)
if ( pLog != NULL && pLog->GetVerbose() ) { if ( pLog != NULL && pLog->GetVerbose() ) {
va_list argptr; va_list argptr;
va_start(argptr, szFormat); va_start(argptr, szFormat);
wxVsprintf(s_szBuf, szFormat, argptr); wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);
va_end(argptr); va_end(argptr);
wxLog::OnLog(wxLOG_Info, s_szBuf, time(NULL)); wxLog::OnLog(wxLOG_Info, s_szBuf, time(NULL));
@@ -146,7 +146,7 @@ void wxLogVerbose(const wxChar *szFormat, ...)
if ( wxLog::GetActiveTarget() != NULL ) { \ if ( wxLog::GetActiveTarget() != NULL ) { \
va_list argptr; \ va_list argptr; \
va_start(argptr, szFormat); \ va_start(argptr, szFormat); \
wxVsprintf(s_szBuf, szFormat, argptr); \ wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr); \
va_end(argptr); \ va_end(argptr); \
\ \
wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL)); \ wxLog::OnLog(wxLOG_##level, s_szBuf, time(NULL)); \
@@ -160,7 +160,7 @@ void wxLogVerbose(const wxChar *szFormat, ...)
if ( pLog != NULL && wxLog::IsAllowedTraceMask(mask) ) { if ( pLog != NULL && wxLog::IsAllowedTraceMask(mask) ) {
va_list argptr; va_list argptr;
va_start(argptr, szFormat); va_start(argptr, szFormat);
wxVsprintf(s_szBuf, szFormat, argptr); wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);
va_end(argptr); va_end(argptr);
wxLog::OnLog(wxLOG_Trace, s_szBuf, time(NULL)); wxLog::OnLog(wxLOG_Trace, s_szBuf, time(NULL));
@@ -177,7 +177,7 @@ void wxLogVerbose(const wxChar *szFormat, ...)
if ( pLog != NULL && ((pLog->GetTraceMask() & mask) == mask) ) { if ( pLog != NULL && ((pLog->GetTraceMask() & mask) == mask) ) {
va_list argptr; va_list argptr;
va_start(argptr, szFormat); va_start(argptr, szFormat);
wxVsprintf(s_szBuf, szFormat, argptr); wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);
va_end(argptr); va_end(argptr);
wxLog::OnLog(wxLOG_Trace, s_szBuf, time(NULL)); wxLog::OnLog(wxLOG_Trace, s_szBuf, time(NULL));
@@ -198,7 +198,8 @@ IMPLEMENT_LOG_DEBUG_FUNCTION(Trace)
void wxLogSysErrorHelper(long lErrCode) void wxLogSysErrorHelper(long lErrCode)
{ {
wxChar szErrMsg[LOG_BUFFER_SIZE / 2]; wxChar szErrMsg[LOG_BUFFER_SIZE / 2];
wxSprintf(szErrMsg, _(" (error %ld: %s)"), lErrCode, wxSysErrorMsg(lErrCode)); wxSnprintf(szErrMsg, WXSIZEOF(szErrMsg),
_(" (error %ld: %s)"), lErrCode, wxSysErrorMsg(lErrCode));
wxStrncat(s_szBuf, szErrMsg, WXSIZEOF(s_szBuf) - wxStrlen(s_szBuf)); wxStrncat(s_szBuf, szErrMsg, WXSIZEOF(s_szBuf) - wxStrlen(s_szBuf));
wxLog::OnLog(wxLOG_Error, s_szBuf, time(NULL)); wxLog::OnLog(wxLOG_Error, s_szBuf, time(NULL));
@@ -208,7 +209,7 @@ void WXDLLEXPORT wxLogSysError(const wxChar *szFormat, ...)
{ {
va_list argptr; va_list argptr;
va_start(argptr, szFormat); va_start(argptr, szFormat);
wxVsprintf(s_szBuf, szFormat, argptr); wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);
va_end(argptr); va_end(argptr);
wxLogSysErrorHelper(wxSysErrorCode()); wxLogSysErrorHelper(wxSysErrorCode());
@@ -218,7 +219,7 @@ void WXDLLEXPORT wxLogSysError(long lErrCode, const wxChar *szFormat, ...)
{ {
va_list argptr; va_list argptr;
va_start(argptr, szFormat); va_start(argptr, szFormat);
wxVsprintf(s_szBuf, szFormat, argptr); wxVsnprintf(s_szBuf, WXSIZEOF(s_szBuf), szFormat, argptr);
va_end(argptr); va_end(argptr);
wxLogSysErrorHelper(lErrCode); wxLogSysErrorHelper(lErrCode);
@@ -365,7 +366,7 @@ void wxLogStderr::DoLogString(const wxChar *szString, time_t WXUNUSED(t))
fputs(str.mb_str(), m_fp); fputs(str.mb_str(), m_fp);
fflush(m_fp); fflush(m_fp);
// under Windows, programs usually don't have stderr at all, so make show the // under Windows, programs usually don't have stderr at all, so show the
// messages also under debugger // messages also under debugger
#ifdef __WXMSW__ #ifdef __WXMSW__
OutputDebugString(str + wxT('\r')); OutputDebugString(str + wxT('\r'));
@@ -558,12 +559,14 @@ void wxOnAssert(const wxChar *szFile, int nLine, const wxChar *szMsg)
// make life easier for people using VC++ IDE: clicking on the message // make life easier for people using VC++ IDE: clicking on the message
// will take us immediately to the place of the failed assert // will take us immediately to the place of the failed assert
wxSnprintf(szBuf, WXSIZEOF(szBuf),
#ifdef __VISUALC__ #ifdef __VISUALC__
wxSprintf(szBuf, wxT("%s(%d): assert failed"), szFile, nLine); wxT("%s(%d): assert failed"),
#else // !VC++ #else // !VC++
// make the error message more clear for all the others // make the error message more clear for all the others
wxSprintf(szBuf, wxT("Assert failed in file %s at line %d"), szFile, nLine); wxT("Assert failed in file %s at line %d"),
#endif // VC/!VC #endif // VC/!VC
szFile, nLine);
if ( szMsg != NULL ) { if ( szMsg != NULL ) {
wxStrcat(szBuf, wxT(": ")); wxStrcat(szBuf, wxT(": "));

View File

@@ -35,10 +35,8 @@
#include "wx/defs.h" #include "wx/defs.h"
#include "wx/string.h" #include "wx/string.h"
#include "wx/intl.h" #include "wx/intl.h"
#if wxUSE_THREADS
#include "wx/thread.h" #include "wx/thread.h"
#endif #endif
#endif
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
@@ -104,28 +102,32 @@ extern const wxChar WXDLLEXPORT *wxEmptyString = &g_strEmpty.dummy;
// we want to find out if the current platform supports vsnprintf()-like // 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 // function: for Unix this is done with configure, for Windows we test the
// compiler explicitly. // 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 __WXMSW__
#ifdef __VISUALC__ #ifdef __VISUALC__
#define wxVsnprintf _vsnprintf #define wxVsnprintfA _vsnprintf
#endif #endif
#else // !Windows #else // !Windows
#ifdef HAVE_VSNPRINTF #ifdef HAVE_VSNPRINTF
#define wxVsnprintf vsnprintf #define wxVsnprintfA vsnprintf
#endif #endif
#endif // Windows/!Windows #endif // Windows/!Windows
#ifndef wxVsnprintf #ifndef wxVsnprintfA
// in this case we'll use vsprintf() (which is ANSI and thus should be // 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 // always available), but it's unsafe because it doesn't check for buffer
// size - so give a warning // 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__) #if defined(__VISUALC__)
#pragma message("Using sprintf() because no snprintf()-like function defined") #pragma message("Using sprintf() because no snprintf()-like function defined")
#elif defined(__GNUG__) && !defined(__UNIX__) #elif defined(__GNUG__) && !defined(__UNIX__)
#warning "Using sprintf() because no snprintf()-like function defined" #warning "Using sprintf() because no snprintf()-like function defined"
#elif defined(__MWERKS__) #elif defined(__MWERKS__)
#warning "Using sprintf() because no snprintf()-like function defined" #warning "Using sprintf() because no snprintf()-like function defined"
#endif //compiler #endif //compiler
#endif // no vsnprintf #endif // no vsnprintf
@@ -184,6 +186,37 @@ ostream& operator<<(ostream& os, const wxString& str)
#endif //std::string compatibility #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
return wxVsnprintfA(buf, len, format, argptr);
#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 // private classes
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -988,6 +1021,7 @@ wxString& wxString::operator<<(double d)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// formatted output // formatted output
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
int wxString::Printf(const wxChar *pszFormat, ...) int wxString::Printf(const wxChar *pszFormat, ...)
{ {
va_list argptr; va_list argptr;
@@ -1002,18 +1036,11 @@ int wxString::Printf(const wxChar *pszFormat, ...)
int wxString::PrintfV(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 #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(); Reinit();
for (size_t n = 0; pszFormat[n]; n++) for (size_t n = 0; pszFormat[n]; n++)
@@ -1120,30 +1147,30 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
s_szFlags[flagofs] = '\0'; s_szFlags[flagofs] = '\0';
if (ilen == 0 ) { if (ilen == 0 ) {
int val = va_arg(argptr, int); int val = va_arg(argptr, int);
::sprintf(s_szScratch, s_szFlags, val); ::sprintf(szScratch, s_szFlags, val);
} }
else if (ilen == -1) { else if (ilen == -1) {
short int val = va_arg(argptr, short int); short int val = va_arg(argptr, short int);
::sprintf(s_szScratch, s_szFlags, val); ::sprintf(szScratch, s_szFlags, val);
} }
else if (ilen == 1) { else if (ilen == 1) {
long int val = va_arg(argptr, long int); long int val = va_arg(argptr, long int);
::sprintf(s_szScratch, s_szFlags, val); ::sprintf(szScratch, s_szFlags, val);
} }
else if (ilen == 2) { else if (ilen == 2) {
#if SIZEOF_LONG_LONG #if SIZEOF_LONG_LONG
long long int val = va_arg(argptr, long long int); long long int val = va_arg(argptr, long long int);
::sprintf(s_szScratch, s_szFlags, val); ::sprintf(szScratch, s_szFlags, val);
#else #else
long int val = va_arg(argptr, long int); long int val = va_arg(argptr, long int);
::sprintf(s_szScratch, s_szFlags, val); ::sprintf(szScratch, s_szFlags, val);
#endif #endif
} }
else if (ilen == 3) { else if (ilen == 3) {
size_t val = va_arg(argptr, size_t); 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; done = TRUE;
break; break;
case wxT('e'): case wxT('e'):
@@ -1156,12 +1183,12 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
s_szFlags[flagofs] = '\0'; s_szFlags[flagofs] = '\0';
if (ilen == 2) { if (ilen == 2) {
long double val = va_arg(argptr, long double); long double val = va_arg(argptr, long double);
::sprintf(s_szScratch, s_szFlags, val); ::sprintf(szScratch, s_szFlags, val);
} else { } else {
double val = va_arg(argptr, double); 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; done = TRUE;
break; break;
case wxT('p'): case wxT('p'):
@@ -1170,8 +1197,8 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
CHECK_PREC CHECK_PREC
s_szFlags[flagofs++] = pszFormat[n]; s_szFlags[flagofs++] = pszFormat[n];
s_szFlags[flagofs] = '\0'; s_szFlags[flagofs] = '\0';
::sprintf(s_szScratch, s_szFlags, val); ::sprintf(szScratch, s_szFlags, val);
*this += wxString(s_szScratch); *this += wxString(szScratch);
done = TRUE; done = TRUE;
} }
break; break;
@@ -1245,39 +1272,43 @@ int wxString::PrintfV(const wxChar* pszFormat, va_list argptr)
} else *this += pszFormat[n]; } else *this += pszFormat[n];
#else #else
// NB: wxVsnprintf() may return either less than the buffer size or -1 if there // buffer to avoid dynamic memory allocation each time for small strings
// is not enough place depending on implementation char szScratch[1024];
int iLen = wxVsnprintf(s_szScratch, WXSIZEOF(s_szScratch), pszFormat, argptr);
char *buffer; // NB: wxVsnprintf() may return either less than the buffer size or -1 if
if ( iLen < (int)WXSIZEOF(s_szScratch) ) { // there is not enough place depending on implementation
buffer = s_szScratch; int iLen = wxVsnprintfA(szScratch, WXSIZEOF(szScratch), pszFormat, argptr);
if ( iLen != -1 ) {
// the whole string is in szScratch
*this = szScratch;
} }
else { else {
int size = WXSIZEOF(s_szScratch) * 2; bool outOfMemory = FALSE;
buffer = (char *)malloc(size); int size = 2*WXSIZEOF(szScratch);
while ( buffer != NULL ) { while ( !outOfMemory ) {
iLen = wxVsnprintf(buffer, WXSIZEOF(s_szScratch), pszFormat, argptr); char *buf = GetWriteBuf(size);
if ( iLen < size ) { if ( buf )
iLen = wxVsnprintfA(buf, size, pszFormat, argptr);
else
outOfMemory = TRUE;
UngetWriteBuf();
if ( iLen != -1 ) {
// ok, there was enough space // ok, there was enough space
break; break;
} }
// still not enough, double it again // still not enough, double it again
buffer = (char *)realloc(buffer, size *= 2); size *= 2;
} }
if ( !buffer ) { if ( outOfMemory ) {
// out of memory // out of memory
return -1; return -1;
} }
} }
#endif // wxUSE_EXPERIMENTAL_PRINTF/!wxUSE_EXPERIMENTAL_PRINTF
wxString s(buffer);
*this = s;
if ( buffer != s_szScratch )
free(buffer);
#endif
return Len(); return Len();
} }