// * wchar_t* if wxUSE_UNICODE_WCHAR or if wxUSE_UNICODE_UTF8 and the current
// 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.
+//
// Parameters:
// [ there are examples in square brackets showing values of the parameters
// for the wxFprintf() wrapper for fprintf() function with the following
// wxFormatString
// ----------------------------------------------------------------------------
-// This class should be used for format string argument of the functions
+// This class must be used for format string argument of the functions
// defined using WX_DEFINE_VARARG_FUNC_* macros. It converts the string to
// char* or wchar_t* for passing to implementation function efficiently (i.e.
// without keeping the converted string in memory for longer than necessary,
-// like c_str())
+// like c_str()). It also converts format string to the correct form that
+// accounts for string changes done by wxArgNormalizer<>
//
// Note that this class can _only_ be used for function arguments!
class WXDLLIMPEXP_BASE wxFormatString
operator const char*() const
{ return wx_const_cast(wxFormatString*, this)->AsChar(); }
private:
+ // InputAsChar() returns the value converted passed to ctor, only converted
+ // to char, while AsChar() takes the the string returned by InputAsChar()
+ // and does format string conversion on it as well (and similarly for
+ // ..AsWChar() below)
+ const char* InputAsChar();
const char* AsChar();
+ wxCharBuffer m_convertedChar;
#endif // !wxUSE_UNICODE_WCHAR
#if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
operator const wchar_t*() const
{ return wx_const_cast(wxFormatString*, this)->AsWChar(); }
private:
+ const wchar_t* InputAsWChar();
const wchar_t* AsWChar();
+ wxWCharBuffer m_convertedWChar;
#endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
private:
#if !wxUSE_UTF8_LOCALE_ONLY
template<>
struct wxArgNormalizerWchar<const wxUniChar&>
- : public wxArgNormalizerWchar<wxChar/*FIXME-UTF8: should be wchar_t after ANSI removal*/>
{
- wxArgNormalizerWchar(const wxUniChar& s)
- : wxArgNormalizerWchar<wxChar>((wxChar)s) {}
+ wxArgNormalizerWchar(const wxUniChar& s) : m_value(s) {}
+
+ // FIXME-UTF8: use wchar_t once ANSI build is removed
+ wxChar get() const { return m_value; }
+
+ wxChar m_value;
};
#endif // !wxUSE_UTF8_LOCALE_ONLY
#if wxUSE_UNICODE_UTF8
template<>
struct wxArgNormalizerUtf8<const wxUniChar&>
- : public wxArgNormalizerUtf8<char>
{
- wxArgNormalizerUtf8(const wxUniChar& s)
- // FIXME-UTF8: this is lossy, we need to convert to string, but that
- // requires format string update
- : wxArgNormalizerUtf8<char>((const char)s) {}
+ wxArgNormalizerUtf8(const wxUniChar& s) : m_value(s.AsUTF8()) {}
+
+ const wxStringCharType *get() const { return m_value; }
+
+ wxUniChar::Utf8CharBuffer m_value;
};
#endif // wxUSE_UNICODE_UTF8
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&);
#undef WX_ARG_NORMALIZER_FORWARD
return GetString();
}
+// ----------------------------------------------------------------------------
+// wxFormatConverter: class doing the "%s" and "%c" normalization
+// ----------------------------------------------------------------------------
+
+/*
+ There are four problems with wxPrintf() etc. format strings:
+
+ 1) The printf vararg macros convert all forms of strings into
+ wxStringCharType* representation. This may make the format string
+ incorrect: for example, if %ls was used together with a wchar_t*
+ variadic argument, this would no longer work, because the templates
+ would change wchar_t* argument to wxStringCharType* and %ls would now
+ be incorrect in e.g. UTF-8 build. We need make sure only one specifier
+ form is used.
+
+ 2) To complicate matters further, the meaning of %s and %c is different
+ under Windows and on Unix. The Windows/MS convention is as follows:
+
+ In ANSI mode:
+
+ format specifier results in
+ -----------------------------------
+ %s, %hs, %hS char*
+ %ls, %S, %lS wchar_t*
+
+ In Unicode mode:
+
+ format specifier results in
+ -----------------------------------
+ %hs, %S, %hS char*
+ %s, %ls, %lS 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.)
+
+ In other words, we should _only_ use %s on Windows and %ls on Unix for
+ wxUSE_UNICODE_WCHAR build.
+
+ 3) To make things even worse, we need two forms in UTF-8 build: one for
+ passing strings to ANSI functions under UTF-8 locales (this one should
+ use %s) and one for widechar functions used under non-UTF-8 locales
+ (this one should use %ls).
+
+ 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
+ versions.
+*/
+
+template<typename T>
+class wxFormatConverterBase
+{
+public:
+ typedef T CharType;
+
+ wxFormatConverterBase()
+ {
+ m_fmtOrig = NULL;
+ m_fmtLast = NULL;
+ m_nCopied = 0;
+ }
+
+ wxCharTypeBuffer<CharType> Convert(const CharType *format)
+ {
+ // this is reset to NULL if we modify the format string
+ m_fmtOrig = format;
+
+ 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
+ SizeModifier size;
+
+ switch ( *format )
+ {
+ case 'h':
+ size = Size_Short;
+ format++;
+ break;
+
+ case 'l':
+ // "ll" has a different meaning!
+ if ( format[1] != 'l' )
+ {
+ size = Size_Long;
+ format++;
+ break;
+ }
+ //else: fall through
+
+ default:
+ size = Size_Default;
+ }
+
+ CharType outConv = *format;
+ SizeModifier outSize = size;
+
+ // and finally we should have the type
+ switch ( *format )
+ {
+ case _T('S'):
+ case _T('s'):
+ // all strings were converted into the same form by
+ // wxArgNormalizer<T>, this form depends on the context
+ // in which the value is used (scanf/printf/wprintf):
+ HandleString(*format, size, outConv, outSize);
+ break;
+
+ case _T('C'):
+ case _T('c'):
+ HandleChar(*format, size, outConv, outSize);
+ break;
+
+ default:
+ // nothing special to do
+ break;
+ }
+
+ if ( outConv == *format && outSize == size ) // no change
+ {
+ if ( size != Size_Default )
+ CopyFmtChar(*(format - 1));
+ CopyFmtChar(*format);
+ }
+ else // something changed
+ {
+ switch ( outSize )
+ {
+ case Size_Long:
+ InsertFmtChar(_T('l'));
+ break;
+
+ case Size_Short:
+ InsertFmtChar(_T('h'));
+ break;
+
+ case Size_Default:
+ // nothing to do
+ break;
+ }
+ InsertFmtChar(outConv);
+ }
+
+ format++;
+ }
+ }
+
+ // notice that we only translated the string if m_fmtOrig == NULL (as
+ // set by CopyAllBefore()), otherwise we should simply use the original
+ // format
+ if ( m_fmtOrig )
+ {
+ return wxCharTypeBuffer<CharType>::CreateNonOwned(m_fmtOrig);
+ }
+ else
+ {
+ // NULL-terminate converted format string:
+ *m_fmtLast = 0;
+ return m_fmt;
+ }
+ }
+
+ virtual ~wxFormatConverterBase() {}
+
+protected:
+ enum SizeModifier
+ {
+ Size_Default,
+ Size_Short,
+ Size_Long
+ };
+
+ // called to handle %S or %s; 'conv' is conversion specifier ('S' or 's'
+ // respectively), 'size' is the preceding size modifier; the new values of
+ // conversion and size specifiers must be written to outConv and outSize
+ virtual void HandleString(CharType conv, SizeModifier size,
+ CharType& outConv, SizeModifier& outSize) = 0;
+
+ // ditto for %C or %c
+ virtual void HandleChar(CharType conv, SizeModifier size,
+ CharType& outConv, SizeModifier& outSize) = 0;
+
+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)
+ CharType CopyFmtChar(CharType ch)
+ {
+ if ( !m_fmtOrig )
+ {
+ // we're translating, do copy
+ *(m_fmtLast++) = 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(CharType ch)
+ {
+ if ( m_fmtOrig )
+ {
+ // so far we haven't translated anything yet
+ CopyAllBefore();
+ }
+
+ *(m_fmtLast++) = ch;
+ }
+
+ void CopyAllBefore()
+ {
+ wxASSERT_MSG( m_fmtOrig && m_fmt.data() == NULL, "logic error" );
+
+ // the modified format string is guaranteed to be no longer than
+ // 3/2 of the original (worst case: the entire format string consists
+ // of "%s" repeated and is expanded to "%ls" on Unix), so we can
+ // allocate the buffer now and not worry about running out of space if
+ // we over-allocate a bit:
+ size_t fmtLen = wxStrlen(m_fmtOrig);
+ // worst case is of even length, so there's no rounding error in *3/2:
+ m_fmt.extend(fmtLen * 3 / 2);
+
+ if ( m_nCopied > 0 )
+ wxStrncpy(m_fmt.data(), m_fmtOrig, m_nCopied);
+ m_fmtLast = m_fmt.data() + m_nCopied;
+
+ // we won't need it any longer and resetting it also indicates that we
+ // modified the format
+ m_fmtOrig = NULL;
+ }
+
+ static bool IsFlagChar(CharType ch)
+ {
+ return ch == _T('-') || ch == _T('+') ||
+ ch == _T('0') || ch == _T(' ') || ch == _T('#');
+ }
+
+ void SkipDigits(const CharType **ptpc)
+ {
+ while ( **ptpc >= _T('0') && **ptpc <= _T('9') )
+ CopyFmtChar(*(*ptpc)++);
+ }
+
+ // the translated format
+ wxCharTypeBuffer<CharType> m_fmt;
+ CharType *m_fmtLast;
+
+ // the original format
+ const CharType *m_fmtOrig;
+
+ // the number of characters already copied (i.e. already parsed, but left
+ // unmodified)
+ size_t m_nCopied;
+};
+
+
+
+#ifdef __WINDOWS
+
+// on Windows, we should use %s and %c regardless of the build:
+class wxPrintfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
+{
+ virtual void HandleString(CharType WXUNUSED(conv),
+ SizeModifier WXUNUSED(size),
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 's';
+ outSize = Size_Default;
+ }
+
+ virtual void HandleChar(CharType WXUNUSED(conv),
+ SizeModifier WXUNUSED(size),
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 'c';
+ outSize = Size_Default;
+ }
+};
+
+#else // !__WINDOWS__
+
+// on Unix, it's %s for ANSI functions and %ls for widechar:
+
+#if !wxUSE_UTF8_LOCALE_ONLY
+class wxPrintfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
+{
+ virtual void HandleString(CharType WXUNUSED(conv),
+ SizeModifier WXUNUSED(size),
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 's';
+ outSize = Size_Long;
+ }
+
+ virtual void HandleChar(CharType WXUNUSED(conv),
+ SizeModifier WXUNUSED(size),
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 'c';
+ outSize = Size_Long;
+ }
+};
+#endif // !wxUSE_UTF8_LOCALE_ONLY
+
+#if wxUSE_UNICODE_UTF8
+class wxPrintfFormatConverterUtf8 : public wxFormatConverterBase<char>
+{
+ virtual void HandleString(CharType WXUNUSED(conv),
+ SizeModifier WXUNUSED(size),
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 's';
+ outSize = Size_Default;
+ }
+
+ virtual void HandleChar(CharType WXUNUSED(conv),
+ SizeModifier WXUNUSED(size),
+ CharType& outConv, SizeModifier& outSize)
+ {
+ // added complication: %c should be translated to %s in UTF-8 build
+ outConv = 's';
+ outSize = Size_Default;
+ }
+};
+#endif // wxUSE_UNICODE_UTF8
+
+#endif // __WINDOWS__/!__WINDOWS__
+
+#if !wxUSE_UNICODE // FIXME-UTF8: remove
+class wxPrintfFormatConverterANSI : public wxFormatConverterBase<char>
+{
+ virtual void HandleString(CharType WXUNUSED(conv),
+ SizeModifier WXUNUSED(size),
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 's';
+ outSize = Size_Default;
+ }
+
+ virtual void HandleChar(CharType WXUNUSED(conv),
+ SizeModifier WXUNUSED(size),
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 'c';
+ outSize = Size_Default;
+ }
+};
+#endif // ANSI
+
+#ifndef __WINDOWS__
+/*
+
+ wxScanf() format translation is different, we need to translate %s to %ls
+ and %c to %lc on Unix (but not Windows and for widechar functions only!).
+
+ So to use native functions in order to get our semantics we must do the
+ following translations in Unicode mode:
+
+ wxWidgets specifier POSIX specifier
+ ----------------------------------------
+
+ %hc, %C, %hC %c
+ %c %lc
+
+ */
+class wxScanfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
+{
+ virtual void HandleString(CharType conv, SizeModifier size,
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 's';
+ outSize = GetOutSize(conv == 'S', size);
+ }
+
+ virtual void HandleChar(CharType conv, SizeModifier size,
+ CharType& outConv, SizeModifier& outSize)
+ {
+ outConv = 'c';
+ outSize = GetOutSize(conv == 'C', size);
+ }
+
+ SizeModifier GetOutSize(bool convIsUpper, SizeModifier size)
+ {
+ // %S and %hS -> %s and %lS -> %ls
+ if ( convIsUpper )
+ {
+ if ( size == Size_Long )
+ return Size_Long;
+ else
+ return Size_Default;
+ }
+ else // %s or %c
+ {
+ if ( size == Size_Default )
+ return Size_Long;
+ else
+ return size;
+ }
+ }
+};
+
+const wxWCharBuffer wxScanfConvertFormatW(const wchar_t *format)
+{
+ return wxScanfFormatConverterWchar().Convert(format);
+}
+#endif // !__WINDOWS__
+
+
// ----------------------------------------------------------------------------
// wxFormatString
// ----------------------------------------------------------------------------
#if !wxUSE_UNICODE_WCHAR
-const char* wxFormatString::AsChar()
+const char* wxFormatString::InputAsChar()
{
if ( m_char )
return m_char.data();
return m_char.data();
}
+
+const char* wxFormatString::AsChar()
+{
+ if ( !m_convertedChar )
+#if !wxUSE_UNICODE // FIXME-UTF8: remove this
+ m_convertedChar = wxPrintfFormatConverterANSI().Convert(InputAsChar());
+#else
+ m_convertedChar = wxPrintfFormatConverterUtf8().Convert(InputAsChar());
+#endif
+
+ return m_convertedChar.data();
+}
#endif // !wxUSE_UNICODE_WCHAR
#if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
-const wchar_t* wxFormatString::AsWChar()
+const wchar_t* wxFormatString::InputAsWChar()
{
if ( m_wchar )
return m_wchar.data();
return m_wchar.data();
}
+
+const wchar_t* wxFormatString::AsWChar()
+{
+ if ( !m_convertedWChar )
+ m_convertedWChar = wxPrintfFormatConverterWchar().Convert(InputAsWChar());
+
+ return m_convertedWChar.data();
+}
#endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
return -1;
}
-#define vswprintf wxCRT_VsnprintfW_
+#define vswprintf wxCRT_VsnprintfW
static int vfwprintf(FILE *stream, const wchar_t *format, va_list 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 wchar_t *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 wchar_t *() 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)
- wchar_t CopyFmtChar(wchar_t 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(wchar_t 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(wchar_t ch)
- {
- return ch == _T('-') || ch == _T('+') ||
- ch == _T('0') || ch == _T(' ') || ch == _T('#');
- }
-
- void SkipDigits(const wchar_t **ptpc)
- {
- while ( **ptpc >= _T('0') && **ptpc <= _T('9') )
- CopyFmtChar(*(*ptpc)++);
- }
-
- // the translated format
- wxString m_fmt;
-
- // the original format
- const wchar_t *m_fmtOrig;
-
- // the number of characters already copied
- size_t m_nCopied;
-};
-
-wxFormatConverter::wxFormatConverter(const wchar_t *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 wchar_t *format)
-{
- return wxString(wxFormatConverter(format));
-}
-#endif
-
// ----------------------------------------------------------------------------
// wxPrintf(), wxScanf() and relatives
// ----------------------------------------------------------------------------
va_list argptr;
va_start(argptr, format);
- int ret = vwprintf( wxFormatConverter(format), argptr );
+ int ret = vwprintf( format, argptr );
va_end(argptr);
va_list argptr;
va_start( argptr, format );
- int ret = vfwprintf( stream, wxFormatConverter(format), argptr );
+ int ret = vfwprintf( stream, format, argptr );
va_end(argptr);
#ifndef wxCRT_VfprintfW
int wxCRT_VfprintfW( FILE *stream, const wchar_t *format, va_list argptr )
{
- return vfwprintf( stream, wxFormatConverter(format), argptr );
+ return vfwprintf( stream, format, argptr );
}
#endif
#ifndef wxCRT_VprintfW
int wxCRT_VprintfW( const wchar_t *format, va_list argptr )
{
- return vwprintf( wxFormatConverter(format), argptr );
+ return vwprintf( format, argptr );
}
#endif
-#ifndef wxCRT_VsnprintfW
-int wxCRT_VsnprintfW(wchar_t *str, size_t size, const wchar_t *format, va_list argptr )
-{
- return vswprintf( str, size, wxFormatConverter(format), argptr );
-}
-#endif // !wxCRT_VsnprintfW
-
#ifndef wxCRT_VsprintfW
int wxCRT_VsprintfW( wchar_t *str, const wchar_t *format, va_list argptr )
{
// same as for wxSprintf()
- return vswprintf(str, INT_MAX / 4, wxFormatConverter(format), argptr);
+ return vswprintf(str, INT_MAX / 4, format, argptr);
}
#endif
#ifdef __VMS
#if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
- int ret = std::vwscanf(wxFormatConverter(format), argptr);
+ int ret = std::vwscanf(format, argptr);
#else
- int ret = vwscanf(wxFormatConverter(format), argptr);
+ int ret = vwscanf(format, argptr);
#endif
#else
- int ret = vwscanf(wxFormatConverter(format), argptr);
+ int ret = vwscanf(format, argptr);
#endif
va_end(argptr);
#ifdef __VMS
#if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
- int ret = std::vswscanf(str, wxFormatConverter(format), argptr);
+ int ret = std::vswscanf(str, format, argptr);
#else
- int ret = vswscanf(str, wxFormatConverter(format), argptr);
+ int ret = vswscanf(str, format, argptr);
#endif
#else
- int ret = vswscanf(str, wxFormatConverter(format), argptr);
+ int ret = vswscanf(str, format, argptr);
#endif
va_end(argptr);
va_start(argptr, format);
#ifdef __VMS
#if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
- int ret = std::vfwscanf(stream, wxFormatConverter(format), argptr);
+ int ret = std::vfwscanf(stream, format, argptr);
#else
- int ret = vfwscanf(stream, wxFormatConverter(format), argptr);
+ int ret = vfwscanf(stream, format, argptr);
#endif
#else
- int ret = vfwscanf(stream, wxFormatConverter(format), argptr);
+ int ret = vfwscanf(stream, format, argptr);
#endif
va_end(argptr);
{
#ifdef __VMS
#if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
- return std::vswscanf(str, wxFormatConverter(format), argptr);
+ return std::vswscanf(str, format, argptr);
#else
- return vswscanf(str, wxFormatConverter(format), argptr);
+ return vswscanf(str, format, argptr);
#endif
#else
- return vswscanf(str, wxFormatConverter(format), argptr);
+ return vswscanf(str, format, argptr);
#endif
}
#endif
// special test mode: define all functions below even if we don't really need
// them to be able to test them
#ifdef wxTEST_PRINTF
- #undef wxCRT_VsnprintfW_
+ #undef wxCRT_VsnprintfW
#undef wxCRT_VsnprintfA
#endif
// common code for both ANSI and Unicode versions
// ----------------------------------------------------------------------------
-#if !defined(wxCRT_VsnprintfW_) || !defined(wxCRT_VsnprintfA)
+#if !defined(wxCRT_VsnprintfW) || !defined(wxCRT_VsnprintfA)
-// wxUSE_STRUTILS says our wxCRT_VsnprintfW_ implementation to use or not to
+// wxUSE_STRUTILS says our wxCRT_VsnprintfW implementation to use or not to
// use wxStrlen and wxStrncpy functions over one-char processing loops.
//
// Some benchmarking revealed that wxUSE_STRUTILS == 1 has the following
namespace
{
-// the conversion specifiers accepted by wxCRT_VsnprintfW_
+// the conversion specifiers accepted by wxCRT_VsnprintfW
enum wxPrintfArgType {
wxPAT_INVALID = -1,
wxPAT_NLONGINT // %ln
};
-// an argument passed to wxCRT_VsnprintfW_
+// an argument passed to wxCRT_VsnprintfW
typedef union {
int pad_int; // %d, %i, %o, %u, %x, %X
long int pad_longint; // %ld, etc
// Contains parsed data relative to a conversion specifier given to
-// wxCRT_VsnprintfW_ and parsed from the format string
+// wxCRT_VsnprintfW and parsed from the format string
// NOTE: in C++ there is almost no difference between struct & classes thus
// there is no performance gain by using a struct here...
template<typename CharType>
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_
+ // 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();
// useful for debugging, to understand if we are really using this function
// rather than the system implementation
#if 0
- wprintf(L"Using wxCRT_VsnprintfW_\n");
+ wprintf(L"Using wxCRT_VsnprintfW\n");
#endif
// required memory:
} // anonymous namespace
-#endif // !defined(wxCRT_VsnprintfW_) || !defined(wxCRT_VsnprintfA)
+#endif // !defined(wxCRT_VsnprintfW) || !defined(wxCRT_VsnprintfA)
// ----------------------------------------------------------------------------
-// wxCRT_VsnprintfW_
+// wxCRT_VsnprintfW
// ----------------------------------------------------------------------------
-#if !defined(wxCRT_VsnprintfW_)
+#if !defined(wxCRT_VsnprintfW)
#if !wxUSE_WXVSNPRINTFW
- #error "wxUSE_WXVSNPRINTFW must be 1 if our wxCRT_VsnprintfW_ is used"
+ #error "wxUSE_WXVSNPRINTFW must be 1 if our wxCRT_VsnprintfW is used"
#endif
-int wxCRT_VsnprintfW_(wchar_t *buf, size_t len,
- const wchar_t *format, va_list argptr)
+int wxCRT_VsnprintfW(wchar_t *buf, size_t len,
+ const wchar_t *format, va_list argptr)
{
return wxDoVsnprintf(buf, len, format, argptr);
}
-#else // wxCRT_VsnprintfW_ is defined
+#else // wxCRT_VsnprintfW is defined
#if wxUSE_WXVSNPRINTFW
- #error "wxUSE_WXVSNPRINTFW must be 0 if our wxCRT_VsnprintfW_ is not used"
+ #error "wxUSE_WXVSNPRINTFW must be 0 if our wxCRT_VsnprintfW is not used"
#endif
-#endif // !wxCRT_VsnprintfW_
+#endif // !wxCRT_VsnprintfW
// ----------------------------------------------------------------------------
// wxCRT_VsnprintfA
#include "wx/wx.h"
#endif
-// wxFormatConverter can only be tested in a Unicode non-Windows debug build
-//
-#if defined(wxNEED_PRINTF_CONVERSION) && defined(__WXDEBUG__)
-#define CAN_TEST
-extern wxString wxConvertFormat(const wxChar *format);
-#endif
using CppUnit::TestCase;
using std::string;
CPPUNIT_TEST(format_c);
CPPUNIT_TEST(format_hc);
CPPUNIT_TEST(format_lc);
-#ifdef CAN_TEST
CPPUNIT_TEST(format_S);
CPPUNIT_TEST(format_hS);
CPPUNIT_TEST(format_lS);
CPPUNIT_TEST(format_hC);
CPPUNIT_TEST(format_lC);
CPPUNIT_TEST(testLonger);
-#endif
CPPUNIT_TEST_SUITE_END();
void format_d();
void format_hc();
void format_lc();
-#ifdef CAN_TEST
void format_S();
void format_hS();
void format_lS();
void format_lC();
void testLonger();
- void doTest(const wxChar *input, const wxChar *expected);
- void check(const wxString& input, const wxString& expected);
-#endif
+ void doTest(const char *input, const char *expectedScanf,
+ const char *expectedUtf8,
+ const char *expectedWcharUnix,
+ const char *expectedWcharWindows);
+ void check(const wxString& input, const wxString& expectedScanf,
+ const wxString& expectedUtf8,
+ const wxString& expectedWcharUnix,
+ const wxString& expectedWcharWindows);
};
void FormatConverterTestCase::format_d()
{
-#ifdef CAN_TEST
- doTest(_T("d"), _T("d"));
-#endif
+ doTest("d", "d", "d", "d", "d");
CPPUNIT_ASSERT(wxString::Format(_T("%d"), 255) == _T("255"));
CPPUNIT_ASSERT(wxString::Format(_T("%05d"), 255) == _T("00255"));
CPPUNIT_ASSERT(wxString::Format(_T("% 5d"), 255) == _T(" 255"));
void FormatConverterTestCase::format_hd()
{
-#ifdef CAN_TEST
- doTest(_T("hd"), _T("hd"));
-#endif
+ doTest("hd", "hd", "hd", "hd", "hd");
short s = 32767;
CPPUNIT_ASSERT(wxString::Format(_T("%hd"), s) == _T("32767"));
}
void FormatConverterTestCase::format_ld()
{
-#ifdef CAN_TEST
- doTest(_T("ld"), _T("ld"));
-#endif
+ doTest("ld", "ld", "ld", "ld", "ld");
long l = 2147483647L;
CPPUNIT_ASSERT(wxString::Format(_T("%ld"), l) == _T("2147483647"));
}
void FormatConverterTestCase::format_s()
{
-#ifdef CAN_TEST
- doTest(_T("s"), _T("ls"));
-#endif
+ doTest("s", "ls", "s", "ls", "s");
CPPUNIT_ASSERT(wxString::Format(_T("%s!"), _T("test")) == _T("test!"));
CPPUNIT_ASSERT(wxString::Format(_T("%6s!"), _T("test")) == _T(" test!"));
CPPUNIT_ASSERT(wxString::Format(_T("%-6s!"), _T("test")) == _T("test !"));
void FormatConverterTestCase::format_hs()
{
-#ifdef CAN_TEST
- doTest(_T("hs"), _T("hs"));
-#endif
+ doTest("hs", "hs", "s", "ls", "s");
CPPUNIT_ASSERT(wxString::Format(wxString(_T("%hs!")), "test") == _T("test!"));
CPPUNIT_ASSERT(wxString::Format(wxString(_T("%6hs!")), "test") == _T(" test!"));
CPPUNIT_ASSERT(wxString::Format(wxString(_T("%-6hs!")), "test") == _T("test !"));
void FormatConverterTestCase::format_ls()
{
-#ifdef CAN_TEST
- doTest(_T("ls"), _T("ls"));
-#endif
+ doTest("ls", "ls", "s", "ls", "s");
CPPUNIT_ASSERT(wxString::Format(_T("%ls!"), L"test") == _T("test!"));
CPPUNIT_ASSERT(wxString::Format(_T("%6ls!"), L"test") == _T(" test!"));
CPPUNIT_ASSERT(wxString::Format(_T("%-6ls!"), L"test") == _T("test !"));
void FormatConverterTestCase::format_c()
{
-#ifdef CAN_TEST
- doTest(_T("c"), _T("lc"));
-#endif
+ doTest("c", "lc", "s", "lc", "c");
CPPUNIT_ASSERT(wxString::Format(_T("%c"), _T('x')) == _T("x"));
CPPUNIT_ASSERT(wxString::Format(_T("%2c"), _T('x')) == _T(" x"));
CPPUNIT_ASSERT(wxString::Format(_T("%-2c"), _T('x')) == _T("x "));
void FormatConverterTestCase::format_hc()
{
-#ifdef CAN_TEST
- doTest(_T("hc"), _T("hc"));
-#endif
+ doTest("hc", "hc", "s", "lc", "c");
CPPUNIT_ASSERT(wxString::Format(wxString(_T("%hc")), 'x') == _T("x"));
CPPUNIT_ASSERT(wxString::Format(wxString(_T("%2hc")), 'x') == _T(" x"));
CPPUNIT_ASSERT(wxString::Format(wxString(_T("%-2hc")), 'x') == _T("x "));
void FormatConverterTestCase::format_lc()
{
-#ifdef CAN_TEST
- doTest(_T("lc"), _T("lc"));
-#endif
+ doTest("lc", "lc", "s", "lc", "c");
CPPUNIT_ASSERT(wxString::Format(_T("%lc"), L'x') == _T("x"));
CPPUNIT_ASSERT(wxString::Format(_T("%2lc"), L'x') == _T(" x"));
CPPUNIT_ASSERT(wxString::Format(_T("%-2lc"), L'x') == _T("x "));
}
-#ifdef CAN_TEST
-void FormatConverterTestCase::format_S() { doTest(_T("S"), _T("s")); }
-void FormatConverterTestCase::format_hS() { doTest(_T("hS"), _T("s")); }
-void FormatConverterTestCase::format_lS() { doTest(_T("lS"), _T("ls")); }
+void FormatConverterTestCase::format_S()
+ { doTest("S", "s", "s", "ls", "s"); }
+void FormatConverterTestCase::format_hS()
+ { doTest("hS", "s", "s", "ls", "s"); }
+void FormatConverterTestCase::format_lS()
+ { doTest("lS", "ls", "s", "ls", "s"); }
-void FormatConverterTestCase::format_C() { doTest(_T("C"), _T("c")); }
-void FormatConverterTestCase::format_hC() { doTest(_T("hC"), _T("c")); }
-void FormatConverterTestCase::format_lC() { doTest(_T("lC"), _T("lc")); }
+void FormatConverterTestCase::format_C()
+ { doTest("C", "c", "s", "lc", "c"); }
+void FormatConverterTestCase::format_hC()
+ { doTest("hC", "c", "s", "lc", "c"); }
+void FormatConverterTestCase::format_lC()
+ { doTest("lC", "lc", "s", "lc", "c"); }
// It's possible that although a format converts correctly alone, it leaves
// the converter in a bad state that will affect subsequent formats, so
void FormatConverterTestCase::testLonger()
{
struct {
- const wxChar *input;
- const wxChar *expected;
+ const char *input;
+ const char *expectedScanf;
+ const char *expectedWcharUnix;
+ const char *expectedWcharWindows;
+ const char *expectedUtf8;
} formats[] = {
- { _T("%d"), _T("%d"), },
- { _T("%*hd"), _T("%*hd") },
- { _T("%.4ld"), _T("%.4ld") },
- { _T("%-.*s"), _T("%-.*ls") },
- { _T("%.*hs"), _T("%.*hs"), },
- { _T("%-.9ls"), _T("%-.9ls") },
- { _T("%-*c"), _T("%-*lc") },
- { _T("%3hc"), _T("%3hc") },
- { _T("%-5lc"), _T("%-5lc") }
+ { "%d", "%d", "%d", "%d", "%d" },
+ { "%*hd", "%*hd", "%*hd", "%*hd", "%*hd" },
+ { "%.4ld", "%.4ld", "%.4ld", "%.4ld", "%.4ld" },
+ { "%-.*s", "%-.*ls", "%-.*ls", "%-.*s", "%-.*s" },
+ { "%.*hs", "%.*hs", "%.*ls", "%.*s", "%.*s" },
+ { "%-.9ls", "%-.9ls", "%-.9ls", "%-.9s", "%-.9s" },
+ { "%-*c", "%-*lc", "%-*lc", "%-*c", "%-*s" },
+ { "%3hc", "%3hc", "%3lc", "%3c", "%3s" },
+ { "%-5lc", "%-5lc", "%-5lc", "%-5c", "%-5s" }
};
size_t i, j;
- // exclude patterns that don't translate correctly alone from the test
- for (i = 0; i < WXSIZEOF(formats); i++)
- if (wxConvertFormat(formats[i].input) != formats[i].expected)
- formats[i].input = NULL;
-
// test all possible pairs of the above patterns
for (i = 0; i < WXSIZEOF(formats); i++) {
if (formats[i].input) {
wxString input(formats[i].input);
- wxString expected(formats[i].expected);
+ wxString expectedScanf(formats[i].expectedScanf);
+ wxString expectedUtf8(formats[i].expectedUtf8);
+ wxString expectedWcharUnix(formats[i].expectedWcharUnix);
+ wxString expectedWcharWindows(formats[i].expectedWcharWindows);
for (j = 0; j < WXSIZEOF(formats); j++)
if (formats[j].input)
check(input + formats[j].input,
- expected + formats[j].expected);
+ expectedScanf + formats[j].expectedScanf,
+ expectedUtf8 + formats[j].expectedUtf8,
+ expectedWcharUnix + formats[j].expectedWcharUnix,
+ expectedWcharWindows + formats[j].expectedWcharWindows);
}
}
}
-void FormatConverterTestCase::doTest(const wxChar *input,
- const wxChar *expected)
+void FormatConverterTestCase::doTest(const char *input,
+ const char *expectedScanf,
+ const char *expectedUtf8,
+ const char *expectedWcharUnix,
+ const char *expectedWcharWindows)
{
static const wxChar *flag_width[] =
{ _T(""), _T("*"), _T("10"), _T("-*"), _T("-10"), NULL };
for (const wxChar **prec = precs; *prec; prec++)
for (const wxChar **width = flag_width; *width; width++)
check(fmt + *width + *prec + input,
- fmt + *width + *prec + expected);
+ fmt + *width + *prec + expectedScanf,
+ fmt + *width + *prec + expectedUtf8,
+ fmt + *width + *prec + expectedWcharUnix,
+ fmt + *width + *prec + expectedWcharWindows);
}
void FormatConverterTestCase::check(const wxString& input,
- const wxString& expected)
+ const wxString& expectedScanf,
+ const wxString& expectedUtf8,
+ const wxString& expectedWcharUnix,
+ const wxString& expectedWcharWindows)
{
- wxString result = wxConvertFormat(input.wc_str());
- wxString msg = _T("input: '") + input +
- _T("', result: '") + result +
- _T("', expected: '") + expected + _T("'");
- CPPUNIT_ASSERT_MESSAGE(string(msg.mb_str()), result == expected);
+ wxString result, msg;
+
+#ifndef __WINDOWS__
+ // on windows, wxScanf() string needs no modifications
+ result = wxScanfConvertFormatW(input.wc_str());
+
+ msg = _T("input: '") + input +
+ _T("', result (scanf): '") + result +
+ _T("', expected: '") + expectedScanf + _T("'");
+ CPPUNIT_ASSERT_MESSAGE(string(msg.mb_str()), result == expectedScanf);
+#endif // !__WINDOWS__
+
+#if wxUSE_UNICODE_UTF8
+ result = (const char*)wxFormatString(input);
+
+ msg = _T("input: '") + input +
+ _T("', result (UTF-8): '") + result +
+ _T("', expected: '") + expectedUtf8 + _T("'");
+ CPPUNIT_ASSERT_MESSAGE(string(msg.mb_str()), result == expectedUtf8);
+#endif // wxUSE_UNICODE_UTF8
+
+#if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
+ result = (const wchar_t*)wxFormatString(input);
+
+#ifdef __WINDOWS__
+ wxString expectedWchar(expectedWcharWindows);
+#else
+ wxString expectedWchar(expectedWcharUnix);
+#endif
+
+ msg = _T("input: '") + input +
+ _T("', result (wchar_t): '") + result +
+ _T("', expected: '") + expectedWchar + _T("'");
+ CPPUNIT_ASSERT_MESSAGE(string(msg.mb_str()), result == expectedWchar);
+#endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
}
-#endif // CAN_TEST
// register in the unnamed registry so that these tests are run by default
CPPUNIT_TEST_SUITE_REGISTRATION(FormatConverterTestCase);