]> git.saurik.com Git - wxWidgets.git/blobdiff - include/wx/private/wxprintf.h
Add RunScript and implement on all backends. Document and add a very simple unit...
[wxWidgets.git] / include / wx / private / wxprintf.h
index f79798181226dec23a77e0537f9c720440f6a3db..ff74ee11ca4eb9f933e2adf3557116f98d1342be 100644 (file)
@@ -17,6 +17,8 @@
 // ---------------------------------------------------------------------------
 
 #include "wx/crt.h"
+#include "wx/log.h"
+#include "wx/utils.h"
 
 #include <string.h>
 
@@ -54,7 +56,8 @@ using namespace std ;
 
 
 // the conversion specifiers accepted by wxCRT_VsnprintfW
-enum wxPrintfArgType {
+enum wxPrintfArgType
+{
     wxPAT_INVALID = -1,
 
     wxPAT_INT,          // %d, %i, %o, %u, %x, %X
@@ -62,7 +65,7 @@ enum wxPrintfArgType {
 #ifdef wxLongLong_t
     wxPAT_LONGLONGINT,  // %Ld, etc
 #endif
-    wxPAT_SIZET,        // %Zd, etc
+    wxPAT_SIZET,        // %zd, etc
 
     wxPAT_DOUBLE,       // %e, %E, %f, %g, %G
     wxPAT_LONGDOUBLE,   // %le, etc
@@ -77,17 +80,20 @@ enum wxPrintfArgType {
 
     wxPAT_NINT,         // %n
     wxPAT_NSHORTINT,    // %hn
-    wxPAT_NLONGINT      // %ln
+    wxPAT_NLONGINT,     // %ln
+
+    wxPAT_STAR          // '*' used for width or precision
 };
 
 // an argument passed to wxCRT_VsnprintfW
-typedef union {
+union wxPrintfArg
+{
     int pad_int;                        //  %d, %i, %o, %u, %x, %X
     long int pad_longint;               // %ld, etc
 #ifdef wxLongLong_t
-    wxLongLong_t pad_longlongint;      // %Ld, etc
+    wxLongLong_t pad_longlongint;       // %Ld, etc
 #endif
-    size_t pad_sizet;                   // %Zd, etc
+    size_t pad_sizet;                   // %zd, etc
 
     double pad_double;                  // %e, %E, %f, %g, %G
     long double pad_longdouble;         // %le, etc
@@ -102,9 +108,9 @@ typedef union {
     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
+// helper for converting string into either char* or wchar_t* depending
 // on the type of wxPrintfConvSpec<T> instantiation:
 template<typename CharType> struct wxPrintfStringHelper {};
 
@@ -155,12 +161,8 @@ public:
     //       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()
+    // 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];
 
 
@@ -199,6 +201,7 @@ void wxPrintfConvSpec<CharType>::Init()
     m_pArgPos = m_pArgEnd = NULL;
     m_type = wxPAT_INVALID;
 
+    memset(m_szFlags, 0, sizeof(m_szFlags));
     // this character will never be removed from m_szFlags array and
     // is important when calling sprintf() in wxPrintfConvSpec::Process() !
     m_szFlags[0] = '%';
@@ -252,7 +255,11 @@ bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
                 break;
 
             case wxT('.'):
-                CHECK_PREC
+                // don't use CHECK_PREC here to avoid warning about the value
+                // assigned to prec_dot inside it being never used (because
+                // overwritten just below) from Borland in release build
+                if (in_prec && !prec_dot)
+                    m_szFlags[flagofs++] = '.';
                 in_prec = true;
                 prec_dot = false;
                 m_nMaxWidth = 0;
@@ -287,22 +294,26 @@ bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
             // 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++;
+                if (*(m_pArgEnd+1) == wxT('6') &&
+                    *(m_pArgEnd+2) == wxT('4'))
+                {
+                    m_pArgEnd++;
+                    m_pArgEnd++;
 
-                ilen = 2;
-                CHECK_PREC
-                m_szFlags[flagofs++] = char(ch);
-                m_szFlags[flagofs++] = '6';
-                m_szFlags[flagofs++] = '4';
-                break;
+                    ilen = 2;
+                    CHECK_PREC
+                    m_szFlags[flagofs++] = char(ch);
+                    m_szFlags[flagofs++] = '6';
+                    m_szFlags[flagofs++] = '4';
+                    break;
+                }
+                // else: fall-through, 'I' is MSVC equivalent of C99 'z'
 #endif      // __WXMSW__
 
+            case wxT('z'):
             case wxT('Z'):
+                // 'z' is C99 standard for size_t and ptrdiff_t, 'Z' was used
+                // for this purpose in libc5 and by wx <= 2.8
                 ilen = 3;
                 CHECK_PREC
                 m_szFlags[flagofs++] = char(ch);
@@ -381,7 +392,6 @@ bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
             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)
@@ -409,7 +419,6 @@ bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
             case wxT('G'):
                 CHECK_PREC
                 m_szFlags[flagofs++] = char(ch);
-                m_szFlags[flagofs] = '\0';
                 if (ilen == 2)
                     m_type = wxPAT_LONGDOUBLE;
                 else
@@ -420,7 +429,6 @@ bool wxPrintfConvSpec<CharType>::Parse(const CharType *format)
             case wxT('p'):
                 m_type = wxPAT_POINTER;
                 m_szFlags[flagofs++] = char(ch);
-                m_szFlags[flagofs] = '\0';
                 done = true;
                 break;
 
@@ -508,7 +516,7 @@ void wxPrintfConvSpec<CharType>::ReplaceAsteriskWith(int width)
 
     // find the first * in our flag buffer
     char *pwidth = strchr(m_szFlags, '*');
-    wxCHECK_RET(pwidth, _T("field width must be specified"));
+    wxCHECK_RET(pwidth, wxT("field width must be specified"));
 
     // save what follows the * (the +1 is to skip the asterisk itself!)
     strcpy(temp, pwidth+1);
@@ -603,6 +611,10 @@ bool wxPrintfConvSpec<CharType>::LoadArg(wxPrintfArg *p, va_list &argptr)
             p->pad_nlongint = va_arg(argptr, long int *);
             break;
 
+        case wxPAT_STAR:
+            // this will be handled as part of the next argument
+            return true;
+
         case wxPAT_INVALID:
         default:
             return false;
@@ -675,24 +687,29 @@ int wxPrintfConvSpec<CharType>::Process(CharType *buf, size_t lenMax, wxPrintfAr
 
                 if (!m_bAlignLeft)
                     for (i = 1; i < (size_t)m_nMinWidth; i++)
-                        APPEND_CH(_T(' '));
+                        APPEND_CH(wxT(' '));
 
                 APPEND_CH(val);
 
                 if (m_bAlignLeft)
                     for (i = 1; i < (size_t)m_nMinWidth; i++)
-                        APPEND_CH(_T(' '));
+                        APPEND_CH(wxT(' '));
             }
             break;
 
         case wxPAT_PCHAR:
         case wxPAT_PWCHAR:
             {
-                wxArgNormalizedString arg(p->pad_str);
-                wxString s = arg;
-
-                if ( !arg.IsValid() && m_nMaxWidth >= 6 )
-                    s = wxT("(null)");
+                wxString s;
+                if ( !p->pad_str )
+                {
+                    if ( m_nMaxWidth >= 6 )
+                        s = wxT("(null)");
+                }
+                else if (m_type == wxPAT_PCHAR)
+                    s.assign(static_cast<const char *>(p->pad_str));
+                else // m_type == wxPAT_PWCHAR
+                    s.assign(static_cast<const wchar_t *>(p->pad_str));
 
                 typename wxPrintfStringHelper<CharType>::ConvertedType strbuf(
                         wxPrintfStringHelper<CharType>::Convert(s));
@@ -706,7 +723,7 @@ int wxPrintfConvSpec<CharType>::Process(CharType *buf, size_t lenMax, wxPrintfAr
                 if (!m_bAlignLeft)
                 {
                     for (i = len; i < m_nMinWidth; i++)
-                        APPEND_CH(_T(' '));
+                        APPEND_CH(wxT(' '));
                 }
 
                 len = wxMin((unsigned int)len, lenMax-lenCur);
@@ -716,7 +733,7 @@ int wxPrintfConvSpec<CharType>::Process(CharType *buf, size_t lenMax, wxPrintfAr
                 if (m_bAlignLeft)
                 {
                     for (i = len; i < m_nMinWidth; i++)
-                        APPEND_CH(_T(' '));
+                        APPEND_CH(wxT(' '));
                 }
             }
             break;
@@ -782,72 +799,139 @@ int wxPrintfConvSpec<CharType>::Process(CharType *buf, size_t lenMax, wxPrintfAr
 template<typename CharType>
 struct wxPrintfConvSpecParser
 {
-    wxPrintfConvSpecParser(const CharType *format)
-        : posarg_present(false), nonposarg_present(false),
-          nargs(0)
+    typedef wxPrintfConvSpec<CharType> ConvSpec;
+
+    wxPrintfConvSpecParser(const CharType *fmt)
     {
-        memset(pspec, 0, sizeof(pspec));
+        nargs = 0;
+        posarg_present =
+        nonposarg_present = false;
 
-        const CharType *toparse = format;
+        memset(pspec, 0, sizeof(pspec));
 
         // parse the format string
-        for (; *toparse != wxT('\0'); toparse++)
+        for ( const CharType *toparse = fmt; *toparse != wxT('\0'); toparse++ )
         {
-            if (*toparse == wxT('%') )
+            // skip everything except format specifications
+            if ( *toparse != '%' )
+                continue;
+
+            // also skip escaped percent signs
+            if ( toparse[1] == '%' )
             {
-                arg[nargs].Init();
+                toparse++;
+                continue;
+            }
 
-                // let's see if this is a (valid) conversion specifier...
-                if (arg[nargs].Parse(toparse))
-                {
-                    // ...yes it is
-                    wxPrintfConvSpec<CharType> *current = &arg[nargs];
+            ConvSpec *spec = &specs[nargs];
+            spec->Init();
 
-                    // make toparse point to the end of this specifier
-                    toparse = current->m_pArgEnd;
+            // attempt to parse this format specification
+            if ( !spec->Parse(toparse) )
+                continue;
 
-                    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;
-                    }
+            // advance to the end of this specifier
+            toparse = spec->m_pArgEnd;
 
-                    // this conversion specifier is tied to the pos-th argument...
-                    pspec[current->m_pos] = current;
-                    nargs++;
+            // special handling for specifications including asterisks: we need
+            // to reserve an extra slot (or two if asterisks were used for both
+            // width and precision) in specs array in this case
+            if ( const char *f = strchr(spec->m_szFlags, '*') )
+            {
+                unsigned numAsterisks = 1;
+                if ( strchr(++f, '*') )
+                    numAsterisks++;
 
-                    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
+                for ( unsigned n = 0; n < numAsterisks; n++ )
                 {
-                    // it's safe to look in the next character of toparse as at
-                    // worst we'll hit its \0
-                    if (*(toparse+1) == wxT('%'))
+                    if ( nargs++ == wxMAX_SVNPRINTF_ARGUMENTS )
+                        break;
+
+                    // TODO: we need to support specifiers of the form "%2$*1$s"
+                    // (this is the same as "%*s") as if any positional arguments
+                    // are used all asterisks must be positional as well but this
+                    // requires a lot of changes in this code (basically we'd need
+                    // to rewrite Parse() to return "*" and conversion itself as
+                    // separate entries)
+                    if ( posarg_present )
                     {
-                        // the Parse() returned false because we've found a %%
-                        toparse++;
+                        wxFAIL_MSG
+                        (
+                            wxString::Format
+                            (
+                                "Format string \"%s\" uses both positional "
+                                "parameters and '*' but this is not currently "
+                                "supported by this implementation, sorry.",
+                                fmt
+                            )
+                        );
                     }
+
+                    specs[nargs] = *spec;
+
+                    // make an entry for '*' and point to it from pspec
+                    spec->Init();
+                    spec->m_type = wxPAT_STAR;
+                    pspec[nargs - 1] = spec;
+
+                    spec = &specs[nargs];
                 }
             }
+
+
+            // check if this is a positional or normal argument
+            if ( spec->m_pos > 0 )
+            {
+                // the positional arguments start from number 1 so we need
+                // to adjust the index
+                spec->m_pos--;
+                posarg_present = true;
+            }
+            else // not a positional argument...
+            {
+                spec->m_pos = nargs;
+                nonposarg_present = true;
+            }
+
+            // this conversion specifier is tied to the pos-th argument...
+            pspec[spec->m_pos] = spec;
+
+            if ( nargs++ == wxMAX_SVNPRINTF_ARGUMENTS )
+                break;
+        }
+
+
+        // warn if we lost any arguments (the program probably will crash
+        // anyhow because of stack corruption...)
+        if ( nargs == wxMAX_SVNPRINTF_ARGUMENTS )
+        {
+            wxFAIL_MSG
+            (
+                wxString::Format
+                (
+                    "wxVsnprintf() currently supports only %d arguments, "
+                    "but format string \"%s\" defines more of them.\n"
+                    "You need to change wxMAX_SVNPRINTF_ARGUMENTS and "
+                    "recompile if more are really needed.",
+                    fmt, wxMAX_SVNPRINTF_ARGUMENTS
+                )
+            );
         }
     }
 
-    wxPrintfConvSpec<CharType> arg[wxMAX_SVNPRINTF_ARGUMENTS];
-    wxPrintfConvSpec<CharType> *pspec[wxMAX_SVNPRINTF_ARGUMENTS];
-    bool posarg_present, nonposarg_present;
+    // total number of valid elements in specs
     unsigned nargs;
+
+    // all format specifications in this format string in order of their
+    // appearance (which may be different from arguments order)
+    ConvSpec specs[wxMAX_SVNPRINTF_ARGUMENTS];
+
+    // pointer to specs array element for the N-th argument
+    ConvSpec *pspec[wxMAX_SVNPRINTF_ARGUMENTS];
+
+    // true if any positional/non-positional parameters are used
+    bool posarg_present,
+         nonposarg_present;
 };
 
 #undef APPEND_CH