X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/247c23b466261737a39641dda2418d3cc0cd02ff..855f31ebe72bef834a32df2c274b41fb282ad265:/src/common/wxchar.cpp diff --git a/src/common/wxchar.cpp b/src/common/wxchar.cpp index d6fd73b0a3..cf4e0a5be5 100644 --- a/src/common/wxchar.cpp +++ b/src/common/wxchar.cpp @@ -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 @@ -165,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. // @@ -185,14 +191,33 @@ bool WXDLLEXPORT wxOKlibc() // some limits of our implementation #define wxMAX_SVNPRINTF_ARGUMENTS 64 #define wxMAX_SVNPRINTF_FLAGBUFFER_LEN 32 +#define wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN 512 + +// prefer snprintf over sprintf +#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 -// the conversion specifiers accepted by wxMyPosVsnprintf_ +// the conversion specifiers accepted by wxVsnprintf_ enum wxPrintfArgType { wxPAT_INVALID = -1, wxPAT_INT, // %d, %i, %o, %u, %x, %X wxPAT_LONGINT, // %ld, etc -#if SIZEOF_LONG_LONG +#ifdef wxLongLong_t wxPAT_LONGLONGINT, // %Ld, etc #endif wxPAT_SIZET, // %Zd, etc @@ -213,12 +238,12 @@ 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 -#if SIZEOF_LONG_LONG - long long int pad_longlongint; // %Ld, etc +#ifdef wxLongLong_t + wxLongLong_t pad_longlongint; // %Ld, etc #endif size_t pad_sizet; // %Zd, etc @@ -240,7 +265,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 +273,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. + char 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(); @@ -292,7 +321,7 @@ public: // Process this conversion specifier and puts the result in the given // buffer. Returns the number of characters written in 'buf' or -1 if // there's not enough space. - int Process(wxChar *buf, size_t lenMax, wxPrintfArg *p); + int Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t written); // Loads the argument of this conversion specifier from given va_list. bool LoadArg(wxPrintfArg *p, va_list &argptr); @@ -304,13 +333,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] = '%'; } bool wxPrintfConvSpec::Parse(const wxChar *format) @@ -319,22 +351,23 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) // temporary parse data size_t flagofs = 1; - bool in_prec, prec_dot; + bool in_prec, // true if we found the dot in some previous iteration + prec_dot; // true if the dot has been already added to m_szFlags 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++] = '.'; \ prec_dot = true; \ } // what follows '%'? - const wxChar ch = *(++argend); + const wxChar ch = *(++m_pArgEnd); switch ( ch ) { case wxT('\0'): @@ -349,47 +382,70 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('+'): case wxT('\''): CHECK_PREC - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = char(ch); break; case wxT('-'): CHECK_PREC - adj_left = true; - szFlags[flagofs++] = ch; + m_bAlignLeft = true; + m_szFlags[flagofs++] = char(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++] = char(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++] = char(ch); break; case wxT('q'): case wxT('L'): ilen = 2; CHECK_PREC - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = char(ch); break; +#ifdef __WXMSW__ + // under Windows we support the special '%I64' notation as longlong + // integer conversion specifier for MSVC compatibility + // (it behaves exactly as '%lli' or '%Li' or '%qi') + case wxT('I'): + if (*(m_pArgEnd+1) != wxT('6') || + *(m_pArgEnd+2) != wxT('4')) + return false; // bad format + + m_pArgEnd++; + m_pArgEnd++; + + ilen = 2; + CHECK_PREC + m_szFlags[flagofs++] = char(ch); + m_szFlags[flagofs++] = '6'; + m_szFlags[flagofs++] = '4'; + break; +#endif // __WXMSW__ case wxT('Z'): ilen = 3; CHECK_PREC - szFlags[flagofs++] = ch; + m_szFlags[flagofs++] = char(ch); break; case wxT('*'): @@ -399,18 +455,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++] = char(ch); break; case wxT('1'): case wxT('2'): case wxT('3'): @@ -419,41 +475,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++] = char(*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 +520,25 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('x'): case wxT('X'): CHECK_PREC - szFlags[flagofs++] = ch; - szFlags[flagofs] = '\0'; + m_szFlags[flagofs++] = char(ch); + m_szFlags[flagofs] = '\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; -#else // !long long - type = wxPAT_LONGINT; -#endif // long long/!long long +#ifdef wxLongLong_t + m_type = wxPAT_LONGLONGINT; +#else // !wxLongLong_t + m_type = wxPAT_LONGINT; +#endif // wxLongLong_t/!wxLongLong_t else if (ilen == 3) - type = wxPAT_SIZET; + m_type = wxPAT_SIZET; done = true; break; @@ -492,17 +548,19 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) case wxT('g'): case wxT('G'): CHECK_PREC - szFlags[flagofs++] = ch; - szFlags[flagofs] = '\0'; + m_szFlags[flagofs++] = char(ch); + m_szFlags[flagofs] = '\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++] = char(ch); + m_szFlags[flagofs] = '\0'; done = true; break; @@ -511,22 +569,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 +595,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 +616,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 +629,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,23 +642,28 @@ bool wxPrintfConvSpec::Parse(const wxChar *format) } -void wxPrintfConvSpec::ReplaceAsteriskWith(int w) +void wxPrintfConvSpec::ReplaceAsteriskWith(int width) { char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN]; // find the first * in our flag buffer - char *pwidth = strchr(szFlags, '*'); - wxASSERT(pwidth); + char *pwidth = strchr(m_szFlags, '*'); + wxCHECK_RET(pwidth, _T("field width must be specified")); - // save what follows the * (the +1 is to skip it!) + // save what follows the * (the +1 is to skip the asterisk itself!) strcpy(temp, pwidth+1); - if (w < 0) { - pwidth[0] = '-'; + 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(*m_szFlags); +#endif + int offset = system_sprintf(pwidth, maxlen, "%d", abs(width)); // restore after the expanded * what was following it strcpy(pwidth+offset, temp); @@ -603,41 +672,41 @@ void wxPrintfConvSpec::ReplaceAsteriskWith(int w) 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; case wxPAT_LONGINT: p->pad_longint = va_arg(argptr, long int); break; -#if SIZEOF_LONG_LONG +#ifdef wxLongLong_t case wxPAT_LONGLONGINT: - p->pad_longlongint = va_arg(argptr, long long int); + p->pad_longlongint = va_arg(argptr, wxLongLong_t); break; -#endif +#endif // wxLongLong_t case wxPAT_SIZET: p->pad_sizet = va_arg(argptr, size_t); break; @@ -652,10 +721,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: @@ -683,11 +752,13 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr) return true; // loading was successful } -int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) +int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t written) { - // 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 + char szScratch[wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN]; + size_t lenScratch = 0, lenCur = 0; #define APPEND_CH(ch) \ { \ @@ -705,36 +776,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 +#ifdef wxLongLong_t 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,33 +815,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 wxUSE_WCHAR_T - if (type == wxPAT_WCHAR) { + 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; @@ -783,17 +860,23 @@ 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 = s = wxString(p->pad_pchar, wxConvLibc); + + //wprintf(L"converting ANSI=>Unicode"); // for debug } #else p->pad_pchar; #if wxUSE_WCHAR_T - if (type == wxPAT_PWCHAR) { + if (m_type == wxPAT_PWCHAR) + { // user passed a string explicitely indicated as Unicode... val = s = wxString(p->pad_pwchar, wxConvLibc); + + //printf("converting Unicode=>ANSI"); // for debug } #endif #endif @@ -802,15 +885,15 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) if (val) { #if wxUSE_STRUTILS - // at this point we are sure that max_width is positive or null + // at this point we are sure that m_nMaxWidth is positive or null // (see top of wxPrintfConvSpec::LoadArg) - len = wxMin((unsigned int)max_width, wxStrlen(val)); + 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; @@ -823,15 +906,13 @@ 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 - // at this point we are sure that max_width is positive or null - // (see top of wxPrintfConvSpec::LoadArg) len = wxMin((unsigned int)len, lenMax-lenCur); wxStrncpy(buf+lenCur, val, len); lenCur += len; @@ -840,24 +921,24 @@ 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(' ')); } } break; case wxPAT_NINT: - *p->pad_nint = lenCur; + *p->pad_nint = written; break; case wxPAT_NSHORTINT: - *p->pad_nshortint = lenCur; + *p->pad_nshortint = (short int)written; break; case wxPAT_NLONGINT: - *p->pad_nlongint = lenCur; + *p->pad_nlongint = written; break; case wxPAT_INVALID: @@ -867,29 +948,64 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p) // 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: -#if SIZEOF_LONG_LONG +#ifdef wxLongLong_t case wxPAT_LONGLONGINT: #endif case wxPAT_SIZET: case wxPAT_LONGDOUBLE: case wxPAT_DOUBLE: case wxPAT_POINTER: -#if wxUSE_STRUTILS + wxASSERT(lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN); +#if !wxUSE_UNICODE { - const wxMB2WXbuf tmp = wxConvLibc.cMB2WX(szScratch); - size_t len = wxMin(lenMax, wxStrlen(tmp)); - wxStrncpy(buf, tmp, len); - lenCur += 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); + // Copy the char scratch to the wide output. This requires + // conversion, but we can optimise by making use of the fact + // that we are formatting numbers, this should mean only 7-bit + // ascii characters are involved. + wxChar *bufptr = buf; + const wxChar *bufend = buf + lenMax; + const char *scratchptr = szScratch; + + // Simply copy each char to a wxChar, stopping on the first + // null or non-ascii byte. Checking '(signed char)*scratchptr + // > 0' is an extra optimisation over '*scratchptr != 0 && + // isascii(*scratchptr)', though it assumes signed char is + // 8-bit 2 complement. + while ((signed char)*scratchptr > 0 && bufptr != bufend) + *bufptr++ = *scratchptr++; + + if (bufptr == bufend) + return -1; + + lenCur += bufptr - buf; + + // check if the loop stopped on a non-ascii char, if yes then + // fall back to wxMB2WX + if (*scratchptr) + { + size_t len = wxMB2WX(bufptr, scratchptr, bufend - bufptr); + + if (len && len != (size_t)(-1)) + if (bufptr[len - 1]) + return -1; + else + lenCur += len; + } } #endif break; @@ -901,19 +1017,23 @@ 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) +// Copy chars from source to dest converting '%%' to '%'. Takes at most maxIn +// chars from source and write at most outMax chars to dest, returns the +// number of chars actually written. Does not treat null specially. +// +static int wxCopyStrWithPercents( + size_t maxOut, + wxChar *dest, + size_t maxIn, + const wxChar *source) { size_t written = 0; - if (n == 0) + if (maxIn == 0) return 0; size_t i; - for ( i = 0; i < n-1; source++, i++) + for ( i = 0; i < maxIn-1 && written < maxOut; source++, i++) { dest[written++] = *source; if (*(source+1) == wxT('%')) @@ -924,7 +1044,7 @@ static int wxCopyStrWithPercents(wxChar *dest, const wxChar *source, size_t n) } } - if (i < n) + if (i < maxIn && written < maxOut) // copy last character inconditionally dest[written++] = *source; @@ -934,10 +1054,16 @@ static int wxCopyStrWithPercents(wxChar *dest, const wxChar *source, size_t n) 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; @@ -962,31 +1088,47 @@ 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 %% } } } if (posarg_present && nonposarg_present) + { + buf[0] = 0; return -1; // format strings with both positional and - // non-positional conversion specifier are unsupported !! + } // 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. @@ -995,18 +1137,23 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, wxVaCopy(ap, argptr); // now load arguments from stack - for (i=0; i < nargs && ok; i++) { - // !pspec[i] if user forgot a positional parameter (e.g. %$1s %$3s) ? - // or LoadArg false if 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... + // (e.g. the user repeated twice the same positional argument) if (!ok) + { + buf[0] = 0; return -1; + } // finally, process each conversion specifier with its own argument toparse = format; @@ -1014,21 +1161,28 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, { // 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 ); - if (lenCur+tocopy >= lenMax) - return -1; // not enough space in the output buffer ! + size_t tocopy = ( arg[i].m_pArgPos - toparse ); - lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy); + lenCur += wxCopyStrWithPercents(lenMax - lenCur, buf + lenCur, + tocopy, toparse); + if (lenCur == lenMax) + { + buf[lenMax - 1] = 0; + return lenMax+1; // not enough space in the output buffer ! + } // 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], lenCur); if (n == -1) - return -1; // not enough space in the output buffer ! + { + buf[lenMax-1] = wxT('\0'); // be sure to always NUL-terminate the string + return lenMax+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 @@ -1036,17 +1190,20 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax, // conversion specifier // NOTE2: the +1 is because we want to copy also the '\0' size_t tocopy = wxStrlen(format) + 1 - ( toparse - format ) ; - if (lenCur+tocopy >= lenMax) - return -1; // not enough space in the output buffer ! - // the -1 is because of the '\0' - lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy) - 1; + lenCur += wxCopyStrWithPercents(lenMax - lenCur, buf + lenCur, + tocopy, toparse) - 1; + if (buf[lenCur]) + { + buf[lenCur] = 0; + return lenMax+1; // not enough space in the output buffer ! + } - // clean the static array portion used... - // NOTE: other arrays do not need cleanup! - memset(pspec, 0, sizeof(wxPrintfConvSpec*)*nargs); + // Don't do: + // wxASSERT(lenCur == wxStrlen(buf)); + // in fact if we embedded NULLs in the output buffer (using %c with a '\0') + // such check would fail - wxASSERT(lenCur == wxStrlen(buf)); return lenCur; } @@ -1054,7 +1211,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, ...) @@ -1094,9 +1257,13 @@ int WXDLLEXPORT wxSnprintf_(wxChar *buf, size_t len, const wxChar *format, ...) #ifdef wxNEED_FPUTS int wxFputs(const wchar_t *ws, FILE *stream) { + wxCharBuffer buf(wxConvLibc.cWC2MB(ws)); + if ( !buf ) + return -1; + // counting the number of wide characters written isn't worth the trouble, // simply distinguish between ok and error - return fputs(wxConvLibc.cWC2MB(ws), stream) == -1 ? -1 : 0; + return fputs(buf, stream) == -1 ? -1 : 0; } #endif // wxNEED_FPUTS @@ -1464,6 +1631,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;