1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/strvararg.cpp
3 // Purpose: macros for implementing type-safe vararg passing of strings
4 // Author: Vaclav Slavik
7 // Copyright: (c) 2007 REA Elektronik GmbH
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
26 #include "wx/strvararg.h"
27 #include "wx/string.h"
29 #include "wx/private/wxprintf.h"
31 // ============================================================================
33 // ============================================================================
35 // ----------------------------------------------------------------------------
37 // ----------------------------------------------------------------------------
39 const wxStringCharType
*wxArgNormalizerNative
<const wxString
&>::get() const
41 return m_value
.wx_str();
44 const wxStringCharType
*wxArgNormalizerNative
<const wxCStrData
&>::get() const
46 return m_value
.AsInternal();
49 #if wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
50 wxArgNormalizerWchar
<const wxString
&>::wxArgNormalizerWchar(
52 const wxFormatString
*fmt
, unsigned index
)
53 : wxArgNormalizerWithBuffer
<wchar_t>(s
.wc_str(), fmt
, index
)
57 wxArgNormalizerWchar
<const wxCStrData
&>::wxArgNormalizerWchar(
59 const wxFormatString
*fmt
, unsigned index
)
60 : wxArgNormalizerWithBuffer
<wchar_t>(s
.AsWCharBuf(), fmt
, index
)
63 #endif // wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
65 // ----------------------------------------------------------------------------
66 // wxArgNormalizedString
67 // ----------------------------------------------------------------------------
69 wxString
wxArgNormalizedString::GetString() const
74 #if wxUSE_UTF8_LOCALE_ONLY
75 return wxString(reinterpret_cast<const char*>(m_ptr
));
77 #if wxUSE_UNICODE_UTF8
79 return wxString(reinterpret_cast<const char*>(m_ptr
));
82 return wxString(reinterpret_cast<const wxChar
*>(m_ptr
));
83 #endif // !wxUSE_UTF8_LOCALE_ONLY
86 wxArgNormalizedString::operator wxString() const
91 // ----------------------------------------------------------------------------
92 // wxFormatConverter: class doing the "%s" and "%c" normalization
93 // ----------------------------------------------------------------------------
96 There are four problems with wxPrintf() etc. format strings:
98 1) The printf vararg macros convert all forms of strings into
99 wxStringCharType* representation. This may make the format string
100 incorrect: for example, if %ls was used together with a wchar_t*
101 variadic argument, this would no longer work, because the templates
102 would change wchar_t* argument to wxStringCharType* and %ls would now
103 be incorrect in e.g. UTF-8 build. We need make sure only one specifier
106 2) To complicate matters further, the meaning of %s and %c is different
107 under Windows and on Unix. The Windows/MS convention is as follows:
111 format specifier results in
112 -----------------------------------
114 %ls, %S, %lS wchar_t*
118 format specifier results in
119 -----------------------------------
121 %s, %ls, %lS wchar_t*
123 (While on POSIX systems we have %C identical to %lc and %c always means
124 char (in any mode) while %lc always means wchar_t.)
126 In other words, we should _only_ use %s on Windows and %ls on Unix for
127 wxUSE_UNICODE_WCHAR build.
129 3) To make things even worse, we need two forms in UTF-8 build: one for
130 passing strings to ANSI functions under UTF-8 locales (this one should
131 use %s) and one for widechar functions used under non-UTF-8 locales
132 (this one should use %ls).
134 And, of course, the same should be done for %c as well.
137 wxScanf() family of functions is simpler, because we don't normalize their
138 variadic arguments and we only have to handle 2) above and only for widechar
143 class wxFormatConverterBase
148 wxFormatConverterBase()
155 wxScopedCharTypeBuffer
<CharType
> Convert(const CharType
*format
)
157 // this is reset to NULL if we modify the format string
162 if ( CopyFmtChar(*format
++) == wxT('%') )
164 #if wxUSE_PRINTF_POS_PARAMS
165 if ( *format
>= '0' && *format
<= '9' )
168 if ( *format
== '$' )
170 // It was a positional argument specification.
171 CopyFmtChar(*format
++);
173 //else: it was a width specification, nothing else to do.
175 #endif // wxUSE_PRINTF_POS_PARAMS
178 while ( IsFlagChar(*format
) )
179 CopyFmtChar(*format
++);
181 // and possible width
182 if ( *format
== wxT('*') )
183 CopyFmtChar(*format
++);
188 if ( *format
== wxT('.') )
190 CopyFmtChar(*format
++);
191 if ( *format
== wxT('*') )
192 CopyFmtChar(*format
++);
197 // next we can have a size modifier
208 // "ll" has a different meaning!
209 if ( format
[1] != 'l' )
221 CharType outConv
= *format
;
222 SizeModifier outSize
= size
;
224 // and finally we should have the type
229 // all strings were converted into the same form by
230 // wxArgNormalizer<T>, this form depends on the context
231 // in which the value is used (scanf/printf/wprintf):
232 HandleString(*format
, size
, outConv
, outSize
);
237 HandleChar(*format
, size
, outConv
, outSize
);
241 // nothing special to do
245 if ( outConv
== *format
&& outSize
== size
) // no change
247 if ( size
!= Size_Default
)
248 CopyFmtChar(*(format
- 1));
249 CopyFmtChar(*format
);
251 else // something changed
256 InsertFmtChar(wxT('l'));
260 InsertFmtChar(wxT('h'));
267 InsertFmtChar(outConv
);
274 // notice that we only translated the string if m_fmtOrig == NULL (as
275 // set by CopyAllBefore()), otherwise we should simply use the original
279 return wxScopedCharTypeBuffer
<CharType
>::CreateNonOwned(m_fmtOrig
);
283 // shrink converted format string to actual size (instead of
284 // over-sized allocation from CopyAllBefore()) and NUL-terminate
286 m_fmt
.shrink(m_fmtLast
- m_fmt
.data());
291 virtual ~wxFormatConverterBase() {}
301 // called to handle %S or %s; 'conv' is conversion specifier ('S' or 's'
302 // respectively), 'size' is the preceding size modifier; the new values of
303 // conversion and size specifiers must be written to outConv and outSize
304 virtual void HandleString(CharType conv
, SizeModifier size
,
305 CharType
& outConv
, SizeModifier
& outSize
) = 0;
307 // ditto for %C or %c
308 virtual void HandleChar(CharType conv
, SizeModifier size
,
309 CharType
& outConv
, SizeModifier
& outSize
) = 0;
312 // copy another character to the translated format: this function does the
313 // copy if we are translating but doesn't do anything at all if we don't,
314 // so we don't create the translated format string at all unless we really
315 // need to (i.e. InsertFmtChar() is called)
316 CharType
CopyFmtChar(CharType ch
)
320 // we're translating, do copy
325 // simply increase the count which should be copied by
326 // CopyAllBefore() later if needed
333 // insert an extra character
334 void InsertFmtChar(CharType ch
)
338 // so far we haven't translated anything yet
347 wxASSERT_MSG( m_fmtOrig
&& m_fmt
.data() == NULL
, "logic error" );
349 // the modified format string is guaranteed to be no longer than
350 // 3/2 of the original (worst case: the entire format string consists
351 // of "%s" repeated and is expanded to "%ls" on Unix), so we can
352 // allocate the buffer now and not worry about running out of space if
353 // we over-allocate a bit:
354 size_t fmtLen
= wxStrlen(m_fmtOrig
);
355 // worst case is of even length, so there's no rounding error in *3/2:
356 m_fmt
.extend(fmtLen
* 3 / 2);
359 wxStrncpy(m_fmt
.data(), m_fmtOrig
, m_nCopied
);
360 m_fmtLast
= m_fmt
.data() + m_nCopied
;
362 // we won't need it any longer and resetting it also indicates that we
363 // modified the format
367 static bool IsFlagChar(CharType ch
)
369 return ch
== wxT('-') || ch
== wxT('+') ||
370 ch
== wxT('0') || ch
== wxT(' ') || ch
== wxT('#');
373 void SkipDigits(const CharType
**ptpc
)
375 while ( **ptpc
>= wxT('0') && **ptpc
<= wxT('9') )
376 CopyFmtChar(*(*ptpc
)++);
379 // the translated format
380 wxCharTypeBuffer
<CharType
> m_fmt
;
383 // the original format
384 const CharType
*m_fmtOrig
;
386 // the number of characters already copied (i.e. already parsed, but left
391 #if defined(__WINDOWS__) && !defined(__CYGWIN__)
393 // on Windows, we should use %s and %c regardless of the build:
394 class wxPrintfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
396 virtual void HandleString(CharType
WXUNUSED(conv
),
397 SizeModifier
WXUNUSED(size
),
398 CharType
& outConv
, SizeModifier
& outSize
)
401 outSize
= Size_Default
;
404 virtual void HandleChar(CharType
WXUNUSED(conv
),
405 SizeModifier
WXUNUSED(size
),
406 CharType
& outConv
, SizeModifier
& outSize
)
409 outSize
= Size_Default
;
413 #else // !__WINDOWS__
415 // on Unix, it's %s for ANSI functions and %ls for widechar:
417 #if !wxUSE_UTF8_LOCALE_ONLY
418 class wxPrintfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
420 virtual void HandleString(CharType
WXUNUSED(conv
),
421 SizeModifier
WXUNUSED(size
),
422 CharType
& outConv
, SizeModifier
& outSize
)
428 virtual void HandleChar(CharType
WXUNUSED(conv
),
429 SizeModifier
WXUNUSED(size
),
430 CharType
& outConv
, SizeModifier
& outSize
)
436 #endif // !wxUSE_UTF8_LOCALE_ONLY
438 #if wxUSE_UNICODE_UTF8
439 class wxPrintfFormatConverterUtf8
: public wxFormatConverterBase
<char>
441 virtual void HandleString(CharType
WXUNUSED(conv
),
442 SizeModifier
WXUNUSED(size
),
443 CharType
& outConv
, SizeModifier
& outSize
)
446 outSize
= Size_Default
;
449 virtual void HandleChar(CharType
WXUNUSED(conv
),
450 SizeModifier
WXUNUSED(size
),
451 CharType
& outConv
, SizeModifier
& outSize
)
453 // chars are represented using wchar_t in both builds, so this is
459 #endif // wxUSE_UNICODE_UTF8
461 #endif // __WINDOWS__/!__WINDOWS__
463 #if !wxUSE_UNICODE // FIXME-UTF8: remove
464 class wxPrintfFormatConverterANSI
: public wxFormatConverterBase
<char>
466 virtual void HandleString(CharType
WXUNUSED(conv
),
467 SizeModifier
WXUNUSED(size
),
468 CharType
& outConv
, SizeModifier
& outSize
)
471 outSize
= Size_Default
;
474 virtual void HandleChar(CharType
WXUNUSED(conv
),
475 SizeModifier
WXUNUSED(size
),
476 CharType
& outConv
, SizeModifier
& outSize
)
479 outSize
= Size_Default
;
487 wxScanf() format translation is different, we need to translate %s to %ls
488 and %c to %lc on Unix (but not Windows and for widechar functions only!).
490 So to use native functions in order to get our semantics we must do the
491 following translations in Unicode mode:
493 wxWidgets specifier POSIX specifier
494 ----------------------------------------
500 class wxScanfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
502 virtual void HandleString(CharType conv
, SizeModifier size
,
503 CharType
& outConv
, SizeModifier
& outSize
)
506 outSize
= GetOutSize(conv
== 'S', size
);
509 virtual void HandleChar(CharType conv
, SizeModifier size
,
510 CharType
& outConv
, SizeModifier
& outSize
)
513 outSize
= GetOutSize(conv
== 'C', size
);
516 SizeModifier
GetOutSize(bool convIsUpper
, SizeModifier size
)
518 // %S and %hS -> %s and %lS -> %ls
521 if ( size
== Size_Long
)
528 if ( size
== Size_Default
)
536 const wxScopedWCharBuffer
wxScanfConvertFormatW(const wchar_t *format
)
538 return wxScanfFormatConverterWchar().Convert(format
);
540 #endif // !__WINDOWS__
543 // ----------------------------------------------------------------------------
545 // ----------------------------------------------------------------------------
547 #if !wxUSE_UNICODE_WCHAR
548 const char* wxFormatString::InputAsChar()
551 return m_char
.data();
553 // in ANSI build, wx_str() returns char*, in UTF-8 build, this function
554 // is only called under UTF-8 locales, so we should return UTF-8 string,
555 // which is, again, what wx_str() returns:
557 return m_str
->wx_str();
561 return m_cstr
->AsInternal();
563 // the last case is that wide string was passed in: in that case, we need
567 m_char
= wxConvLibc
.cWC2MB(m_wchar
.data());
569 return m_char
.data();
572 const char* wxFormatString::AsChar()
574 if ( !m_convertedChar
)
575 #if !wxUSE_UNICODE // FIXME-UTF8: remove this
576 m_convertedChar
= wxPrintfFormatConverterANSI().Convert(InputAsChar());
578 m_convertedChar
= wxPrintfFormatConverterUtf8().Convert(InputAsChar());
581 return m_convertedChar
.data();
583 #endif // !wxUSE_UNICODE_WCHAR
585 #if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
586 const wchar_t* wxFormatString::InputAsWChar()
589 return m_wchar
.data();
591 #if wxUSE_UNICODE_WCHAR
593 return m_str
->wc_str();
595 return m_cstr
->AsInternal();
596 #else // wxUSE_UNICODE_UTF8
599 m_wchar
= m_str
->wc_str();
600 return m_wchar
.data();
604 m_wchar
= m_cstr
->AsWCharBuf();
605 return m_wchar
.data();
607 #endif // wxUSE_UNICODE_WCHAR/UTF8
609 // the last case is that narrow string was passed in: in that case, we need
613 m_wchar
= wxConvLibc
.cMB2WC(m_char
.data());
615 return m_wchar
.data();
618 const wchar_t* wxFormatString::AsWChar()
620 if ( !m_convertedWChar
)
621 m_convertedWChar
= wxPrintfFormatConverterWchar().Convert(InputAsWChar());
623 return m_convertedWChar
.data();
625 #endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
627 wxString
wxFormatString::InputAsString() const
632 return m_cstr
->AsString();
634 return wxString(m_wchar
);
636 return wxString(m_char
);
638 wxFAIL_MSG( "invalid wxFormatString - not initialized?" );
642 // ----------------------------------------------------------------------------
643 // wxFormatString::GetArgumentType()
644 // ----------------------------------------------------------------------------
649 template<typename CharType
>
650 wxFormatString::ArgumentType
DoGetArgumentType(const CharType
*format
,
653 wxCHECK_MSG( format
, wxFormatString::Arg_Unknown
,
654 "empty format string not allowed here" );
656 wxPrintfConvSpecParser
<CharType
> parser(format
);
658 wxCHECK_MSG( n
<= parser
.nargs
, wxFormatString::Arg_Unknown
,
659 "more arguments than format string specifiers?" );
661 wxCHECK_MSG( parser
.pspec
[n
-1] != NULL
, wxFormatString::Arg_Unknown
,
662 "requested argument not found - invalid format string?" );
664 switch ( parser
.pspec
[n
-1]->m_type
)
668 return wxFormatString::Arg_Char
;
672 return wxFormatString::Arg_String
;
675 return wxFormatString::Arg_Int
;
677 return wxFormatString::Arg_LongInt
;
679 case wxPAT_LONGLONGINT
:
680 return wxFormatString::Arg_LongLongInt
;
683 return wxFormatString::Arg_Size_t
;
686 return wxFormatString::Arg_Double
;
687 case wxPAT_LONGDOUBLE
:
688 return wxFormatString::Arg_LongDouble
;
691 return wxFormatString::Arg_Pointer
;
694 return wxFormatString::Arg_IntPtr
;
695 case wxPAT_NSHORTINT
:
696 return wxFormatString::Arg_ShortIntPtr
;
698 return wxFormatString::Arg_LongIntPtr
;
701 // "*" requires argument of type int
702 return wxFormatString::Arg_Int
;
705 // (handled after the switch statement)
710 wxFAIL_MSG( "unexpected argument type" );
711 return wxFormatString::Arg_Unknown
;
714 } // anonymous namespace
716 wxFormatString::ArgumentType
wxFormatString::GetArgumentType(unsigned n
) const
719 return DoGetArgumentType(m_char
.data(), n
);
721 return DoGetArgumentType(m_wchar
.data(), n
);
723 return DoGetArgumentType(m_str
->wx_str(), n
);
725 return DoGetArgumentType(m_cstr
->AsInternal(), n
);
727 wxFAIL_MSG( "unreachable code" );