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 // ============================================================================
31 // ============================================================================
33 // ----------------------------------------------------------------------------
35 // ----------------------------------------------------------------------------
37 const wxStringCharType
*wxArgNormalizerNative
<const wxString
&>::get() const
39 return m_value
.wx_str();
42 const wxStringCharType
*wxArgNormalizerNative
<const wxCStrData
&>::get() const
44 return m_value
.AsInternal();
47 #if wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
48 wxArgNormalizerWchar
<const wxString
&>::wxArgNormalizerWchar(const wxString
& s
)
49 : wxArgNormalizerWithBuffer
<wchar_t>(s
.wc_str())
53 wxArgNormalizerWchar
<const wxCStrData
&>::wxArgNormalizerWchar(const wxCStrData
& s
)
54 : wxArgNormalizerWithBuffer
<wchar_t>(s
.AsWCharBuf())
57 #endif // wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
59 // ----------------------------------------------------------------------------
60 // wxArgNormalizedString
61 // ----------------------------------------------------------------------------
63 wxString
wxArgNormalizedString::GetString() const
68 #if wxUSE_UTF8_LOCALE_ONLY
69 return wxString(wx_reinterpret_cast(const char*, m_ptr
));
71 #if wxUSE_UNICODE_UTF8
73 return wxString(wx_reinterpret_cast(const char*, m_ptr
));
76 return wxString(wx_reinterpret_cast(const wxChar
*, m_ptr
));
77 #endif // !wxUSE_UTF8_LOCALE_ONLY
80 wxArgNormalizedString::operator wxString() const
85 // ----------------------------------------------------------------------------
86 // wxFormatConverter: class doing the "%s" and "%c" normalization
87 // ----------------------------------------------------------------------------
90 There are four problems with wxPrintf() etc. format strings:
92 1) The printf vararg macros convert all forms of strings into
93 wxStringCharType* representation. This may make the format string
94 incorrect: for example, if %ls was used together with a wchar_t*
95 variadic argument, this would no longer work, because the templates
96 would change wchar_t* argument to wxStringCharType* and %ls would now
97 be incorrect in e.g. UTF-8 build. We need make sure only one specifier
100 2) To complicate matters further, the meaning of %s and %c is different
101 under Windows and on Unix. The Windows/MS convention is as follows:
105 format specifier results in
106 -----------------------------------
108 %ls, %S, %lS wchar_t*
112 format specifier results in
113 -----------------------------------
115 %s, %ls, %lS wchar_t*
117 (While on POSIX systems we have %C identical to %lc and %c always means
118 char (in any mode) while %lc always means wchar_t.)
120 In other words, we should _only_ use %s on Windows and %ls on Unix for
121 wxUSE_UNICODE_WCHAR build.
123 3) To make things even worse, we need two forms in UTF-8 build: one for
124 passing strings to ANSI functions under UTF-8 locales (this one should
125 use %s) and one for widechar functions used under non-UTF-8 locales
126 (this one should use %ls).
128 And, of course, the same should be done for %c as well.
130 4) Finally, in UTF-8 build when calling ANSI printf() function, we need to
131 translate %c to %s, because not every Unicode character can be
132 represented by a char.
135 wxScanf() family of functions is simpler, because we don't normalize their
136 variadic arguments and we only have to handle 2) above and only for widechar
141 class wxFormatConverterBase
146 wxFormatConverterBase()
153 wxCharTypeBuffer
<CharType
> Convert(const CharType
*format
)
155 // this is reset to NULL if we modify the format string
160 if ( CopyFmtChar(*format
++) == _T('%') )
163 while ( IsFlagChar(*format
) )
164 CopyFmtChar(*format
++);
166 // and possible width
167 if ( *format
== _T('*') )
168 CopyFmtChar(*format
++);
173 if ( *format
== _T('.') )
175 CopyFmtChar(*format
++);
176 if ( *format
== _T('*') )
177 CopyFmtChar(*format
++);
182 // next we can have a size modifier
193 // "ll" has a different meaning!
194 if ( format
[1] != 'l' )
206 CharType outConv
= *format
;
207 SizeModifier outSize
= size
;
209 // and finally we should have the type
214 // all strings were converted into the same form by
215 // wxArgNormalizer<T>, this form depends on the context
216 // in which the value is used (scanf/printf/wprintf):
217 HandleString(*format
, size
, outConv
, outSize
);
222 HandleChar(*format
, size
, outConv
, outSize
);
226 // nothing special to do
230 if ( outConv
== *format
&& outSize
== size
) // no change
232 if ( size
!= Size_Default
)
233 CopyFmtChar(*(format
- 1));
234 CopyFmtChar(*format
);
236 else // something changed
241 InsertFmtChar(_T('l'));
245 InsertFmtChar(_T('h'));
252 InsertFmtChar(outConv
);
259 // notice that we only translated the string if m_fmtOrig == NULL (as
260 // set by CopyAllBefore()), otherwise we should simply use the original
264 return wxCharTypeBuffer
<CharType
>::CreateNonOwned(m_fmtOrig
);
268 // NULL-terminate converted format string:
274 virtual ~wxFormatConverterBase() {}
284 // called to handle %S or %s; 'conv' is conversion specifier ('S' or 's'
285 // respectively), 'size' is the preceding size modifier; the new values of
286 // conversion and size specifiers must be written to outConv and outSize
287 virtual void HandleString(CharType conv
, SizeModifier size
,
288 CharType
& outConv
, SizeModifier
& outSize
) = 0;
290 // ditto for %C or %c
291 virtual void HandleChar(CharType conv
, SizeModifier size
,
292 CharType
& outConv
, SizeModifier
& outSize
) = 0;
295 // copy another character to the translated format: this function does the
296 // copy if we are translating but doesn't do anything at all if we don't,
297 // so we don't create the translated format string at all unless we really
298 // need to (i.e. InsertFmtChar() is called)
299 CharType
CopyFmtChar(CharType ch
)
303 // we're translating, do copy
308 // simply increase the count which should be copied by
309 // CopyAllBefore() later if needed
316 // insert an extra character
317 void InsertFmtChar(CharType ch
)
321 // so far we haven't translated anything yet
330 wxASSERT_MSG( m_fmtOrig
&& m_fmt
.data() == NULL
, "logic error" );
332 // the modified format string is guaranteed to be no longer than
333 // 3/2 of the original (worst case: the entire format string consists
334 // of "%s" repeated and is expanded to "%ls" on Unix), so we can
335 // allocate the buffer now and not worry about running out of space if
336 // we over-allocate a bit:
337 size_t fmtLen
= wxStrlen(m_fmtOrig
);
338 // worst case is of even length, so there's no rounding error in *3/2:
339 m_fmt
.extend(fmtLen
* 3 / 2);
342 wxStrncpy(m_fmt
.data(), m_fmtOrig
, m_nCopied
);
343 m_fmtLast
= m_fmt
.data() + m_nCopied
;
345 // we won't need it any longer and resetting it also indicates that we
346 // modified the format
350 static bool IsFlagChar(CharType ch
)
352 return ch
== _T('-') || ch
== _T('+') ||
353 ch
== _T('0') || ch
== _T(' ') || ch
== _T('#');
356 void SkipDigits(const CharType
**ptpc
)
358 while ( **ptpc
>= _T('0') && **ptpc
<= _T('9') )
359 CopyFmtChar(*(*ptpc
)++);
362 // the translated format
363 wxCharTypeBuffer
<CharType
> m_fmt
;
366 // the original format
367 const CharType
*m_fmtOrig
;
369 // the number of characters already copied (i.e. already parsed, but left
378 // on Windows, we should use %s and %c regardless of the build:
379 class wxPrintfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
381 virtual void HandleString(CharType
WXUNUSED(conv
),
382 SizeModifier
WXUNUSED(size
),
383 CharType
& outConv
, SizeModifier
& outSize
)
386 outSize
= Size_Default
;
389 virtual void HandleChar(CharType
WXUNUSED(conv
),
390 SizeModifier
WXUNUSED(size
),
391 CharType
& outConv
, SizeModifier
& outSize
)
394 outSize
= Size_Default
;
398 #else // !__WINDOWS__
400 // on Unix, it's %s for ANSI functions and %ls for widechar:
402 #if !wxUSE_UTF8_LOCALE_ONLY
403 class wxPrintfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
405 virtual void HandleString(CharType
WXUNUSED(conv
),
406 SizeModifier
WXUNUSED(size
),
407 CharType
& outConv
, SizeModifier
& outSize
)
413 virtual void HandleChar(CharType
WXUNUSED(conv
),
414 SizeModifier
WXUNUSED(size
),
415 CharType
& outConv
, SizeModifier
& outSize
)
421 #endif // !wxUSE_UTF8_LOCALE_ONLY
423 #if wxUSE_UNICODE_UTF8
424 class wxPrintfFormatConverterUtf8
: public wxFormatConverterBase
<char>
426 virtual void HandleString(CharType
WXUNUSED(conv
),
427 SizeModifier
WXUNUSED(size
),
428 CharType
& outConv
, SizeModifier
& outSize
)
431 outSize
= Size_Default
;
434 virtual void HandleChar(CharType
WXUNUSED(conv
),
435 SizeModifier
WXUNUSED(size
),
436 CharType
& outConv
, SizeModifier
& outSize
)
438 // added complication: %c should be translated to %s in UTF-8 build
440 outSize
= Size_Default
;
443 #endif // wxUSE_UNICODE_UTF8
445 #endif // __WINDOWS__/!__WINDOWS__
447 #if !wxUSE_UNICODE // FIXME-UTF8: remove
448 class wxPrintfFormatConverterANSI
: public wxFormatConverterBase
<char>
450 virtual void HandleString(CharType
WXUNUSED(conv
),
451 SizeModifier
WXUNUSED(size
),
452 CharType
& outConv
, SizeModifier
& outSize
)
455 outSize
= Size_Default
;
458 virtual void HandleChar(CharType
WXUNUSED(conv
),
459 SizeModifier
WXUNUSED(size
),
460 CharType
& outConv
, SizeModifier
& outSize
)
463 outSize
= Size_Default
;
471 wxScanf() format translation is different, we need to translate %s to %ls
472 and %c to %lc on Unix (but not Windows and for widechar functions only!).
474 So to use native functions in order to get our semantics we must do the
475 following translations in Unicode mode:
477 wxWidgets specifier POSIX specifier
478 ----------------------------------------
484 class wxScanfFormatConverterWchar
: public wxFormatConverterBase
<wchar_t>
486 virtual void HandleString(CharType conv
, SizeModifier size
,
487 CharType
& outConv
, SizeModifier
& outSize
)
490 outSize
= GetOutSize(conv
== 'S', size
);
493 virtual void HandleChar(CharType conv
, SizeModifier size
,
494 CharType
& outConv
, SizeModifier
& outSize
)
497 outSize
= GetOutSize(conv
== 'C', size
);
500 SizeModifier
GetOutSize(bool convIsUpper
, SizeModifier size
)
502 // %S and %hS -> %s and %lS -> %ls
505 if ( size
== Size_Long
)
512 if ( size
== Size_Default
)
520 const wxWCharBuffer
wxScanfConvertFormatW(const wchar_t *format
)
522 return wxScanfFormatConverterWchar().Convert(format
);
524 #endif // !__WINDOWS__
527 // ----------------------------------------------------------------------------
529 // ----------------------------------------------------------------------------
531 #if !wxUSE_UNICODE_WCHAR
532 const char* wxFormatString::InputAsChar()
535 return m_char
.data();
537 // in ANSI build, wx_str() returns char*, in UTF-8 build, this function
538 // is only called under UTF-8 locales, so we should return UTF-8 string,
539 // which is, again, what wx_str() returns:
541 return m_str
->wx_str();
545 return m_cstr
->AsInternal();
547 // the last case is that wide string was passed in: in that case, we need
551 m_char
= wxConvLibc
.cWC2MB(m_wchar
.data());
553 return m_char
.data();
556 const char* wxFormatString::AsChar()
558 if ( !m_convertedChar
)
559 #if !wxUSE_UNICODE // FIXME-UTF8: remove this
560 m_convertedChar
= wxPrintfFormatConverterANSI().Convert(InputAsChar());
562 m_convertedChar
= wxPrintfFormatConverterUtf8().Convert(InputAsChar());
565 return m_convertedChar
.data();
567 #endif // !wxUSE_UNICODE_WCHAR
569 #if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
570 const wchar_t* wxFormatString::InputAsWChar()
573 return m_wchar
.data();
575 #if wxUSE_UNICODE_WCHAR
577 return m_str
->wc_str();
579 return m_cstr
->AsInternal();
580 #else // wxUSE_UNICODE_UTF8
583 m_wchar
= m_str
->wc_str();
584 return m_wchar
.data();
588 m_wchar
= m_cstr
->AsWCharBuf();
589 return m_wchar
.data();
591 #endif // wxUSE_UNICODE_WCHAR/UTF8
593 // the last case is that narrow string was passed in: in that case, we need
597 m_wchar
= wxConvLibc
.cMB2WC(m_char
.data());
599 return m_wchar
.data();
602 const wchar_t* wxFormatString::AsWChar()
604 if ( !m_convertedWChar
)
605 m_convertedWChar
= wxPrintfFormatConverterWchar().Convert(InputAsWChar());
607 return m_convertedWChar
.data();
609 #endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY