+ // required memory:
+ wxPrintfConvSpec arg[wxMAX_SVNPRINTF_ARGUMENTS];
+ wxPrintfArg argdata[wxMAX_SVNPRINTF_ARGUMENTS];
+ wxPrintfConvSpec *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 wxChar *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 *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)
+ return -1; // format strings with both positional and
+ // non-positional conversion specifier are unsupported !!
+
+ // on platforms where va_list is an array type, it is necessary to make a
+ // copy to be able to pass it to LoadArg as a reference.
+ bool ok = true;
+ va_list ap;
+ wxVaCopy(ap, argptr);
+
+ // now load arguments from stack
+ for (i=0; i < nargs && ok; i++)
+ {
+ // !pspec[i] means that the user forgot a positional parameter (e.g. %$1s %$3s);
+ // LoadArg == false means that wxPrintfConvSpec::Parse failed to set the
+ // conversion specifier 'type' to a valid value...
+ ok = pspec[i] && pspec[i]->LoadArg(&argdata[i], ap);
+ }
+
+ va_end(ap);
+
+ // something failed while loading arguments from the variable list...
+ if (!ok)
+ return -1;
+
+ // finally, process each conversion specifier with its own argument
+ toparse = format;
+ for (i=0; i < nargs; i++)
+ {
+ // copy in the output buffer the portion of the format string between
+ // last specifier and the current one
+ size_t tocopy = ( arg[i].m_pArgPos - toparse );
+ if (lenCur+tocopy >= lenMax)
+ {
+ // not enough space in the output buffer !
+ // copy until the end of remaining space and then stop
+ wxCopyStrWithPercents(buf+lenCur, toparse, lenMax - lenCur - 1);
+ buf[lenMax-1] = wxT('\0');
+ return -1;
+ }
+
+ lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy);
+
+ // process this specifier directly in the output buffer
+ int n = arg[i].Process(buf+lenCur, lenMax - lenCur, &argdata[arg[i].m_pos]);
+ if (n == -1)
+ {
+ buf[lenMax-1] = wxT('\0'); // be sure to always NUL-terminate the string
+ return -1; // not enough space in the output buffer !
+ }
+ lenCur += n;
+
+ // the +1 is because wxPrintfConvSpec::m_pArgEnd points to the last character
+ // of the format specifier, but we are not interested to it...
+ toparse = arg[i].m_pArgEnd + 1;
+ }
+
+ // copy portion of the format string after last specifier
+ // NOTE: toparse is pointing to the character just after the last processed
+ // conversion specifier
+ // NOTE2: the +1 is because we want to copy also the '\0'
+ size_t tocopy = wxStrlen(format) + 1 - ( toparse - format ) ;
+ if (lenCur+tocopy >= lenMax)
+ return -1; // not enough space in the output buffer !
+
+ // the -1 is because of the '\0'
+ lenCur += wxCopyStrWithPercents(buf+lenCur, toparse, tocopy) - 1;
+
+ wxASSERT(lenCur == wxStrlen(buf));
+ return lenCur;
+}
+
+#undef APPEND_CH
+#undef APPEND_STR
+#undef CHECK_PREC
+
+#endif // !wxVsnprintfA
+
+#if !defined(wxSnprintf_)
+int WXDLLEXPORT wxSnprintf_(wxChar *buf, size_t len, const wxChar *format, ...)
+{
+ va_list argptr;
+ va_start(argptr, format);
+
+ int iLen = wxVsnprintf_(buf, len, format, argptr);
+
+ va_end(argptr);
+
+ return iLen;
+}
+#endif // wxSnprintf_
+
+#if defined(__DMC__)
+ /* Digital Mars adds count to _stprintf (C99) so convert */
+ #if wxUSE_UNICODE
+ int wxSprintf (wchar_t * __RESTRICT s, const wchar_t * __RESTRICT format, ... )
+ {
+ va_list arglist;
+
+ va_start( arglist, format );
+ int iLen = swprintf ( s, -1, format, arglist );
+ va_end( arglist );
+ return iLen ;
+ }
+
+ #endif // wxUSE_UNICODE
+
+#endif //__DMC__
+
+// ----------------------------------------------------------------------------
+// implement the standard IO functions for wide char if libc doesn't have them
+// ----------------------------------------------------------------------------
+
+#ifdef wxNEED_FPUTS
+int wxFputs(const wchar_t *ws, FILE *stream)
+{
+ // counting the number of wide characters written isn't worth the trouble,
+ // simply distinguish between ok and error
+ return fputs(wxConvLibc.cWC2MB(ws), stream) == -1 ? -1 : 0;
+}
+#endif // wxNEED_FPUTS
+
+#ifdef wxNEED_PUTS
+int wxPuts(const wxChar *ws)
+{
+ int rc = wxFputs(ws, stdout);
+ if ( rc != -1 )
+ {
+ if ( wxFputs(L"\n", stdout) == -1 )
+ return -1;
+
+ rc++;
+ }
+
+ return rc;
+}
+#endif // wxNEED_PUTS
+
+#ifdef wxNEED_PUTC
+int /* not wint_t */ wxPutc(wchar_t wc, FILE *stream)
+{
+ wchar_t ws[2] = { wc, L'\0' };
+
+ return wxFputs(ws, stream);
+}
+#endif // wxNEED_PUTC
+
+// NB: we only implement va_list functions here, the ones taking ... are
+// defined below for wxNEED_PRINTF_CONVERSION case anyhow and we reuse
+// the definitions there to avoid duplicating them here
+#ifdef wxNEED_WPRINTF
+
+// TODO: implement the scanf() functions
+int vwscanf(const wxChar *format, va_list argptr)
+{
+ wxFAIL_MSG( _T("TODO") );
+
+ return -1;
+}
+
+int vswscanf(const wxChar *ws, const wxChar *format, va_list argptr)
+{
+ // The best we can do without proper Unicode support in glibc is to
+ // convert the strings into MB representation and run ANSI version
+ // of the function. This doesn't work with %c and %s because of difference
+ // in size of char and wchar_t, though.
+
+ wxCHECK_MSG( wxStrstr(format, _T("%s")) == NULL, -1,
+ _T("incomplete vswscanf implementation doesn't allow %s") );
+ wxCHECK_MSG( wxStrstr(format, _T("%c")) == NULL, -1,
+ _T("incomplete vswscanf implementation doesn't allow %c") );
+
+ va_list argcopy;
+ wxVaCopy(argcopy, argptr);
+ return vsscanf(wxConvLibc.cWX2MB(ws), wxConvLibc.cWX2MB(format), argcopy);
+}
+
+int vfwscanf(FILE *stream, const wxChar *format, va_list argptr)
+{
+ wxFAIL_MSG( _T("TODO") );
+
+ return -1;
+}
+
+#define vswprintf wxVsnprintf_
+
+int vfwprintf(FILE *stream, const wxChar *format, va_list argptr)
+{
+ wxString s;
+ int rc = s.PrintfV(format, argptr);
+
+ if ( rc != -1 )
+ {
+ // we can't do much better without Unicode support in libc...
+ if ( fprintf(stream, "%s", (const char*)s.mb_str() ) == -1 )
+ return -1;
+ }
+
+ return rc;
+}
+
+int vwprintf(const wxChar *format, va_list argptr)
+{
+ return wxVfprintf(stdout, format, argptr);
+}
+
+#endif // wxNEED_WPRINTF
+
+#ifdef wxNEED_PRINTF_CONVERSION
+
+// ----------------------------------------------------------------------------
+// wxFormatConverter: class doing the "%s" -> "%ls" conversion
+// ----------------------------------------------------------------------------
+
+/*
+ Here are the gory details. We want to follow the Windows/MS conventions,
+ that is to have
+
+ In ANSI mode:
+
+ format specifier results in
+ -----------------------------------
+ %c, %hc, %hC char
+ %lc, %C, %lC wchar_t
+
+ In Unicode mode:
+
+ format specifier results in
+ -----------------------------------
+ %hc, %C, %hC char
+ %c, %lc, %lC wchar_t
+
+
+ while on POSIX systems we have %C identical to %lc and %c always means char
+ (in any mode) while %lc always means wchar_t,
+
+ So to use native functions in order to get our semantics we must do the
+ following translations in Unicode mode (nothing to do in ANSI mode):
+
+ wxWidgets specifier POSIX specifier
+ ----------------------------------------
+
+ %hc, %C, %hC %c
+ %c %lc
+
+
+ And, of course, the same should be done for %s as well.
+*/
+
+class wxFormatConverter
+{
+public:
+ wxFormatConverter(const wxChar *format);
+
+ // notice that we only translated the string if m_fmtOrig == NULL (as set
+ // by CopyAllBefore()), otherwise we should simply use the original format
+ operator const wxChar *() const
+ { return m_fmtOrig ? m_fmtOrig : m_fmt.c_str(); }
+
+private:
+ // copy another character to the translated format: this function does the
+ // copy if we are translating but doesn't do anything at all if we don't,
+ // so we don't create the translated format string at all unless we really
+ // need to (i.e. InsertFmtChar() is called)
+ wxChar CopyFmtChar(wxChar ch)
+ {
+ if ( !m_fmtOrig )
+ {
+ // we're translating, do copy
+ m_fmt += ch;
+ }
+ else
+ {
+ // simply increase the count which should be copied by
+ // CopyAllBefore() later if needed
+ m_nCopied++;
+ }
+
+ return ch;
+ }
+
+ // insert an extra character
+ void InsertFmtChar(wxChar ch)
+ {
+ if ( m_fmtOrig )
+ {
+ // so far we haven't translated anything yet
+ CopyAllBefore();
+ }
+
+ m_fmt += ch;
+ }
+
+ void CopyAllBefore()
+ {
+ wxASSERT_MSG( m_fmtOrig && m_fmt.empty(), _T("logic error") );
+
+ m_fmt = wxString(m_fmtOrig, m_nCopied);
+
+ // we won't need it any longer
+ m_fmtOrig = NULL;
+ }
+
+ static bool IsFlagChar(wxChar ch)
+ {
+ return ch == _T('-') || ch == _T('+') ||
+ ch == _T('0') || ch == _T(' ') || ch == _T('#');
+ }
+
+ void SkipDigits(const wxChar **ptpc)
+ {
+ while ( **ptpc >= _T('0') && **ptpc <= _T('9') )
+ CopyFmtChar(*(*ptpc)++);
+ }
+
+ // the translated format
+ wxString m_fmt;
+
+ // the original format
+ const wxChar *m_fmtOrig;
+
+ // the number of characters already copied
+ size_t m_nCopied;
+};
+
+wxFormatConverter::wxFormatConverter(const wxChar *format)
+{
+ m_fmtOrig = format;
+ m_nCopied = 0;
+
+ while ( *format )
+ {
+ if ( CopyFmtChar(*format++) == _T('%') )
+ {
+ // skip any flags
+ while ( IsFlagChar(*format) )
+ CopyFmtChar(*format++);
+
+ // and possible width
+ if ( *format == _T('*') )
+ CopyFmtChar(*format++);
+ else
+ SkipDigits(&format);
+
+ // precision?
+ if ( *format == _T('.') )
+ {
+ CopyFmtChar(*format++);
+ if ( *format == _T('*') )
+ CopyFmtChar(*format++);
+ else
+ SkipDigits(&format);
+ }
+
+ // next we can have a size modifier
+ enum
+ {
+ Default,
+ Short,
+ Long
+ } size;
+
+ switch ( *format )
+ {
+ case _T('h'):
+ size = Short;
+ format++;
+ break;
+
+ case _T('l'):
+ // "ll" has a different meaning!
+ if ( format[1] != _T('l') )
+ {
+ size = Long;
+ format++;
+ break;
+ }
+ //else: fall through
+
+ default:
+ size = Default;
+ }
+
+ // and finally we should have the type
+ switch ( *format )
+ {
+ case _T('C'):
+ case _T('S'):
+ // %C and %hC -> %c and %lC -> %lc
+ if ( size == Long )
+ CopyFmtChar(_T('l'));
+
+ InsertFmtChar(*format++ == _T('C') ? _T('c') : _T('s'));
+ break;
+
+ case _T('c'):
+ case _T('s'):
+ // %c -> %lc but %hc stays %hc and %lc is still %lc
+ if ( size == Default)
+ InsertFmtChar(_T('l'));
+ // fall through
+
+ default:
+ // nothing special to do
+ if ( size != Default )
+ CopyFmtChar(*(format - 1));
+ CopyFmtChar(*format++);
+ }
+ }
+ }
+}
+
+#else // !wxNEED_PRINTF_CONVERSION
+ // no conversion necessary
+ #define wxFormatConverter(x) (x)
+#endif // wxNEED_PRINTF_CONVERSION/!wxNEED_PRINTF_CONVERSION
+
+#ifdef __WXDEBUG__
+// For testing the format converter
+wxString wxConvertFormat(const wxChar *format)
+{
+ return wxString(wxFormatConverter(format));
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// wxPrintf(), wxScanf() and relatives
+// ----------------------------------------------------------------------------
+
+#if defined(wxNEED_PRINTF_CONVERSION) || defined(wxNEED_WPRINTF)
+
+int wxScanf( const wxChar *format, ... )
+{
+ va_list argptr;
+ va_start(argptr, format);
+
+ int ret = vwscanf(wxFormatConverter(format), argptr );
+
+ va_end(argptr);
+
+ return ret;
+}
+
+int wxSscanf( const wxChar *str, const wxChar *format, ... )
+{
+ va_list argptr;
+ va_start(argptr, format);
+
+ int ret = vswscanf( str, wxFormatConverter(format), argptr );
+
+ va_end(argptr);
+
+ return ret;
+}
+
+int wxFscanf( FILE *stream, const wxChar *format, ... )
+{
+ va_list argptr;
+ va_start(argptr, format);
+ int ret = vfwscanf(stream, wxFormatConverter(format), argptr);
+
+ va_end(argptr);
+
+ return ret;
+}
+
+int wxPrintf( const wxChar *format, ... )
+{
+ va_list argptr;
+ va_start(argptr, format);
+
+ int ret = vwprintf( wxFormatConverter(format), argptr );
+
+ va_end(argptr);
+
+ return ret;
+}
+
+#ifndef wxSnprintf
+int wxSnprintf( wxChar *str, size_t size, const wxChar *format, ... )
+{
+ va_list argptr;
+ va_start(argptr, format);
+
+ int ret = vswprintf( str, size, wxFormatConverter(format), argptr );
+
+ // VsnprintfTestCase reveals that glibc's implementation of vswprintf
+ // doesn't nul terminate on truncation.
+ str[size - 1] = 0;
+
+ va_end(argptr);
+
+ return ret;
+}
+#endif // wxSnprintf
+
+int wxSprintf( wxChar *str, const wxChar *format, ... )
+{
+ va_list argptr;
+ va_start(argptr, format);
+
+ // note that wxString::FormatV() uses wxVsnprintf(), not wxSprintf(), so
+ // it's safe to implement this one in terms of it
+ wxString s(wxString::FormatV(format, argptr));
+ wxStrcpy(str, s);
+
+ va_end(argptr);
+
+ return s.length();
+}
+
+int wxFprintf( FILE *stream, const wxChar *format, ... )
+{
+ va_list argptr;
+ va_start( argptr, format );
+
+ int ret = vfwprintf( stream, wxFormatConverter(format), argptr );
+
+ va_end(argptr);
+
+ return ret;
+}
+
+int wxVsscanf( const wxChar *str, const wxChar *format, va_list argptr )
+{
+ return vswscanf( str, wxFormatConverter(format), argptr );
+}
+
+int wxVfprintf( FILE *stream, const wxChar *format, va_list argptr )
+{
+ return vfwprintf( stream, wxFormatConverter(format), argptr );
+}
+
+int wxVprintf( const wxChar *format, va_list argptr )
+{
+ return vwprintf( wxFormatConverter(format), argptr );
+}
+
+#ifndef wxVsnprintf
+int wxVsnprintf( wxChar *str, size_t size, const wxChar *format, va_list argptr )
+{
+ return vswprintf( str, size, wxFormatConverter(format), argptr );
+}
+#endif // wxVsnprintf
+
+int wxVsprintf( wxChar *str, const wxChar *format, va_list argptr )
+{
+ // same as for wxSprintf()
+ return vswprintf(str, INT_MAX / 4, wxFormatConverter(format), argptr);
+}
+
+#endif // wxNEED_PRINTF_CONVERSION
+
+#if wxUSE_WCHAR_T
+
+// ----------------------------------------------------------------------------
+// ctype.h stuff (currently unused)
+// ----------------------------------------------------------------------------
+