X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7a828c7ffb2e77f6e8c65b3d72f7364826131d1f..80a779275ae04443c568dca919adb26cf6f5002c:/src/common/wxchar.cpp diff --git a/src/common/wxchar.cpp b/src/common/wxchar.cpp index a547a1e201..10bcd27cb4 100644 --- a/src/common/wxchar.cpp +++ b/src/common/wxchar.cpp @@ -1,8 +1,8 @@ ///////////////////////////////////////////////////////////////////////////// // Name: src/common/wxchar.cpp // Purpose: wxChar implementation -// Author: Ove Kåven -// Modified by: Ron Lee +// Author: Ove Kaven +// Modified by: Ron Lee, Francesco Montorsi // Created: 09/04/99 // RCS-ID: $Id$ // Copyright: (c) wxWidgets copyright @@ -20,6 +20,8 @@ #pragma hdrstop #endif +#include "wx/wxchar.h" + #define _ISOC9X_SOURCE 1 // to get vsscanf() #define _BSD_SOURCE 1 // to still get strdup() @@ -35,11 +37,11 @@ #endif #ifndef WX_PRECOMP - #include "wx/wxchar.h" #include "wx/string.h" #include "wx/hash.h" + #include "wx/utils.h" // for wxMin and wxMax + #include "wx/log.h" #endif - #include "wx/utils.h" // for wxMin and wxMax #if defined(__WIN32__) && defined(wxNEED_WX_CTYPE_H) #include @@ -183,10 +185,74 @@ bool WXDLLEXPORT wxOKlibc() #endif // some limits of our implementation -#define wxMAX_SVNPRINTF_ARGUMENTS 64 +#define wxMAX_SVNPRINTF_ARGUMENTS 16 #define wxMAX_SVNPRINTF_FLAGBUFFER_LEN 32 +#define wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN 512 + -// the conversion specifiers accepted by wxMyPosVsnprintf_ +// wxVsnprintf() needs to use a *system* implementation of swnprintf() +// in order to perform some internal tasks. +// NB: we cannot just use wxSnprintf() because for some systems it maybe +// implemented later in this file using wxVsnprintf() and that would +// result in an endless recursion and thus in a stack overflow +#if wxUSE_UNICODE + #if defined(__WINDOWS__) && !defined(HAVE_SWPRINTF) + // all compilers under Windows should have swprintf() + #define HAVE_SWPRINTF + #endif + + // NB: MSVC 6 has only non-standard swprintf() declaration and while MSVC 7 + // and 7.1 do have the standard one, it's completely broken unless + // /Zc:wchar_t is used while the other one works so use it instead, and + // only VC8 has a working standard-compliant swprintf() + #if defined(__WXWINCE__) || \ + (defined(__VISUALC__) && __VISUALC__ < 1400) || \ + defined(__GNUWIN32__) || \ + defined(__BORLANDC__) + #ifndef HAVE_BROKEN_SWPRINTF_DECL + #define HAVE_BROKEN_SWPRINTF_DECL + #endif + #endif + + // problem: on some systems swprintf takes the 'max' argument while on + // others it doesn't + #if defined(HAVE_BROKEN_SWPRINTF_DECL) + // like when using sprintf(), since 'max' is not used, wxVsnprintf() + // should always ensure that 'buff' is big enough for all common needs + #define system_sprintf(buff, max, flags, data) \ + ::swprintf(buff, flags, data) + + #define SYSTEM_SPRINTF_IS_UNSAFE + #else + #if !defined(HAVE_SWPRINTF) + #error wxVsnprintf() needs a system swprintf() implementation! + #endif + + #define system_sprintf(buff, max, flags, data) \ + ::swprintf(buff, max, flags, data) + #endif +#else // !wxUSE_UNICODE + #if defined(__VISUALC__) || \ + (defined(__BORLANDC__) && __BORLANDC__ >= 0x540) + #define system_sprintf(buff, max, flags, data) \ + ::_snprintf(buff, max, flags, data) + #elif defined(HAVE_SNPRINTF) + #define system_sprintf(buff, max, flags, data) \ + ::snprintf(buff, max, flags, data) + #else // NB: at least sprintf() should always be available + // since 'max' is not used in this case, wxVsnprintf() should always + // ensure that 'buff' is big enough for all common needs + // (see wxMAX_SVNPRINTF_FLAGBUFFER_LEN and wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN) + #define system_sprintf(buff, max, flags, data) \ + ::sprintf(buff, flags, data) + + #define SYSTEM_SPRINTF_IS_UNSAFE + #endif +#endif // wxUSE_UNICODE/!wxUSE_UNICODE + + + +// the conversion specifiers accepted by wxVsnprintf_ enum wxPrintfArgType { wxPAT_INVALID = -1, @@ -213,7 +279,7 @@ enum wxPrintfArgType { wxPAT_NLONGINT // %ln }; -// an argument passed to wxMyPosVsnprintf_ +// an argument passed to wxVsnprintf_ typedef union { int pad_int; // %d, %i, %o, %u, %x, %X long int pad_longint; // %ld, etc @@ -240,7 +306,7 @@ typedef union { // Contains parsed data relative to a conversion specifier given to -// wxMyPosVsnprintf_ and parsed from the format string +// wxVsnprintf_ and parsed from the format string // NOTE: in C++ there is almost no difference between struct & classes thus // there is no performance gain by using a struct here... class wxPrintfConvSpec @@ -248,39 +314,43 @@ class wxPrintfConvSpec public: // the position of the argument relative to this conversion specifier - size_t pos; + size_t m_pos; // the type of this conversion specifier - wxPrintfArgType type; + wxPrintfArgType m_type; // the minimum and maximum width // when one of this var is set to -1 it means: use the following argument // in the stack as minimum/maximum width for this conversion specifier - int min_width, max_width; + int m_nMinWidth, m_nMaxWidth; // does the argument need to the be aligned to left ? - bool adj_left; + bool m_bAlignLeft; // pointer to the '%' of this conversion specifier in the format string // NOTE: this points somewhere in the string given to the Parse() function - // it's task of the caller ensure that memory is still valid ! - const wxChar *argpos; + const wxChar *m_pArgPos; // pointer to the last character of this conversion specifier in the // format string // NOTE: this points somewhere in the string given to the Parse() function - // it's task of the caller ensure that memory is still valid ! - const wxChar *argend; + const wxChar *m_pArgEnd; // a little buffer where formatting flags like #+\.hlqLZ are stored by Parse() // for use in Process() - char szFlags[wxMAX_SVNPRINTF_FLAGBUFFER_LEN]; + // NB: even if this buffer is used only for numeric conversion specifiers and + // thus could be safely declared as a char[] buffer, we want it to be wxChar + // so that in Unicode builds we can avoid to convert its contents to Unicode + // chars when copying it in user's buffer. + wxChar m_szFlags[wxMAX_SVNPRINTF_FLAGBUFFER_LEN]; public: // we don't declare this as a constructor otherwise it would be called - // automatically and we don't want this: to be optimized, wxMyPosVsnprintf_ + // automatically and we don't want this: to be optimized, wxVsnprintf_ // calls this function only on really-used instances of this class. void Init(); @@ -304,13 +374,16 @@ private: void wxPrintfConvSpec::Init() { - min_width = 0; - max_width = 0xFFFF; - pos = 0; - adj_left = false; - argpos = argend = NULL; - type = wxPAT_INVALID; - szFlags[0] = wxT('%'); + m_nMinWidth = 0; + m_nMaxWidth = 0xFFFF; + m_pos = 0; + m_bAlignLeft = false; + m_pArgPos = m_pArgEnd = NULL; + m_type = wxPAT_INVALID; + + // this character will never be removed from m_szFlags array and + // is important when calling sprintf() in wxPrintfConvSpec::Process() ! + m_szFlags[0] = wxT('%'); } bool wxPrintfConvSpec::Parse(const wxChar *format) @@ -322,19 +395,19 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) bool in_prec, prec_dot; int ilen = 0; - adj_left = in_prec = prec_dot = false; - argpos = argend = format; + m_bAlignLeft = in_prec = prec_dot = false; + m_pArgPos = m_pArgEnd = format; do { #define CHECK_PREC \ if (in_prec && !prec_dot) \ { \ - szFlags[flagofs++] = '.'; \ + m_szFlags[flagofs++] = wxT('.'); \ prec_dot = true; \ } // what follows '%'? - const wxChar ch = *(++argend); + const wxChar ch = *(++m_pArgEnd); switch ( ch ) { case wxT('\0'): @@ -349,47 +422,51 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('+'): case wxT('\''): CHECK_PREC - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = ch; break; case wxT('-'): CHECK_PREC - adj_left = true; - szFlags[flagofs++] = ch; + m_bAlignLeft = true; + m_szFlags[flagofs++] = ch; break; case wxT('.'): CHECK_PREC in_prec = true; prec_dot = false; - max_width = 0; - // dot will be auto-added to szFlags if non-negative + m_nMaxWidth = 0; + // dot will be auto-added to m_szFlags if non-negative // number follows break; case wxT('h'): ilen = -1; CHECK_PREC - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = ch; break; case wxT('l'): + // NB: it's safe to use flagofs-1 as flagofs always start from 1 + if (m_szFlags[flagofs-1] == 'l') // 'll' modifier is the same as 'L' or 'q' + ilen = 2; + else ilen = 1; CHECK_PREC - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = ch; break; case wxT('q'): case wxT('L'): ilen = 2; CHECK_PREC - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = ch; break; case wxT('Z'): ilen = 3; CHECK_PREC - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = ch; break; case wxT('*'): @@ -399,18 +476,18 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) // tell Process() to use the next argument // in the stack as maxwidth... - max_width = -1; + m_nMaxWidth = -1; } else { // tell Process() to use the next argument // in the stack as minwidth... - min_width = -1; + m_nMinWidth = -1; } // save the * in our formatting buffer... // will be replaced later by Process() - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = ch; break; case wxT('1'): case wxT('2'): case wxT('3'): @@ -419,41 +496,41 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) { int len = 0; CHECK_PREC - while ( (*argend >= wxT('0')) && - (*argend <= wxT('9')) ) + while ( (*m_pArgEnd >= wxT('0')) && + (*m_pArgEnd <= wxT('9')) ) { - szFlags[flagofs++] = *argend; - len = len*10 + (*argend - wxT('0')); - argend++; + m_szFlags[flagofs++] = (*m_pArgEnd); + len = len*10 + (*m_pArgEnd - wxT('0')); + m_pArgEnd++; } if (in_prec) - max_width = len; + m_nMaxWidth = len; else - min_width = len; + m_nMinWidth = len; - argend--; // the main loop pre-increments n again + m_pArgEnd--; // the main loop pre-increments n again } break; case wxT('$'): // a positional parameter (e.g. %2$s) ? { - if (min_width <= 0) + if (m_nMinWidth <= 0) break; // ignore this formatting flag as no // numbers are preceding it - // remove from szFlags all digits previously added + // remove from m_szFlags all digits previously added do { flagofs--; - } while (szFlags[flagofs] >= '1' && - szFlags[flagofs] <= '9'); + } while (m_szFlags[flagofs] >= '1' && + m_szFlags[flagofs] <= '9'); // re-adjust the offset making it point to the - // next free char of szFlags + // next free char of m_szFlags flagofs++; - pos = min_width; - min_width = 0; + m_pos = m_nMinWidth; + m_nMinWidth = 0; } break; @@ -464,25 +541,25 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('x'): case wxT('X'): CHECK_PREC - szFlags[flagofs++] = ch; - szFlags[flagofs] = '\0'; + m_szFlags[flagofs++] = ch; + m_szFlags[flagofs] = wxT('\0'); if (ilen == 0) - type = wxPAT_INT; + m_type = wxPAT_INT; else if (ilen == -1) // NB: 'short int' value passed through '...' // is promoted to 'int', so we have to get // an int from stack even if we need a short - type = wxPAT_INT; + m_type = wxPAT_INT; else if (ilen == 1) - type = wxPAT_LONGINT; + m_type = wxPAT_LONGINT; else if (ilen == 2) #if SIZEOF_LONG_LONG - type = wxPAT_LONGLONGINT; + m_type = wxPAT_LONGLONGINT; #else // !long long - type = wxPAT_LONGINT; + m_type = wxPAT_LONGINT; #endif // long long/!long long else if (ilen == 3) - type = wxPAT_SIZET; + m_type = wxPAT_SIZET; done = true; break; @@ -492,17 +569,19 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('g'): case wxT('G'): CHECK_PREC - szFlags[flagofs++] = ch; - szFlags[flagofs] = '\0'; + m_szFlags[flagofs++] = ch; + m_szFlags[flagofs] = wxT('\0'); if (ilen == 2) - type = wxPAT_LONGDOUBLE; + m_type = wxPAT_LONGDOUBLE; else - type = wxPAT_DOUBLE; + m_type = wxPAT_DOUBLE; done = true; break; case wxT('p'): - type = wxPAT_POINTER; + m_type = wxPAT_POINTER; + m_szFlags[flagofs++] = ch; + m_szFlags[flagofs] = '\0'; done = true; break; @@ -511,22 +590,22 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) { // in Unicode mode %hc == ANSI character // and in ANSI mode, %hc == %c == ANSI... - type = wxPAT_CHAR; + m_type = wxPAT_CHAR; } else if (ilen == 1) { // in ANSI mode %lc == Unicode character // and in Unicode mode, %lc == %c == Unicode... - type = wxPAT_WCHAR; + m_type = wxPAT_WCHAR; } else { #if wxUSE_UNICODE // in Unicode mode, %c == Unicode character - type = wxPAT_WCHAR; + m_type = wxPAT_WCHAR; #else // in ANSI mode, %c == ANSI character - type = wxPAT_CHAR; + m_type = wxPAT_CHAR; #endif } done = true; @@ -537,20 +616,20 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) { // Unicode mode wx extension: we'll let %hs mean non-Unicode // strings (when in ANSI mode, %s == %hs == ANSI string) - type = wxPAT_PCHAR; + m_type = wxPAT_PCHAR; } else if (ilen == 1) { // in Unicode mode, %ls == %s == Unicode string // in ANSI mode, %ls == Unicode string - type = wxPAT_PWCHAR; + m_type = wxPAT_PWCHAR; } else { #if wxUSE_UNICODE - type = wxPAT_PWCHAR; + m_type = wxPAT_PWCHAR; #else - type = wxPAT_PCHAR; + m_type = wxPAT_PCHAR; #endif } done = true; @@ -558,11 +637,11 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('n'): if (ilen == 0) - type = wxPAT_NINT; + m_type = wxPAT_NINT; else if (ilen == -1) - type = wxPAT_NSHORTINT; + m_type = wxPAT_NSHORTINT; else if (ilen >= 1) - type = wxPAT_NLONGINT; + m_type = wxPAT_NLONGINT; done = true; break; @@ -571,6 +650,12 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) // leave it unchanged return false; } + + if (flagofs == wxMAX_SVNPRINTF_FLAGBUFFER_LEN) + { + wxLogDebug(wxT("Too many flags specified for a single conversion specifier!")); + return false; + } } while (!done); @@ -578,55 +663,60 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) } -void wxPrintfConvSpec::ReplaceAsteriskWith(int w) +void wxPrintfConvSpec::ReplaceAsteriskWith(int width) { - char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN]; + wxChar temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN]; // find the first * in our flag buffer - char *pwidth = strchr(szFlags, '*'); + wxChar *pwidth = wxStrchr(m_szFlags, wxT('*')); wxASSERT(pwidth); - // save what follows the * (the +1 is to skip it!) - strcpy(temp, pwidth+1); - if (w < 0) { - pwidth[0] = '-'; + // save what follows the * (the +1 is to skip the asterisk itself!) + wxStrcpy(temp, pwidth+1); + if (width < 0) + { + pwidth[0] = wxT('-'); pwidth++; } // replace * with the actual integer given as width - int offset = ::sprintf(pwidth,"%d",abs(w)); +#ifndef SYSTEM_SPRINTF_IS_UNSAFE + int maxlen = (m_szFlags + wxMAX_SVNPRINTF_FLAGBUFFER_LEN - pwidth) / + sizeof(wxChar); +#endif + int offset = system_sprintf(pwidth, maxlen, wxT("%d"), abs(width)); // restore after the expanded * what was following it - strcpy(pwidth+offset, temp); + wxStrcpy(pwidth+offset, temp); } bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr) { // did the '*' width/precision specifier was used ? - if (max_width == -1) + if (m_nMaxWidth == -1) { // take the maxwidth specifier from the stack - max_width = va_arg(argptr, int); - if (max_width < 0) - max_width = 0; + m_nMaxWidth = va_arg(argptr, int); + if (m_nMaxWidth < 0) + m_nMaxWidth = 0; else - ReplaceAsteriskWith(max_width); + ReplaceAsteriskWith(m_nMaxWidth); } - if (min_width == -1) + if (m_nMinWidth == -1) { // take the minwidth specifier from the stack - min_width = va_arg(argptr, int); + m_nMinWidth = va_arg(argptr, int); - ReplaceAsteriskWith(min_width); - if (min_width < 0) + ReplaceAsteriskWith(m_nMinWidth); + if (m_nMinWidth < 0) { - adj_left = !adj_left; - min_width = -min_width; + m_bAlignLeft = !m_bAlignLeft; + m_nMinWidth = -m_nMinWidth; } } - switch (type) { + switch (m_type) { case wxPAT_INT: p->pad_int = va_arg(argptr, int); break; @@ -652,10 +742,10 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr) break; case wxPAT_CHAR: - p->pad_char = va_arg(argptr, int); // char is promoted to int when passed through '...' + p->pad_char = (char)va_arg(argptr, int); // char is promoted to int when passed through '...' break; case wxPAT_WCHAR: - p->pad_wchar = va_arg(argptr, int); // char is promoted to int when passed through '...' + p->pad_wchar = (wchar_t)va_arg(argptr, int); // char is promoted to int when passed through '...' break; case wxPAT_PCHAR: @@ -685,9 +775,11 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr) int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) { - // buffer to avoid dynamic memory allocation each time for small strings - static char szScratch[1024]; - size_t lenCur = 0; + // buffer to avoid dynamic memory allocation each time for small strings; + // note that this buffer is used only to hold results of number formatting, + // %s directly writes user's string in buf, without using szScratch + wxChar szScratch[wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN]; + size_t lenScratch = 0, lenCur = 0; #define APPEND_CH(ch) \ { \ @@ -705,36 +797,36 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) } \ } - switch ( type ) + switch ( m_type ) { case wxPAT_INT: - ::sprintf(szScratch, szFlags, p->pad_int); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_int); break; case wxPAT_LONGINT: - ::sprintf(szScratch, szFlags, p->pad_longint); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longint); break; #if SIZEOF_LONG_LONG case wxPAT_LONGLONGINT: - ::sprintf(szScratch, szFlags, p->pad_longlongint); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longlongint); break; #endif // SIZEOF_LONG_LONG case wxPAT_SIZET: - ::sprintf(szScratch, szFlags, p->pad_sizet); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_sizet); break; case wxPAT_LONGDOUBLE: - ::sprintf(szScratch, szFlags, p->pad_longdouble); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longdouble); break; case wxPAT_DOUBLE: - ::sprintf(szScratch, szFlags, p->pad_double); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_double); break; case wxPAT_POINTER: - ::sprintf(szScratch, szFlags, p->pad_pointer); + lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_pointer); break; case wxPAT_CHAR: @@ -744,31 +836,39 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) #if wxUSE_UNICODE p->pad_wchar; - if (type == wxPAT_CHAR) { + if (m_type == wxPAT_CHAR) + { // user passed a character explicitely indicated as ANSI... const char buf[2] = { p->pad_char, 0 }; val = wxString(buf, wxConvLibc)[0u]; + + //wprintf(L"converting ANSI=>Unicode"); // for debug } #else p->pad_char; - if (type == wxPAT_WCHAR) { +#if wxUSE_WCHAR_T + if (m_type == wxPAT_WCHAR) + { // user passed a character explicitely indicated as Unicode... const wchar_t buf[2] = { p->pad_wchar, 0 }; val = wxString(buf, wxConvLibc)[0u]; + + //printf("converting Unicode=>ANSI"); // for debug } +#endif #endif size_t i; - if (!adj_left) - for (i = 1; i < (size_t)min_width; i++) + if (!m_bAlignLeft) + for (i = 1; i < (size_t)m_nMinWidth; i++) APPEND_CH(_T(' ')); APPEND_CH(val); - if (adj_left) - for (i = 1; i < (size_t)min_width; i++) + if (m_bAlignLeft) + for (i = 1; i < (size_t)m_nMinWidth; i++) APPEND_CH(_T(' ')); } break; @@ -781,30 +881,40 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) #if wxUSE_UNICODE p->pad_pwchar; - if (type == wxPAT_PCHAR) { + if (m_type == wxPAT_PCHAR) + { // user passed a string explicitely indicated as ANSI... - val = wxString(p->pad_pchar, wxConvLibc); + val = s = wxString(p->pad_pchar, wxConvLibc); + + //wprintf(L"converting ANSI=>Unicode"); // for debug } #else p->pad_pchar; - if (type == wxPAT_PWCHAR) { +#if wxUSE_WCHAR_T + if (m_type == wxPAT_PWCHAR) + { // user passed a string explicitely indicated as Unicode... - val = wxString(p->pad_pwchar, wxConvLibc); + val = s = wxString(p->pad_pwchar, wxConvLibc); + + //printf("converting Unicode=>ANSI"); // for debug } +#endif #endif int len; if (val) { #if wxUSE_STRUTILS - len = wxMin(max_width, wxStrlen(val)); + // at this point we are sure that m_nMaxWidth is positive or null + // (see top of wxPrintfConvSpec::LoadArg) + len = wxMin((unsigned int)m_nMaxWidth, wxStrlen(val)); #else - for ( len = 0; val[len] && (len < max_width); len++ ) + for ( len = 0; val[len] && (len < m_nMaxWidth); len++ ) ; #endif } - else if (max_width >= 6) + else if (m_nMaxWidth >= 6) { val = wxT("(null)"); len = 6; @@ -817,14 +927,14 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) int i; - if (!adj_left) + if (!m_bAlignLeft) { - for (i = len; i < min_width; i++) + for (i = len; i < m_nMinWidth; i++) APPEND_CH(_T(' ')); } #if wxUSE_STRUTILS - len = wxMin(len, lenMax-lenCur); + len = wxMin((unsigned int)len, lenMax-lenCur); wxStrncpy(buf+lenCur, val, len); lenCur += len; #else @@ -832,9 +942,9 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) APPEND_CH(val[i]); #endif - if (adj_left) + if (m_bAlignLeft) { - for (i = len; i < min_width; i++) + for (i = len; i < m_nMinWidth; i++) APPEND_CH(_T(' ')); } } @@ -845,7 +955,7 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) break; case wxPAT_NSHORTINT: - *p->pad_nshortint = lenCur; + *p->pad_nshortint = (short int)lenCur; break; case wxPAT_NLONGINT: @@ -857,9 +967,13 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) return -1; } +#ifdef HAVE_BROKEN_SWPRINTF_DECL + wxUnusedVar(lenScratch); // avoid dummy warnings +#endif + // if we used system's sprintf() then we now need to append the s_szScratch // buffer to the given one... - switch (type) + switch (m_type) { case wxPAT_INT: case wxPAT_LONGINT: @@ -872,16 +986,19 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) case wxPAT_POINTER: #if wxUSE_STRUTILS { - const wxMB2WXbuf tmp = wxConvLibc.cMB2WX(szScratch); - size_t len = wxMin(lenMax, wxStrlen(tmp)); - wxStrncpy(buf, tmp, len); - lenCur += len; + wxASSERT( /* lenScratch >= 0 && */ lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN); + if (lenMax < lenScratch) + { + // fill output buffer and then return -1 + wxStrncpy(buf, szScratch, lenMax); + return -1; + } + wxStrncpy(buf, szScratch, lenScratch); + lenCur += lenScratch; } #else { - const wxMB2WXbuf tmp = - wxConvLibc.cMB2WX(szScratch); - APPEND_STR(tmp); + APPEND_STR(szScratch); } #endif break; @@ -893,13 +1010,49 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) return lenCur; } +// differences from standard strncpy: +// 1) copies everything from 'source' except for '%%' sequence which is copied as '%' +// 2) returns the number of written characters in 'dest' as it could differ from given 'n' +// 3) much less optimized, unfortunately... +static int wxCopyStrWithPercents(wxChar *dest, const wxChar *source, size_t n) +{ + size_t written = 0; + + if (n == 0) + return 0; + + size_t i; + for ( i = 0; i < n-1; source++, i++) + { + dest[written++] = *source; + if (*(source+1) == wxT('%')) + { + // skip this additional '%' character + source++; + i++; + } + } + + if (i < n) + // copy last character inconditionally + dest[written++] = *source; + + return written; +} + int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, const wxChar *format, va_list argptr) { - // cached data - static wxPrintfConvSpec arg[wxMAX_SVNPRINTF_ARGUMENTS]; - static wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS]; - static wxPrintfConvSpec *pspec[wxMAX_SVNPRINTF_ARGUMENTS] = { NULL }; + // useful for debugging, to understand if we are really using this function + // rather than the system implementation +#if 0 + wprintf(L"Using wxVsnprintf_\n"); +#endif + + // required memory: + wxPrintfConvSpec arg[wxMAX_SVNPRINTF_ARGUMENTS]; + wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS]; + wxPrintfConvSpec *pspec[wxMAX_SVNPRINTF_ARGUMENTS] = { NULL }; size_t i; @@ -924,24 +1077,38 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, wxPrintfConvSpec *current = &arg[nargs]; // make toparse point to the end of this specifier - toparse = current->argend; + toparse = current->m_pArgEnd; - if (current->pos > 0) { + if (current->m_pos > 0) + { // the positionals start from number 1... adjust the index - current->pos--; + current->m_pos--; posarg_present = true; - } else { + } + else + { // not a positional argument... - current->pos = nargs; + current->m_pos = nargs; nonposarg_present = true; } // this conversion specifier is tied to the pos-th argument... - pspec[current->pos] = current; + pspec[current->m_pos] = current; nargs++; if (nargs == wxMAX_SVNPRINTF_ARGUMENTS) + { + wxLogDebug(wxT("A single call to wxVsnprintf() has more than %d arguments; ") + wxT("ignoring all remaining arguments."), wxMAX_SVNPRINTF_ARGUMENTS); break; // cannot handle any additional conv spec + } + } + else + { + // it's safe to look in the next character of toparse as at worst + // we'll hit its \0 + if (*(toparse+1) == wxT('%')) + toparse++; // the Parse() returned false because we've found a %% } } } @@ -950,37 +1117,57 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, return -1; // format strings with both positional and // non-positional conversion specifier are unsupported !! + // on platforms where va_list is an array type, it is necessary to make a + // copy to be able to pass it to LoadArg as a reference. + bool ok = true; + va_list ap; + wxVaCopy(ap, argptr); + // now load arguments from stack - for (i=0; i < nargs; i++) { - if (!pspec[i]) - return -1; // user forgot a positional parameter (e.g. %$1s %$3s) ? - if (!pspec[i]->LoadArg(&argdata[i], argptr)) - return -1; // this means that wxPrintfConvSpec::Parse failed - // to set its 'type' to a valid value... + for (i=0; i < nargs && ok; i++) + { + // !pspec[i] means that the user forgot a positional parameter (e.g. %$1s %$3s); + // LoadArg == false means that wxPrintfConvSpec::Parse failed to set the + // conversion specifier 'type' to a valid value... + ok = pspec[i] && pspec[i]->LoadArg(&argdata[i], ap); } + va_end(ap); + + // something failed while loading arguments from the variable list... + if (!ok) + return -1; + // finally, process each conversion specifier with its own argument toparse = format; for (i=0; i < nargs; i++) { // copy in the output buffer the portion of the format string between // last specifier and the current one - size_t tocopy = ( arg[i].argpos - toparse ); + size_t tocopy = ( arg[i].m_pArgPos - toparse ); if (lenCur+tocopy >= lenMax) - return -1; // not enough space in the output buffer ! + { + // not enough space in the output buffer ! + // copy until the end of remaining space and then stop + wxCopyStrWithPercents(buf+lenCur, toparse, lenMax - lenCur - 1); + buf[lenMax-1] = wxT('\0'); + return -1; + } - wxStrncpy(buf+lenCur, toparse, tocopy); - lenCur += tocopy; + lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy); // process this specifier directly in the output buffer - int n = arg[i].Process(buf+lenCur, lenMax - lenCur, &argdata[arg[i].pos]); + int n = arg[i].Process(buf+lenCur, lenMax - lenCur, &argdata[arg[i].m_pos]); 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 ! + } lenCur += n; - // the +1 is because wxPrintfConvSpec::argend points to the last character + // the +1 is because wxPrintfConvSpec::m_pArgEnd points to the last character // of the format specifier, but we are not interested to it... - toparse = arg[i].argend + 1; + toparse = arg[i].m_pArgEnd + 1; } // copy portion of the format string after last specifier @@ -990,12 +1177,9 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, size_t tocopy = wxStrlen(format) + 1 - ( toparse - format ) ; if (lenCur+tocopy >= lenMax) return -1; // not enough space in the output buffer ! - wxStrncpy(buf+lenCur, toparse, tocopy); - lenCur += tocopy - 1; // the -1 is because of the '\0' - // clean the static array portion used... - // NOTE: other arrays do not need cleanup! - memset(pspec, 0, sizeof(wxPrintfConvSpec*)*nargs); + // the -1 is because of the '\0' + lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy) - 1; wxASSERT(lenCur == wxStrlen(buf)); return lenCur; @@ -1415,6 +1599,10 @@ int wxSnprintf( wxChar *str, size_t size, const wxChar *format, ... ) int ret = vswprintf( str, size, wxFormatConverter(format), argptr ); + // VsnprintfTestCase reveals that glibc's implementation of vswprintf + // doesn't nul terminate on truncation. + str[size - 1] = 0; + va_end(argptr); return ret;