1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/strvararg.cpp
3 // Purpose: macros for implementing type-safe vararg passing of strings
4 // Author: Vaclav Slavik
6 // Copyright: (c) 2007 REA Elektronik GmbH
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
25 #include "wx/strvararg.h"
26 #include "wx/string.h"
28 #include "wx/private/wxprintf.h"
30 // ============================================================================
32 // ============================================================================
34 // ----------------------------------------------------------------------------
36 // ----------------------------------------------------------------------------
38 const wxStringCharType
*wxArgNormalizerNative
<const wxString
&>::get() const
40 return m_value
.wx_str();
43 const wxStringCharType
*wxArgNormalizerNative
<const wxCStrData
&>::get() const
45 return m_value
.AsInternal();
48 #if wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
49 wxArgNormalizerWchar
<const wxString
&>::wxArgNormalizerWchar(
51 const wxFormatString
*fmt
, unsigned index
)
52 : wxArgNormalizerWithBuffer
<wchar_t>(s
.wc_str(), fmt
, index
)
56 wxArgNormalizerWchar
<const wxCStrData
&>::wxArgNormalizerWchar(
58 const wxFormatString
*fmt
, unsigned index
)
59 : wxArgNormalizerWithBuffer
<wchar_t>(s
.AsWCharBuf(), fmt
, index
)
62 #endif // wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
64 // ----------------------------------------------------------------------------
65 // wxArgNormalizedString
66 // ----------------------------------------------------------------------------
68 wxString
wxArgNormalizedString::GetString() const
73 #if wxUSE_UTF8_LOCALE_ONLY
74 return wxString(reinterpret_cast<const char*>(m_ptr
));
76 #if wxUSE_UNICODE_UTF8
78 return wxString(reinterpret_cast<const char*>(m_ptr
));
81 return wxString(reinterpret_cast<const wxChar
*>(m_ptr
));
82 #endif // !wxUSE_UTF8_LOCALE_ONLY
85 wxArgNormalizedString::operator wxString() const
90 // ----------------------------------------------------------------------------
91 // wxFormatConverter: class doing the "%s" and "%c" normalization
92 // ----------------------------------------------------------------------------
95 There are four problems with wxPrintf() etc. format strings:
97 1) The printf vararg macros convert all forms of strings into
98 wxStringCharType* representation. This may make the format string
99 incorrect: for example, if %ls was used together with a wchar_t*
100 variadic argument, this would no longer work, because the templates
101 would change wchar_t* argument to wxStringCharType* and %ls would now
102 be incorrect in e.g. UTF-8 build. We need make sure only one specifier
105 2) To complicate matters further, the meaning of %s and %c is different
106 under Windows and on Unix. The Windows/MS convention is as follows:
110 format specifier results in
111 -----------------------------------
113 %ls, %S, %lS wchar_t*
117 format specifier results in
118 -----------------------------------
120 %s, %ls, %lS wchar_t*
122 (While on POSIX systems we have %C identical to %lc and %c always means
123 char (in any mode) while %lc always means wchar_t.)
125 In other words, we should _only_ use %s on Windows and %ls on Unix for
126 wxUSE_UNICODE_WCHAR build.
128 3) To make things even worse, we need two forms in UTF-8 build: one for
129 passing strings to ANSI functions under UTF-8 locales (this one should
130 use %s) and one for widechar functions used under non-UTF-8 locales
131 (this one should use %ls).
133 And, of course, the same should be done for %c as well.
136 wxScanf() family of functions is simpler, because we don't normalize their
137 variadic arguments and we only have to handle 2) above and only for widechar
142 class wxFormatConverterBase
147 wxFormatConverterBase()
154 wxScopedCharTypeBuffer
<CharType
> Convert(const CharType
*format
)
156 // this is reset to NULL if we modify the format string
161 if ( CopyFmtChar(*format
++) == wxT('%') )
163 #if wxUSE_PRINTF_POS_PARAMS
164 if ( *format
>= '0' && *format
<= '9' )
167 if ( *format
== '$' )
169 // It was a positional argument specification.
170 CopyFmtChar(*format
++);
172 //else: it was a width specification, nothing else to do.
174 #endif // wxUSE_PRINTF_POS_PARAMS
177 while ( IsFlagChar(*format
) )
178 CopyFmtChar(*format
++);
180 // and possible width
181 if ( *format
== wxT('*') )
182 CopyFmtChar(*format
++);
187 if ( *format
== wxT('.') )
189 CopyFmtChar(*format
++);
190 if ( *format
== wxT('*') )
191 CopyFmtChar(*format
++);
196 // next we can have a size modifier
207 // "ll" has a different meaning!
208 if ( format
[1] != 'l' )
220 CharType outConv
= *format
;
221 SizeModifier outSize
= size
;
223 // and finally we should have the type
228 // all strings were converted into the same form by
229 // wxArgNormalizer<T>, this form depends on the context
230 // in which the value is used (scanf/printf/wprintf):
231 HandleString(*format
, size
, outConv
, outSize
);
236 HandleChar(*format
, size
, outConv
, outSize
);
240 // nothing special to do
244 if ( outConv
== *format
&& outSize
== size
) // no change
246 if ( size
!= Size_Default
)
247 CopyFmtChar(*(format
- 1));
248 CopyFmtChar(*format
);
250 else // something changed
255 InsertFmtChar(wxT('l'));
259 InsertFmtChar(wxT('h'));
266 InsertFmtChar(outConv
);
273 // notice that we only translated the string if m_fmtOrig == NULL (as
274 // set by CopyAllBefore()), otherwise we should simply use the original
278 return wxScopedCharTypeBuffer
<CharType
>::CreateNonOwned(m_fmtOrig
);
282 // shrink converted format string to actual size (instead of
283 // over-sized allocation from CopyAllBefore()) and NUL-terminate
285 m_fmt
.shrink(m_fmtLast
- m_fmt
.data());
290 virtual ~wxFormatConverterBase() {}
300 // called to handle %S or %s; 'conv' is conversion specifier ('S' or 's'
301 // respectively), 'size' is the preceding size modifier; the new values of
302 // conversion and size specifiers must be written to outConv and outSize
303 virtual void HandleString(CharType conv
, SizeModifier size
,
304 CharType
& outConv
, SizeModifier
& outSize
) = 0;
306 // ditto for %C or %c
307 virtual void HandleChar(CharType conv
, SizeModifier size
,
308 CharType
& outConv
, SizeModifier
& outSize
) = 0;
311 // copy another character to the translated format: this function does the
312 // copy if we are translating but doesn't do anything at all if we don't,
313 // so we don't create the translated format string at all unless we really
314 // need to (i.e. InsertFmtChar() is called)
315 CharType
CopyFmtChar(CharType ch
)
319 // we're translating, do copy
324 // simply increase the count which should be copied by
325 // CopyAllBefore() later if needed
332 // insert an extra character
333 void InsertFmtChar(CharType ch
)
337 // so far we haven't translated anything yet
346 wxASSERT_MSG( m_fmtOrig
&& m_fmt
.data() == NULL
, "logic error" );
348 // the modified format string is guaranteed to be no longer than
349 // 3/2 of the original (worst case: the entire format string consists
350 // of "%s" repeated and is expanded to "%ls" on Unix), so we can
351 // allocate the buffer now and not worry about running out of space if
352 // we over-allocate a bit:
353 size_t fmtLen
= wxStrlen(m_fmtOrig
);
354 // worst case is of even length, so there's no rounding error in *3/2:
355 m_fmt
.extend(fmtLen
* 3 / 2);
358 wxStrncpy(m_fmt
.data(), m_fmtOrig
, m_nCopied
);
359 m_fmtLast
= m_fmt
.data() + m_nCopied
;
361 // we won't need it any longer and resetting it also indicates that we
362 // modified the format
366 static bool IsFlagChar(CharType ch
)
368 return ch
== wxT('-') || ch
== wxT('+') ||
369 ch
== wxT('0') || ch
== wxT(' ') || ch
== wxT('#');
372 void SkipDigits(const CharType
**ptpc
)
374 while ( **ptpc
>= wxT('0') && **ptpc
<= wxT('9') )
375 CopyFmtChar(*(*ptpc
)++);
378 // the translated format
379 wxCharTypeBuffer
<CharType
> m_fmt
;
382 // the original format
383 const CharType
*m_fmtOrig
;
385 // the number of characters already copied (i.e. already parsed, but left
390 #if defined(__WINDOWS__) && !defined(__CYGWIN__)
392 // on Windows, we should use %s and %c regardless of the build:
393 class wxPrintfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
395 virtual void HandleString(CharType
WXUNUSED(conv
),
396 SizeModifier
WXUNUSED(size
),
397 CharType
& outConv
, SizeModifier
& outSize
)
400 outSize
= Size_Default
;
403 virtual void HandleChar(CharType
WXUNUSED(conv
),
404 SizeModifier
WXUNUSED(size
),
405 CharType
& outConv
, SizeModifier
& outSize
)
408 outSize
= Size_Default
;
412 #else // !__WINDOWS__
414 // on Unix, it's %s for ANSI functions and %ls for widechar:
416 #if !wxUSE_UTF8_LOCALE_ONLY
417 class wxPrintfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
419 virtual void HandleString(CharType
WXUNUSED(conv
),
420 SizeModifier
WXUNUSED(size
),
421 CharType
& outConv
, SizeModifier
& outSize
)
427 virtual void HandleChar(CharType
WXUNUSED(conv
),
428 SizeModifier
WXUNUSED(size
),
429 CharType
& outConv
, SizeModifier
& outSize
)
435 #endif // !wxUSE_UTF8_LOCALE_ONLY
437 #endif // __WINDOWS__/!__WINDOWS__
439 #if wxUSE_UNICODE_UTF8
440 class wxPrintfFormatConverterUtf8
: public wxFormatConverterBase
<char>
442 virtual void HandleString(CharType
WXUNUSED(conv
),
443 SizeModifier
WXUNUSED(size
),
444 CharType
& outConv
, SizeModifier
& outSize
)
447 outSize
= Size_Default
;
450 virtual void HandleChar(CharType
WXUNUSED(conv
),
451 SizeModifier
WXUNUSED(size
),
452 CharType
& outConv
, SizeModifier
& outSize
)
454 // chars are represented using wchar_t in both builds, so this is
460 #endif // wxUSE_UNICODE_UTF8
462 #if !wxUSE_UNICODE // FIXME-UTF8: remove
463 class wxPrintfFormatConverterANSI
: public wxFormatConverterBase
<char>
465 virtual void HandleString(CharType
WXUNUSED(conv
),
466 SizeModifier
WXUNUSED(size
),
467 CharType
& outConv
, SizeModifier
& outSize
)
470 outSize
= Size_Default
;
473 virtual void HandleChar(CharType
WXUNUSED(conv
),
474 SizeModifier
WXUNUSED(size
),
475 CharType
& outConv
, SizeModifier
& outSize
)
478 outSize
= Size_Default
;
486 wxScanf() format translation is different, we need to translate %s to %ls
487 and %c to %lc on Unix (but not Windows and for widechar functions only!).
489 So to use native functions in order to get our semantics we must do the
490 following translations in Unicode mode:
492 wxWidgets specifier POSIX specifier
493 ----------------------------------------
499 class wxScanfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
501 virtual void HandleString(CharType conv
, SizeModifier size
,
502 CharType
& outConv
, SizeModifier
& outSize
)
505 outSize
= GetOutSize(conv
== 'S', size
);
508 virtual void HandleChar(CharType conv
, SizeModifier size
,
509 CharType
& outConv
, SizeModifier
& outSize
)
512 outSize
= GetOutSize(conv
== 'C', size
);
515 SizeModifier
GetOutSize(bool convIsUpper
, SizeModifier size
)
517 // %S and %hS -> %s and %lS -> %ls
520 if ( size
== Size_Long
)
527 if ( size
== Size_Default
)
535 const wxScopedWCharBuffer
wxScanfConvertFormatW(const wchar_t *format
)
537 return wxScanfFormatConverterWchar().Convert(format
);
539 #endif // !__WINDOWS__
542 // ----------------------------------------------------------------------------
544 // ----------------------------------------------------------------------------
546 #if !wxUSE_UNICODE_WCHAR
547 const char* wxFormatString::InputAsChar()
550 return m_char
.data();
552 // in ANSI build, wx_str() returns char*, in UTF-8 build, this function
553 // is only called under UTF-8 locales, so we should return UTF-8 string,
554 // which is, again, what wx_str() returns:
556 return m_str
->wx_str();
560 return m_cstr
->AsInternal();
562 // the last case is that wide string was passed in: in that case, we need
566 m_char
= wxConvLibc
.cWC2MB(m_wchar
.data());
568 return m_char
.data();
571 const char* wxFormatString::AsChar()
573 if ( !m_convertedChar
)
574 #if !wxUSE_UNICODE // FIXME-UTF8: remove this
575 m_convertedChar
= wxPrintfFormatConverterANSI().Convert(InputAsChar());
577 m_convertedChar
= wxPrintfFormatConverterUtf8().Convert(InputAsChar());
580 return m_convertedChar
.data();
582 #endif // !wxUSE_UNICODE_WCHAR
584 #if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
585 const wchar_t* wxFormatString::InputAsWChar()
588 return m_wchar
.data();
590 #if wxUSE_UNICODE_WCHAR
592 return m_str
->wc_str();
594 return m_cstr
->AsInternal();
595 #else // wxUSE_UNICODE_UTF8
598 m_wchar
= m_str
->wc_str();
599 return m_wchar
.data();
603 m_wchar
= m_cstr
->AsWCharBuf();
604 return m_wchar
.data();
606 #endif // wxUSE_UNICODE_WCHAR/UTF8
608 // the last case is that narrow string was passed in: in that case, we need
612 m_wchar
= wxConvLibc
.cMB2WC(m_char
.data());
614 return m_wchar
.data();
617 const wchar_t* wxFormatString::AsWChar()
619 if ( !m_convertedWChar
)
620 m_convertedWChar
= wxPrintfFormatConverterWchar().Convert(InputAsWChar());
622 return m_convertedWChar
.data();
624 #endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
626 wxString
wxFormatString::InputAsString() const
631 return m_cstr
->AsString();
633 return wxString(m_wchar
);
635 return wxString(m_char
);
637 wxFAIL_MSG( "invalid wxFormatString - not initialized?" );
641 // ----------------------------------------------------------------------------
642 // wxFormatString::GetArgumentType()
643 // ----------------------------------------------------------------------------
648 template<typename CharType
>
649 wxFormatString::ArgumentType
DoGetArgumentType(const CharType
*format
,
652 wxCHECK_MSG( format
, wxFormatString::Arg_Unknown
,
653 "empty format string not allowed here" );
655 wxPrintfConvSpecParser
<CharType
> parser(format
);
657 wxCHECK_MSG( n
<= parser
.nargs
, wxFormatString::Arg_Unknown
,
658 "more arguments than format string specifiers?" );
660 wxCHECK_MSG( parser
.pspec
[n
-1] != NULL
, wxFormatString::Arg_Unknown
,
661 "requested argument not found - invalid format string?" );
663 switch ( parser
.pspec
[n
-1]->m_type
)
667 return wxFormatString::Arg_Char
;
671 return wxFormatString::Arg_String
;
674 return wxFormatString::Arg_Int
;
676 return wxFormatString::Arg_LongInt
;
678 case wxPAT_LONGLONGINT
:
679 return wxFormatString::Arg_LongLongInt
;
682 return wxFormatString::Arg_Size_t
;
685 return wxFormatString::Arg_Double
;
686 case wxPAT_LONGDOUBLE
:
687 return wxFormatString::Arg_LongDouble
;
690 return wxFormatString::Arg_Pointer
;
693 return wxFormatString::Arg_IntPtr
;
694 case wxPAT_NSHORTINT
:
695 return wxFormatString::Arg_ShortIntPtr
;
697 return wxFormatString::Arg_LongIntPtr
;
700 // "*" requires argument of type int
701 return wxFormatString::Arg_Int
;
704 // (handled after the switch statement)
709 wxFAIL_MSG( "unexpected argument type" );
710 return wxFormatString::Arg_Unknown
;
713 } // anonymous namespace
715 wxFormatString::ArgumentType
wxFormatString::GetArgumentType(unsigned n
) const
718 return DoGetArgumentType(m_char
.data(), n
);
720 return DoGetArgumentType(m_wchar
.data(), n
);
722 return DoGetArgumentType(m_str
->wx_str(), n
);
724 return DoGetArgumentType(m_cstr
->AsInternal(), n
);
726 wxFAIL_MSG( "unreachable code" );