From 412a5c570d18aa64f0077a3278ca232776f3f259 Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Tue, 5 Sep 2006 21:14:38 +0000
Subject: [PATCH] wx printf() implementation bug fixes ('%' handling, thread
 safety, ...) and optimisations (patch 1548750)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41023 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/function.tex       |   6 +
 docs/latex/wx/wxstring.tex       |  13 ++
 src/common/wxchar.cpp            | 378 ++++++++++++++++++-------------
 tests/benchmarks/printfbench.cpp | 271 ++++++++++++++++++++++
 tests/strings/vsnprintf.cpp      |  54 ++++-
 tests/test.bkl                   |  13 ++
 6 files changed, 574 insertions(+), 161 deletions(-)
 create mode 100644 tests/benchmarks/printfbench.cpp

diff --git a/docs/latex/wx/function.tex b/docs/latex/wx/function.tex
index 0618858ecf..07e674a0cd 100644
--- a/docs/latex/wx/function.tex
+++ b/docs/latex/wx/function.tex
@@ -1746,6 +1746,12 @@ wxGetTranslation wouldn't find them.
 The same as \helpref{wxSnprintf}{wxsnprintf} but takes a {\tt va\_list }
 argument instead of arbitrary number of parameters.
 
+Note that if \texttt{wxUSE_PRINTF_POS_PARAMS} is set to 1, then this function supports
+positional arguments (see \helpref{wxString::Printf}{wxstringprintf} for more information).
+However other functions of the same family (wxPrintf, wxSprintf, wxFprintf, wxVfprintf,
+wxVfprintf, wxVprintf, wxVsprintf) currently do not to support positional parameters
+even when \texttt{wxUSE_PRINTF_POS_PARAMS} is 1.
+
 \wxheading{See also}
 
 \helpref{wxSnprintf}{wxsnprintf}, \helpref{wxString::PrintfV}{wxstringprintfv}
diff --git a/docs/latex/wx/wxstring.tex b/docs/latex/wx/wxstring.tex
index 40305dd472..1ed930cc20 100644
--- a/docs/latex/wx/wxstring.tex
+++ b/docs/latex/wx/wxstring.tex
@@ -897,6 +897,19 @@ Prepends {\it str} to this string, returning a reference to this string.
 Similar to the standard function {\it sprintf()}. Returns the number of
 characters written, or an integer less than zero on error.
 
+Note that if {\tt wxUSE_PRINTF_POS_PARAMS} is set to 1, then this function supports
+Unix98-style positional parameters:
+
+\begin{verbatim}
+    wxString str;
+
+    str.Printf(wxT("%d %d %d"), 1, 2, 3);
+    // str now contains "1 2 3"
+
+    str.Printf(wxT("%2$d %3$d %1$d"), 1, 2, 3);
+    // str now contains "2 3 1"
+\end{verbatim}
+
 {\bf NB:} This function will use a safe version of {\it vsprintf()} (usually called 
 {\it vsnprintf()}) whenever available to always allocate the buffer of correct
 size. Unfortunately, this function is not available on all platforms and the
diff --git a/src/common/wxchar.cpp b/src/common/wxchar.cpp
index 5e92ae1962..c77dc9cec3 100644
--- a/src/common/wxchar.cpp
+++ b/src/common/wxchar.cpp
@@ -183,7 +183,7 @@ 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
 
 // the conversion specifiers accepted by wxVsnprintf_
@@ -248,36 +248,37 @@ 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()
-    // NB: this buffer can be safely a char buffer instead of a wchar_t buffer
-    //     since it's used only for numeric conversion specifier and always
-    //     with sprintf().
-    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:
@@ -307,16 +308,16 @@ private:
 
 void wxPrintfConvSpec::Init()
 {
-    min_width = 0;
-    max_width = 0xFFFF;
-    pos = 0;
-    adj_left = false;
-    argpos = argend = NULL;
-    type = wxPAT_INVALID;
+    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 szFlags array and
+    // this character will never be removed from m_szFlags array and
     // is important when calling sprintf() in wxPrintfConvSpec::Process() !
-    szFlags[0] = '%';
+    m_szFlags[0] = wxT('%');
 }
 
 bool wxPrintfConvSpec::Parse(const wxChar *format)
@@ -328,19 +329,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++] = (char)'.'; \
+            m_szFlags[flagofs++] = '.'; \
             prec_dot = true; \
         }
 
         // what follows '%'?
-        const wxChar ch = *(++argend);
+        const wxChar ch = *(++m_pArgEnd);
         switch ( ch )
         {
             case wxT('\0'):
@@ -355,47 +356,51 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
             case wxT('+'):
             case wxT('\''):
                 CHECK_PREC
-                szFlags[flagofs++] = (char)ch;
+                m_szFlags[flagofs++] = ch;
                 break;
 
             case wxT('-'):
                 CHECK_PREC
-                adj_left = true;
-                szFlags[flagofs++] = (char)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++] = (char)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++] = (char)ch;
+                m_szFlags[flagofs++] = ch;
                 break;
 
             case wxT('q'):
             case wxT('L'):
                 ilen = 2;
                 CHECK_PREC
-                szFlags[flagofs++] = (char)ch;
+                m_szFlags[flagofs++] = ch;
                 break;
 
             case wxT('Z'):
                 ilen = 3;
                 CHECK_PREC
-                szFlags[flagofs++] = (char)ch;
+                m_szFlags[flagofs++] = ch;
                 break;
 
             case wxT('*'):
@@ -405,18 +410,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++] = (char)ch;
+                m_szFlags[flagofs++] = ch;
                 break;
 
             case wxT('1'): case wxT('2'): case wxT('3'):
@@ -425,41 +430,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++] = (char)(*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;
 
@@ -470,25 +475,25 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
             case wxT('x'):
             case wxT('X'):
                 CHECK_PREC
-                szFlags[flagofs++] = (char)ch;
-                szFlags[flagofs] = (char)'\0';
+                m_szFlags[flagofs++] = 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;
+                    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;
 
@@ -498,17 +503,17 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
             case wxT('g'):
             case wxT('G'):
                 CHECK_PREC
-                szFlags[flagofs++] = (char)ch;
-                szFlags[flagofs] = (char)'\0';
+                m_szFlags[flagofs++] = 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;
                 done = true;
                 break;
 
@@ -517,22 +522,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;
@@ -543,20 +548,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;
@@ -564,11 +569,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;
 
@@ -577,6 +582,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);
 
@@ -584,55 +595,61 @@ 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));
+#if wxUSE_UNICODE
+    int maxlen = (m_szFlags + wxMAX_SVNPRINTF_FLAGBUFFER_LEN - pwidth) / sizeof(wxChar);
+    int offset = ::swprintf(pwidth, maxlen, L"%d", abs(width));
+#else
+    int offset = ::sprintf(pwidth, "%d", abs(width));
+#endif
 
     // 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;
@@ -658,7 +675,7 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
             break;
 
         case wxPAT_CHAR:
-            p->pad_char = (char)va_arg(argptr, int);  // char is promoted to int when passed through '...'
+            p->pad_char = va_arg(argptr, int);  // char is promoted to int when passed through '...'
             break;
         case wxPAT_WCHAR:
             p->pad_wchar = (wchar_t)va_arg(argptr, int);  // char is promoted to int when passed through '...'
@@ -691,9 +708,19 @@ 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
+#define wxSCRATCH_BUFFER_SIZE       512
+
+    wxChar szScratch[wxSCRATCH_BUFFER_SIZE];
+    size_t lenScratch = 0, lenCur = 0;
+
+#if wxUSE_UNICODE
+#define system_sprintf(buff, flags, data)      ::swprintf(buff, wxSCRATCH_BUFFER_SIZE, flags, data)
+#else
+#define system_sprintf                         ::sprintf
+#endif
 
 #define APPEND_CH(ch) \
                 { \
@@ -711,36 +738,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, m_szFlags, p->pad_int);
             break;
 
         case wxPAT_LONGINT:
-            ::sprintf(szScratch, szFlags, p->pad_longint);
+            lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_longint);
             break;
 
 #if SIZEOF_LONG_LONG
         case wxPAT_LONGLONGINT:
-            ::sprintf(szScratch, szFlags, p->pad_longlongint);
+            lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_longlongint);
             break;
 #endif // SIZEOF_LONG_LONG
 
         case wxPAT_SIZET:
-            ::sprintf(szScratch, szFlags, p->pad_sizet);
+            lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_sizet);
             break;
 
         case wxPAT_LONGDOUBLE:
-            ::sprintf(szScratch, szFlags, p->pad_longdouble);
+            lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_longdouble);
             break;
 
         case wxPAT_DOUBLE:
-            ::sprintf(szScratch, szFlags, p->pad_double);
+            lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_double);
             break;
 
         case wxPAT_POINTER:
-            ::sprintf(szScratch, szFlags, p->pad_pointer);
+            lenScratch = system_sprintf(szScratch, m_szFlags, p->pad_pointer);
             break;
 
         case wxPAT_CHAR:
@@ -750,33 +777,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;
@@ -789,17 +822,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
@@ -808,15 +847,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;
@@ -829,15 +868,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;
@@ -846,9 +883,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(' '));
                 }
             }
@@ -873,7 +910,7 @@ 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:
@@ -886,16 +923,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 < wxSCRATCH_BUFFER_SIZE);
+                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;
@@ -940,10 +980,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;
 
@@ -968,24 +1014,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 %%
             }
         }
     }
@@ -1001,10 +1061,11 @@ 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);
     }
 
@@ -1020,21 +1081,30 @@ 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 );
+        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;
+        }
 
         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
@@ -1048,10 +1118,6 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
     // the -1 is because of the '\0'
     lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy) - 1;
 
-    // clean the static array portion used...
-    // NOTE: other arrays do not need cleanup!
-    memset(pspec, 0, sizeof(wxPrintfConvSpec*)*nargs);
-
     wxASSERT(lenCur == wxStrlen(buf));
     return lenCur;
 }
diff --git a/tests/benchmarks/printfbench.cpp b/tests/benchmarks/printfbench.cpp
new file mode 100644
index 0000000000..49f5d8e433
--- /dev/null
+++ b/tests/benchmarks/printfbench.cpp
@@ -0,0 +1,271 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        printfbench.cpp
+// Purpose:     A sample console app which benchmarks wxPrintf*() functions
+// Author:      Francesco Montorsi
+// Modified by:
+// Created:     27/3/2006
+// RCS-ID:      $Id$
+// Copyright:   (c) 2006 Francesco Montorsi
+// Licence:     wxWindows license
+/////////////////////////////////////////////////////////////////////////////
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+#include <wx/string.h>
+#include <wx/stopwatch.h>
+#include <wx/utils.h>
+#include <wx/cmdline.h>
+#include <wx/app.h>
+
+
+// ----------------------------------------------------------------------------
+// command line
+// ----------------------------------------------------------------------------
+
+#define HELP_SWITCH              wxT("h")
+#define NUMBENCHMARK_OPTION           wxT("n")
+
+static const wxCmdLineEntryDesc g_cmdLineDesc[] =
+{
+    { wxCMD_LINE_SWITCH, HELP_SWITCH, wxT("help"),
+      wxT("displays help on the command line parameters") },
+
+    { wxCMD_LINE_OPTION, NUMBENCHMARK_OPTION, wxT("numtest"),
+      wxT("the number of wxPrintf() calls to benchmark"), wxCMD_LINE_VAL_NUMBER },
+
+    { wxCMD_LINE_NONE }
+};
+
+
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
+#define DEFAULT_NUMBENCHMARKS    100000
+#define BUFSIZE                  10000
+
+// set wxTEST_WX_ONLY to 1 when you want to profile wx's implementation only.
+// A little reminder about profiling under Linux:
+//
+//   1) configure wxWidgets in release mode
+//   2) make sure that HAVE_UNIX98_PRINTF is undefined (just #defining it to zero
+//      does not work; you must comment out the entire #define) in your setup.h;
+//      and also that wxUSE_PRINTF_POS_PARAMS is set to 1; this will force the
+//      use of wx's own implementation of wxVsnprintf()
+//   3) compile wx
+//   4) set wxTEST_WX_ONLY to 1 and compile tests as well
+//   5) run "callgrind ./printfbench"
+//   6) run "kcachegrind dump_file_generated_by_callgrind"
+//
+#define wxTEST_WX_ONLY           1
+
+
+const wxString g_verylongString =
+    wxT("very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very ")
+    wxT("very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string!\n\n\n");
+
+
+// ----------------------------------------------------------------------------
+// benchmarking helpers
+// ----------------------------------------------------------------------------
+
+#define DO_LONG_BENCHMARK(fnc) \
+    fnc(buffer, BUFSIZE, \
+        wxT("This is a reasonably long string with various %s arguments, exactly %d, ") \
+        wxT("and is used as benchmark for %s - %% %.2f %d %s"), \
+        wxT("(many!!)"), 6, wxT("this program"), 23.342f, 999, \
+        g_verylongString.c_str());
+
+#define DO_LONG_POSITIONAL_BENCHMARK(fnc) \
+    fnc(buffer, BUFSIZE, \
+        wxT("This is a %2$s and thus is harder to parse... let's %1$s ") \
+        wxT("for our benchmarking aims - %% %3$f %5$d %4$s"), \
+        wxT("test it"), wxT("string with positional arguments"), 23.342f, \
+        g_verylongString.c_str(), 999);
+
+#define DO_BENCHMARK(fnc) \
+    fnc(buffer, BUFSIZE, \
+        wxT("This is a short %s string with very few words"), wxT("test"));
+
+#define DO_POSITIONAL_BENCHMARK(fnc) \
+    fnc(buffer, BUFSIZE, \
+        wxT("This is a %2$s and thus is harder to parse... nonetheless, %1$s !"), \
+        wxT("test it"), wxT("string with positional arguments"));
+
+// the configure script of wxWidgets will define HAVE_UNIX98_PRINTF on those
+// system with a *printf() family of functions conformant to Unix 98 standard;
+// systems without the configure script as build system (e.g. Windows) do not
+// have positional support anyway
+#ifdef HAVE_UNIX98_PRINTF
+    #define wxSYSTEM_HAS_POSPARAM_SUPPORT   1
+#else
+    #define wxSYSTEM_HAS_POSPARAM_SUPPORT   1
+#endif
+
+// we need to avoid the use of wxPrintf() here since it could have been mapped
+// to wxWidgets' implementation of wxVsnPrintf() !
+#if wxUSE_UNICODE
+    #define sys_printf swprintf
+#else
+    #define sys_printf snprintf
+#endif
+
+// the given stopwatch returns a time delta in milliseconds thus this macro
+// returns the number of microseconds required for a single *printf() call
+#define wxFMT(sw)      ((double)((sw.Time()*1000.0)/tests))
+
+
+
+
+// ----------------------------------------------------------------------------
+// main
+// ----------------------------------------------------------------------------
+
+int main(int argc, char **argv)
+{
+    wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
+    wxInitializer initializer;
+    if ( !initializer )
+    {
+        fprintf(stderr, "Failed to initialize the wxWidgets library, aborting.");
+
+        return -1;
+    }
+
+    // parse the command line
+    // ----------------------
+
+    wxCmdLineParser cmdParser(g_cmdLineDesc, argc, argv);
+    if (cmdParser.Parse() != 0)
+        return false;
+
+    if (cmdParser.Found(HELP_SWITCH))
+    {
+        cmdParser.Usage();
+        return false;
+    }
+
+    long tests;
+    if (!cmdParser.Found(NUMBENCHMARK_OPTION, &tests))
+        tests = DEFAULT_NUMBENCHMARKS;
+
+
+
+    // print some info useful to compare different benchmarks
+    // -------------------------------------------------------
+
+    wxPrintf(wxT("\nRunning on %s\n"), wxGetOsDescription().c_str());
+    wxPrintf(wxT("Compiled in %s-%s-%s mode...\n\n"),
+#if wxUSE_UNICODE
+    wxT("unicode"),
+#else
+    wxT("ansi"),
+#endif
+#ifdef __WXDEBUG__
+    wxT("debug"),
+#else
+    wxT("release"),
+#endif
+#ifdef WXUSINGDLL
+    wxT("shared")
+#else
+    wxT("static")
+#endif
+            );
+    wxPrintf(wxT("Running %ld tests, for each configuration/implementation\n\n"), tests);
+
+
+
+    // start!
+    // ----------------------
+
+    wxChar buffer[BUFSIZE];
+
+#if !wxTEST_WX_ONLY
+
+#if wxUSE_PRINTF_POS_PARAMS
+    wxStopWatch wxPos;
+    for (int i=0; i < tests; i++)
+    {
+        DO_LONG_POSITIONAL_BENCHMARK(wxSnprintf)
+        DO_POSITIONAL_BENCHMARK(wxSnprintf)
+    }
+    wxPos.Pause();
+#endif
+
+    // benchmark system implementation of snprintf()
+    wxStopWatch sys;
+    for (int i=0; i < tests; i++)
+    {
+        DO_LONG_BENCHMARK(sys_printf)
+        DO_BENCHMARK(sys_printf)
+    }
+    sys.Pause();
+
+#if wxSYSTEM_HAS_POSPARAM_SUPPORT
+    wxStopWatch sysPos;
+    for (int i=0; i < tests; i++)
+    {
+        DO_LONG_POSITIONAL_BENCHMARK(wxSnprintf)
+        DO_POSITIONAL_BENCHMARK(wxSnprintf)
+    }
+    sysPos.Pause();
+#endif
+
+#else       // !wxTEST_WX_ONLY
+
+    // fake stopwatches
+    wxStopWatch wxPos, sys, sysPos;
+    wxPos.Pause();
+    sys.Pause();
+    sysPos.Pause();
+
+#endif      // !wxTEST_WX_ONLY
+
+    // benchmark wxWidgets implementation of wxSnprintf()
+    wxStopWatch wx;
+    for (int i=0; i < tests; i++)
+    {
+        DO_LONG_BENCHMARK(wxSnprintf)
+        DO_BENCHMARK(wxSnprintf)
+    }
+    wx.Pause();
+
+    // print results
+    // ----------------------
+
+    wxPrintf(wxT("\n ============================== RESULTS ==============================\n"));
+    wxPrintf(wxT(" => Time for the system's snprintf():                  %.5f microsec\n"), wxFMT(sys));
+#if wxSYSTEM_HAS_POSPARAM_SUPPORT
+    wxPrintf(wxT(" => Time for the system's snprintf() with positionals: %.5f microsec\n"), wxFMT(sysPos));
+#endif
+    wxPrintf(wxT(" => Time for wxSnprintf():                             %.5f microsec\n"), wxFMT(wx));
+#if wxUSE_PRINTF_POS_PARAMS
+    wxPrintf(wxT(" => Time for wxSnprintf() with positionals:            %.5f microsec\n"), wxFMT(wxPos));
+#endif
+
+    double medium;
+#if wxSYSTEM_HAS_POSPARAM_SUPPORT && wxUSE_PRINTF_POS_PARAMS
+    medium = ((double)wx.Time() / (double)sys.Time() + (double)wxPos.Time() / (double)sysPos.Time()) / 2;
+#else
+    medium = (double)wx.Time() / (double)sys.Time();
+#endif
+
+    if (medium > 0.0)
+    {
+        // sometimes it happens that with a small number of tests, medium results zero;
+        // in that case doing the 1.0/medium will not be a wise thing!
+        wxPrintf(wxT("\nwxWidgets implementation is %.3f times slower\n")
+                 wxT("(i.e. %.3f times faster) than system implementation.\n"), medium, 1.0/medium);
+    }
+
+    wxPrintf(wxT("\n\n"));
+    return 0;
+}
+
diff --git a/tests/strings/vsnprintf.cpp b/tests/strings/vsnprintf.cpp
index 70f7b12961..d8e96f2811 100644
--- a/tests/strings/vsnprintf.cpp
+++ b/tests/strings/vsnprintf.cpp
@@ -81,6 +81,8 @@ private:
         CPPUNIT_TEST( G );
         CPPUNIT_TEST( S );
         CPPUNIT_TEST( Asterisk );
+        CPPUNIT_TEST( Percent );
+        CPPUNIT_TEST( LongLong );
 
         CPPUNIT_TEST( BigToSmallBuffer );
     CPPUNIT_TEST_SUITE_END();
@@ -90,6 +92,9 @@ private:
     void G();
     void S();
     void Asterisk();
+    void Percent();
+    void LongLong();
+    void Unicode();
 
     void BigToSmallBuffer();
     void Misc(wxChar *buffer, int size);
@@ -179,10 +184,50 @@ void VsnprintfTestCase::S()
 
     CMP3("abcde", "%.5s", wxT("abcdefghi"));
 
+    // do the same tests but with Unicode characters:
+#if wxUSE_UNICODE
+    #define ALPHA     "\x3B1"
+    #define BETA      "\x3B2"
+    #define GAMMA     "\x3B3"
+    #define DELTA     "\x3B4"
+    #define EPSILON   "\x3B5"
+    #define ZETA      "\x3B6"
+    #define ETA       "\x3B7"
+    #define THETA     "\x3B8"
+    #define IOTA      "\x3B9"
+
+    #define ABC         ALPHA BETA GAMMA
+    #define ABCDE       ALPHA BETA GAMMA DELTA EPSILON
+    #define ABCDEFGHI   ALPHA BETA GAMMA DELTA EPSILON ZETA ETA THETA IOTA
+
+    CMP3("  " ABC, "%5s", wxT(ABC));
+    CMP3("    " ALPHA, "%5s", wxT(ALPHA));
+    CMP3(ABCDEFGHI, "%5s", wxT(ABCDEFGHI));
+    CMP3(ABC "  ", "%-5s", wxT(ABC));
+    CMP3(ABCDEFGHI, "%-5s", wxT(ABCDEFGHI));
+
+    CMP3(ABCDE, "%.5s", wxT(ABCDEFGHI));
+#endif
+}
+
+void VsnprintfTestCase::Asterisk()
+{
+    CMP5("       0.1", "%*.*f", 10, 1, 0.123);
+    CMP5("    0.1230", "%*.*f", 10, 4, 0.123);
+    CMP5("0.1", "%*.*f", 3, 1, 0.123);
+
+    CMP4("%0.002", "%%%.*f", 3, 0.0023456789);
+}
+
+void VsnprintfTestCase::Percent()
+{
     // some tests without any argument passed through ...
     CMP2("%", "%%");
     CMP2("%%%", "%%%%%%");
 
+    CMP3("%  abc", "%%%5s", wxT("abc"));
+    CMP3("%  abc%", "%%%5s%%", wxT("abc"));
+
     // do not test odd number of '%' symbols as different implementations
     // of snprintf() give different outputs as this situation is not considered
     // by any standard (in fact, GCC will also warn you about a spurious % if
@@ -190,13 +235,12 @@ void VsnprintfTestCase::S()
     // Compare(wxT("%"), wxT("%%%"));
 }
 
-void VsnprintfTestCase::Asterisk()
+void VsnprintfTestCase::LongLong()
 {
-    CMP5("       0.1", "%*.*f", 10, 1, 0.123);
-    CMP5("    0.1230", "%*.*f", 10, 4, 0.123);
-    CMP5("0.1", "%*.*f", 3, 1, 0.123);
+    CMP3("123456789", "%lld", (long long int)123456789);
+    CMP3("-123456789", "%lld", (long long int)-123456789);
 
-    CMP4("%0.002", "%%%.*f", 3, 0.0023456789);
+    CMP3("123456789", "%llu", (unsigned long long int)123456789);
 }
 
 void VsnprintfTestCase::Misc(wxChar *buffer, int size)
diff --git a/tests/test.bkl b/tests/test.bkl
index ac376224e1..67aeca9930 100644
--- a/tests/test.bkl
+++ b/tests/test.bkl
@@ -88,4 +88,17 @@
         <files>testdata.fc</files>
     </wx-data>
 
+
+
+    <!-- BENCHMARKS -->
+
+    <exe id="printfbench" template="wx_sample_console,wx_test"
+                          template_append="wx_append_base">
+        <sources>
+            benchmarks/printfbench.cpp
+        </sources>
+        <wx-lib>base</wx-lib>
+    </exe>
+
+
 </makefile>
-- 
2.50.0

