]> git.saurik.com Git - wxWidgets.git/blame - src/common/strvararg.cpp
wxMessageBox off the main thread lost result code.
[wxWidgets.git] / src / common / strvararg.cpp
CommitLineData
c9f78968
VS
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/common/strvararg.cpp
3// Purpose: macros for implementing type-safe vararg passing of strings
4// Author: Vaclav Slavik
5// Created: 2007-02-19
c9f78968
VS
6// Copyright: (c) 2007 REA Elektronik GmbH
7// Licence: wxWindows licence
8///////////////////////////////////////////////////////////////////////////////
9
10// ============================================================================
11// declarations
12// ============================================================================
13
14// ----------------------------------------------------------------------------
15// headers
16// ----------------------------------------------------------------------------
17
18// for compilers that support precompilation, includes "wx.h".
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
22 #pragma hdrstop
23#endif
24
25#include "wx/strvararg.h"
c9f78968 26#include "wx/string.h"
f6e38901 27#include "wx/crt.h"
47346406 28#include "wx/private/wxprintf.h"
c9f78968
VS
29
30// ============================================================================
31// implementation
32// ============================================================================
33
1528e0b8
VS
34// ----------------------------------------------------------------------------
35// wxArgNormalizer<>
36// ----------------------------------------------------------------------------
c9f78968 37
2523e9b7 38const wxStringCharType *wxArgNormalizerNative<const wxString&>::get() const
c9f78968 39{
8f93a29f 40 return m_value.wx_str();
c9f78968 41}
c9f78968 42
2523e9b7 43const wxStringCharType *wxArgNormalizerNative<const wxCStrData&>::get() const
c9f78968 44{
2523e9b7 45 return m_value.AsInternal();
c9f78968
VS
46}
47
111d9948 48#if wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
47346406
VS
49wxArgNormalizerWchar<const wxString&>::wxArgNormalizerWchar(
50 const wxString& s,
51 const wxFormatString *fmt, unsigned index)
52 : wxArgNormalizerWithBuffer<wchar_t>(s.wc_str(), fmt, index)
c9f78968 53{
c9f78968
VS
54}
55
47346406
VS
56wxArgNormalizerWchar<const wxCStrData&>::wxArgNormalizerWchar(
57 const wxCStrData& s,
58 const wxFormatString *fmt, unsigned index)
59 : wxArgNormalizerWithBuffer<wchar_t>(s.AsWCharBuf(), fmt, index)
c9f78968 60{
81727065 61}
111d9948 62#endif // wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
81727065 63
1528e0b8
VS
64// ----------------------------------------------------------------------------
65// wxArgNormalizedString
66// ----------------------------------------------------------------------------
67
2523e9b7 68wxString wxArgNormalizedString::GetString() const
359bd4d1 69{
2523e9b7
VS
70 if ( !IsValid() )
71 return wxEmptyString;
72
73#if wxUSE_UTF8_LOCALE_ONLY
5c33522f 74 return wxString(reinterpret_cast<const char*>(m_ptr));
2523e9b7
VS
75#else
76 #if wxUSE_UNICODE_UTF8
77 if ( wxLocaleIsUtf8 )
5c33522f 78 return wxString(reinterpret_cast<const char*>(m_ptr));
2523e9b7
VS
79 else
80 #endif
5c33522f 81 return wxString(reinterpret_cast<const wxChar*>(m_ptr));
2523e9b7 82#endif // !wxUSE_UTF8_LOCALE_ONLY
359bd4d1
VS
83}
84
2523e9b7 85wxArgNormalizedString::operator wxString() const
359bd4d1 86{
2523e9b7 87 return GetString();
359bd4d1 88}
1528e0b8 89
50e27899
VS
90// ----------------------------------------------------------------------------
91// wxFormatConverter: class doing the "%s" and "%c" normalization
92// ----------------------------------------------------------------------------
93
94/*
95 There are four problems with wxPrintf() etc. format strings:
96
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
103 form is used.
104
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:
107
108 In ANSI mode:
109
110 format specifier results in
111 -----------------------------------
112 %s, %hs, %hS char*
113 %ls, %S, %lS wchar_t*
114
115 In Unicode mode:
116
117 format specifier results in
118 -----------------------------------
119 %hs, %S, %hS char*
120 %s, %ls, %lS wchar_t*
121
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.)
124
125 In other words, we should _only_ use %s on Windows and %ls on Unix for
126 wxUSE_UNICODE_WCHAR build.
127
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).
132
133 And, of course, the same should be done for %c as well.
134
50e27899
VS
135
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
138 versions.
139*/
140
141template<typename T>
142class wxFormatConverterBase
143{
144public:
145 typedef T CharType;
146
147 wxFormatConverterBase()
148 {
149 m_fmtOrig = NULL;
150 m_fmtLast = NULL;
151 m_nCopied = 0;
152 }
153
a6bb7a28 154 wxScopedCharTypeBuffer<CharType> Convert(const CharType *format)
50e27899
VS
155 {
156 // this is reset to NULL if we modify the format string
157 m_fmtOrig = format;
158
159 while ( *format )
160 {
9a83f860 161 if ( CopyFmtChar(*format++) == wxT('%') )
50e27899 162 {
75717cb1
VZ
163#if wxUSE_PRINTF_POS_PARAMS
164 if ( *format >= '0' && *format <= '9' )
165 {
166 SkipDigits(&format);
167 if ( *format == '$' )
168 {
169 // It was a positional argument specification.
170 CopyFmtChar(*format++);
171 }
172 //else: it was a width specification, nothing else to do.
173 }
174#endif // wxUSE_PRINTF_POS_PARAMS
175
50e27899
VS
176 // skip any flags
177 while ( IsFlagChar(*format) )
178 CopyFmtChar(*format++);
179
180 // and possible width
9a83f860 181 if ( *format == wxT('*') )
50e27899
VS
182 CopyFmtChar(*format++);
183 else
184 SkipDigits(&format);
185
186 // precision?
9a83f860 187 if ( *format == wxT('.') )
50e27899
VS
188 {
189 CopyFmtChar(*format++);
9a83f860 190 if ( *format == wxT('*') )
50e27899
VS
191 CopyFmtChar(*format++);
192 else
193 SkipDigits(&format);
194 }
195
196 // next we can have a size modifier
197 SizeModifier size;
198
199 switch ( *format )
200 {
201 case 'h':
202 size = Size_Short;
203 format++;
204 break;
205
206 case 'l':
207 // "ll" has a different meaning!
208 if ( format[1] != 'l' )
209 {
210 size = Size_Long;
211 format++;
212 break;
213 }
214 //else: fall through
215
216 default:
217 size = Size_Default;
218 }
219
220 CharType outConv = *format;
221 SizeModifier outSize = size;
222
223 // and finally we should have the type
224 switch ( *format )
225 {
9a83f860
VZ
226 case wxT('S'):
227 case wxT('s'):
50e27899
VS
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);
232 break;
233
9a83f860
VZ
234 case wxT('C'):
235 case wxT('c'):
50e27899
VS
236 HandleChar(*format, size, outConv, outSize);
237 break;
238
239 default:
240 // nothing special to do
241 break;
242 }
243
244 if ( outConv == *format && outSize == size ) // no change
245 {
246 if ( size != Size_Default )
247 CopyFmtChar(*(format - 1));
248 CopyFmtChar(*format);
249 }
250 else // something changed
251 {
252 switch ( outSize )
253 {
254 case Size_Long:
9a83f860 255 InsertFmtChar(wxT('l'));
50e27899
VS
256 break;
257
258 case Size_Short:
9a83f860 259 InsertFmtChar(wxT('h'));
50e27899
VS
260 break;
261
262 case Size_Default:
263 // nothing to do
264 break;
265 }
266 InsertFmtChar(outConv);
267 }
268
269 format++;
270 }
271 }
272
273 // notice that we only translated the string if m_fmtOrig == NULL (as
274 // set by CopyAllBefore()), otherwise we should simply use the original
275 // format
276 if ( m_fmtOrig )
277 {
a6bb7a28 278 return wxScopedCharTypeBuffer<CharType>::CreateNonOwned(m_fmtOrig);
50e27899
VS
279 }
280 else
281 {
a6bb7a28
VS
282 // shrink converted format string to actual size (instead of
283 // over-sized allocation from CopyAllBefore()) and NUL-terminate
284 // it:
285 m_fmt.shrink(m_fmtLast - m_fmt.data());
50e27899
VS
286 return m_fmt;
287 }
288 }
289
290 virtual ~wxFormatConverterBase() {}
291
292protected:
293 enum SizeModifier
294 {
295 Size_Default,
296 Size_Short,
297 Size_Long
298 };
299
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;
305
306 // ditto for %C or %c
307 virtual void HandleChar(CharType conv, SizeModifier size,
308 CharType& outConv, SizeModifier& outSize) = 0;
309
310private:
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)
316 {
317 if ( !m_fmtOrig )
318 {
319 // we're translating, do copy
320 *(m_fmtLast++) = ch;
321 }
322 else
323 {
324 // simply increase the count which should be copied by
325 // CopyAllBefore() later if needed
326 m_nCopied++;
327 }
328
329 return ch;
330 }
331
332 // insert an extra character
333 void InsertFmtChar(CharType ch)
334 {
335 if ( m_fmtOrig )
336 {
337 // so far we haven't translated anything yet
338 CopyAllBefore();
339 }
340
341 *(m_fmtLast++) = ch;
342 }
343
344 void CopyAllBefore()
345 {
346 wxASSERT_MSG( m_fmtOrig && m_fmt.data() == NULL, "logic error" );
347
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);
356
357 if ( m_nCopied > 0 )
358 wxStrncpy(m_fmt.data(), m_fmtOrig, m_nCopied);
359 m_fmtLast = m_fmt.data() + m_nCopied;
360
361 // we won't need it any longer and resetting it also indicates that we
362 // modified the format
363 m_fmtOrig = NULL;
364 }
365
366 static bool IsFlagChar(CharType ch)
367 {
9a83f860
VZ
368 return ch == wxT('-') || ch == wxT('+') ||
369 ch == wxT('0') || ch == wxT(' ') || ch == wxT('#');
50e27899
VS
370 }
371
372 void SkipDigits(const CharType **ptpc)
373 {
9a83f860 374 while ( **ptpc >= wxT('0') && **ptpc <= wxT('9') )
50e27899
VS
375 CopyFmtChar(*(*ptpc)++);
376 }
377
378 // the translated format
379 wxCharTypeBuffer<CharType> m_fmt;
380 CharType *m_fmtLast;
381
382 // the original format
383 const CharType *m_fmtOrig;
384
385 // the number of characters already copied (i.e. already parsed, but left
386 // unmodified)
387 size_t m_nCopied;
388};
389
715e4f7e 390#if defined(__WINDOWS__) && !defined(__CYGWIN__)
50e27899
VS
391
392// on Windows, we should use %s and %c regardless of the build:
393class wxPrintfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
394{
395 virtual void HandleString(CharType WXUNUSED(conv),
396 SizeModifier WXUNUSED(size),
397 CharType& outConv, SizeModifier& outSize)
398 {
399 outConv = 's';
400 outSize = Size_Default;
401 }
402
403 virtual void HandleChar(CharType WXUNUSED(conv),
404 SizeModifier WXUNUSED(size),
405 CharType& outConv, SizeModifier& outSize)
406 {
407 outConv = 'c';
408 outSize = Size_Default;
409 }
410};
411
412#else // !__WINDOWS__
413
414// on Unix, it's %s for ANSI functions and %ls for widechar:
415
416#if !wxUSE_UTF8_LOCALE_ONLY
417class wxPrintfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
418{
419 virtual void HandleString(CharType WXUNUSED(conv),
420 SizeModifier WXUNUSED(size),
421 CharType& outConv, SizeModifier& outSize)
422 {
423 outConv = 's';
424 outSize = Size_Long;
425 }
426
427 virtual void HandleChar(CharType WXUNUSED(conv),
428 SizeModifier WXUNUSED(size),
429 CharType& outConv, SizeModifier& outSize)
430 {
431 outConv = 'c';
432 outSize = Size_Long;
433 }
434};
435#endif // !wxUSE_UTF8_LOCALE_ONLY
436
b2b6da49
VZ
437#endif // __WINDOWS__/!__WINDOWS__
438
50e27899
VS
439#if wxUSE_UNICODE_UTF8
440class wxPrintfFormatConverterUtf8 : public wxFormatConverterBase<char>
441{
442 virtual void HandleString(CharType WXUNUSED(conv),
443 SizeModifier WXUNUSED(size),
444 CharType& outConv, SizeModifier& outSize)
445 {
446 outConv = 's';
447 outSize = Size_Default;
448 }
449
450 virtual void HandleChar(CharType WXUNUSED(conv),
451 SizeModifier WXUNUSED(size),
452 CharType& outConv, SizeModifier& outSize)
453 {
47346406
VS
454 // chars are represented using wchar_t in both builds, so this is
455 // the same as above
456 outConv = 'c';
457 outSize = Size_Long;
50e27899
VS
458 }
459};
460#endif // wxUSE_UNICODE_UTF8
461
50e27899
VS
462#if !wxUSE_UNICODE // FIXME-UTF8: remove
463class wxPrintfFormatConverterANSI : public wxFormatConverterBase<char>
464{
465 virtual void HandleString(CharType WXUNUSED(conv),
466 SizeModifier WXUNUSED(size),
467 CharType& outConv, SizeModifier& outSize)
468 {
469 outConv = 's';
470 outSize = Size_Default;
471 }
472
473 virtual void HandleChar(CharType WXUNUSED(conv),
474 SizeModifier WXUNUSED(size),
475 CharType& outConv, SizeModifier& outSize)
476 {
477 outConv = 'c';
478 outSize = Size_Default;
479 }
480};
481#endif // ANSI
482
483#ifndef __WINDOWS__
484/*
485
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!).
488
489 So to use native functions in order to get our semantics we must do the
490 following translations in Unicode mode:
491
492 wxWidgets specifier POSIX specifier
493 ----------------------------------------
494
495 %hc, %C, %hC %c
496 %c %lc
497
498 */
499class wxScanfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
500{
501 virtual void HandleString(CharType conv, SizeModifier size,
502 CharType& outConv, SizeModifier& outSize)
503 {
504 outConv = 's';
505 outSize = GetOutSize(conv == 'S', size);
506 }
507
508 virtual void HandleChar(CharType conv, SizeModifier size,
509 CharType& outConv, SizeModifier& outSize)
510 {
511 outConv = 'c';
512 outSize = GetOutSize(conv == 'C', size);
513 }
514
515 SizeModifier GetOutSize(bool convIsUpper, SizeModifier size)
516 {
517 // %S and %hS -> %s and %lS -> %ls
518 if ( convIsUpper )
519 {
520 if ( size == Size_Long )
521 return Size_Long;
522 else
523 return Size_Default;
524 }
525 else // %s or %c
526 {
527 if ( size == Size_Default )
528 return Size_Long;
529 else
530 return size;
531 }
532 }
533};
534
de4983f3 535const wxScopedWCharBuffer wxScanfConvertFormatW(const wchar_t *format)
50e27899
VS
536{
537 return wxScanfFormatConverterWchar().Convert(format);
538}
539#endif // !__WINDOWS__
540
541
1528e0b8
VS
542// ----------------------------------------------------------------------------
543// wxFormatString
544// ----------------------------------------------------------------------------
545
546#if !wxUSE_UNICODE_WCHAR
50e27899 547const char* wxFormatString::InputAsChar()
1528e0b8
VS
548{
549 if ( m_char )
550 return m_char.data();
551
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:
555 if ( m_str )
556 return m_str->wx_str();
557
558 // ditto wxCStrData:
559 if ( m_cstr )
560 return m_cstr->AsInternal();
561
562 // the last case is that wide string was passed in: in that case, we need
563 // to convert it:
564 wxASSERT( m_wchar );
565
566 m_char = wxConvLibc.cWC2MB(m_wchar.data());
567
568 return m_char.data();
569}
50e27899
VS
570
571const char* wxFormatString::AsChar()
572{
573 if ( !m_convertedChar )
574#if !wxUSE_UNICODE // FIXME-UTF8: remove this
575 m_convertedChar = wxPrintfFormatConverterANSI().Convert(InputAsChar());
576#else
577 m_convertedChar = wxPrintfFormatConverterUtf8().Convert(InputAsChar());
578#endif
579
580 return m_convertedChar.data();
581}
1528e0b8
VS
582#endif // !wxUSE_UNICODE_WCHAR
583
584#if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
50e27899 585const wchar_t* wxFormatString::InputAsWChar()
1528e0b8
VS
586{
587 if ( m_wchar )
588 return m_wchar.data();
589
590#if wxUSE_UNICODE_WCHAR
591 if ( m_str )
592 return m_str->wc_str();
593 if ( m_cstr )
594 return m_cstr->AsInternal();
595#else // wxUSE_UNICODE_UTF8
596 if ( m_str )
597 {
598 m_wchar = m_str->wc_str();
599 return m_wchar.data();
600 }
601 if ( m_cstr )
602 {
603 m_wchar = m_cstr->AsWCharBuf();
604 return m_wchar.data();
605 }
606#endif // wxUSE_UNICODE_WCHAR/UTF8
607
608 // the last case is that narrow string was passed in: in that case, we need
609 // to convert it:
610 wxASSERT( m_char );
611
612 m_wchar = wxConvLibc.cMB2WC(m_char.data());
613
614 return m_wchar.data();
615}
50e27899
VS
616
617const wchar_t* wxFormatString::AsWChar()
618{
619 if ( !m_convertedWChar )
620 m_convertedWChar = wxPrintfFormatConverterWchar().Convert(InputAsWChar());
621
622 return m_convertedWChar.data();
623}
1528e0b8 624#endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
47346406 625
e68a8744
VS
626wxString wxFormatString::InputAsString() const
627{
628 if ( m_str )
629 return *m_str;
630 if ( m_cstr )
631 return m_cstr->AsString();
632 if ( m_wchar )
633 return wxString(m_wchar);
634 if ( m_char )
635 return wxString(m_char);
636
637 wxFAIL_MSG( "invalid wxFormatString - not initialized?" );
638 return wxString();
639}
640
47346406
VS
641// ----------------------------------------------------------------------------
642// wxFormatString::GetArgumentType()
643// ----------------------------------------------------------------------------
644
645namespace
646{
647
648template<typename CharType>
649wxFormatString::ArgumentType DoGetArgumentType(const CharType *format,
650 unsigned n)
651{
0f895b0c 652 wxCHECK_MSG( format, wxFormatString::Arg_Unknown,
47346406
VS
653 "empty format string not allowed here" );
654
655 wxPrintfConvSpecParser<CharType> parser(format);
656
bb2ce935
VS
657 wxCHECK_MSG( n <= parser.nargs, wxFormatString::Arg_Unknown,
658 "more arguments than format string specifiers?" );
659
0f895b0c 660 wxCHECK_MSG( parser.pspec[n-1] != NULL, wxFormatString::Arg_Unknown,
47346406
VS
661 "requested argument not found - invalid format string?" );
662
663 switch ( parser.pspec[n-1]->m_type )
664 {
665 case wxPAT_CHAR:
666 case wxPAT_WCHAR:
667 return wxFormatString::Arg_Char;
668
0f895b0c
VS
669 case wxPAT_PCHAR:
670 case wxPAT_PWCHAR:
671 return wxFormatString::Arg_String;
672
673 case wxPAT_INT:
674 return wxFormatString::Arg_Int;
675 case wxPAT_LONGINT:
676 return wxFormatString::Arg_LongInt;
677#ifdef wxLongLong_t
678 case wxPAT_LONGLONGINT:
679 return wxFormatString::Arg_LongLongInt;
680#endif
681 case wxPAT_SIZET:
682 return wxFormatString::Arg_Size_t;
683
684 case wxPAT_DOUBLE:
685 return wxFormatString::Arg_Double;
686 case wxPAT_LONGDOUBLE:
687 return wxFormatString::Arg_LongDouble;
688
689 case wxPAT_POINTER:
690 return wxFormatString::Arg_Pointer;
691
692 case wxPAT_NINT:
693 return wxFormatString::Arg_IntPtr;
694 case wxPAT_NSHORTINT:
695 return wxFormatString::Arg_ShortIntPtr;
696 case wxPAT_NLONGINT:
697 return wxFormatString::Arg_LongIntPtr;
698
699 case wxPAT_STAR:
700 // "*" requires argument of type int
701 return wxFormatString::Arg_Int;
702
703 case wxPAT_INVALID:
704 // (handled after the switch statement)
705 break;
47346406 706 }
0f895b0c
VS
707
708 // silence warning
709 wxFAIL_MSG( "unexpected argument type" );
710 return wxFormatString::Arg_Unknown;
47346406
VS
711}
712
713} // anonymous namespace
714
715wxFormatString::ArgumentType wxFormatString::GetArgumentType(unsigned n) const
716{
717 if ( m_char )
718 return DoGetArgumentType(m_char.data(), n);
719 else if ( m_wchar )
720 return DoGetArgumentType(m_wchar.data(), n);
721 else if ( m_str )
722 return DoGetArgumentType(m_str->wx_str(), n);
723 else if ( m_cstr )
724 return DoGetArgumentType(m_cstr->AsInternal(), n);
725
726 wxFAIL_MSG( "unreachable code" );
0f895b0c 727 return Arg_Unknown;
47346406 728}