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"
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(const wxString
& s
)
50 : wxArgNormalizerWithBuffer
<wchar_t>(s
.wc_str())
54 wxArgNormalizerWchar
<const wxCStrData
&>::wxArgNormalizerWchar(const wxCStrData
& s
)
55 : wxArgNormalizerWithBuffer
<wchar_t>(s
.AsWCharBuf())
58 #endif // wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
60 // ----------------------------------------------------------------------------
61 // wxArgNormalizedString
62 // ----------------------------------------------------------------------------
64 wxString
wxArgNormalizedString::GetString() const
69 #if wxUSE_UTF8_LOCALE_ONLY
70 return wxString(wx_reinterpret_cast(const char*, m_ptr
));
72 #if wxUSE_UNICODE_UTF8
74 return wxString(wx_reinterpret_cast(const char*, m_ptr
));
77 return wxString(wx_reinterpret_cast(const wxChar
*, m_ptr
));
78 #endif // !wxUSE_UTF8_LOCALE_ONLY
81 wxArgNormalizedString::operator wxString() const
86 // ----------------------------------------------------------------------------
87 // wxFormatConverter: class doing the "%s" and "%c" normalization
88 // ----------------------------------------------------------------------------
91 There are four problems with wxPrintf() etc. format strings:
93 1) The printf vararg macros convert all forms of strings into
94 wxStringCharType* representation. This may make the format string
95 incorrect: for example, if %ls was used together with a wchar_t*
96 variadic argument, this would no longer work, because the templates
97 would change wchar_t* argument to wxStringCharType* and %ls would now
98 be incorrect in e.g. UTF-8 build. We need make sure only one specifier
101 2) To complicate matters further, the meaning of %s and %c is different
102 under Windows and on Unix. The Windows/MS convention is as follows:
106 format specifier results in
107 -----------------------------------
109 %ls, %S, %lS wchar_t*
113 format specifier results in
114 -----------------------------------
116 %s, %ls, %lS wchar_t*
118 (While on POSIX systems we have %C identical to %lc and %c always means
119 char (in any mode) while %lc always means wchar_t.)
121 In other words, we should _only_ use %s on Windows and %ls on Unix for
122 wxUSE_UNICODE_WCHAR build.
124 3) To make things even worse, we need two forms in UTF-8 build: one for
125 passing strings to ANSI functions under UTF-8 locales (this one should
126 use %s) and one for widechar functions used under non-UTF-8 locales
127 (this one should use %ls).
129 And, of course, the same should be done for %c as well.
131 4) Finally, in UTF-8 build when calling ANSI printf() function, we need to
132 translate %c to %s, because not every Unicode character can be
133 represented by a char.
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 wxCharTypeBuffer
<CharType
> Convert(const CharType
*format
)
156 // this is reset to NULL if we modify the format string
161 if ( CopyFmtChar(*format
++) == _T('%') )
164 while ( IsFlagChar(*format
) )
165 CopyFmtChar(*format
++);
167 // and possible width
168 if ( *format
== _T('*') )
169 CopyFmtChar(*format
++);
174 if ( *format
== _T('.') )
176 CopyFmtChar(*format
++);
177 if ( *format
== _T('*') )
178 CopyFmtChar(*format
++);
183 // next we can have a size modifier
194 // "ll" has a different meaning!
195 if ( format
[1] != 'l' )
207 CharType outConv
= *format
;
208 SizeModifier outSize
= size
;
210 // and finally we should have the type
215 // all strings were converted into the same form by
216 // wxArgNormalizer<T>, this form depends on the context
217 // in which the value is used (scanf/printf/wprintf):
218 HandleString(*format
, size
, outConv
, outSize
);
223 HandleChar(*format
, size
, outConv
, outSize
);
227 // nothing special to do
231 if ( outConv
== *format
&& outSize
== size
) // no change
233 if ( size
!= Size_Default
)
234 CopyFmtChar(*(format
- 1));
235 CopyFmtChar(*format
);
237 else // something changed
242 InsertFmtChar(_T('l'));
246 InsertFmtChar(_T('h'));
253 InsertFmtChar(outConv
);
260 // notice that we only translated the string if m_fmtOrig == NULL (as
261 // set by CopyAllBefore()), otherwise we should simply use the original
265 return wxCharTypeBuffer
<CharType
>::CreateNonOwned(m_fmtOrig
);
269 // NULL-terminate converted format string:
275 virtual ~wxFormatConverterBase() {}
285 // called to handle %S or %s; 'conv' is conversion specifier ('S' or 's'
286 // respectively), 'size' is the preceding size modifier; the new values of
287 // conversion and size specifiers must be written to outConv and outSize
288 virtual void HandleString(CharType conv
, SizeModifier size
,
289 CharType
& outConv
, SizeModifier
& outSize
) = 0;
291 // ditto for %C or %c
292 virtual void HandleChar(CharType conv
, SizeModifier size
,
293 CharType
& outConv
, SizeModifier
& outSize
) = 0;
296 // copy another character to the translated format: this function does the
297 // copy if we are translating but doesn't do anything at all if we don't,
298 // so we don't create the translated format string at all unless we really
299 // need to (i.e. InsertFmtChar() is called)
300 CharType
CopyFmtChar(CharType ch
)
304 // we're translating, do copy
309 // simply increase the count which should be copied by
310 // CopyAllBefore() later if needed
317 // insert an extra character
318 void InsertFmtChar(CharType ch
)
322 // so far we haven't translated anything yet
331 wxASSERT_MSG( m_fmtOrig
&& m_fmt
.data() == NULL
, "logic error" );
333 // the modified format string is guaranteed to be no longer than
334 // 3/2 of the original (worst case: the entire format string consists
335 // of "%s" repeated and is expanded to "%ls" on Unix), so we can
336 // allocate the buffer now and not worry about running out of space if
337 // we over-allocate a bit:
338 size_t fmtLen
= wxStrlen(m_fmtOrig
);
339 // worst case is of even length, so there's no rounding error in *3/2:
340 m_fmt
.extend(fmtLen
* 3 / 2);
343 wxStrncpy(m_fmt
.data(), m_fmtOrig
, m_nCopied
);
344 m_fmtLast
= m_fmt
.data() + m_nCopied
;
346 // we won't need it any longer and resetting it also indicates that we
347 // modified the format
351 static bool IsFlagChar(CharType ch
)
353 return ch
== _T('-') || ch
== _T('+') ||
354 ch
== _T('0') || ch
== _T(' ') || ch
== _T('#');
357 void SkipDigits(const CharType
**ptpc
)
359 while ( **ptpc
>= _T('0') && **ptpc
<= _T('9') )
360 CopyFmtChar(*(*ptpc
)++);
363 // the translated format
364 wxCharTypeBuffer
<CharType
> m_fmt
;
367 // the original format
368 const CharType
*m_fmtOrig
;
370 // the number of characters already copied (i.e. already parsed, but left
379 // on Windows, we should use %s and %c regardless of the build:
380 class wxPrintfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
382 virtual void HandleString(CharType
WXUNUSED(conv
),
383 SizeModifier
WXUNUSED(size
),
384 CharType
& outConv
, SizeModifier
& outSize
)
387 outSize
= Size_Default
;
390 virtual void HandleChar(CharType
WXUNUSED(conv
),
391 SizeModifier
WXUNUSED(size
),
392 CharType
& outConv
, SizeModifier
& outSize
)
395 outSize
= Size_Default
;
399 #else // !__WINDOWS__
401 // on Unix, it's %s for ANSI functions and %ls for widechar:
403 #if !wxUSE_UTF8_LOCALE_ONLY
404 class wxPrintfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
406 virtual void HandleString(CharType
WXUNUSED(conv
),
407 SizeModifier
WXUNUSED(size
),
408 CharType
& outConv
, SizeModifier
& outSize
)
414 virtual void HandleChar(CharType
WXUNUSED(conv
),
415 SizeModifier
WXUNUSED(size
),
416 CharType
& outConv
, SizeModifier
& outSize
)
422 #endif // !wxUSE_UTF8_LOCALE_ONLY
424 #if wxUSE_UNICODE_UTF8
425 class wxPrintfFormatConverterUtf8
: public wxFormatConverterBase
<char>
427 virtual void HandleString(CharType
WXUNUSED(conv
),
428 SizeModifier
WXUNUSED(size
),
429 CharType
& outConv
, SizeModifier
& outSize
)
432 outSize
= Size_Default
;
435 virtual void HandleChar(CharType
WXUNUSED(conv
),
436 SizeModifier
WXUNUSED(size
),
437 CharType
& outConv
, SizeModifier
& outSize
)
439 // added complication: %c should be translated to %s in UTF-8 build
441 outSize
= Size_Default
;
444 #endif // wxUSE_UNICODE_UTF8
446 #endif // __WINDOWS__/!__WINDOWS__
448 #if !wxUSE_UNICODE // FIXME-UTF8: remove
449 class wxPrintfFormatConverterANSI
: public wxFormatConverterBase
<char>
451 virtual void HandleString(CharType
WXUNUSED(conv
),
452 SizeModifier
WXUNUSED(size
),
453 CharType
& outConv
, SizeModifier
& outSize
)
456 outSize
= Size_Default
;
459 virtual void HandleChar(CharType
WXUNUSED(conv
),
460 SizeModifier
WXUNUSED(size
),
461 CharType
& outConv
, SizeModifier
& outSize
)
464 outSize
= Size_Default
;
472 wxScanf() format translation is different, we need to translate %s to %ls
473 and %c to %lc on Unix (but not Windows and for widechar functions only!).
475 So to use native functions in order to get our semantics we must do the
476 following translations in Unicode mode:
478 wxWidgets specifier POSIX specifier
479 ----------------------------------------
485 class wxScanfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
487 virtual void HandleString(CharType conv
, SizeModifier size
,
488 CharType
& outConv
, SizeModifier
& outSize
)
491 outSize
= GetOutSize(conv
== 'S', size
);
494 virtual void HandleChar(CharType conv
, SizeModifier size
,
495 CharType
& outConv
, SizeModifier
& outSize
)
498 outSize
= GetOutSize(conv
== 'C', size
);
501 SizeModifier
GetOutSize(bool convIsUpper
, SizeModifier size
)
503 // %S and %hS -> %s and %lS -> %ls
506 if ( size
== Size_Long
)
513 if ( size
== Size_Default
)
521 const wxWCharBuffer
wxScanfConvertFormatW(const wchar_t *format
)
523 return wxScanfFormatConverterWchar().Convert(format
);
525 #endif // !__WINDOWS__
528 // ----------------------------------------------------------------------------
530 // ----------------------------------------------------------------------------
532 #if !wxUSE_UNICODE_WCHAR
533 const char* wxFormatString::InputAsChar()
536 return m_char
.data();
538 // in ANSI build, wx_str() returns char*, in UTF-8 build, this function
539 // is only called under UTF-8 locales, so we should return UTF-8 string,
540 // which is, again, what wx_str() returns:
542 return m_str
->wx_str();
546 return m_cstr
->AsInternal();
548 // the last case is that wide string was passed in: in that case, we need
552 m_char
= wxConvLibc
.cWC2MB(m_wchar
.data());
554 return m_char
.data();
557 const char* wxFormatString::AsChar()
559 if ( !m_convertedChar
)
560 #if !wxUSE_UNICODE // FIXME-UTF8: remove this
561 m_convertedChar
= wxPrintfFormatConverterANSI().Convert(InputAsChar());
563 m_convertedChar
= wxPrintfFormatConverterUtf8().Convert(InputAsChar());
566 return m_convertedChar
.data();
568 #endif // !wxUSE_UNICODE_WCHAR
570 #if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
571 const wchar_t* wxFormatString::InputAsWChar()
574 return m_wchar
.data();
576 #if wxUSE_UNICODE_WCHAR
578 return m_str
->wc_str();
580 return m_cstr
->AsInternal();
581 #else // wxUSE_UNICODE_UTF8
584 m_wchar
= m_str
->wc_str();
585 return m_wchar
.data();
589 m_wchar
= m_cstr
->AsWCharBuf();
590 return m_wchar
.data();
592 #endif // wxUSE_UNICODE_WCHAR/UTF8
594 // the last case is that narrow string was passed in: in that case, we need
598 m_wchar
= wxConvLibc
.cMB2WC(m_char
.data());
600 return m_wchar
.data();
603 const wchar_t* wxFormatString::AsWChar()
605 if ( !m_convertedWChar
)
606 m_convertedWChar
= wxPrintfFormatConverterWchar().Convert(InputAsWChar());
608 return m_convertedWChar
.data();
610 #endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY