]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/wxprintf.cpp
Rework the wxCusor ctor taking wx stock number to provide as many cursors as
[wxWidgets.git] / src / common / wxprintf.cpp
index 565f1649218201b64de46d512da3fd69c4d9c562..ee415e8bac9f2b3f56d21ca512602f196008bf87 100644 (file)
@@ -20,7 +20,7 @@
     #pragma hdrstop
 #endif
 
     #pragma hdrstop
 #endif
 
-#include "wx/wxchar.h"
+#include "wx/crt.h"
 
 #include <string.h>
 
 
 #include <string.h>
 
@@ -44,7 +44,8 @@ using namespace std ;
 // special test mode: define all functions below even if we don't really need
 // them to be able to test them
 #ifdef wxTEST_PRINTF
 // special test mode: define all functions below even if we don't really need
 // them to be able to test them
 #ifdef wxTEST_PRINTF
-    #undef wxVsnprintf_
+    #undef wxCRT_VsnprintfW
+    #undef wxCRT_VsnprintfA
 #endif
 
 // ----------------------------------------------------------------------------
 #endif
 
 // ----------------------------------------------------------------------------
@@ -53,13 +54,13 @@ using namespace std ;
 // (very useful for i18n purposes)
 // ----------------------------------------------------------------------------
 
 // (very useful for i18n purposes)
 // ----------------------------------------------------------------------------
 
-#if !defined(wxVsnprintf_)
+// ----------------------------------------------------------------------------
+// common code for both ANSI and Unicode versions
+// ----------------------------------------------------------------------------
 
 
-#if !wxUSE_WXVSNPRINTF
-    #error wxUSE_WXVSNPRINTF must be 1 if our wxVsnprintf_ is used
-#endif
+#if !defined(wxCRT_VsnprintfW) || !defined(wxCRT_VsnprintfA)
 
 
-// wxUSE_STRUTILS says our wxVsnprintf_ implementation to use or not to
+// wxUSE_STRUTILS says our wxCRT_VsnprintfW implementation to use or not to
 // use wxStrlen and wxStrncpy functions over one-char processing loops.
 //
 // Some benchmarking revealed that wxUSE_STRUTILS == 1 has the following
 // use wxStrlen and wxStrncpy functions over one-char processing loops.
 //
 // Some benchmarking revealed that wxUSE_STRUTILS == 1 has the following
@@ -99,7 +100,10 @@ using namespace std ;
     #define SYSTEM_SPRINTF_IS_UNSAFE
 #endif
 
     #define SYSTEM_SPRINTF_IS_UNSAFE
 #endif
 
-// the conversion specifiers accepted by wxVsnprintf_
+namespace
+{
+
+// the conversion specifiers accepted by wxCRT_VsnprintfW
 enum wxPrintfArgType {
     wxPAT_INVALID = -1,
 
 enum wxPrintfArgType {
     wxPAT_INVALID = -1,
 
@@ -126,7 +130,7 @@ enum wxPrintfArgType {
     wxPAT_NLONGINT      // %ln
 };
 
     wxPAT_NLONGINT      // %ln
 };
 
-// an argument passed to wxVsnprintf_
+// an argument passed to wxCRT_VsnprintfW
 typedef union {
     int pad_int;                        //  %d, %i, %o, %u, %x, %X
     long int pad_longint;               // %ld, etc
 typedef union {
     int pad_int;                        //  %d, %i, %o, %u, %x, %X
     long int pad_longint;               // %ld, etc
@@ -143,19 +147,35 @@ typedef union {
     char pad_char;                      // %hc  (in ANSI mode: %c, too)
     wchar_t pad_wchar;                  // %lc  (in Unicode mode: %c, too)
 
     char pad_char;                      // %hc  (in ANSI mode: %c, too)
     wchar_t pad_wchar;                  // %lc  (in Unicode mode: %c, too)
 
-    char *pad_pchar;                    // %s   (related to a char *)
-    wchar_t *pad_pwchar;                // %s   (related to a wchar_t *)
+    void *pad_str;                      // %s
 
     int *pad_nint;                      // %n
     short int *pad_nshortint;           // %hn
     long int *pad_nlongint;             // %ln
 } wxPrintfArg;
 
 
     int *pad_nint;                      // %n
     short int *pad_nshortint;           // %hn
     long int *pad_nlongint;             // %ln
 } wxPrintfArg;
 
+// helper for converting string into either char* or wchar_t* dependening
+// on the type of wxPrintfConvSpec<T> instantiation:
+template<typename CharType> struct wxPrintfStringHelper {};
+
+template<> struct wxPrintfStringHelper<char>
+{
+    typedef const wxWX2MBbuf ConvertedType;
+    static ConvertedType Convert(const wxString& s) { return s.mb_str(); }
+};
+
+template<> struct wxPrintfStringHelper<wchar_t>
+{
+    typedef const wxWX2WCbuf ConvertedType;
+    static ConvertedType Convert(const wxString& s) { return s.wc_str(); }
+};
+
 
 // Contains parsed data relative to a conversion specifier given to
 
 // Contains parsed data relative to a conversion specifier given to
-// wxVsnprintf_ and parsed from the format string
+// wxCRT_VsnprintfW 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...
 // NOTE: in C++ there is almost no difference between struct & classes thus
 //       there is no performance gain by using a struct here...
+template<typename CharType>
 class wxPrintfConvSpec
 {
 public:
 class wxPrintfConvSpec
 {
 public:
@@ -177,18 +197,18 @@ public:
     // 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 !
     // 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 *m_pArgPos;
+    const CharType *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 !
 
     // 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 *m_pArgEnd;
+    const CharType *m_pArgEnd;
 
     // a little buffer where formatting flags like #+\.hlqLZ are stored by Parse()
     // for use in Process()
     // NB: even if this buffer is used only for numeric conversion specifiers and
 
     // a little buffer where formatting flags like #+\.hlqLZ are stored by Parse()
     // for use in Process()
     // 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
+    //     thus could be safely declared as a char[] buffer, we want it to be wchar_t
     //     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];
     //     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];
@@ -197,19 +217,19 @@ public:
 public:
 
     // we don't declare this as a constructor otherwise it would be called
 public:
 
     // we don't declare this as a constructor otherwise it would be called
-    // automatically and we don't want this: to be optimized, wxVsnprintf_
+    // automatically and we don't want this: to be optimized, wxCRT_VsnprintfW
     // calls this function only on really-used instances of this class.
     void Init();
 
     // Parses the first conversion specifier in the given string, which must
     // begin with a '%'. Returns false if the first '%' does not introduce a
     // (valid) conversion specifier and thus should be ignored.
     // calls this function only on really-used instances of this class.
     void Init();
 
     // Parses the first conversion specifier in the given string, which must
     // begin with a '%'. Returns false if the first '%' does not introduce a
     // (valid) conversion specifier and thus should be ignored.
-    bool Parse(const wxChar *format);
+    bool Parse(const CharType *format);
 
     // 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.
 
     // 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, size_t written);
+    int Process(CharType *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);
 
     // Loads the argument of this conversion specifier from given va_list.
     bool LoadArg(wxPrintfArg *p, va_list &argptr);
@@ -219,7 +239,8 @@ private:
     void ReplaceAsteriskWith(int w);
 };
 
     void ReplaceAsteriskWith(int w);
 };
 
-void wxPrintfConvSpec::Init()
+template<typename CharType>
+void wxPrintfConvSpec<CharType>::Init()
 {
     m_nMinWidth = 0;
     m_nMaxWidth = 0xFFFF;
 {
     m_nMinWidth = 0;
     m_nMaxWidth = 0xFFFF;
@@ -233,7 +254,8 @@ void wxPrintfConvSpec::Init()
     m_szFlags[0] = '%';
 }
 
     m_szFlags[0] = '%';
 }
 
-bool wxPrintfConvSpec::Parse(const wxChar *format)
+template<typename CharType>
+bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
 {
     bool done = false;
 
 {
     bool done = false;
 
@@ -255,7 +277,7 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
         }
 
         // what follows '%'?
         }
 
         // what follows '%'?
-        const wxChar ch = *(++m_pArgEnd);
+        const CharType ch = *(++m_pArgEnd);
         switch ( ch )
         {
             case wxT('\0'):
         switch ( ch )
         {
             case wxT('\0'):
@@ -363,8 +385,8 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
                 {
                     int len = 0;
                     CHECK_PREC
                 {
                     int len = 0;
                     CHECK_PREC
-                    while ( (*m_pArgEnd >= wxT('0')) &&
-                            (*m_pArgEnd <= wxT('9')) )
+                    while ( (*m_pArgEnd >= CharType('0')) &&
+                            (*m_pArgEnd <= CharType('9')) )
                     {
                         m_szFlags[flagofs++] = char(*m_pArgEnd);
                         len = len*10 + (*m_pArgEnd - wxT('0'));
                     {
                         m_szFlags[flagofs++] = char(*m_pArgEnd);
                         len = len*10 + (*m_pArgEnd - wxT('0'));
@@ -529,8 +551,8 @@ bool wxPrintfConvSpec::Parse(const wxChar *format)
     return true;        // parsing was successful
 }
 
     return true;        // parsing was successful
 }
 
-
-void wxPrintfConvSpec::ReplaceAsteriskWith(int width)
+template<typename CharType>
+void wxPrintfConvSpec<CharType>::ReplaceAsteriskWith(int width)
 {
     char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
 
 {
     char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
 
@@ -557,7 +579,8 @@ void wxPrintfConvSpec::ReplaceAsteriskWith(int width)
     strcpy(pwidth+offset, temp);
 }
 
     strcpy(pwidth+offset, temp);
 }
 
-bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
+template<typename CharType>
+bool wxPrintfConvSpec<CharType>::LoadArg(wxPrintfArg *p, va_list &argptr)
 {
     // did the '*' width/precision specifier was used ?
     if (m_nMaxWidth == -1)
 {
     // did the '*' width/precision specifier was used ?
     if (m_nMaxWidth == -1)
@@ -616,10 +639,8 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
             break;
 
         case wxPAT_PCHAR:
             break;
 
         case wxPAT_PCHAR:
-            p->pad_pchar = va_arg(argptr, char *);
-            break;
         case wxPAT_PWCHAR:
         case wxPAT_PWCHAR:
-            p->pad_pwchar = va_arg(argptr, wchar_t *);
+            p->pad_str = va_arg(argptr, void *);
             break;
 
         case wxPAT_NINT:
             break;
 
         case wxPAT_NINT:
@@ -640,7 +661,8 @@ bool wxPrintfConvSpec::LoadArg(wxPrintfArg *p, va_list &argptr)
     return true;    // loading was successful
 }
 
     return true;    // loading was successful
 }
 
-int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t written)
+template<typename CharType>
+int wxPrintfConvSpec<CharType>::Process(CharType *buf, size_t lenMax, wxPrintfArg *p, size_t written)
 {
     // buffer to avoid dynamic memory allocation each time for small strings;
     // note that this buffer is used only to hold results of number formatting,
 {
     // buffer to avoid dynamic memory allocation each time for small strings;
     // note that this buffer is used only to hold results of number formatting,
@@ -656,14 +678,6 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t
                     buf[lenCur++] = ch; \
                 }
 
                     buf[lenCur++] = ch; \
                 }
 
-#define APPEND_STR(s) \
-                { \
-                    for ( const wxChar *p = s; *p; p++ ) \
-                    { \
-                        APPEND_CH(*p); \
-                    } \
-                }
-
     switch ( m_type )
     {
         case wxPAT_INT:
     switch ( m_type )
     {
         case wxPAT_INT:
@@ -699,32 +713,13 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t
         case wxPAT_CHAR:
         case wxPAT_WCHAR:
             {
         case wxPAT_CHAR:
         case wxPAT_WCHAR:
             {
-                wxChar val =
-#if wxUSE_UNICODE
-                    p->pad_wchar;
-
+                wxUniChar ch;
                 if (m_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;
+                    ch = p->pad_char;
+                else // m_type == wxPAT_WCHAR
+                    ch = p->pad_wchar;
 
 
-#if wxUSE_WCHAR_T
-                if (m_type == wxPAT_WCHAR)
-                {
-                    // user passed a character explicitely indicated as Unicode...
-                    const wchar_t buf[2] = { p->pad_wchar, 0 };
-                    val = wxString(buf, wxConvLibc)[0u];
-
-                    //printf("converting Unicode=>ANSI");   // for debug
-                }
-#endif
-#endif
+                CharType val = ch;
 
                 size_t i;
 
 
                 size_t i;
 
@@ -743,54 +738,18 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t
         case wxPAT_PCHAR:
         case wxPAT_PWCHAR:
             {
         case wxPAT_PCHAR:
         case wxPAT_PWCHAR:
             {
-                wxString s;
-                const wxChar *val =
-#if wxUSE_UNICODE
-                    p->pad_pwchar;
+                wxArgNormalizedString arg(p->pad_str);
+                wxString s = arg;
 
 
-                if (m_type == wxPAT_PCHAR)
-                {
-                    // user passed a string explicitely indicated as ANSI...
-                    val = s = wxString(p->pad_pchar, wxConvLibc);
+                if ( !arg.IsValid() && m_nMaxWidth >= 6 )
+                    s = wxT("(null)");
 
 
-                    //wprintf(L"converting ANSI=>Unicode");   // for debug
-                }
-#else
-                    p->pad_pchar;
+                typename wxPrintfStringHelper<CharType>::ConvertedType strbuf(
+                        wxPrintfStringHelper<CharType>::Convert(s));
 
 
-#if wxUSE_WCHAR_T
-                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
-                int len;
-
-                if (val)
-                {
-#if wxUSE_STRUTILS
-                    // at this point we are sure that m_nMaxWidth is positive or null
-                    // (see top of wxPrintfConvSpec::LoadArg)
-                    len = wxMin((unsigned int)m_nMaxWidth, wxStrlen(val));
-#else
-                    for ( len = 0; val[len] && (len < m_nMaxWidth); len++ )
-                        ;
-#endif
-                }
-                else if (m_nMaxWidth >= 6)
-                {
-                    val = wxT("(null)");
-                    len = 6;
-                }
-                else
-                {
-                    val = wxEmptyString;
-                    len = 0;
-                }
+                // at this point we are sure that m_nMaxWidth is positive or
+                // null (see top of wxPrintfConvSpec::LoadArg)
+                int len = wxMin((unsigned int)m_nMaxWidth, wxStrlen(strbuf));
 
                 int i;
 
 
                 int i;
 
@@ -800,14 +759,9 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t
                         APPEND_CH(_T(' '));
                 }
 
                         APPEND_CH(_T(' '));
                 }
 
-#if wxUSE_STRUTILS
                 len = wxMin((unsigned int)len, lenMax-lenCur);
                 len = wxMin((unsigned int)len, lenMax-lenCur);
-                wxStrncpy(buf+lenCur, val, len);
+                wxStrncpy(buf+lenCur, strbuf, len);
                 lenCur += len;
                 lenCur += len;
-#else
-                for (i = 0; i < len; i++)
-                    APPEND_CH(val[i]);
-#endif
 
                 if (m_bAlignLeft)
                 {
 
                 if (m_bAlignLeft)
                 {
@@ -848,54 +802,22 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t
         case wxPAT_DOUBLE:
         case wxPAT_POINTER:
             wxASSERT(lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN);
         case wxPAT_DOUBLE:
         case wxPAT_POINTER:
             wxASSERT(lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN);
-#if !wxUSE_UNICODE
+            // NB: 1) we can compare lenMax (for CharType*, i.e. possibly
+            //        wchar_t*) with lenScratch (char*) because this code is
+            //        formatting integers and that will have the same length
+            //        even in UTF-8 (the only case when char* length may be
+            //        more than wchar_t* length of the same string)
+            //     2) wxStrncpy converts the 2nd argument to 1st argument's
+            //        type transparently if their types differ, so this code
+            //        works for both instantiations
+            if (lenMax < lenScratch)
             {
             {
-                if (lenMax < lenScratch)
-                {
-                    // fill output buffer and then return -1
-                    wxStrncpy(buf, szScratch, lenMax);
-                    return -1;
-                }
-                wxStrncpy(buf, szScratch, lenScratch);
-                lenCur += lenScratch;
+                // fill output buffer and then return -1
+                wxStrncpy(buf, szScratch, lenMax);
+                return -1;
             }
             }
-#else
-            {
-                // 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
+            wxStrncpy(buf, szScratch, lenScratch);
+            lenCur += lenScratch;
             break;
 
         default:
             break;
 
         default:
@@ -908,12 +830,12 @@ int wxPrintfConvSpec::Process(wxChar *buf, size_t lenMax, wxPrintfArg *p, size_t
 // 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.
 // 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.
-//
+template<typename CharType>
 static int wxCopyStrWithPercents(
         size_t maxOut,
 static int wxCopyStrWithPercents(
         size_t maxOut,
-        wxChar *dest,
+        CharType *dest,
         size_t maxIn,
         size_t maxIn,
-        const wxChar *source)
+        const CharType *source)
 {
     size_t written = 0;
 
 {
     size_t written = 0;
 
@@ -939,19 +861,20 @@ static int wxCopyStrWithPercents(
     return written;
 }
 
     return written;
 }
 
-int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
-                             const wxChar *format, va_list argptr)
+template<typename CharType>
+static int wxDoVsnprintf(CharType *buf, size_t lenMax,
+                         const CharType *format, va_list argptr)
 {
     // useful for debugging, to understand if we are really using this function
     // rather than the system implementation
 #if 0
 {
     // useful for debugging, to understand if we are really using this function
     // rather than the system implementation
 #if 0
-    wprintf(L"Using wxVsnprintf_\n");
+    wprintf(L"Using wxCRT_VsnprintfW\n");
 #endif
 
     // required memory:
 #endif
 
     // required memory:
-    wxPrintfConvSpec arg[wxMAX_SVNPRINTF_ARGUMENTS];
+    wxPrintfConvSpec<CharType> arg[wxMAX_SVNPRINTF_ARGUMENTS];
     wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS];
     wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS];
-    wxPrintfConvSpec *pspec[wxMAX_SVNPRINTF_ARGUMENTS] = { NULL };
+    wxPrintfConvSpec<CharType> *pspec[wxMAX_SVNPRINTF_ARGUMENTS] = { NULL };
 
     size_t i;
 
 
     size_t i;
 
@@ -959,7 +882,7 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
     size_t lenCur = 0;
 
     size_t nargs = 0;
     size_t lenCur = 0;
 
     size_t nargs = 0;
-    const wxChar *toparse = format;
+    const CharType *toparse = format;
 
     // parse the format string
     bool posarg_present = false, nonposarg_present = false;
 
     // parse the format string
     bool posarg_present = false, nonposarg_present = false;
@@ -973,7 +896,7 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
             if (arg[nargs].Parse(toparse))
             {
                 // ...yes it is
             if (arg[nargs].Parse(toparse))
             {
                 // ...yes it is
-                wxPrintfConvSpec *current = &arg[nargs];
+                wxPrintfConvSpec<CharType> *current = &arg[nargs];
 
                 // make toparse point to the end of this specifier
                 toparse = current->m_pArgEnd;
 
                 // make toparse point to the end of this specifier
                 toparse = current->m_pArgEnd;
@@ -1096,13 +1019,56 @@ int WXDLLEXPORT wxVsnprintf_(wxChar *buf, size_t lenMax,
 }
 
 #undef APPEND_CH
 }
 
 #undef APPEND_CH
-#undef APPEND_STR
 #undef CHECK_PREC
 
 #undef CHECK_PREC
 
-#else    // wxVsnprintf_ is defined
+} // anonymous namespace
+
+#endif // !defined(wxCRT_VsnprintfW) || !defined(wxCRT_VsnprintfA)
+
+// ----------------------------------------------------------------------------
+// wxCRT_VsnprintfW
+// ----------------------------------------------------------------------------
+
+#if !defined(wxCRT_VsnprintfW)
+
+#if !wxUSE_WXVSNPRINTFW
+    #error "wxUSE_WXVSNPRINTFW must be 1 if our wxCRT_VsnprintfW is used"
+#endif
+
+int wxCRT_VsnprintfW(wchar_t *buf, size_t len,
+                     const wchar_t *format, va_list argptr)
+{
+    return wxDoVsnprintf(buf, len, format, argptr);
+}
+
+#else    // wxCRT_VsnprintfW is defined
+
+#if wxUSE_WXVSNPRINTFW
+    #error "wxUSE_WXVSNPRINTFW must be 0 if our wxCRT_VsnprintfW is not used"
+#endif
+
+#endif // !wxCRT_VsnprintfW
+
+// ----------------------------------------------------------------------------
+// wxCRT_VsnprintfA
+// ----------------------------------------------------------------------------
+
+#ifndef wxCRT_VsnprintfA
+
+#if !wxUSE_WXVSNPRINTFA
+    #error "wxUSE_WXVSNPRINTFA must be 1 if our wxCRT_VsnprintfA is used"
+#endif
+
+int wxCRT_VsnprintfA(char *buf, size_t len,
+                     const char *format, va_list argptr)
+{
+    return wxDoVsnprintf(buf, len, format, argptr);
+}
+
+#else    // wxCRT_VsnprintfA is defined
 
 
-#if wxUSE_WXVSNPRINTF
-    #error wxUSE_WXVSNPRINTF must be 0 if our wxVsnprintf_ is not used
+#if wxUSE_WXVSNPRINTFA
+    #error "wxUSE_WXVSNPRINTFA must be 0 if our wxCRT_VsnprintfA is not used"
 #endif
 
 #endif
 
-#endif // !wxVsnprintf_
+#endif // !wxCRT_VsnprintfA