]> git.saurik.com Git - wxWidgets.git/commitdiff
fixed variadic templates in the case when char value is passed in place of (e.g....
authorVáclav Slavík <vslavik@fastmail.fm>
Mon, 20 Aug 2007 19:20:10 +0000 (19:20 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Mon, 20 Aug 2007 19:20:10 +0000 (19:20 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@48224 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/private/wxprintf.h [new file with mode: 0644]
include/wx/strvararg.h
src/common/strvararg.cpp
src/common/wxprintf.cpp
tests/strings/vararg.cpp

diff --git a/include/wx/private/wxprintf.h b/include/wx/private/wxprintf.h
new file mode 100644 (file)
index 0000000..f797981
--- /dev/null
@@ -0,0 +1,856 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        wx/private/wxprintf.h
+// Purpose:     wxWidgets wxPrintf() implementation
+// Author:      Ove Kaven
+// Modified by: Ron Lee, Francesco Montorsi
+// Created:     09/04/99
+// RCS-ID:      $Id$
+// Copyright:   (c) wxWidgets copyright
+// Licence:     wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_PRIVATE_WXPRINTF_H_
+#define _WX_PRIVATE_WXPRINTF_H_
+
+// ---------------------------------------------------------------------------
+// headers and macros
+// ---------------------------------------------------------------------------
+
+#include "wx/crt.h"
+
+#include <string.h>
+
+#if defined(__MWERKS__) && __MSL__ >= 0x6000
+namespace std {}
+using namespace std ;
+#endif
+
+// 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
+
+// ---------------------------------------------------------------------------
+// printf format string parsing
+// ---------------------------------------------------------------------------
+
+// some limits of our implementation
+#define wxMAX_SVNPRINTF_ARGUMENTS         64
+#define wxMAX_SVNPRINTF_FLAGBUFFER_LEN    32
+#define wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN   512
+
+
+// the conversion specifiers accepted by wxCRT_VsnprintfW
+enum wxPrintfArgType {
+    wxPAT_INVALID = -1,
+
+    wxPAT_INT,          // %d, %i, %o, %u, %x, %X
+    wxPAT_LONGINT,      // %ld, etc
+#ifdef wxLongLong_t
+    wxPAT_LONGLONGINT,  // %Ld, etc
+#endif
+    wxPAT_SIZET,        // %Zd, etc
+
+    wxPAT_DOUBLE,       // %e, %E, %f, %g, %G
+    wxPAT_LONGDOUBLE,   // %le, etc
+
+    wxPAT_POINTER,      // %p
+
+    wxPAT_CHAR,         // %hc  (in ANSI mode: %c, too)
+    wxPAT_WCHAR,        // %lc  (in Unicode mode: %c, too)
+
+    wxPAT_PCHAR,        // %s   (related to a char *)
+    wxPAT_PWCHAR,       // %s   (related to a wchar_t *)
+
+    wxPAT_NINT,         // %n
+    wxPAT_NSHORTINT,    // %hn
+    wxPAT_NLONGINT      // %ln
+};
+
+// an argument passed to wxCRT_VsnprintfW
+typedef union {
+    int pad_int;                        //  %d, %i, %o, %u, %x, %X
+    long int pad_longint;               // %ld, etc
+#ifdef wxLongLong_t
+    wxLongLong_t pad_longlongint;      // %Ld, etc
+#endif
+    size_t pad_sizet;                   // %Zd, etc
+
+    double pad_double;                  // %e, %E, %f, %g, %G
+    long double pad_longdouble;         // %le, etc
+
+    void *pad_pointer;                  // %p
+
+    char pad_char;                      // %hc  (in ANSI mode: %c, too)
+    wchar_t pad_wchar;                  // %lc  (in Unicode mode: %c, too)
+
+    void *pad_str;                      // %s
+
+    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
+// 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...
+template<typename CharType>
+class wxPrintfConvSpec
+{
+public:
+
+    // the position of the argument relative to this conversion specifier
+    size_t m_pos;
+
+    // the type of this conversion specifier
+    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 m_nMinWidth, m_nMaxWidth;
+
+    // does the argument need to the be aligned to 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 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 !
+    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 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];
+
+
+public:
+
+    // we don't declare this as a constructor otherwise it would be called
+    // 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.
+    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.
+    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);
+
+private:
+    // An helper function of LoadArg() which is used to handle the '*' flag
+    void ReplaceAsteriskWith(int w);
+};
+
+template<typename CharType>
+void wxPrintfConvSpec<CharType>::Init()
+{
+    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] = '%';
+}
+
+template<typename CharType>
+bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
+{
+    bool done = false;
+
+    // temporary parse data
+    size_t flagofs = 1;
+    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;
+
+    m_bAlignLeft = in_prec = prec_dot = false;
+    m_pArgPos = m_pArgEnd = format;
+    do
+    {
+#define CHECK_PREC \
+        if (in_prec && !prec_dot) \
+        { \
+            m_szFlags[flagofs++] = '.'; \
+            prec_dot = true; \
+        }
+
+        // what follows '%'?
+        const CharType ch = *(++m_pArgEnd);
+        switch ( ch )
+        {
+            case wxT('\0'):
+                return false;       // not really an argument
+
+            case wxT('%'):
+                return false;       // not really an argument
+
+            case wxT('#'):
+            case wxT('0'):
+            case wxT(' '):
+            case wxT('+'):
+            case wxT('\''):
+                CHECK_PREC
+                m_szFlags[flagofs++] = char(ch);
+                break;
+
+            case wxT('-'):
+                CHECK_PREC
+                m_bAlignLeft = true;
+                m_szFlags[flagofs++] = char(ch);
+                break;
+
+            case wxT('.'):
+                CHECK_PREC
+                in_prec = true;
+                prec_dot = false;
+                m_nMaxWidth = 0;
+                // dot will be auto-added to m_szFlags if non-negative
+                // number follows
+                break;
+
+            case wxT('h'):
+                ilen = -1;
+                CHECK_PREC
+                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
+                m_szFlags[flagofs++] = char(ch);
+                break;
+
+            case wxT('q'):
+            case wxT('L'):
+                ilen = 2;
+                CHECK_PREC
+                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
+                m_szFlags[flagofs++] = char(ch);
+                break;
+
+            case wxT('*'):
+                if (in_prec)
+                {
+                    CHECK_PREC
+
+                    // tell Process() to use the next argument
+                    // in the stack as maxwidth...
+                    m_nMaxWidth = -1;
+                }
+                else
+                {
+                    // tell Process() to use the next argument
+                    // in the stack as minwidth...
+                    m_nMinWidth = -1;
+                }
+
+                // save the * in our formatting buffer...
+                // will be replaced later by Process()
+                m_szFlags[flagofs++] = char(ch);
+                break;
+
+            case wxT('1'): case wxT('2'): case wxT('3'):
+            case wxT('4'): case wxT('5'): case wxT('6'):
+            case wxT('7'): case wxT('8'): case wxT('9'):
+                {
+                    int len = 0;
+                    CHECK_PREC
+                    while ( (*m_pArgEnd >= CharType('0')) &&
+                            (*m_pArgEnd <= CharType('9')) )
+                    {
+                        m_szFlags[flagofs++] = char(*m_pArgEnd);
+                        len = len*10 + (*m_pArgEnd - wxT('0'));
+                        m_pArgEnd++;
+                    }
+
+                    if (in_prec)
+                        m_nMaxWidth = len;
+                    else
+                        m_nMinWidth = len;
+
+                    m_pArgEnd--; // the main loop pre-increments n again
+                }
+                break;
+
+            case wxT('$'):      // a positional parameter (e.g. %2$s) ?
+                {
+                    if (m_nMinWidth <= 0)
+                        break;      // ignore this formatting flag as no
+                                    // numbers are preceding it
+
+                    // remove from m_szFlags all digits previously added
+                    do {
+                        flagofs--;
+                    } while (m_szFlags[flagofs] >= '1' &&
+                             m_szFlags[flagofs] <= '9');
+
+                    // re-adjust the offset making it point to the
+                    // next free char of m_szFlags
+                    flagofs++;
+
+                    m_pos = m_nMinWidth;
+                    m_nMinWidth = 0;
+                }
+                break;
+
+            case wxT('d'):
+            case wxT('i'):
+            case wxT('o'):
+            case wxT('u'):
+            case wxT('x'):
+            case wxT('X'):
+                CHECK_PREC
+                m_szFlags[flagofs++] = char(ch);
+                m_szFlags[flagofs] = '\0';
+                if (ilen == 0)
+                    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
+                    m_type = wxPAT_INT;
+                else if (ilen == 1)
+                    m_type = wxPAT_LONGINT;
+                else if (ilen == 2)
+#ifdef wxLongLong_t
+                    m_type = wxPAT_LONGLONGINT;
+#else // !wxLongLong_t
+                    m_type = wxPAT_LONGINT;
+#endif // wxLongLong_t/!wxLongLong_t
+                else if (ilen == 3)
+                    m_type = wxPAT_SIZET;
+                done = true;
+                break;
+
+            case wxT('e'):
+            case wxT('E'):
+            case wxT('f'):
+            case wxT('g'):
+            case wxT('G'):
+                CHECK_PREC
+                m_szFlags[flagofs++] = char(ch);
+                m_szFlags[flagofs] = '\0';
+                if (ilen == 2)
+                    m_type = wxPAT_LONGDOUBLE;
+                else
+                    m_type = wxPAT_DOUBLE;
+                done = true;
+                break;
+
+            case wxT('p'):
+                m_type = wxPAT_POINTER;
+                m_szFlags[flagofs++] = char(ch);
+                m_szFlags[flagofs] = '\0';
+                done = true;
+                break;
+
+            case wxT('c'):
+                if (ilen == -1)
+                {
+                    // in Unicode mode %hc == ANSI character
+                    // and in ANSI mode, %hc == %c == ANSI...
+                    m_type = wxPAT_CHAR;
+                }
+                else if (ilen == 1)
+                {
+                    // in ANSI mode %lc == Unicode character
+                    // and in Unicode mode, %lc == %c == Unicode...
+                    m_type = wxPAT_WCHAR;
+                }
+                else
+                {
+#if wxUSE_UNICODE
+                    // in Unicode mode, %c == Unicode character
+                    m_type = wxPAT_WCHAR;
+#else
+                    // in ANSI mode, %c == ANSI character
+                    m_type = wxPAT_CHAR;
+#endif
+                }
+                done = true;
+                break;
+
+            case wxT('s'):
+                if (ilen == -1)
+                {
+                    // Unicode mode wx extension: we'll let %hs mean non-Unicode
+                    // strings (when in ANSI mode, %s == %hs == ANSI string)
+                    m_type = wxPAT_PCHAR;
+                }
+                else if (ilen == 1)
+                {
+                    // in Unicode mode, %ls == %s == Unicode string
+                    // in ANSI mode, %ls == Unicode string
+                    m_type = wxPAT_PWCHAR;
+                }
+                else
+                {
+#if wxUSE_UNICODE
+                    m_type = wxPAT_PWCHAR;
+#else
+                    m_type = wxPAT_PCHAR;
+#endif
+                }
+                done = true;
+                break;
+
+            case wxT('n'):
+                if (ilen == 0)
+                    m_type = wxPAT_NINT;
+                else if (ilen == -1)
+                    m_type = wxPAT_NSHORTINT;
+                else if (ilen >= 1)
+                    m_type = wxPAT_NLONGINT;
+                done = true;
+                break;
+
+            default:
+                // bad format, don't consider this an argument;
+                // 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);
+
+    return true;        // parsing was successful
+}
+
+template<typename CharType>
+void wxPrintfConvSpec<CharType>::ReplaceAsteriskWith(int width)
+{
+    char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
+
+    // find the first * in our flag buffer
+    char *pwidth = strchr(m_szFlags, '*');
+    wxCHECK_RET(pwidth, _T("field width must be specified"));
+
+    // save what follows the * (the +1 is to skip the asterisk itself!)
+    strcpy(temp, pwidth+1);
+    if (width < 0)
+    {
+        pwidth[0] = wxT('-');
+        pwidth++;
+    }
+
+    // replace * with the actual integer given as width
+#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);
+}
+
+template<typename CharType>
+bool wxPrintfConvSpec<CharType>::LoadArg(wxPrintfArg *p, va_list &argptr)
+{
+    // did the '*' width/precision specifier was used ?
+    if (m_nMaxWidth == -1)
+    {
+        // take the maxwidth specifier from the stack
+        m_nMaxWidth = va_arg(argptr, int);
+        if (m_nMaxWidth < 0)
+            m_nMaxWidth = 0;
+        else
+            ReplaceAsteriskWith(m_nMaxWidth);
+    }
+
+    if (m_nMinWidth == -1)
+    {
+        // take the minwidth specifier from the stack
+        m_nMinWidth = va_arg(argptr, int);
+
+        ReplaceAsteriskWith(m_nMinWidth);
+        if (m_nMinWidth < 0)
+        {
+            m_bAlignLeft = !m_bAlignLeft;
+            m_nMinWidth = -m_nMinWidth;
+        }
+    }
+
+    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;
+#ifdef wxLongLong_t
+        case wxPAT_LONGLONGINT:
+            p->pad_longlongint = va_arg(argptr, wxLongLong_t);
+            break;
+#endif // wxLongLong_t
+        case wxPAT_SIZET:
+            p->pad_sizet = va_arg(argptr, size_t);
+            break;
+        case wxPAT_DOUBLE:
+            p->pad_double = va_arg(argptr, double);
+            break;
+        case wxPAT_LONGDOUBLE:
+            p->pad_longdouble = va_arg(argptr, long double);
+            break;
+        case wxPAT_POINTER:
+            p->pad_pointer = va_arg(argptr, void *);
+            break;
+
+        case wxPAT_CHAR:
+            p->pad_char = (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 '...'
+            break;
+
+        case wxPAT_PCHAR:
+        case wxPAT_PWCHAR:
+            p->pad_str = va_arg(argptr, void *);
+            break;
+
+        case wxPAT_NINT:
+            p->pad_nint = va_arg(argptr, int *);
+            break;
+        case wxPAT_NSHORTINT:
+            p->pad_nshortint = va_arg(argptr, short int *);
+            break;
+        case wxPAT_NLONGINT:
+            p->pad_nlongint = va_arg(argptr, long int *);
+            break;
+
+        case wxPAT_INVALID:
+        default:
+            return false;
+    }
+
+    return true;    // loading was successful
+}
+
+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,
+    // %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) \
+                { \
+                    if ( lenCur == lenMax ) \
+                        return -1; \
+                    \
+                    buf[lenCur++] = ch; \
+                }
+
+    switch ( m_type )
+    {
+        case wxPAT_INT:
+            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_int);
+            break;
+
+        case wxPAT_LONGINT:
+            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longint);
+            break;
+
+#ifdef wxLongLong_t
+        case wxPAT_LONGLONGINT:
+            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longlongint);
+            break;
+#endif // SIZEOF_LONG_LONG
+
+        case wxPAT_SIZET:
+            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_sizet);
+            break;
+
+        case wxPAT_LONGDOUBLE:
+            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longdouble);
+            break;
+
+        case wxPAT_DOUBLE:
+            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_double);
+            break;
+
+        case wxPAT_POINTER:
+            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_pointer);
+            break;
+
+        case wxPAT_CHAR:
+        case wxPAT_WCHAR:
+            {
+                wxUniChar ch;
+                if (m_type == wxPAT_CHAR)
+                    ch = p->pad_char;
+                else // m_type == wxPAT_WCHAR
+                    ch = p->pad_wchar;
+
+                CharType val = ch;
+
+                size_t i;
+
+                if (!m_bAlignLeft)
+                    for (i = 1; i < (size_t)m_nMinWidth; i++)
+                        APPEND_CH(_T(' '));
+
+                APPEND_CH(val);
+
+                if (m_bAlignLeft)
+                    for (i = 1; i < (size_t)m_nMinWidth; i++)
+                        APPEND_CH(_T(' '));
+            }
+            break;
+
+        case wxPAT_PCHAR:
+        case wxPAT_PWCHAR:
+            {
+                wxArgNormalizedString arg(p->pad_str);
+                wxString s = arg;
+
+                if ( !arg.IsValid() && m_nMaxWidth >= 6 )
+                    s = wxT("(null)");
+
+                typename wxPrintfStringHelper<CharType>::ConvertedType strbuf(
+                        wxPrintfStringHelper<CharType>::Convert(s));
+
+                // 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;
+
+                if (!m_bAlignLeft)
+                {
+                    for (i = len; i < m_nMinWidth; i++)
+                        APPEND_CH(_T(' '));
+                }
+
+                len = wxMin((unsigned int)len, lenMax-lenCur);
+                wxStrncpy(buf+lenCur, strbuf, len);
+                lenCur += len;
+
+                if (m_bAlignLeft)
+                {
+                    for (i = len; i < m_nMinWidth; i++)
+                        APPEND_CH(_T(' '));
+                }
+            }
+            break;
+
+        case wxPAT_NINT:
+            *p->pad_nint = written;
+            break;
+
+        case wxPAT_NSHORTINT:
+            *p->pad_nshortint = (short int)written;
+            break;
+
+        case wxPAT_NLONGINT:
+            *p->pad_nlongint = written;
+            break;
+
+        case wxPAT_INVALID:
+        default:
+            return -1;
+    }
+
+    // if we used system's sprintf() then we now need to append the s_szScratch
+    // buffer to the given one...
+    switch (m_type)
+    {
+        case wxPAT_INT:
+        case wxPAT_LONGINT:
+#ifdef wxLongLong_t
+        case wxPAT_LONGLONGINT:
+#endif
+        case wxPAT_SIZET:
+        case wxPAT_LONGDOUBLE:
+        case wxPAT_DOUBLE:
+        case wxPAT_POINTER:
+            wxASSERT(lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN);
+            // 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)
+            {
+                // fill output buffer and then return -1
+                wxStrncpy(buf, szScratch, lenMax);
+                return -1;
+            }
+            wxStrncpy(buf, szScratch, lenScratch);
+            lenCur += lenScratch;
+            break;
+
+        default:
+            break;      // all other cases were completed previously
+    }
+
+    return lenCur;
+}
+
+
+// helper that parses format string
+template<typename CharType>
+struct wxPrintfConvSpecParser
+{
+    wxPrintfConvSpecParser(const CharType *format)
+        : posarg_present(false), nonposarg_present(false),
+          nargs(0)
+    {
+        memset(pspec, 0, sizeof(pspec));
+
+        const CharType *toparse = format;
+
+        // parse the format string
+        for (; *toparse != wxT('\0'); toparse++)
+        {
+            if (*toparse == wxT('%') )
+            {
+                arg[nargs].Init();
+
+                // let's see if this is a (valid) conversion specifier...
+                if (arg[nargs].Parse(toparse))
+                {
+                    // ...yes it is
+                    wxPrintfConvSpec<CharType> *current = &arg[nargs];
+
+                    // make toparse point to the end of this specifier
+                    toparse = current->m_pArgEnd;
+
+                    if (current->m_pos > 0)
+                    {
+                        // the positionals start from number 1... adjust the index
+                        current->m_pos--;
+                        posarg_present = true;
+                    }
+                    else
+                    {
+                        // not a positional argument...
+                        current->m_pos = nargs;
+                        nonposarg_present = true;
+                    }
+
+                    // this conversion specifier is tied to the pos-th argument...
+                    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('%'))
+                    {
+                        // the Parse() returned false because we've found a %%
+                        toparse++;
+                    }
+                }
+            }
+        }
+    }
+
+    wxPrintfConvSpec<CharType> arg[wxMAX_SVNPRINTF_ARGUMENTS];
+    wxPrintfConvSpec<CharType> *pspec[wxMAX_SVNPRINTF_ARGUMENTS];
+    bool posarg_present, nonposarg_present;
+    unsigned nargs;
+};
+
+#undef APPEND_CH
+#undef CHECK_PREC
+
+#endif // _WX_PRIVATE_WXPRINTF_H_
index a5445af31a4f4146b50baab8cba81ed473b2f6e8..ae06ff1e5a0262c9d2deb66c2fd5f3e792f2500b 100644 (file)
@@ -48,7 +48,9 @@ class WXDLLIMPEXP_FWD_BASE wxString;
 //     locale is not UTF-8
 //
 // Note that wxFormatString *must* be used for the format parameter of these
-// functions, otherwise the implementation won't work correctly.
+// functions, otherwise the implementation won't work correctly. Furthermore,
+// it must be passed by value, not reference, because it's modified by the
+// vararg templates internally.
 //
 // Parameters:
 // [ there are examples in square brackets showing values of the parameters
@@ -138,6 +140,20 @@ public:
     wxFormatString(const wxWCharBuffer& str)
         : m_wchar(str), m_str(NULL), m_cstr(NULL) {}
 
+
+    enum ArgumentType
+    {
+        Arg_Char,    // character as char
+
+        Arg_Other    // something else, for example int for %d
+    };
+
+    // returns the type of format specifier for n-th variadic argument (this is
+    // not necessarily n-th format specifier if positional specifiers are used);
+    // called by wxArgNormalizer<> specializations to get information about
+    // n-th variadic argument desired representation
+    ArgumentType GetArgumentType(unsigned n) const;
+
 #if !wxUSE_UNICODE_WCHAR
     operator const char*() const
         { return wx_const_cast(wxFormatString*, this)->AsChar(); }
@@ -182,6 +198,38 @@ private:
     DECLARE_NO_COPY_CLASS(wxFormatString)
 };
 
+// these two helper classes are used to find wxFormatString argument among fixed
+// arguments passed to a vararg template
+struct wxFormatStringArgument
+{
+    wxFormatStringArgument(const wxFormatString *s = NULL) : m_str(s) {}
+    const wxFormatString *m_str;
+
+    // overriding this operator allows us to reuse _WX_VARARG_JOIN macro
+    wxFormatStringArgument operator,(const wxFormatStringArgument& a) const
+    {
+        wxASSERT_MSG( m_str == NULL || a.m_str == NULL,
+                      "can't have two format strings in vararg function" );
+        return wxFormatStringArgument(m_str ? m_str : a.m_str);
+    }
+
+    operator const wxFormatString*() const { return m_str; }
+};
+
+template<typename T>
+inline wxFormatStringArgument wxFindFormatStringArgument(T WXUNUSED(arg))
+{
+    // by default, arguments are not format strings, so return "not found"
+    return wxFormatStringArgument();
+}
+
+inline wxFormatStringArgument
+wxFindFormatStringArgument(const wxFormatString& arg)
+{
+    return wxFormatStringArgument(&arg);
+};
+
+
 // ----------------------------------------------------------------------------
 // wxArgNormalizer*<T> converters
 // ----------------------------------------------------------------------------
@@ -192,7 +240,13 @@ private:
 template<typename T>
 struct wxArgNormalizer
 {
-    wxArgNormalizer(const T& value) : m_value(value) {}
+    // Ctor. 'value' is the value passed as variadic argument, 'fmt' is pointer
+    // to printf-like format string or NULL if the variadic function doesn't
+    // use format string and 'index' is index of 'value' in variadic arguments
+    // list (starting at 1)
+    wxArgNormalizer(T value,
+                    const wxFormatString *WXUNUSED(fmt), unsigned WXUNUSED(index))
+        : m_value(value) {}
 
     // Returns the value in a form that can be safely passed to real vararg
     // functions. In case of strings, this is char* in ANSI build and wchar_t*
@@ -209,7 +263,9 @@ struct wxArgNormalizer
 template<typename T>
 struct wxArgNormalizerWchar : public wxArgNormalizer<T>
 {
-    wxArgNormalizerWchar(const T& value) : wxArgNormalizer<T>(value) {}
+    wxArgNormalizerWchar(T value,
+                         const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizer<T>(value, fmt, index) {}
 };
 #endif // !wxUSE_UTF8_LOCALE_ONLY
 
@@ -219,7 +275,9 @@ struct wxArgNormalizerWchar : public wxArgNormalizer<T>
     template<typename T>
     struct wxArgNormalizerUtf8 : public wxArgNormalizer<T>
     {
-        wxArgNormalizerUtf8(const T& value) : wxArgNormalizer<T>(value) {}
+        wxArgNormalizerUtf8(T value,
+                            const wxFormatString *fmt, unsigned index)
+            : wxArgNormalizer<T>(value, fmt, index) {}
     };
 
     #define wxArgNormalizerNative wxArgNormalizerUtf8
@@ -241,7 +299,10 @@ struct wxArgNormalizerWithBuffer
     typedef wxCharTypeBuffer<CharType> CharBuffer;
 
     wxArgNormalizerWithBuffer() {}
-    wxArgNormalizerWithBuffer(const CharBuffer& buf) : m_value(buf) {}
+    wxArgNormalizerWithBuffer(const CharBuffer& buf,
+                              const wxFormatString *WXUNUSED(fmt),
+                              unsigned WXUNUSED(index))
+        : m_value(buf) {}
 
     const CharType *get() const { return m_value; }
 
@@ -252,7 +313,11 @@ struct wxArgNormalizerWithBuffer
 template<>
 struct WXDLLIMPEXP_BASE wxArgNormalizerNative<const wxString&>
 {
-    wxArgNormalizerNative(const wxString& s) : m_value(s) {}
+    wxArgNormalizerNative(const wxString& s,
+                          const wxFormatString *WXUNUSED(fmt),
+                          unsigned WXUNUSED(index))
+        : m_value(s) {}
+
     const wxStringCharType *get() const;
 
     const wxString& m_value;
@@ -262,7 +327,11 @@ struct WXDLLIMPEXP_BASE wxArgNormalizerNative<const wxString&>
 template<>
 struct WXDLLIMPEXP_BASE wxArgNormalizerNative<const wxCStrData&>
 {
-    wxArgNormalizerNative(const wxCStrData& value) : m_value(value) {}
+    wxArgNormalizerNative(const wxCStrData& value,
+                          const wxFormatString *WXUNUSED(fmt),
+                          unsigned WXUNUSED(index))
+        : m_value(value) {}
+
     const wxStringCharType *get() const;
 
     const wxCStrData& m_value;
@@ -274,14 +343,16 @@ template<>
 struct WXDLLIMPEXP_BASE wxArgNormalizerWchar<const wxString&>
     : public wxArgNormalizerWithBuffer<wchar_t>
 {
-    wxArgNormalizerWchar(const wxString& s);
+    wxArgNormalizerWchar(const wxString& s,
+                         const wxFormatString *fmt, unsigned index);
 };
 
 template<>
 struct WXDLLIMPEXP_BASE wxArgNormalizerWchar<const wxCStrData&>
     : public wxArgNormalizerWithBuffer<wchar_t>
 {
-    wxArgNormalizerWchar(const wxCStrData& s);
+    wxArgNormalizerWchar(const wxCStrData& s,
+                         const wxFormatString *fmt, unsigned index);
 };
 #endif // wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
 
@@ -294,8 +365,9 @@ template<>
 struct wxArgNormalizerWchar<const char*>
     : public wxArgNormalizerWithBuffer<wchar_t>
 {
-    wxArgNormalizerWchar(const char* s)
-        : wxArgNormalizerWithBuffer<wchar_t>(wxConvLibc.cMB2WC(s)) {}
+    wxArgNormalizerWchar(const char* s,
+                         const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerWithBuffer<wchar_t>(wxConvLibc.cMB2WC(s), fmt, index) {}
 };
 
 #elif wxUSE_UNICODE_UTF8
@@ -304,15 +376,18 @@ template<>
 struct wxArgNormalizerUtf8<const wchar_t*>
     : public wxArgNormalizerWithBuffer<char>
 {
-    wxArgNormalizerUtf8(const wchar_t* s)
-        : wxArgNormalizerWithBuffer<char>(wxConvUTF8.cWC2MB(s)) {}
+    wxArgNormalizerUtf8(const wchar_t* s,
+                        const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerWithBuffer<char>(wxConvUTF8.cWC2MB(s), fmt, index) {}
 };
 
 template<>
 struct wxArgNormalizerUtf8<const char*>
     : public wxArgNormalizerWithBuffer<char>
 {
-    wxArgNormalizerUtf8(const char* s)
+    wxArgNormalizerUtf8(const char* s,
+                        const wxFormatString *WXUNUSED(fmt),
+                        unsigned WXUNUSED(index))
     {
         if ( wxLocaleIsUtf8 )
         {
@@ -336,8 +411,9 @@ template<>
 struct wxArgNormalizerWchar<const char*>
     : public wxArgNormalizerWithBuffer<wchar_t>
 {
-    wxArgNormalizerWchar(const char* s)
-        : wxArgNormalizerWithBuffer<wchar_t>(wxConvLibc.cMB2WC(s)) {}
+    wxArgNormalizerWchar(const char* s,
+                         const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerWithBuffer<wchar_t>(wxConvLibc.cMB2WC(s), fmt, index) {}
 };
 #endif // !wxUSE_UTF8_LOCALE_ONLY
 
@@ -347,8 +423,9 @@ template<>
 struct wxArgNormalizerWchar<const wchar_t*>
     : public wxArgNormalizerWithBuffer<char>
 {
-    wxArgNormalizerWchar(const wchar_t* s)
-        : wxArgNormalizerWithBuffer<char>(wxConvLibc.cWC2MB(s)) {}
+    wxArgNormalizerWchar(const wchar_t* s,
+                         const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerWithBuffer<char>(wxConvLibc.cWC2MB(s), fmt, index) {}
 };
 
 #endif // wxUSE_UNICODE_WCHAR/wxUSE_UNICODE_UTF8/ANSI
@@ -376,7 +453,9 @@ struct wxArgNormalizerWchar<const wchar_t*>
     template<>                                                              \
     struct Normalizer<T> : public Normalizer<BaseT>                         \
     {                                                                       \
-        Normalizer(BaseT value) : Normalizer<BaseT>(value) {}               \
+        Normalizer(BaseT value,                                             \
+                   const wxFormatString *fmt, unsigned index)               \
+            : Normalizer<BaseT>(value, fmt, index) {}                       \
     }
 
 // non-reference versions of specializations for string objects
@@ -403,16 +482,18 @@ template<>
 struct wxArgNormalizerWchar<const std::string&>
     : public wxArgNormalizerWchar<const char*>
 {
-    wxArgNormalizerWchar(const std::string& s)
-        : wxArgNormalizerWchar<const char*>(s.c_str()) {}
+    wxArgNormalizerWchar(const std::string& s,
+                         const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerWchar<const char*>(s.c_str(), fmt, index) {}
 };
 
 template<>
 struct wxArgNormalizerWchar<const wxStdWideString&>
     : public wxArgNormalizerWchar<const wchar_t*>
 {
-    wxArgNormalizerWchar(const wxStdWideString& s)
-        : wxArgNormalizerWchar<const wchar_t*>(s.c_str()) {}
+    wxArgNormalizerWchar(const wxStdWideString& s,
+                         const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerWchar<const wchar_t*>(s.c_str(), fmt, index) {}
 };
 #endif // !wxUSE_UTF8_LOCALE_ONLY
 
@@ -421,16 +502,18 @@ template<>
 struct wxArgNormalizerUtf8<const std::string&>
     : public wxArgNormalizerUtf8<const char*>
 {
-    wxArgNormalizerUtf8(const std::string& s)
-        : wxArgNormalizerUtf8<const char*>(s.c_str()) {}
+    wxArgNormalizerUtf8(const std::string& s,
+                        const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerUtf8<const char*>(s.c_str(), fmt, index) {}
 };
 
 template<>
 struct wxArgNormalizerUtf8<const wxStdWideString&>
     : public wxArgNormalizerUtf8<const wchar_t*>
 {
-    wxArgNormalizerUtf8(const wxStdWideString& s)
-        : wxArgNormalizerUtf8<const wchar_t*>(s.c_str()) {}
+    wxArgNormalizerUtf8(const wxStdWideString& s,
+                        const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerUtf8<const wchar_t*>(s.c_str(), fmt, index) {}
 };
 #endif // wxUSE_UNICODE_UTF8
 
@@ -441,42 +524,69 @@ WX_ARG_NORMALIZER_FORWARD(wxStdWideString, const wxStdWideString&);
 
 
 // versions for wxUniChar, wxUniCharRef:
-
-#if !wxUSE_UTF8_LOCALE_ONLY
+// (this is same for UTF-8 and Wchar builds, we just convert to wchar_t)
 template<>
-struct wxArgNormalizerWchar<const wxUniChar&>
+struct wxArgNormalizer<const wxUniChar&> : public wxArgNormalizer<wchar_t>
+{
+    wxArgNormalizer(const wxUniChar& s,
+                    const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizer<wchar_t>(s.GetValue(), fmt, index) {}
+};
+
+// for wchar_t, default handler does the right thing
+
+// char has to be treated differently in Unicode builds: a char argument may
+// be used either for a character value (which should be converted into
+// wxUniChar) or as an integer value (which should be left as-is). We take
+// advantage of the fact that both char and wchar_t are converted into int
+// in variadic arguments here.
+#if wxUSE_UNICODE
+template<typename T>
+struct wxArgNormalizerNarrowChar
 {
-    wxArgNormalizerWchar(const wxUniChar& s) : m_value(s) {}
+    wxArgNormalizerNarrowChar(T value,
+                              const wxFormatString *fmt, unsigned index)
+    {
+        // FIXME-UTF8: which one is better default in absence of fmt string
+        //             (i.e. when used like e.g. Foo("foo", "bar", 'c', NULL)?
+        if ( !fmt || fmt->GetArgumentType(index) == wxFormatString::Arg_Char )
+            m_value = wxUniChar(value).GetValue();
+        else
+            m_value = value;
+    }
 
-    // FIXME-UTF8: use wchar_t once ANSI build is removed
-    wxChar get() const { return m_value; }
+    int get() const { return m_value; }
 
-    wxChar m_value;
+    T m_value;
 };
-#endif // !wxUSE_UTF8_LOCALE_ONLY
 
-#if wxUSE_UNICODE_UTF8
 template<>
-struct wxArgNormalizerUtf8<const wxUniChar&>
+struct wxArgNormalizer<char> : public wxArgNormalizerNarrowChar<char>
 {
-    wxArgNormalizerUtf8(const wxUniChar& s) : m_value(s.AsUTF8()) {}
-
-    const wxStringCharType *get() const { return m_value; }
+    wxArgNormalizer(char value,
+                    const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerNarrowChar<char>(value, fmt, index) {}
+};
 
-    wxUniChar::Utf8CharBuffer m_value;
+template<>
+struct wxArgNormalizer<unsigned char>
+    : public wxArgNormalizerNarrowChar<unsigned char>
+{
+    wxArgNormalizer(unsigned char value,
+                    const wxFormatString *fmt, unsigned index)
+        : wxArgNormalizerNarrowChar<unsigned char>(value, fmt, index) {}
 };
-#endif // wxUSE_UNICODE_UTF8
 
+#endif // wxUSE_UNICODE
+
+// convert references:
 WX_ARG_NORMALIZER_FORWARD(wxUniChar, const wxUniChar&);
 WX_ARG_NORMALIZER_FORWARD(const wxUniCharRef&, const wxUniChar&);
 WX_ARG_NORMALIZER_FORWARD(wxUniCharRef, const wxUniChar&);
-// convert char/wchar_t to wxUniChar to get output in the right encoding:
-WX_ARG_NORMALIZER_FORWARD(char, const wxUniChar&);
-WX_ARG_NORMALIZER_FORWARD(const char&, const wxUniChar&);
-WX_ARG_NORMALIZER_FORWARD(unsigned char, const wxUniChar&);
-WX_ARG_NORMALIZER_FORWARD(const unsigned char&, const wxUniChar&);
-WX_ARG_NORMALIZER_FORWARD(wchar_t, const wxUniChar&);
-WX_ARG_NORMALIZER_FORWARD(const wchar_t&, const wxUniChar&);
+WX_ARG_NORMALIZER_FORWARD(const wchar_t&, wchar_t);
+
+WX_ARG_NORMALIZER_FORWARD(const char&, char);
+WX_ARG_NORMALIZER_FORWARD(const unsigned char&, unsigned char);
 
 
 #undef WX_ARG_NORMALIZER_FORWARD
@@ -605,7 +715,6 @@ private:
 #define _WX_VARARG_FIXED_UNUSED_EXPAND_4(t1,t2,t3,t4) \
          t1 WXUNUSED(f1), t2 WXUNUSED(f2), t3 WXUNUSED(f3), t4 WXUNUSED(f4)
 
-
 // This macro expands N-items tuple of fixed arguments types into part of
 // function's declaration. For example,
 // "_WX_VARARG_FIXED_EXPAND(3, (int, char*, int))" expands into
@@ -621,7 +730,6 @@ private:
 #define _WX_VARARG_FIXED_UNUSED_EXPAND_IMPL(N, args) \
                 _WX_VARARG_FIXED_UNUSED_EXPAND_##N args
 
-
 // This macro calls another macro 'm' passed as second argument 'N' times,
 // with its only argument set to 1..N, and concatenates the results using
 // comma as separator.
@@ -667,13 +775,21 @@ private:
 
 // Generates code snippet for passing i-th argument of vararg function
 // wrapper to its implementation, normalizing it in the process:
-#define _WX_VARARG_PASS_WCHAR(i)        wxArgNormalizerWchar<T##i>(a##i).get()
-#define _WX_VARARG_PASS_UTF8(i)         wxArgNormalizerUtf8<T##i>(a##i).get()
+#define _WX_VARARG_PASS_WCHAR(i) \
+    wxArgNormalizerWchar<T##i>(a##i, fmt, i).get()
+#define _WX_VARARG_PASS_UTF8(i) \
+    wxArgNormalizerUtf8<T##i>(a##i, fmt, i).get()
 
 
 // And the same for fixed arguments, _not_ normalizing it:
 #define _WX_VARARG_PASS_FIXED(i)        f##i
 
+#define _WX_VARARG_FIND_FMT(i)          (wxFindFormatStringArgument(f##i))
+
+#define _WX_VARARG_FORMAT_STRING(numfixed, fixed)                             \
+    const wxFormatString *fmt =                                               \
+            (_WX_VARARG_JOIN(numfixed, _WX_VARARG_FIND_FMT))
+
 #if wxUSE_UNICODE_UTF8
     #define _WX_VARARG_DO_CALL_UTF8(return_kw, impl, implUtf8, N, numfixed)   \
         return_kw implUtf8(_WX_VARARG_JOIN(numfixed, _WX_VARARG_PASS_FIXED),  \
@@ -719,6 +835,7 @@ private:
     rettype name(_WX_VARARG_FIXED_EXPAND(numfixed, fixed),                    \
                  _WX_VARARG_JOIN(N, _WX_VARARG_ARG))                          \
     {                                                                         \
+        _WX_VARARG_FORMAT_STRING(numfixed, fixed);                            \
         _WX_VARARG_DO_CALL(return, impl, implUtf8, N, numfixed);              \
     }
 
@@ -739,6 +856,7 @@ private:
     void name(_WX_VARARG_FIXED_EXPAND(numfixed, fixed),                       \
                  _WX_VARARG_JOIN(N, _WX_VARARG_ARG))                          \
     {                                                                         \
+        _WX_VARARG_FORMAT_STRING(numfixed, fixed);                            \
         _WX_VARARG_DO_CALL(wxEMPTY_PARAMETER_VALUE,                           \
                            impl, implUtf8, N, numfixed);                      \
     }
@@ -760,6 +878,7 @@ private:
     name(_WX_VARARG_FIXED_EXPAND(numfixed, fixed),                            \
                 _WX_VARARG_JOIN(N, _WX_VARARG_ARG))                           \
     {                                                                         \
+        _WX_VARARG_FORMAT_STRING(numfixed, fixed);                            \
         _WX_VARARG_DO_CALL(wxEMPTY_PARAMETER_VALUE,                           \
                            impl, implUtf8, N, numfixed);                      \
     }
index 4559748fb4ff8d875c94c1878b064f441c1f6a82..dc86bf2bb6abe29da9ed945a2c98f663fb7c57b1 100644 (file)
@@ -26,6 +26,7 @@
 #include "wx/strvararg.h"
 #include "wx/string.h"
 #include "wx/crt.h"
+#include "wx/private/wxprintf.h"
 
 // ============================================================================
 // implementation
@@ -46,13 +47,17 @@ const wxStringCharType *wxArgNormalizerNative<const wxCStrData&>::get() const
 }
 
 #if wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
-wxArgNormalizerWchar<const wxString&>::wxArgNormalizerWchar(const wxString& s)
-    : wxArgNormalizerWithBuffer<wchar_t>(s.wc_str())
+wxArgNormalizerWchar<const wxString&>::wxArgNormalizerWchar(
+                            const wxString& s,
+                            const wxFormatString *fmt, unsigned index)
+    : wxArgNormalizerWithBuffer<wchar_t>(s.wc_str(), fmt, index)
 {
 }
 
-wxArgNormalizerWchar<const wxCStrData&>::wxArgNormalizerWchar(const wxCStrData& s)
-    : wxArgNormalizerWithBuffer<wchar_t>(s.AsWCharBuf())
+wxArgNormalizerWchar<const wxCStrData&>::wxArgNormalizerWchar(
+                            const wxCStrData& s,
+                            const wxFormatString *fmt, unsigned index)
+    : wxArgNormalizerWithBuffer<wchar_t>(s.AsWCharBuf(), fmt, index)
 {
 }
 #endif // wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
@@ -128,10 +133,6 @@ wxArgNormalizedString::operator wxString() const
 
    And, of course, the same should be done for %c as well.
 
-   4) Finally, in UTF-8 build when calling ANSI printf() function, we need to
-      translate %c to %s, because not every Unicode character can be
-      represented by a char.
-
 
    wxScanf() family of functions is simpler, because we don't normalize their
    variadic arguments and we only have to handle 2) above and only for widechar
@@ -436,9 +437,10 @@ class wxPrintfFormatConverterUtf8 : public wxFormatConverterBase<char>
                             SizeModifier WXUNUSED(size),
                             CharType& outConv, SizeModifier& outSize)
     {
-        // added complication: %c should be translated to %s in UTF-8 build
-        outConv = 's';
-        outSize = Size_Default;
+        // chars are represented using wchar_t in both builds, so this is
+        // the same as above
+        outConv = 'c';
+        outSize = Size_Long;
     }
 };
 #endif // wxUSE_UNICODE_UTF8
@@ -608,3 +610,50 @@ const wchar_t* wxFormatString::AsWChar()
     return m_convertedWChar.data();
 }
 #endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
+
+// ----------------------------------------------------------------------------
+// wxFormatString::GetArgumentType()
+// ----------------------------------------------------------------------------
+
+namespace
+{
+
+template<typename CharType>
+wxFormatString::ArgumentType DoGetArgumentType(const CharType *format,
+                                               unsigned n)
+{
+    wxCHECK_MSG( format, wxFormatString::Arg_Other,
+                 "empty format string not allowed here" );
+
+    wxPrintfConvSpecParser<CharType> parser(format);
+
+    wxCHECK_MSG( parser.pspec[n-1] != NULL, wxFormatString::Arg_Other,
+                 "requested argument not found - invalid format string?" );
+
+    switch ( parser.pspec[n-1]->m_type )
+    {
+        case wxPAT_CHAR:
+        case wxPAT_WCHAR:
+            return wxFormatString::Arg_Char;
+
+        default:
+            return wxFormatString::Arg_Other;
+    }
+}
+
+} // anonymous namespace
+
+wxFormatString::ArgumentType wxFormatString::GetArgumentType(unsigned n) const
+{
+    if ( m_char )
+        return DoGetArgumentType(m_char.data(), n);
+    else if ( m_wchar )
+        return DoGetArgumentType(m_wchar.data(), n);
+    else if ( m_str )
+        return DoGetArgumentType(m_str->wx_str(), n);
+    else if ( m_cstr )
+        return DoGetArgumentType(m_cstr->AsInternal(), n);
+
+    wxFAIL_MSG( "unreachable code" );
+    return Arg_Other;
+}
index f1b2d3914a817e07259cfd2739e4817574498a22..f0c4c221b99eb8b84c13b067265e2c20e8d220f7 100644 (file)
     #pragma hdrstop
 #endif
 
-#include "wx/crt.h"
-
-#include <string.h>
-
 #ifndef WX_PRECOMP
     #include "wx/string.h"
     #include "wx/hash.h"
     #include "wx/log.h"
 #endif
 
-#if defined(__MWERKS__) && __MSL__ >= 0x6000
-namespace std {}
-using namespace std ;
-#endif
+#include "wx/private/wxprintf.h"
 
 
 // ============================================================================
@@ -60,755 +53,6 @@ using namespace std ;
 
 #if !defined(wxCRT_VsnprintfW) || !defined(wxCRT_VsnprintfA)
 
-// 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
-
-namespace
-{
-
-// the conversion specifiers accepted by wxCRT_VsnprintfW
-enum wxPrintfArgType {
-    wxPAT_INVALID = -1,
-
-    wxPAT_INT,          // %d, %i, %o, %u, %x, %X
-    wxPAT_LONGINT,      // %ld, etc
-#ifdef wxLongLong_t
-    wxPAT_LONGLONGINT,  // %Ld, etc
-#endif
-    wxPAT_SIZET,        // %Zd, etc
-
-    wxPAT_DOUBLE,       // %e, %E, %f, %g, %G
-    wxPAT_LONGDOUBLE,   // %le, etc
-
-    wxPAT_POINTER,      // %p
-
-    wxPAT_CHAR,         // %hc  (in ANSI mode: %c, too)
-    wxPAT_WCHAR,        // %lc  (in Unicode mode: %c, too)
-
-    wxPAT_PCHAR,        // %s   (related to a char *)
-    wxPAT_PWCHAR,       // %s   (related to a wchar_t *)
-
-    wxPAT_NINT,         // %n
-    wxPAT_NSHORTINT,    // %hn
-    wxPAT_NLONGINT      // %ln
-};
-
-// an argument passed to wxCRT_VsnprintfW
-typedef union {
-    int pad_int;                        //  %d, %i, %o, %u, %x, %X
-    long int pad_longint;               // %ld, etc
-#ifdef wxLongLong_t
-    wxLongLong_t pad_longlongint;      // %Ld, etc
-#endif
-    size_t pad_sizet;                   // %Zd, etc
-
-    double pad_double;                  // %e, %E, %f, %g, %G
-    long double pad_longdouble;         // %le, etc
-
-    void *pad_pointer;                  // %p
-
-    char pad_char;                      // %hc  (in ANSI mode: %c, too)
-    wchar_t pad_wchar;                  // %lc  (in Unicode mode: %c, too)
-
-    void *pad_str;                      // %s
-
-    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
-// 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...
-template<typename CharType>
-class wxPrintfConvSpec
-{
-public:
-
-    // the position of the argument relative to this conversion specifier
-    size_t m_pos;
-
-    // the type of this conversion specifier
-    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 m_nMinWidth, m_nMaxWidth;
-
-    // does the argument need to the be aligned to 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 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 !
-    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
-    //     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];
-
-
-public:
-
-    // we don't declare this as a constructor otherwise it would be called
-    // 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.
-    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.
-    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);
-
-private:
-    // An helper function of LoadArg() which is used to handle the '*' flag
-    void ReplaceAsteriskWith(int w);
-};
-
-template<typename CharType>
-void wxPrintfConvSpec<CharType>::Init()
-{
-    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] = '%';
-}
-
-template<typename CharType>
-bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
-{
-    bool done = false;
-
-    // temporary parse data
-    size_t flagofs = 1;
-    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;
-
-    m_bAlignLeft = in_prec = prec_dot = false;
-    m_pArgPos = m_pArgEnd = format;
-    do
-    {
-#define CHECK_PREC \
-        if (in_prec && !prec_dot) \
-        { \
-            m_szFlags[flagofs++] = '.'; \
-            prec_dot = true; \
-        }
-
-        // what follows '%'?
-        const CharType ch = *(++m_pArgEnd);
-        switch ( ch )
-        {
-            case wxT('\0'):
-                return false;       // not really an argument
-
-            case wxT('%'):
-                return false;       // not really an argument
-
-            case wxT('#'):
-            case wxT('0'):
-            case wxT(' '):
-            case wxT('+'):
-            case wxT('\''):
-                CHECK_PREC
-                m_szFlags[flagofs++] = char(ch);
-                break;
-
-            case wxT('-'):
-                CHECK_PREC
-                m_bAlignLeft = true;
-                m_szFlags[flagofs++] = char(ch);
-                break;
-
-            case wxT('.'):
-                CHECK_PREC
-                in_prec = true;
-                prec_dot = false;
-                m_nMaxWidth = 0;
-                // dot will be auto-added to m_szFlags if non-negative
-                // number follows
-                break;
-
-            case wxT('h'):
-                ilen = -1;
-                CHECK_PREC
-                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
-                m_szFlags[flagofs++] = char(ch);
-                break;
-
-            case wxT('q'):
-            case wxT('L'):
-                ilen = 2;
-                CHECK_PREC
-                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
-                m_szFlags[flagofs++] = char(ch);
-                break;
-
-            case wxT('*'):
-                if (in_prec)
-                {
-                    CHECK_PREC
-
-                    // tell Process() to use the next argument
-                    // in the stack as maxwidth...
-                    m_nMaxWidth = -1;
-                }
-                else
-                {
-                    // tell Process() to use the next argument
-                    // in the stack as minwidth...
-                    m_nMinWidth = -1;
-                }
-
-                // save the * in our formatting buffer...
-                // will be replaced later by Process()
-                m_szFlags[flagofs++] = char(ch);
-                break;
-
-            case wxT('1'): case wxT('2'): case wxT('3'):
-            case wxT('4'): case wxT('5'): case wxT('6'):
-            case wxT('7'): case wxT('8'): case wxT('9'):
-                {
-                    int len = 0;
-                    CHECK_PREC
-                    while ( (*m_pArgEnd >= CharType('0')) &&
-                            (*m_pArgEnd <= CharType('9')) )
-                    {
-                        m_szFlags[flagofs++] = char(*m_pArgEnd);
-                        len = len*10 + (*m_pArgEnd - wxT('0'));
-                        m_pArgEnd++;
-                    }
-
-                    if (in_prec)
-                        m_nMaxWidth = len;
-                    else
-                        m_nMinWidth = len;
-
-                    m_pArgEnd--; // the main loop pre-increments n again
-                }
-                break;
-
-            case wxT('$'):      // a positional parameter (e.g. %2$s) ?
-                {
-                    if (m_nMinWidth <= 0)
-                        break;      // ignore this formatting flag as no
-                                    // numbers are preceding it
-
-                    // remove from m_szFlags all digits previously added
-                    do {
-                        flagofs--;
-                    } while (m_szFlags[flagofs] >= '1' &&
-                             m_szFlags[flagofs] <= '9');
-
-                    // re-adjust the offset making it point to the
-                    // next free char of m_szFlags
-                    flagofs++;
-
-                    m_pos = m_nMinWidth;
-                    m_nMinWidth = 0;
-                }
-                break;
-
-            case wxT('d'):
-            case wxT('i'):
-            case wxT('o'):
-            case wxT('u'):
-            case wxT('x'):
-            case wxT('X'):
-                CHECK_PREC
-                m_szFlags[flagofs++] = char(ch);
-                m_szFlags[flagofs] = '\0';
-                if (ilen == 0)
-                    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
-                    m_type = wxPAT_INT;
-                else if (ilen == 1)
-                    m_type = wxPAT_LONGINT;
-                else if (ilen == 2)
-#ifdef wxLongLong_t
-                    m_type = wxPAT_LONGLONGINT;
-#else // !wxLongLong_t
-                    m_type = wxPAT_LONGINT;
-#endif // wxLongLong_t/!wxLongLong_t
-                else if (ilen == 3)
-                    m_type = wxPAT_SIZET;
-                done = true;
-                break;
-
-            case wxT('e'):
-            case wxT('E'):
-            case wxT('f'):
-            case wxT('g'):
-            case wxT('G'):
-                CHECK_PREC
-                m_szFlags[flagofs++] = char(ch);
-                m_szFlags[flagofs] = '\0';
-                if (ilen == 2)
-                    m_type = wxPAT_LONGDOUBLE;
-                else
-                    m_type = wxPAT_DOUBLE;
-                done = true;
-                break;
-
-            case wxT('p'):
-                m_type = wxPAT_POINTER;
-                m_szFlags[flagofs++] = char(ch);
-                m_szFlags[flagofs] = '\0';
-                done = true;
-                break;
-
-            case wxT('c'):
-                if (ilen == -1)
-                {
-                    // in Unicode mode %hc == ANSI character
-                    // and in ANSI mode, %hc == %c == ANSI...
-                    m_type = wxPAT_CHAR;
-                }
-                else if (ilen == 1)
-                {
-                    // in ANSI mode %lc == Unicode character
-                    // and in Unicode mode, %lc == %c == Unicode...
-                    m_type = wxPAT_WCHAR;
-                }
-                else
-                {
-#if wxUSE_UNICODE
-                    // in Unicode mode, %c == Unicode character
-                    m_type = wxPAT_WCHAR;
-#else
-                    // in ANSI mode, %c == ANSI character
-                    m_type = wxPAT_CHAR;
-#endif
-                }
-                done = true;
-                break;
-
-            case wxT('s'):
-                if (ilen == -1)
-                {
-                    // Unicode mode wx extension: we'll let %hs mean non-Unicode
-                    // strings (when in ANSI mode, %s == %hs == ANSI string)
-                    m_type = wxPAT_PCHAR;
-                }
-                else if (ilen == 1)
-                {
-                    // in Unicode mode, %ls == %s == Unicode string
-                    // in ANSI mode, %ls == Unicode string
-                    m_type = wxPAT_PWCHAR;
-                }
-                else
-                {
-#if wxUSE_UNICODE
-                    m_type = wxPAT_PWCHAR;
-#else
-                    m_type = wxPAT_PCHAR;
-#endif
-                }
-                done = true;
-                break;
-
-            case wxT('n'):
-                if (ilen == 0)
-                    m_type = wxPAT_NINT;
-                else if (ilen == -1)
-                    m_type = wxPAT_NSHORTINT;
-                else if (ilen >= 1)
-                    m_type = wxPAT_NLONGINT;
-                done = true;
-                break;
-
-            default:
-                // bad format, don't consider this an argument;
-                // 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);
-
-    return true;        // parsing was successful
-}
-
-template<typename CharType>
-void wxPrintfConvSpec<CharType>::ReplaceAsteriskWith(int width)
-{
-    char temp[wxMAX_SVNPRINTF_FLAGBUFFER_LEN];
-
-    // find the first * in our flag buffer
-    char *pwidth = strchr(m_szFlags, '*');
-    wxCHECK_RET(pwidth, _T("field width must be specified"));
-
-    // save what follows the * (the +1 is to skip the asterisk itself!)
-    strcpy(temp, pwidth+1);
-    if (width < 0)
-    {
-        pwidth[0] = wxT('-');
-        pwidth++;
-    }
-
-    // replace * with the actual integer given as width
-#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);
-}
-
-template<typename CharType>
-bool wxPrintfConvSpec<CharType>::LoadArg(wxPrintfArg *p, va_list &argptr)
-{
-    // did the '*' width/precision specifier was used ?
-    if (m_nMaxWidth == -1)
-    {
-        // take the maxwidth specifier from the stack
-        m_nMaxWidth = va_arg(argptr, int);
-        if (m_nMaxWidth < 0)
-            m_nMaxWidth = 0;
-        else
-            ReplaceAsteriskWith(m_nMaxWidth);
-    }
-
-    if (m_nMinWidth == -1)
-    {
-        // take the minwidth specifier from the stack
-        m_nMinWidth = va_arg(argptr, int);
-
-        ReplaceAsteriskWith(m_nMinWidth);
-        if (m_nMinWidth < 0)
-        {
-            m_bAlignLeft = !m_bAlignLeft;
-            m_nMinWidth = -m_nMinWidth;
-        }
-    }
-
-    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;
-#ifdef wxLongLong_t
-        case wxPAT_LONGLONGINT:
-            p->pad_longlongint = va_arg(argptr, wxLongLong_t);
-            break;
-#endif // wxLongLong_t
-        case wxPAT_SIZET:
-            p->pad_sizet = va_arg(argptr, size_t);
-            break;
-        case wxPAT_DOUBLE:
-            p->pad_double = va_arg(argptr, double);
-            break;
-        case wxPAT_LONGDOUBLE:
-            p->pad_longdouble = va_arg(argptr, long double);
-            break;
-        case wxPAT_POINTER:
-            p->pad_pointer = va_arg(argptr, void *);
-            break;
-
-        case wxPAT_CHAR:
-            p->pad_char = (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 '...'
-            break;
-
-        case wxPAT_PCHAR:
-        case wxPAT_PWCHAR:
-            p->pad_str = va_arg(argptr, void *);
-            break;
-
-        case wxPAT_NINT:
-            p->pad_nint = va_arg(argptr, int *);
-            break;
-        case wxPAT_NSHORTINT:
-            p->pad_nshortint = va_arg(argptr, short int *);
-            break;
-        case wxPAT_NLONGINT:
-            p->pad_nlongint = va_arg(argptr, long int *);
-            break;
-
-        case wxPAT_INVALID:
-        default:
-            return false;
-    }
-
-    return true;    // loading was successful
-}
-
-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,
-    // %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) \
-                { \
-                    if ( lenCur == lenMax ) \
-                        return -1; \
-                    \
-                    buf[lenCur++] = ch; \
-                }
-
-    switch ( m_type )
-    {
-        case wxPAT_INT:
-            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_int);
-            break;
-
-        case wxPAT_LONGINT:
-            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longint);
-            break;
-
-#ifdef wxLongLong_t
-        case wxPAT_LONGLONGINT:
-            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longlongint);
-            break;
-#endif // SIZEOF_LONG_LONG
-
-        case wxPAT_SIZET:
-            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_sizet);
-            break;
-
-        case wxPAT_LONGDOUBLE:
-            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_longdouble);
-            break;
-
-        case wxPAT_DOUBLE:
-            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_double);
-            break;
-
-        case wxPAT_POINTER:
-            lenScratch = system_sprintf(szScratch, wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN, m_szFlags, p->pad_pointer);
-            break;
-
-        case wxPAT_CHAR:
-        case wxPAT_WCHAR:
-            {
-                wxUniChar ch;
-                if (m_type == wxPAT_CHAR)
-                    ch = p->pad_char;
-                else // m_type == wxPAT_WCHAR
-                    ch = p->pad_wchar;
-
-                CharType val = ch;
-
-                size_t i;
-
-                if (!m_bAlignLeft)
-                    for (i = 1; i < (size_t)m_nMinWidth; i++)
-                        APPEND_CH(_T(' '));
-
-                APPEND_CH(val);
-
-                if (m_bAlignLeft)
-                    for (i = 1; i < (size_t)m_nMinWidth; i++)
-                        APPEND_CH(_T(' '));
-            }
-            break;
-
-        case wxPAT_PCHAR:
-        case wxPAT_PWCHAR:
-            {
-                wxArgNormalizedString arg(p->pad_str);
-                wxString s = arg;
-
-                if ( !arg.IsValid() && m_nMaxWidth >= 6 )
-                    s = wxT("(null)");
-
-                typename wxPrintfStringHelper<CharType>::ConvertedType strbuf(
-                        wxPrintfStringHelper<CharType>::Convert(s));
-
-                // 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;
-
-                if (!m_bAlignLeft)
-                {
-                    for (i = len; i < m_nMinWidth; i++)
-                        APPEND_CH(_T(' '));
-                }
-
-                len = wxMin((unsigned int)len, lenMax-lenCur);
-                wxStrncpy(buf+lenCur, strbuf, len);
-                lenCur += len;
-
-                if (m_bAlignLeft)
-                {
-                    for (i = len; i < m_nMinWidth; i++)
-                        APPEND_CH(_T(' '));
-                }
-            }
-            break;
-
-        case wxPAT_NINT:
-            *p->pad_nint = written;
-            break;
-
-        case wxPAT_NSHORTINT:
-            *p->pad_nshortint = (short int)written;
-            break;
-
-        case wxPAT_NLONGINT:
-            *p->pad_nlongint = written;
-            break;
-
-        case wxPAT_INVALID:
-        default:
-            return -1;
-    }
-
-    // if we used system's sprintf() then we now need to append the s_szScratch
-    // buffer to the given one...
-    switch (m_type)
-    {
-        case wxPAT_INT:
-        case wxPAT_LONGINT:
-#ifdef wxLongLong_t
-        case wxPAT_LONGLONGINT:
-#endif
-        case wxPAT_SIZET:
-        case wxPAT_LONGDOUBLE:
-        case wxPAT_DOUBLE:
-        case wxPAT_POINTER:
-            wxASSERT(lenScratch < wxMAX_SVNPRINTF_SCRATCHBUFFER_LEN);
-            // 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)
-            {
-                // fill output buffer and then return -1
-                wxStrncpy(buf, szScratch, lenMax);
-                return -1;
-            }
-            wxStrncpy(buf, szScratch, lenScratch);
-            lenCur += lenScratch;
-            break;
-
-        default:
-            break;      // all other cases were completed previously
-    }
-
-    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
@@ -854,71 +98,16 @@ static int wxDoVsnprintf(CharType *buf, size_t lenMax,
     wprintf(L"Using wxCRT_VsnprintfW\n");
 #endif
 
-    // required memory:
-    wxPrintfConvSpec<CharType> arg[wxMAX_SVNPRINTF_ARGUMENTS];
+    wxPrintfConvSpecParser<CharType> parser(format);
+
     wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS];
-    wxPrintfConvSpec<CharType> *pspec[wxMAX_SVNPRINTF_ARGUMENTS] = { NULL };
 
     size_t i;
 
     // number of characters in the buffer so far, must be less than lenMax
     size_t lenCur = 0;
 
-    size_t nargs = 0;
-    const CharType *toparse = format;
-
-    // parse the format string
-    bool posarg_present = false, nonposarg_present = false;
-    for (; *toparse != wxT('\0'); toparse++)
-    {
-        if (*toparse == wxT('%') )
-        {
-            arg[nargs].Init();
-
-            // let's see if this is a (valid) conversion specifier...
-            if (arg[nargs].Parse(toparse))
-            {
-                // ...yes it is
-                wxPrintfConvSpec<CharType> *current = &arg[nargs];
-
-                // make toparse point to the end of this specifier
-                toparse = current->m_pArgEnd;
-
-                if (current->m_pos > 0)
-                {
-                    // the positionals start from number 1... adjust the index
-                    current->m_pos--;
-                    posarg_present = true;
-                }
-                else
-                {
-                    // not a positional argument...
-                    current->m_pos = nargs;
-                    nonposarg_present = true;
-                }
-
-                // this conversion specifier is tied to the pos-th argument...
-                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)
+    if (parser.posarg_present && parser.nonposarg_present)
     {
         buf[0] = 0;
         return -1;      // format strings with both positional and
@@ -931,12 +120,12 @@ static int wxDoVsnprintf(CharType *buf, size_t lenMax,
     wxVaCopy(ap, argptr);
 
     // now load arguments from stack
-    for (i=0; i < nargs && ok; i++)
+    for (i=0; i < parser.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);
+        ok = parser.pspec[i] && parser.pspec[i]->LoadArg(&argdata[i], ap);
     }
 
     va_end(ap);
@@ -951,7 +140,7 @@ static int wxDoVsnprintf(CharType *buf, size_t lenMax,
 
     // finally, process each conversion specifier with its own argument
     toparse = format;
-    for (i=0; i < nargs; i++)
+    for (i=0; i < parser.nargs; i++)
     {
         // copy in the output buffer the portion of the format string between
         // last specifier and the current one
@@ -1001,9 +190,6 @@ static int wxDoVsnprintf(CharType *buf, size_t lenMax,
     return lenCur;
 }
 
-#undef APPEND_CH
-#undef CHECK_PREC
-
 } // anonymous namespace
 
 #endif // !defined(wxCRT_VsnprintfW) || !defined(wxCRT_VsnprintfA)
index de259e3534696169944f2250649287af1484a82a..556dd74ca2770f9cf66f97afa6a870288facf9de 100644 (file)
@@ -36,6 +36,7 @@ public:
 private:
     CPPUNIT_TEST_SUITE( VarArgTestCase );
         CPPUNIT_TEST( StringPrintf );
+        CPPUNIT_TEST( CharPrintf );
 #if wxUSE_STD_STRING
         CPPUNIT_TEST( StdString );
 #endif
@@ -43,6 +44,7 @@ private:
     CPPUNIT_TEST_SUITE_END();
 
     void StringPrintf();
+    void CharPrintf();
 #if wxUSE_STD_STRING
     void StdString();
 #endif
@@ -91,7 +93,34 @@ void VarArgTestCase::StringPrintf()
     // literal:
     bool cond = true;
     s2.Printf(_T("foo %s"), !cond ? s.c_str() : _T("bar"));
+}
+
+void VarArgTestCase::CharPrintf()
+{
+    wxString foo("foo");
+    wxString s;
+
+    // test using wchar_t:
+    s.Printf("char=%c", L'c');
+    WX_ASSERT_STR_EQUAL( "char=c", s );
+
+    // test wxUniCharRef:
+    s.Printf("string[1] is %c", foo[1]);
+    WX_ASSERT_STR_EQUAL( "string[1] is o", s );
+
+    // test char
+    char c = 'z';
+    s.Printf("%c to %c", 'a', c);
+    WX_ASSERT_STR_EQUAL( "a to z", s );
+
+    // test char used as integer:
+    c = 240;
+    s.Printf("value is %i (int)", c);
+    WX_ASSERT_STR_EQUAL( wxString("value is -16 (int)"), s );
 
+    unsigned char u = 240;
+    s.Printf("value is %i (int)", u);
+    WX_ASSERT_STR_EQUAL( wxString("value is 240 (int)"), s );
 }
 
 #if wxUSE_STD_STRING