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
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"
26 #include "wx/string.h"
27 #include "wx/crt.h"
28 #include "wx/private/wxprintf.h"
29 
30 // ============================================================================
31 // implementation
32 // ============================================================================
33 
34 // ----------------------------------------------------------------------------
35 // wxArgNormalizer<>
36 // ----------------------------------------------------------------------------
37 
get() const38 const wxStringCharType *wxArgNormalizerNative<const wxString&>::get() const
39 {
40     return m_value.wx_str();
41 }
42 
get() const43 const wxStringCharType *wxArgNormalizerNative<const wxCStrData&>::get() const
44 {
45     return m_value.AsInternal();
46 }
47 
48 #if wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
wxArgNormalizerWchar(const wxString & s,const wxFormatString * fmt,unsigned index)49 wxArgNormalizerWchar<const wxString&>::wxArgNormalizerWchar(
50                             const wxString& s,
51                             const wxFormatString *fmt, unsigned index)
52     : wxArgNormalizerWithBuffer<wchar_t>(s.wc_str(), fmt, index)
53 {
54 }
55 
wxArgNormalizerWchar(const wxCStrData & s,const wxFormatString * fmt,unsigned index)56 wxArgNormalizerWchar<const wxCStrData&>::wxArgNormalizerWchar(
57                             const wxCStrData& s,
58                             const wxFormatString *fmt, unsigned index)
59     : wxArgNormalizerWithBuffer<wchar_t>(s.AsWCharBuf(), fmt, index)
60 {
61 }
62 #endif // wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
63 
64 // ----------------------------------------------------------------------------
65 // wxArgNormalizedString
66 // ----------------------------------------------------------------------------
67 
GetString() const68 wxString wxArgNormalizedString::GetString() const
69 {
70     if ( !IsValid() )
71         return wxEmptyString;
72 
73 #if wxUSE_UTF8_LOCALE_ONLY
74     return wxString(reinterpret_cast<const char*>(m_ptr));
75 #else
76     #if wxUSE_UNICODE_UTF8
77         if ( wxLocaleIsUtf8 )
78             return wxString(reinterpret_cast<const char*>(m_ptr));
79         else
80     #endif
81         return wxString(reinterpret_cast<const wxChar*>(m_ptr));
82 #endif // !wxUSE_UTF8_LOCALE_ONLY
83 }
84 
operator wxString() const85 wxArgNormalizedString::operator wxString() const
86 {
87     return GetString();
88 }
89 
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 
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 
141 template<typename T>
142 class wxFormatConverterBase
143 {
144 public:
145     typedef T CharType;
146 
wxFormatConverterBase()147     wxFormatConverterBase()
148     {
149         m_fmtOrig = NULL;
150         m_fmtLast = NULL;
151         m_nCopied = 0;
152     }
153 
Convert(const CharType * format)154     wxScopedCharTypeBuffer<CharType> Convert(const CharType *format)
155     {
156         // this is reset to NULL if we modify the format string
157         m_fmtOrig = format;
158 
159         while ( *format )
160         {
161             if ( CopyFmtChar(*format++) == wxT('%') )
162             {
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 
176                 // skip any flags
177                 while ( IsFlagChar(*format) )
178                     CopyFmtChar(*format++);
179 
180                 // and possible width
181                 if ( *format == wxT('*') )
182                     CopyFmtChar(*format++);
183                 else
184                     SkipDigits(&format);
185 
186                 // precision?
187                 if ( *format == wxT('.') )
188                 {
189                     CopyFmtChar(*format++);
190                     if ( *format == wxT('*') )
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                 {
226                     case wxT('S'):
227                     case wxT('s'):
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 
234                     case wxT('C'):
235                     case wxT('c'):
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:
255                             InsertFmtChar(wxT('l'));
256                             break;
257 
258                         case Size_Short:
259                             InsertFmtChar(wxT('h'));
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         {
278             return wxScopedCharTypeBuffer<CharType>::CreateNonOwned(m_fmtOrig);
279         }
280         else
281         {
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());
286             return m_fmt;
287         }
288     }
289 
~wxFormatConverterBase()290     virtual ~wxFormatConverterBase() {}
291 
292 protected:
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 
310 private:
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)
CopyFmtChar(CharType ch)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
InsertFmtChar(CharType ch)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 
CopyAllBefore()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 
IsFlagChar(CharType ch)366     static bool IsFlagChar(CharType ch)
367     {
368         return ch == wxT('-') || ch == wxT('+') ||
369                ch == wxT('0') || ch == wxT(' ') || ch == wxT('#');
370     }
371 
SkipDigits(const CharType ** ptpc)372     void SkipDigits(const CharType **ptpc)
373     {
374         while ( **ptpc >= wxT('0') && **ptpc <= wxT('9') )
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 
390 #if defined(__WINDOWS__) && !defined(__CYGWIN__)
391 
392 // on Windows, we should use %s and %c regardless of the build:
393 class wxPrintfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
394 {
HandleString(CharType WXUNUSED (conv),SizeModifier WXUNUSED (size),CharType & outConv,SizeModifier & outSize)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 
HandleChar(CharType WXUNUSED (conv),SizeModifier WXUNUSED (size),CharType & outConv,SizeModifier & outSize)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
417 class wxPrintfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
418 {
HandleString(CharType WXUNUSED (conv),SizeModifier WXUNUSED (size),CharType & outConv,SizeModifier & outSize)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 
HandleChar(CharType WXUNUSED (conv),SizeModifier WXUNUSED (size),CharType & outConv,SizeModifier & outSize)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 
437 #endif // __WINDOWS__/!__WINDOWS__
438 
439 #if wxUSE_UNICODE_UTF8
440 class wxPrintfFormatConverterUtf8 : public wxFormatConverterBase<char>
441 {
HandleString(CharType WXUNUSED (conv),SizeModifier WXUNUSED (size),CharType & outConv,SizeModifier & outSize)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 
HandleChar(CharType WXUNUSED (conv),SizeModifier WXUNUSED (size),CharType & outConv,SizeModifier & outSize)450     virtual void HandleChar(CharType WXUNUSED(conv),
451                             SizeModifier WXUNUSED(size),
452                             CharType& outConv, SizeModifier& outSize)
453     {
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;
458     }
459 };
460 #endif // wxUSE_UNICODE_UTF8
461 
462 #if !wxUSE_UNICODE // FIXME-UTF8: remove
463 class wxPrintfFormatConverterANSI : public wxFormatConverterBase<char>
464 {
HandleString(CharType WXUNUSED (conv),SizeModifier WXUNUSED (size),CharType & outConv,SizeModifier & outSize)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 
HandleChar(CharType WXUNUSED (conv),SizeModifier WXUNUSED (size),CharType & outConv,SizeModifier & outSize)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  */
499 class wxScanfFormatConverterWchar : public wxFormatConverterBase<wchar_t>
500 {
HandleString(CharType conv,SizeModifier size,CharType & outConv,SizeModifier & outSize)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 
HandleChar(CharType conv,SizeModifier size,CharType & outConv,SizeModifier & outSize)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 
GetOutSize(bool convIsUpper,SizeModifier size)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 
wxScanfConvertFormatW(const wchar_t * format)535 const wxScopedWCharBuffer wxScanfConvertFormatW(const wchar_t *format)
536 {
537     return wxScanfFormatConverterWchar().Convert(format);
538 }
539 #endif // !__WINDOWS__
540 
541 
542 // ----------------------------------------------------------------------------
543 // wxFormatString
544 // ----------------------------------------------------------------------------
545 
546 #if !wxUSE_UNICODE_WCHAR
InputAsChar()547 const char* wxFormatString::InputAsChar()
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 }
570 
AsChar()571 const 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 }
582 #endif // !wxUSE_UNICODE_WCHAR
583 
584 #if wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
InputAsWChar()585 const wchar_t* wxFormatString::InputAsWChar()
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 }
616 
AsWChar()617 const wchar_t* wxFormatString::AsWChar()
618 {
619     if ( !m_convertedWChar )
620         m_convertedWChar = wxPrintfFormatConverterWchar().Convert(InputAsWChar());
621 
622     return m_convertedWChar.data();
623 }
624 #endif // wxUSE_UNICODE && !wxUSE_UTF8_LOCALE_ONLY
625 
InputAsString() const626 wxString 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 
641 // ----------------------------------------------------------------------------
642 // wxFormatString::GetArgumentType()
643 // ----------------------------------------------------------------------------
644 
645 namespace
646 {
647 
648 template<typename CharType>
DoGetArgumentType(const CharType * format,unsigned n)649 wxFormatString::ArgumentType DoGetArgumentType(const CharType *format,
650                                                unsigned n)
651 {
652     wxCHECK_MSG( format, wxFormatString::Arg_Unknown,
653                  "empty format string not allowed here" );
654 
655     wxPrintfConvSpecParser<CharType> parser(format);
656 
657     if ( n > parser.nargs )
658     {
659         // The n-th argument doesn't appear in the format string and is unused.
660         // This can happen e.g. if a translation of the format string is used
661         // and the translation language tends to avoid numbers in singular forms.
662         // The translator would then typically replace "%d" with "One" (e.g. in
663         // Hebrew). Passing too many vararg arguments does not harm, so its
664         // better to be more permissive here and allow legitimate uses in favour
665         // of catching harmless errors.
666         return wxFormatString::Arg_Unused;
667     }
668 
669     wxCHECK_MSG( parser.pspec[n-1] != NULL, wxFormatString::Arg_Unknown,
670                  "requested argument not found - invalid format string?" );
671 
672     switch ( parser.pspec[n-1]->m_type )
673     {
674         case wxPAT_CHAR:
675         case wxPAT_WCHAR:
676             return wxFormatString::Arg_Char;
677 
678         case wxPAT_PCHAR:
679         case wxPAT_PWCHAR:
680             return wxFormatString::Arg_String;
681 
682         case wxPAT_INT:
683             return wxFormatString::Arg_Int;
684         case wxPAT_LONGINT:
685             return wxFormatString::Arg_LongInt;
686 #ifdef wxLongLong_t
687         case wxPAT_LONGLONGINT:
688             return wxFormatString::Arg_LongLongInt;
689 #endif
690         case wxPAT_SIZET:
691             return wxFormatString::Arg_Size_t;
692 
693         case wxPAT_DOUBLE:
694             return wxFormatString::Arg_Double;
695         case wxPAT_LONGDOUBLE:
696             return wxFormatString::Arg_LongDouble;
697 
698         case wxPAT_POINTER:
699             return wxFormatString::Arg_Pointer;
700 
701         case wxPAT_NINT:
702             return wxFormatString::Arg_IntPtr;
703         case wxPAT_NSHORTINT:
704             return wxFormatString::Arg_ShortIntPtr;
705         case wxPAT_NLONGINT:
706             return wxFormatString::Arg_LongIntPtr;
707 
708         case wxPAT_STAR:
709             // "*" requires argument of type int
710             return wxFormatString::Arg_Int;
711 
712         case wxPAT_INVALID:
713             // (handled after the switch statement)
714             break;
715     }
716 
717     // silence warning
718     wxFAIL_MSG( "unexpected argument type" );
719     return wxFormatString::Arg_Unknown;
720 }
721 
722 } // anonymous namespace
723 
GetArgumentType(unsigned n) const724 wxFormatString::ArgumentType wxFormatString::GetArgumentType(unsigned n) const
725 {
726     if ( m_char )
727         return DoGetArgumentType(m_char.data(), n);
728     else if ( m_wchar )
729         return DoGetArgumentType(m_wchar.data(), n);
730     else if ( m_str )
731         return DoGetArgumentType(m_str->wx_str(), n);
732     else if ( m_cstr )
733         return DoGetArgumentType(m_cstr->AsInternal(), n);
734 
735     wxFAIL_MSG( "unreachable code" );
736     return Arg_Unknown;
737 }
738