]> git.saurik.com Git - wxWidgets.git/blob - src/common/wxcrt.cpp
make our vsnprintf() implementation work for ANSI version too
[wxWidgets.git] / src / common / wxcrt.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/wxcrt.cpp
3 // Purpose: wxChar CRT wrappers implementation
4 // Author: Ove Kaven
5 // Modified by: Ron Lee, Francesco Montorsi
6 // Created: 09/04/99
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets copyright
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // headers, declarations, constants
14 // ===========================================================================
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #include "wx/crt.h"
24
25 #define _ISOC9X_SOURCE 1 // to get vsscanf()
26 #define _BSD_SOURCE 1 // to still get strdup()
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #ifndef __WXWINCE__
33 #include <time.h>
34 #include <locale.h>
35 #else
36 #include "wx/msw/wince/time.h"
37 #endif
38
39 #ifndef WX_PRECOMP
40 #include "wx/string.h"
41 #include "wx/hash.h"
42 #include "wx/utils.h" // for wxMin and wxMax
43 #include "wx/log.h"
44 #endif
45
46 #ifdef __WXWINCE__
47 // there is no errno.h under CE apparently
48 #define wxSET_ERRNO(value)
49 #else
50 #include <errno.h>
51
52 #define wxSET_ERRNO(value) errno = value
53 #endif
54
55 #if defined(__MWERKS__) && __MSL__ >= 0x6000
56 namespace std {}
57 using namespace std ;
58 #endif
59
60 #if wxUSE_WCHAR_T
61 size_t WXDLLEXPORT wxMB2WC(wchar_t *buf, const char *psz, size_t n)
62 {
63 // assume that we have mbsrtowcs() too if we have wcsrtombs()
64 #ifdef HAVE_WCSRTOMBS
65 mbstate_t mbstate;
66 memset(&mbstate, 0, sizeof(mbstate_t));
67 #endif
68
69 if (buf) {
70 if (!n || !*psz) {
71 if (n) *buf = wxT('\0');
72 return 0;
73 }
74 #ifdef HAVE_WCSRTOMBS
75 return mbsrtowcs(buf, &psz, n, &mbstate);
76 #else
77 return wxMbstowcs(buf, psz, n);
78 #endif
79 }
80
81 // note that we rely on common (and required by Unix98 but unfortunately not
82 // C99) extension which allows to call mbs(r)towcs() with NULL output pointer
83 // to just get the size of the needed buffer -- this is needed as otherwise
84 // we have no idea about how much space we need and if the CRT doesn't
85 // support it (the only currently known example being Metrowerks, see
86 // wx/crt.h) we don't use its mbstowcs() at all
87 #ifdef HAVE_WCSRTOMBS
88 return mbsrtowcs((wchar_t *) NULL, &psz, 0, &mbstate);
89 #else
90 return wxMbstowcs((wchar_t *) NULL, psz, 0);
91 #endif
92 }
93
94 size_t WXDLLEXPORT wxWC2MB(char *buf, const wchar_t *pwz, size_t n)
95 {
96 #ifdef HAVE_WCSRTOMBS
97 mbstate_t mbstate;
98 memset(&mbstate, 0, sizeof(mbstate_t));
99 #endif
100
101 if (buf) {
102 if (!n || !*pwz) {
103 // glibc2.1 chokes on null input
104 if (n) *buf = '\0';
105 return 0;
106 }
107 #ifdef HAVE_WCSRTOMBS
108 return wcsrtombs(buf, &pwz, n, &mbstate);
109 #else
110 return wxWcstombs(buf, pwz, n);
111 #endif
112 }
113
114 #ifdef HAVE_WCSRTOMBS
115 return wcsrtombs((char *) NULL, &pwz, 0, &mbstate);
116 #else
117 return wxWcstombs((char *) NULL, pwz, 0);
118 #endif
119 }
120 #endif // wxUSE_WCHAR_T
121
122 bool WXDLLEXPORT wxOKlibc()
123 {
124 #if wxUSE_WCHAR_T && defined(__UNIX__) && defined(__GLIBC__) && !defined(__WINE__)
125 // glibc 2.0 uses UTF-8 even when it shouldn't
126 wchar_t res = 0;
127 if ((MB_CUR_MAX == 2) &&
128 (wxMB2WC(&res, "\xdd\xa5", 1) == 1) &&
129 (res==0x765)) {
130 // this is UTF-8 allright, check whether that's what we want
131 char *cur_locale = setlocale(LC_CTYPE, NULL);
132 if ((strlen(cur_locale) < 4) ||
133 (strcasecmp(cur_locale + strlen(cur_locale) - 4, "utf8")) ||
134 (strcasecmp(cur_locale + strlen(cur_locale) - 5, "utf-8"))) {
135 // nope, don't use libc conversion
136 return false;
137 }
138 }
139 #endif
140 return true;
141 }
142
143 char* wxSetlocale(int category, const char *locale)
144 {
145 char *rv = setlocale(category, locale);
146 if ( locale != NULL /* setting locale, not querying */ &&
147 rv /* call was successful */ )
148 {
149 wxUpdateLocaleIsUtf8();
150 }
151 return rv;
152 }
153
154 // ============================================================================
155 // printf() functions business
156 // ============================================================================
157
158 // special test mode: define all functions below even if we don't really need
159 // them to be able to test them
160 #ifdef wxTEST_PRINTF
161 #undef wxFprintf
162 #undef wxPrintf
163 #undef wxSprintf
164 #undef wxVfprintf
165 #undef wxVsprintf
166 #undef wxVprintf
167 #undef wxVsnprintf_
168
169 #define wxNEED_WPRINTF
170
171 int wxCRT_VfprintfW( FILE *stream, const wchar_t *format, va_list argptr );
172 #endif
173
174 #if defined(__DMC__)
175 /* Digital Mars adds count to _stprintf (C99) so convert */
176 int wxCRT_SprintfW (wchar_t * __RESTRICT s, const wchar_t * __RESTRICT format, ... )
177 {
178 va_list arglist;
179
180 va_start( arglist, format );
181 int iLen = swprintf ( s, -1, format, arglist );
182 va_end( arglist );
183 return iLen ;
184 }
185 #endif //__DMC__
186
187 // ----------------------------------------------------------------------------
188 // implement the standard IO functions for wide char if libc doesn't have them
189 // ----------------------------------------------------------------------------
190
191 #ifndef wxCRT_FputsW
192 int wxCRT_FputsW(const wchar_t *ws, FILE *stream)
193 {
194 wxCharBuffer buf(wxConvLibc.cWC2MB(ws));
195 if ( !buf )
196 return -1;
197
198 // counting the number of wide characters written isn't worth the trouble,
199 // simply distinguish between ok and error
200 return wxCRT_FputsA(buf, stream) == -1 ? -1 : 0;
201 }
202 #endif // !wxCRT_FputsW
203
204 #ifndef wxCRT_PutsW
205 int wxCRT_PutsW(const wchar_t *ws)
206 {
207 int rc = wxCRT_FputsW(ws, stdout);
208 if ( rc != -1 )
209 {
210 if ( wxCRT_FputsW(L"\n", stdout) == -1 )
211 return -1;
212
213 rc++;
214 }
215
216 return rc;
217 }
218 #endif // !wxCRT_PutsW
219
220 #ifndef wxCRT_FputcW
221 int /* not wint_t */ wxCRT_FputcW(wchar_t wc, FILE *stream)
222 {
223 wchar_t ws[2] = { wc, L'\0' };
224
225 return wxCRT_FputsW(ws, stream);
226 }
227 #endif // !wxCRT_FputcW
228
229 // NB: we only implement va_list functions here, the ones taking ... are
230 // defined below for wxNEED_PRINTF_CONVERSION case anyhow and we reuse
231 // the definitions there to avoid duplicating them here
232 #ifdef wxNEED_WPRINTF
233
234 // TODO: implement the scanf() functions
235 static int vwscanf(const wchar_t *format, va_list argptr)
236 {
237 wxFAIL_MSG( _T("TODO") );
238
239 return -1;
240 }
241
242 static int vswscanf(const wchar_t *ws, const wchar_t *format, va_list argptr)
243 {
244 // The best we can do without proper Unicode support in glibc is to
245 // convert the strings into MB representation and run ANSI version
246 // of the function. This doesn't work with %c and %s because of difference
247 // in size of char and wchar_t, though.
248
249 wxCHECK_MSG( wxStrstr(format, _T("%s")) == NULL, -1,
250 _T("incomplete vswscanf implementation doesn't allow %s") );
251 wxCHECK_MSG( wxStrstr(format, _T("%c")) == NULL, -1,
252 _T("incomplete vswscanf implementation doesn't allow %c") );
253
254 return vsscanf(wxConvLibc.cWX2MB(ws), wxConvLibc.cWX2MB(format), argptr);
255 }
256
257 static int vfwscanf(FILE *stream, const wchar_t *format, va_list argptr)
258 {
259 wxFAIL_MSG( _T("TODO") );
260
261 return -1;
262 }
263
264 #define vswprintf wxCRT_VsnprintfW_
265
266 static int vfwprintf(FILE *stream, const wchar_t *format, va_list argptr)
267 {
268 wxString s;
269 int rc = s.PrintfV(format, argptr);
270
271 if ( rc != -1 )
272 {
273 // we can't do much better without Unicode support in libc...
274 if ( fprintf(stream, "%s", (const char*)s.mb_str() ) == -1 )
275 return -1;
276 }
277
278 return rc;
279 }
280
281 static int vwprintf(const wchar_t *format, va_list argptr)
282 {
283 return wxCRT_VfprintfW(stdout, format, argptr);
284 }
285
286 #endif // wxNEED_WPRINTF
287
288 #ifdef wxNEED_PRINTF_CONVERSION
289
290 // ----------------------------------------------------------------------------
291 // wxFormatConverter: class doing the "%s" -> "%ls" conversion
292 // ----------------------------------------------------------------------------
293
294 /*
295 Here are the gory details. We want to follow the Windows/MS conventions,
296 that is to have
297
298 In ANSI mode:
299
300 format specifier results in
301 -----------------------------------
302 %c, %hc, %hC char
303 %lc, %C, %lC wchar_t
304
305 In Unicode mode:
306
307 format specifier results in
308 -----------------------------------
309 %hc, %C, %hC char
310 %c, %lc, %lC wchar_t
311
312
313 while on POSIX systems we have %C identical to %lc and %c always means char
314 (in any mode) while %lc always means wchar_t,
315
316 So to use native functions in order to get our semantics we must do the
317 following translations in Unicode mode (nothing to do in ANSI mode):
318
319 wxWidgets specifier POSIX specifier
320 ----------------------------------------
321
322 %hc, %C, %hC %c
323 %c %lc
324
325
326 And, of course, the same should be done for %s as well.
327 */
328
329 class wxFormatConverter
330 {
331 public:
332 wxFormatConverter(const wchar_t *format);
333
334 // notice that we only translated the string if m_fmtOrig == NULL (as set
335 // by CopyAllBefore()), otherwise we should simply use the original format
336 operator const wchar_t *() const
337 { return m_fmtOrig ? m_fmtOrig : m_fmt.c_str(); }
338
339 private:
340 // copy another character to the translated format: this function does the
341 // copy if we are translating but doesn't do anything at all if we don't,
342 // so we don't create the translated format string at all unless we really
343 // need to (i.e. InsertFmtChar() is called)
344 wchar_t CopyFmtChar(wchar_t ch)
345 {
346 if ( !m_fmtOrig )
347 {
348 // we're translating, do copy
349 m_fmt += ch;
350 }
351 else
352 {
353 // simply increase the count which should be copied by
354 // CopyAllBefore() later if needed
355 m_nCopied++;
356 }
357
358 return ch;
359 }
360
361 // insert an extra character
362 void InsertFmtChar(wchar_t ch)
363 {
364 if ( m_fmtOrig )
365 {
366 // so far we haven't translated anything yet
367 CopyAllBefore();
368 }
369
370 m_fmt += ch;
371 }
372
373 void CopyAllBefore()
374 {
375 wxASSERT_MSG( m_fmtOrig && m_fmt.empty(), _T("logic error") );
376
377 m_fmt = wxString(m_fmtOrig, m_nCopied);
378
379 // we won't need it any longer
380 m_fmtOrig = NULL;
381 }
382
383 static bool IsFlagChar(wchar_t ch)
384 {
385 return ch == _T('-') || ch == _T('+') ||
386 ch == _T('0') || ch == _T(' ') || ch == _T('#');
387 }
388
389 void SkipDigits(const wchar_t **ptpc)
390 {
391 while ( **ptpc >= _T('0') && **ptpc <= _T('9') )
392 CopyFmtChar(*(*ptpc)++);
393 }
394
395 // the translated format
396 wxString m_fmt;
397
398 // the original format
399 const wchar_t *m_fmtOrig;
400
401 // the number of characters already copied
402 size_t m_nCopied;
403 };
404
405 wxFormatConverter::wxFormatConverter(const wchar_t *format)
406 {
407 m_fmtOrig = format;
408 m_nCopied = 0;
409
410 while ( *format )
411 {
412 if ( CopyFmtChar(*format++) == _T('%') )
413 {
414 // skip any flags
415 while ( IsFlagChar(*format) )
416 CopyFmtChar(*format++);
417
418 // and possible width
419 if ( *format == _T('*') )
420 CopyFmtChar(*format++);
421 else
422 SkipDigits(&format);
423
424 // precision?
425 if ( *format == _T('.') )
426 {
427 CopyFmtChar(*format++);
428 if ( *format == _T('*') )
429 CopyFmtChar(*format++);
430 else
431 SkipDigits(&format);
432 }
433
434 // next we can have a size modifier
435 enum
436 {
437 Default,
438 Short,
439 Long
440 } size;
441
442 switch ( *format )
443 {
444 case _T('h'):
445 size = Short;
446 format++;
447 break;
448
449 case _T('l'):
450 // "ll" has a different meaning!
451 if ( format[1] != _T('l') )
452 {
453 size = Long;
454 format++;
455 break;
456 }
457 //else: fall through
458
459 default:
460 size = Default;
461 }
462
463 // and finally we should have the type
464 switch ( *format )
465 {
466 case _T('C'):
467 case _T('S'):
468 // %C and %hC -> %c and %lC -> %lc
469 if ( size == Long )
470 CopyFmtChar(_T('l'));
471
472 InsertFmtChar(*format++ == _T('C') ? _T('c') : _T('s'));
473 break;
474
475 case _T('c'):
476 case _T('s'):
477 // %c -> %lc but %hc stays %hc and %lc is still %lc
478 if ( size == Default)
479 InsertFmtChar(_T('l'));
480 // fall through
481
482 default:
483 // nothing special to do
484 if ( size != Default )
485 CopyFmtChar(*(format - 1));
486 CopyFmtChar(*format++);
487 }
488 }
489 }
490 }
491
492 #else // !wxNEED_PRINTF_CONVERSION
493 // no conversion necessary
494 #define wxFormatConverter(x) (x)
495 #endif // wxNEED_PRINTF_CONVERSION/!wxNEED_PRINTF_CONVERSION
496
497 #ifdef __WXDEBUG__
498 // For testing the format converter
499 wxString wxConvertFormat(const wchar_t *format)
500 {
501 return wxString(wxFormatConverter(format));
502 }
503 #endif
504
505 // ----------------------------------------------------------------------------
506 // wxPrintf(), wxScanf() and relatives
507 // ----------------------------------------------------------------------------
508
509 // FIXME-UTF8: do format conversion using (modified) wxFormatConverter in
510 // template wrappers, not here; note that it will needed to
511 // translate all forms of string specifiers to %(l)s for wxPrintf(),
512 // but it only should do what it did in 2.8 for wxScanf()!
513
514 #ifndef wxCRT_PrintfW
515 int wxCRT_PrintfW( const wchar_t *format, ... )
516 {
517 va_list argptr;
518 va_start(argptr, format);
519
520 int ret = vwprintf( wxFormatConverter(format), argptr );
521
522 va_end(argptr);
523
524 return ret;
525 }
526 #endif
527
528 #ifndef wxCRT_FprintfW
529 int wxCRT_FprintfW( FILE *stream, const wchar_t *format, ... )
530 {
531 va_list argptr;
532 va_start( argptr, format );
533
534 int ret = vfwprintf( stream, wxFormatConverter(format), argptr );
535
536 va_end(argptr);
537
538 return ret;
539 }
540 #endif
541
542 #ifndef wxCRT_VfprintfW
543 int wxCRT_VfprintfW( FILE *stream, const wchar_t *format, va_list argptr )
544 {
545 return vfwprintf( stream, wxFormatConverter(format), argptr );
546 }
547 #endif
548
549 #ifndef wxCRT_VprintfW
550 int wxCRT_VprintfW( const wchar_t *format, va_list argptr )
551 {
552 return vwprintf( wxFormatConverter(format), argptr );
553 }
554 #endif
555
556 #ifndef wxCRT_VsnprintfW
557 int wxCRT_VsnprintfW(wchar_t *str, size_t size, const wchar_t *format, va_list argptr )
558 {
559 return vswprintf( str, size, wxFormatConverter(format), argptr );
560 }
561 #endif // !wxCRT_VsnprintfW
562
563 #ifndef wxCRT_VsprintfW
564 int wxCRT_VsprintfW( wchar_t *str, const wchar_t *format, va_list argptr )
565 {
566 // same as for wxSprintf()
567 return vswprintf(str, INT_MAX / 4, wxFormatConverter(format), argptr);
568 }
569 #endif
570
571 #ifndef wxCRT_ScanfW
572 int wxCRT_ScanfW(const wchar_t *format, ...)
573 {
574 va_list argptr;
575 va_start(argptr, format);
576
577 #ifdef __VMS
578 #if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
579 int ret = std::vwscanf(wxFormatConverter(format), argptr);
580 #else
581 int ret = vwscanf(wxFormatConverter(format), argptr);
582 #endif
583 #else
584 int ret = vwscanf(wxFormatConverter(format), argptr);
585 #endif
586
587 va_end(argptr);
588
589 return ret;
590 }
591 #endif
592
593 #ifndef wxCRT_SscanfW
594 int wxCRT_SscanfW(const wchar_t *str, const wchar_t *format, ...)
595 {
596 va_list argptr;
597 va_start(argptr, format);
598
599 #ifdef __VMS
600 #if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
601 int ret = std::vswscanf(str, wxFormatConverter(format), argptr);
602 #else
603 int ret = vswscanf(str, wxFormatConverter(format), argptr);
604 #endif
605 #else
606 int ret = vswscanf(str, wxFormatConverter(format), argptr);
607 #endif
608
609 va_end(argptr);
610
611 return ret;
612 }
613 #endif
614
615 #ifndef wxCRT_FscanfW
616 int wxCRT_FscanfW(FILE *stream, const wchar_t *format, ...)
617 {
618 va_list argptr;
619 va_start(argptr, format);
620 #ifdef __VMS
621 #if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
622 int ret = std::vfwscanf(stream, wxFormatConverter(format), argptr);
623 #else
624 int ret = vfwscanf(stream, wxFormatConverter(format), argptr);
625 #endif
626 #else
627 int ret = vfwscanf(stream, wxFormatConverter(format), argptr);
628 #endif
629
630 va_end(argptr);
631
632 return ret;
633 }
634 #endif
635
636 #ifndef wxCRT_VsscanfW
637 int wxCRT_VsscanfW(const wchar_t *str, const wchar_t *format, va_list argptr)
638 {
639 #ifdef __VMS
640 #if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
641 return std::vswscanf(str, wxFormatConverter(format), argptr);
642 #else
643 return vswscanf(str, wxFormatConverter(format), argptr);
644 #endif
645 #else
646 return vswscanf(str, wxFormatConverter(format), argptr);
647 #endif
648 }
649 #endif
650
651
652 // ----------------------------------------------------------------------------
653 // wrappers to printf and scanf function families
654 // ----------------------------------------------------------------------------
655
656 #if !wxUSE_UTF8_LOCALE_ONLY
657 int wxDoSprintfWchar(char *str, const wxChar *format, ...)
658 {
659 va_list argptr;
660 va_start(argptr, format);
661
662 int rv = wxVsprintf(str, format, argptr);
663
664 va_end(argptr);
665 return rv;
666 }
667 #endif // !wxUSE_UTF8_LOCALE_ONLY
668
669 #if wxUSE_UNICODE_UTF8
670 int wxDoSprintfUtf8(char *str, const char *format, ...)
671 {
672 va_list argptr;
673 va_start(argptr, format);
674
675 int rv = wxVsprintf(str, format, argptr);
676
677 va_end(argptr);
678 return rv;
679 }
680 #endif // wxUSE_UNICODE_UTF8
681
682 #if wxUSE_UNICODE
683
684 #if !wxUSE_UTF8_LOCALE_ONLY
685 int wxDoSprintfWchar(wchar_t *str, const wxChar *format, ...)
686 {
687 va_list argptr;
688 va_start(argptr, format);
689
690 int rv = wxVsprintf(str, format, argptr);
691
692 va_end(argptr);
693 return rv;
694 }
695 #endif // !wxUSE_UTF8_LOCALE_ONLY
696
697 #if wxUSE_UNICODE_UTF8
698 int wxDoSprintfUtf8(wchar_t *str, const char *format, ...)
699 {
700 va_list argptr;
701 va_start(argptr, format);
702
703 int rv = wxVsprintf(str, format, argptr);
704
705 va_end(argptr);
706 return rv;
707 }
708 #endif // wxUSE_UNICODE_UTF8
709
710 #endif // wxUSE_UNICODE
711
712 #if !wxUSE_UTF8_LOCALE_ONLY
713 int wxDoSnprintfWchar(char *str, size_t size, const wxChar *format, ...)
714 {
715 va_list argptr;
716 va_start(argptr, format);
717
718 int rv = wxVsnprintf(str, size, format, argptr);
719
720 va_end(argptr);
721 return rv;
722 }
723 #endif // !wxUSE_UTF8_LOCALE_ONLY
724
725 #if wxUSE_UNICODE_UTF8
726 int wxDoSnprintfUtf8(char *str, size_t size, const char *format, ...)
727 {
728 va_list argptr;
729 va_start(argptr, format);
730
731 int rv = wxVsnprintf(str, size, format, argptr);
732
733 va_end(argptr);
734 return rv;
735 }
736 #endif // wxUSE_UNICODE_UTF8
737
738 #if wxUSE_UNICODE
739
740 #if !wxUSE_UTF8_LOCALE_ONLY
741 int wxDoSnprintfWchar(wchar_t *str, size_t size, const wxChar *format, ...)
742 {
743 va_list argptr;
744 va_start(argptr, format);
745
746 int rv = wxVsnprintf(str, size, format, argptr);
747
748 va_end(argptr);
749 return rv;
750 }
751 #endif // !wxUSE_UTF8_LOCALE_ONLY
752
753 #if wxUSE_UNICODE_UTF8
754 int wxDoSnprintfUtf8(wchar_t *str, size_t size, const char *format, ...)
755 {
756 va_list argptr;
757 va_start(argptr, format);
758
759 int rv = wxVsnprintf(str, size, format, argptr);
760
761 va_end(argptr);
762 return rv;
763 }
764 #endif // wxUSE_UNICODE_UTF8
765
766 #endif // wxUSE_UNICODE
767
768
769 #ifdef HAVE_BROKEN_VSNPRINTF_DECL
770 #define vsnprintf wx_fixed_vsnprintf
771 #endif
772
773 #if wxUSE_UNICODE
774
775 #if !wxUSE_UTF8_LOCALE_ONLY
776 static int ConvertStringToBuf(const wxString& s, char *out, size_t outsize)
777 {
778 const wxWX2WCbuf buf = s.wc_str();
779
780 size_t len = wxConvLibc.FromWChar(out, outsize, buf);
781 if ( len != wxCONV_FAILED )
782 return len-1;
783 else
784 return wxConvLibc.FromWChar(NULL, 0, buf);
785 }
786 #endif // !wxUSE_UTF8_LOCALE_ONLY
787
788 #if wxUSE_UNICODE_UTF8
789 static int ConvertStringToBuf(const wxString& s, wchar_t *out, size_t outsize)
790 {
791 const wxWX2WCbuf buf(s.wc_str());
792 size_t len = wxWcslen(buf);
793 if ( outsize > len )
794 memcpy(out, buf, (len+1) * sizeof(wchar_t));
795 // else: not enough space
796 return len;
797 }
798 #endif // wxUSE_UNICODE_UTF8
799
800 template<typename T>
801 static size_t PrintfViaString(T *out, size_t outsize,
802 const wxString& format, va_list argptr)
803 {
804 wxString s;
805 s.PrintfV(format, argptr);
806
807 return ConvertStringToBuf(s, out, outsize);
808 }
809 #endif // wxUSE_UNICODE
810
811 int wxVsprintf(char *str, const wxString& format, va_list argptr)
812 {
813 #if wxUSE_UTF8_LOCALE_ONLY
814 return vsprintf(str, format.wx_str(), argptr);
815 #else
816 #if wxUSE_UNICODE_UTF8
817 if ( wxLocaleIsUtf8 )
818 return vsprintf(str, format.wx_str(), argptr);
819 else
820 #endif
821 #if wxUSE_UNICODE
822 return PrintfViaString(str, wxNO_LEN, format, argptr);
823 #else
824 return wxCRT_VsprintfA(str, format.mb_str(), argptr);
825 #endif
826 #endif
827 }
828
829 #if wxUSE_UNICODE
830 int wxVsprintf(wchar_t *str, const wxString& format, va_list argptr)
831 {
832 #if wxUSE_UNICODE_WCHAR
833 return wxCRT_VsprintfW(str, format.wc_str(), argptr);
834 #else // wxUSE_UNICODE_UTF8
835 #if !wxUSE_UTF8_LOCALE_ONLY
836 if ( !wxLocaleIsUtf8 )
837 return wxCRT_VsprintfW(str, format.wc_str(), argptr);
838 else
839 #endif
840 return PrintfViaString(str, wxNO_LEN, format, argptr);
841 #endif // wxUSE_UNICODE_UTF8
842 }
843 #endif // wxUSE_UNICODE
844
845 int wxVsnprintf(char *str, size_t size, const wxString& format, va_list argptr)
846 {
847 int rv;
848 #if wxUSE_UTF8_LOCALE_ONLY
849 rv = wxCRT_VsnprintfA(str, size, format.wx_str(), argptr);
850 #else
851 #if wxUSE_UNICODE_UTF8
852 if ( wxLocaleIsUtf8 )
853 rv = wxCRT_VsnprintfA(str, size, format.wx_str(), argptr);
854 else
855 #endif
856 #if wxUSE_UNICODE
857 {
858 // NB: if this code is called, then wxString::PrintV() would use the
859 // wchar_t* version of wxVsnprintf(), so it's safe to use PrintV()
860 // from here
861 rv = PrintfViaString(str, size, format, argptr);
862 }
863 #else
864 rv = wxCRT_VsnprintfA(str, size, format.mb_str(), argptr);
865 #endif
866 #endif
867
868 // VsnprintfTestCase reveals that glibc's implementation of vswprintf
869 // doesn't nul terminate on truncation.
870 str[size - 1] = 0;
871
872 return rv;
873 }
874
875 #if wxUSE_UNICODE
876 int wxVsnprintf(wchar_t *str, size_t size, const wxString& format, va_list argptr)
877 {
878 int rv;
879
880 #if wxUSE_UNICODE_WCHAR
881 rv = wxCRT_VsnprintfW(str, size, format.wc_str(), argptr);
882 #else // wxUSE_UNICODE_UTF8
883 #if !wxUSE_UTF8_LOCALE_ONLY
884 if ( !wxLocaleIsUtf8 )
885 rv = wxCRT_VsnprintfW(str, size, format.wc_str(), argptr);
886 else
887 #endif
888 {
889 // NB: if this code is called, then wxString::PrintV() would use the
890 // char* version of wxVsnprintf(), so it's safe to use PrintV()
891 // from here
892 rv = PrintfViaString(str, size, format, argptr);
893 }
894 #endif // wxUSE_UNICODE_UTF8
895
896 // VsnprintfTestCase reveals that glibc's implementation of vswprintf
897 // doesn't nul terminate on truncation.
898 str[size - 1] = 0;
899
900 return rv;
901 }
902 #endif // wxUSE_UNICODE
903
904 #if wxUSE_WCHAR_T
905
906 // ----------------------------------------------------------------------------
907 // ctype.h stuff (currently unused)
908 // ----------------------------------------------------------------------------
909
910 #ifdef wxNEED_WX_MBSTOWCS
911
912 WXDLLEXPORT size_t wxMbstowcs (wchar_t * out, const char * in, size_t outlen)
913 {
914 if (!out)
915 {
916 size_t outsize = 0;
917 while(*in++)
918 outsize++;
919 return outsize;
920 }
921
922 const char* origin = in;
923
924 while (outlen-- && *in)
925 {
926 *out++ = (wchar_t) *in++;
927 }
928
929 *out = '\0';
930
931 return in - origin;
932 }
933
934 WXDLLEXPORT size_t wxWcstombs (char * out, const wchar_t * in, size_t outlen)
935 {
936 if (!out)
937 {
938 size_t outsize = 0;
939 while(*in++)
940 outsize++;
941 return outsize;
942 }
943
944 const wchar_t* origin = in;
945
946 while (outlen-- && *in)
947 {
948 *out++ = (char) *in++;
949 }
950
951 *out = '\0';
952
953 return in - origin;
954 }
955
956 #endif // wxNEED_WX_MBSTOWCS
957
958 #ifndef wxCRT_StrdupA
959 WXDLLEXPORT char *wxCRT_StrdupA(const char *s)
960 {
961 return strcpy((char *)malloc(strlen(s) + 1), s);
962 }
963 #endif // wxCRT_StrdupA
964
965 #ifndef wxCRT_StrdupW
966 WXDLLEXPORT wchar_t * wxCRT_StrdupW(const wchar_t *pwz)
967 {
968 size_t size = (wxWcslen(pwz) + 1) * sizeof(wchar_t);
969 wchar_t *ret = (wchar_t *) malloc(size);
970 memcpy(ret, pwz, size);
971 return ret;
972 }
973 #endif // wxCRT_StrdupW
974
975 #ifndef wxCRT_StricmpA
976 int WXDLLEXPORT wxCRT_StricmpA(const char *psz1, const char *psz2)
977 {
978 register char c1, c2;
979 do {
980 c1 = wxTolower(*psz1++);
981 c2 = wxTolower(*psz2++);
982 } while ( c1 && (c1 == c2) );
983 return c1 - c2;
984 }
985 #endif // !defined(wxCRT_StricmpA)
986
987 #ifndef wxCRT_StricmpW
988 int WXDLLEXPORT wxCRT_StricmpW(const wchar_t *psz1, const wchar_t *psz2)
989 {
990 register wchar_t c1, c2;
991 do {
992 c1 = wxTolower(*psz1++);
993 c2 = wxTolower(*psz2++);
994 } while ( c1 && (c1 == c2) );
995 return c1 - c2;
996 }
997 #endif // !defined(wxCRT_StricmpW)
998
999 #ifndef wxCRT_StrnicmpA
1000 int WXDLLEXPORT wxCRT_StrnicmpA(const char *s1, const char *s2, size_t n)
1001 {
1002 // initialize the variables just to suppress stupid gcc warning
1003 register char c1 = 0, c2 = 0;
1004 while (n && ((c1 = wxTolower(*s1)) == (c2 = wxTolower(*s2)) ) && c1) n--, s1++, s2++;
1005 if (n) {
1006 if (c1 < c2) return -1;
1007 if (c1 > c2) return 1;
1008 }
1009 return 0;
1010 }
1011 #endif // !defined(wxCRT_StrnicmpA)
1012
1013 #ifndef wxCRT_StrnicmpW
1014 int WXDLLEXPORT wxCRT_StrnicmpW(const wchar_t *s1, const wchar_t *s2, size_t n)
1015 {
1016 // initialize the variables just to suppress stupid gcc warning
1017 register wchar_t c1 = 0, c2 = 0;
1018 while (n && ((c1 = wxTolower(*s1)) == (c2 = wxTolower(*s2)) ) && c1) n--, s1++, s2++;
1019 if (n) {
1020 if (c1 < c2) return -1;
1021 if (c1 > c2) return 1;
1022 }
1023 return 0;
1024 }
1025 #endif // !defined(wxCRT_StrnicmpW)
1026
1027 // ----------------------------------------------------------------------------
1028 // string.h functions
1029 // ----------------------------------------------------------------------------
1030
1031 // this (and wxCRT_StrncmpW below) are extern "C" because they are needed
1032 // by regex code, the rest isn't needed, so it's not declared as extern "C"
1033 #ifndef wxCRT_StrlenW
1034 extern "C" WXDLLEXPORT size_t wxCRT_StrlenW(const wchar_t *s)
1035 {
1036 size_t n = 0;
1037 while ( *s++ )
1038 n++;
1039
1040 return n;
1041 }
1042 #endif
1043
1044 // ----------------------------------------------------------------------------
1045 // stdlib.h functions
1046 // ----------------------------------------------------------------------------
1047
1048 #ifndef wxCRT_GetenvW
1049 wchar_t* WXDLLEXPORT wxCRT_GetenvW(const wchar_t *name)
1050 {
1051 // NB: buffer returned by getenv() is allowed to be overwritten next
1052 // time getenv() is called, so it is OK to use static string
1053 // buffer to hold the data.
1054 static wxWCharBuffer value((wchar_t*)NULL);
1055 value = wxConvLibc.cMB2WC(getenv(wxConvLibc.cWC2MB(name)));
1056 return value.data();
1057 }
1058 #endif // !wxCRT_GetenvW
1059
1060 #ifndef wxCRT_StrftimeW
1061 WXDLLEXPORT size_t
1062 wxCRT_StrftimeW(wchar_t *s, size_t maxsize, const wchar_t *fmt, const struct tm *tm)
1063 {
1064 if ( !maxsize )
1065 return 0;
1066
1067 wxCharBuffer buf(maxsize);
1068
1069 wxCharBuffer bufFmt(wxConvLibc.cWX2MB(fmt));
1070 if ( !bufFmt )
1071 return 0;
1072
1073 size_t ret = strftime(buf.data(), maxsize, bufFmt, tm);
1074 if ( !ret )
1075 return 0;
1076
1077 wxWCharBuffer wbuf = wxConvLibc.cMB2WX(buf);
1078 if ( !wbuf )
1079 return 0;
1080
1081 wxCRT_StrncpyW(s, wbuf, maxsize);
1082 return wxCRT_StrlenW(s);
1083 }
1084 #endif // !wxCRT_StrftimeW
1085
1086 #endif // wxUSE_WCHAR_T
1087
1088 template<typename T>
1089 static wxULongLong_t
1090 wxCRT_StrtoullBase(const T* nptr, T** endptr, int base, T* sign)
1091 {
1092 wxULongLong_t sum = 0;
1093 wxString wxstr(nptr);
1094 wxString::const_iterator i = wxstr.begin();
1095 wxString::const_iterator end = wxstr.end();
1096
1097 // Skip spaces
1098 while ( i != end && wxIsspace(*i) ) i++;
1099
1100 // Starts with sign?
1101 *sign = wxT(' ');
1102 if ( i != end )
1103 {
1104 T c = *i;
1105 if ( c == wxT('+') || c == wxT('-') )
1106 {
1107 *sign = c;
1108 i++;
1109 }
1110 }
1111
1112 // Starts with 0x?
1113 if ( i != end && *i == wxT('0') )
1114 {
1115 i++;
1116 if ( i != end )
1117 {
1118 if ( *i == wxT('x') && (base == 16 || base == 0) )
1119 {
1120 base = 16;
1121 i++;
1122 }
1123 else
1124 {
1125 if ( endptr )
1126 *endptr = (T*) nptr;
1127 wxSET_ERRNO(EINVAL);
1128 return sum;
1129 }
1130 }
1131 else
1132 i--;
1133 }
1134
1135 if ( base == 0 )
1136 base = 10;
1137
1138 for ( ; i != end; i++ )
1139 {
1140 unsigned int n;
1141
1142 T c = *i;
1143 if ( c >= wxT('0') )
1144 {
1145 if ( c <= wxT('9') )
1146 n = c - wxT('0');
1147 else
1148 n = wxTolower(c) - wxT('a') + 10;
1149 }
1150 else
1151 break;
1152
1153 if ( n >= (unsigned int)base )
1154 // Invalid character (for this base)
1155 break;
1156
1157 wxULongLong_t prevsum = sum;
1158 sum = (sum * base) + n;
1159
1160 if ( sum < prevsum )
1161 {
1162 wxSET_ERRNO(ERANGE);
1163 break;
1164 }
1165 }
1166
1167 if ( endptr )
1168 {
1169 *endptr = (T*)(nptr + (i - wxstr.begin()));
1170 }
1171
1172 return sum;
1173 }
1174
1175 template<typename T>
1176 static wxULongLong_t wxCRT_DoStrtoull(const T* nptr, T** endptr, int base)
1177 {
1178 T sign;
1179 wxULongLong_t uval = wxCRT_StrtoullBase(nptr, endptr, base, &sign);
1180
1181 if ( sign == wxT('-') )
1182 {
1183 wxSET_ERRNO(ERANGE);
1184 uval = 0;
1185 }
1186
1187 return uval;
1188 }
1189
1190 template<typename T>
1191 static wxLongLong_t wxCRT_DoStrtoll(const T* nptr, T** endptr, int base)
1192 {
1193 T sign;
1194 wxULongLong_t uval = wxCRT_StrtoullBase(nptr, endptr, base, &sign);
1195 wxLongLong_t val = 0;
1196
1197 if ( sign == wxT('-') )
1198 {
1199 if ( uval <= wxULL(wxINT64_MAX+1) )
1200 {
1201 if ( uval == wxULL(wxINT64_MAX+1))
1202 val = -((wxLongLong_t)wxINT64_MAX) - 1;
1203 else
1204 val = -((wxLongLong_t)uval);
1205 }
1206 else
1207 {
1208 wxSET_ERRNO(ERANGE);
1209 }
1210 }
1211 else if ( uval <= wxINT64_MAX )
1212 {
1213 val = uval;
1214 }
1215 else
1216 {
1217 wxSET_ERRNO(ERANGE);
1218 }
1219
1220 return val;
1221 }
1222
1223 #ifndef wxCRT_StrtollA
1224 wxLongLong_t wxCRT_StrtollA(const char* nptr, char** endptr, int base)
1225 { return wxCRT_DoStrtoll(nptr, endptr, base); }
1226 #endif
1227 #ifndef wxCRT_StrtollW
1228 wxLongLong_t wxCRT_StrtollW(const wchar_t* nptr, wchar_t** endptr, int base)
1229 { return wxCRT_DoStrtoll(nptr, endptr, base); }
1230 #endif
1231
1232 #ifndef wxCRT_StrtoullA
1233 wxULongLong_t wxCRT_StrtoullA(const char* nptr, char** endptr, int base)
1234 { return wxCRT_DoStrtoull(nptr, endptr, base); }
1235 #endif
1236 #ifndef wxCRT_StrtoullW
1237 wxULongLong_t wxCRT_StrtoullW(const wchar_t* nptr, wchar_t** endptr, int base)
1238 { return wxCRT_DoStrtoull(nptr, endptr, base); }
1239 #endif
1240
1241 // ----------------------------------------------------------------------------
1242 // functions which we may need even if !wxUSE_WCHAR_T
1243 // ----------------------------------------------------------------------------
1244
1245 template<typename T>
1246 static T *wxCRT_DoStrtok(T *psz, const T *delim, T **save_ptr)
1247 {
1248 if (!psz)
1249 {
1250 psz = *save_ptr;
1251 if ( !psz )
1252 return NULL;
1253 }
1254
1255 psz += wxStrspn(psz, delim);
1256 if (!*psz)
1257 {
1258 *save_ptr = (T *)NULL;
1259 return (T *)NULL;
1260 }
1261
1262 T *ret = psz;
1263 psz = wxStrpbrk(psz, delim);
1264 if (!psz)
1265 {
1266 *save_ptr = (T*)NULL;
1267 }
1268 else
1269 {
1270 *psz = wxT('\0');
1271 *save_ptr = psz + 1;
1272 }
1273
1274 return ret;
1275 }
1276
1277 #ifndef wxCRT_StrtokA
1278 char *wxCRT_StrtokA(char *psz, const char *delim, char **save_ptr)
1279 { return wxCRT_DoStrtok(psz, delim, save_ptr); }
1280 #endif
1281 #ifndef wxCRT_StrtokW
1282 wchar_t *wxCRT_StrtokW(wchar_t *psz, const wchar_t *delim, wchar_t **save_ptr)
1283 { return wxCRT_DoStrtok(psz, delim, save_ptr); }
1284 #endif
1285
1286 // ----------------------------------------------------------------------------
1287 // missing C RTL functions
1288 // ----------------------------------------------------------------------------
1289
1290 #ifdef wxNEED_STRDUP
1291
1292 char *strdup(const char *s)
1293 {
1294 char *dest = (char*) malloc( strlen( s ) + 1 ) ;
1295 if ( dest )
1296 strcpy( dest , s ) ;
1297 return dest ;
1298 }
1299 #endif // wxNEED_STRDUP
1300
1301 #if defined(__WXWINCE__) && (_WIN32_WCE <= 211)
1302
1303 void *calloc( size_t num, size_t size )
1304 {
1305 void** ptr = (void **)malloc(num * size);
1306 memset( ptr, 0, num * size);
1307 return ptr;
1308 }
1309
1310 #endif // __WXWINCE__ <= 211
1311
1312 #ifdef __WXWINCE__
1313 int wxCRT_RemoveW(const wchar_t *path)
1314 {
1315 return ::DeleteFile(path) == 0;
1316 }
1317 #endif
1318
1319 #ifndef wxCRT_TmpnamW
1320 wchar_t *wxCRT_TmpnamW(wchar_t *s)
1321 {
1322 // tmpnam_r() returns NULL if s=NULL, do the same
1323 wxCHECK_MSG( s, NULL, "wxTmpnam must be called with a buffer" );
1324
1325 #ifndef L_tmpnam
1326 #define L_tmpnam 1024
1327 #endif
1328 wxCharBuffer buf(L_tmpnam);
1329 tmpnam(buf.data());
1330
1331 wxConvLibc.ToWChar(s, L_tmpnam+1, buf.data());
1332 return s;
1333 }
1334 #endif // !wxCRT_TmpnamW
1335
1336
1337 // ============================================================================
1338 // wxLocaleIsUtf8
1339 // ============================================================================
1340
1341 #if wxUSE_UNICODE_UTF8
1342
1343 #if !wxUSE_UTF8_LOCALE_ONLY
1344 bool wxLocaleIsUtf8 = false; // the safer setting if not known
1345 #endif
1346
1347 static bool wxIsLocaleUtf8()
1348 {
1349 // NB: we intentionally don't use wxLocale::GetSystemEncodingName(),
1350 // because a) it may be unavailable in some builds and b) has slightly
1351 // different semantics (default locale instead of current)
1352
1353 #if defined(HAVE_LANGINFO_H) && defined(CODESET)
1354 // GNU libc provides current character set this way (this conforms to
1355 // Unix98)
1356 const char *charset = nl_langinfo(CODESET);
1357 if ( charset )
1358 {
1359 // "UTF-8" is used by modern glibc versions, but test other variants
1360 // as well, just in case:
1361 if ( strcmp(charset, "UTF-8") == 0 ||
1362 strcmp(charset, "utf-8") == 0 ||
1363 strcmp(charset, "UTF8") == 0 ||
1364 strcmp(charset, "utf8") == 0 )
1365 {
1366 return true;
1367 }
1368 }
1369 #endif
1370
1371 // check if we're running under the "C" locale: it is 7bit subset
1372 // of UTF-8, so it can be safely used with the UTF-8 build:
1373 const char *lc_ctype = setlocale(LC_CTYPE, NULL);
1374 if ( lc_ctype &&
1375 (strcmp(lc_ctype, "C") == 0 || strcmp(lc_ctype, "POSIX") == 0) )
1376 {
1377 return true;
1378 }
1379
1380 // we don't know what charset libc is using, so assume the worst
1381 // to be safe:
1382 return false;
1383 }
1384
1385 void wxUpdateLocaleIsUtf8()
1386 {
1387 #if wxUSE_UTF8_LOCALE_ONLY
1388 if ( !wxIsLocaleUtf8() )
1389 {
1390 wxLogFatalError(_T("This program requires UTF-8 locale to run."));
1391 }
1392 #else // !wxUSE_UTF8_LOCALE_ONLY
1393 wxLocaleIsUtf8 = wxIsLocaleUtf8();
1394 #endif
1395 }
1396
1397 #endif // wxUSE_UNICODE_UTF8
1398
1399 // ============================================================================
1400 // wx wrappers for CRT functions
1401 // ============================================================================
1402
1403 #if wxUSE_UNICODE_WCHAR
1404 #define CALL_ANSI_OR_UNICODE(return_kw, callA, callW) return_kw callW
1405 #elif wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
1406 #define CALL_ANSI_OR_UNICODE(return_kw, callA, callW) \
1407 return_kw wxLocaleIsUtf8 ? callA : callW
1408 #else // ANSI or UTF8 only
1409 #define CALL_ANSI_OR_UNICODE(return_kw, callA, callW) return_kw callA
1410 #endif
1411
1412 int wxPuts(const wxString& s)
1413 {
1414 CALL_ANSI_OR_UNICODE(return,
1415 wxCRT_PutsA(s.mb_str()),
1416 wxCRT_PutsW(s.wc_str()));
1417 }
1418
1419 int wxFputs(const wxString& s, FILE *stream)
1420 {
1421 CALL_ANSI_OR_UNICODE(return,
1422 wxCRT_FputsA(s.mb_str(), stream),
1423 wxCRT_FputsW(s.wc_str(), stream));
1424 }
1425
1426 int wxFputc(const wxUniChar& c, FILE *stream)
1427 {
1428 #if !wxUSE_UNICODE // FIXME-UTF8: temporary, remove this with ANSI build
1429 return wxCRT_FputcA((char)c, stream);
1430 #else
1431 CALL_ANSI_OR_UNICODE(return,
1432 wxCRT_FputsA(c.AsUTF8(), stream),
1433 wxCRT_FputcW((wchar_t)c, stream));
1434 #endif
1435 }
1436
1437 void wxPerror(const wxString& s)
1438 {
1439 #ifdef wxCRT_PerrorW
1440 CALL_ANSI_OR_UNICODE(wxEMPTY_PARAMETER_VALUE,
1441 wxCRT_PerrorA(s.mb_str()),
1442 wxCRT_PerrorW(s.wc_str()));
1443 #else
1444 wxCRT_PerrorA(s.mb_str());
1445 #endif
1446 }
1447
1448 wchar_t *wxFgets(wchar_t *s, int size, FILE *stream)
1449 {
1450 wxCHECK_MSG( s, NULL, "empty buffer passed to wxFgets()" );
1451
1452 wxCharBuffer buf(size - 1);
1453 // FIXME: this reads too little data if wxConvLibc uses UTF-8 ('size' wide
1454 // characters may be encoded by up to 'size'*4 bytes), but what
1455 // else can we do?
1456 if ( wxFgets(buf.data(), size, stream) == NULL )
1457 return NULL;
1458
1459 if ( wxConvLibc.ToWChar(s, size, buf, wxNO_LEN) == wxCONV_FAILED )
1460 return NULL;
1461
1462 return s;
1463 }
1464
1465 // ----------------------------------------------------------------------------
1466 // wxScanf() and friends
1467 // ----------------------------------------------------------------------------
1468
1469 #ifndef __VISUALC__
1470 int wxVsscanf(const char *str, const char *format, va_list ap)
1471 { return wxCRT_VsscanfA(str, format, ap); }
1472 int wxVsscanf(const wchar_t *str, const wchar_t *format, va_list ap)
1473 { return wxCRT_VsscanfW(str, format, ap); }
1474 int wxVsscanf(const wxCharBuffer& str, const char *format, va_list ap)
1475 { return wxCRT_VsscanfA(str, format, ap); }
1476 int wxVsscanf(const wxWCharBuffer& str, const wchar_t *format, va_list ap)
1477 { return wxCRT_VsscanfW(str, format, ap); }
1478 int wxVsscanf(const wxString& str, const char *format, va_list ap)
1479 { return wxCRT_VsscanfA(str.mb_str(), format, ap); }
1480 int wxVsscanf(const wxString& str, const wchar_t *format, va_list ap)
1481 { return wxCRT_VsscanfW(str.wc_str(), format, ap); }
1482 int wxVsscanf(const wxCStrData& str, const char *format, va_list ap)
1483 { return wxCRT_VsscanfA(str.AsCharBuf(), format, ap); }
1484 int wxVsscanf(const wxCStrData& str, const wchar_t *format, va_list ap)
1485 { return wxCRT_VsscanfW(str.AsWCharBuf(), format, ap); }
1486 #endif // !__VISUALC__