/////////////////////////////////////////////////////////////////////////////
// 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
#pragma hdrstop
#endif
+#include "wx/wxchar.h"
+
#define _ISOC9X_SOURCE 1 // to get vsscanf()
#define _BSD_SOURCE 1 // to still get strdup()
#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 <windef.h>
#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.
//
// 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
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
// 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
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();
// 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);
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)
// 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'):
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('*'):
// 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'):
{
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;
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;
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;
{
// 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;
{
// 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;
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;
// 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);
}
-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);
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;
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:
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) \
{ \
} \
}
- 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:
#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;
#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
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;
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;
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:
// 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;
return lenCur;
}
+// 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 (maxIn == 0)
+ return 0;
+
+ size_t i;
+ for ( i = 0; i < maxIn-1 && written < maxOut; source++, i++)
+ {
+ dest[written++] = *source;
+ if (*(source+1) == wxT('%'))
+ {
+ // skip this additional '%' character
+ source++;
+ i++;
+ }
+ }
+
+ if (i < maxIn && written < maxOut)
+ // 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;
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.
+ 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...
+ // (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
{
// 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 );
- wxStrncpy(buf+lenCur, toparse, tocopy);
- lenCur += 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
// 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 !
- 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);
+ 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 !
+ }
wxASSERT(lenCur == wxStrlen(buf));
return lenCur;
#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, ...)
#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
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;