]> git.saurik.com Git - wxWidgets.git/blob - src/common/wxchar.cpp
%s to %ls conversion
[wxWidgets.git] / src / common / wxchar.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: wxchar.cpp
3 // Purpose: wxChar implementation
4 // Author: Ove Kåven
5 // Modified by:
6 // Created: 09/04/99
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWindows copyright
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "wxchar.h"
14 #endif
15
16 // ===========================================================================
17 // headers, declarations, constants
18 // ===========================================================================
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #define _ISOC9X_SOURCE 1 // to get vsscanf()
28 #define _BSD_SOURCE 1 // to still get strdup()
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <locale.h>
34 #include <time.h>
35
36 #ifndef WX_PRECOMP
37 #include "wx/defs.h"
38 #include "wx/wxchar.h"
39 #include "wx/string.h"
40 #include "wx/hash.h"
41 #endif
42
43 #include "wx/msgdlg.h"
44
45 #if defined(__WIN32__) && defined(wxNEED_WX_CTYPE_H)
46 #include <windef.h>
47 #include <winbase.h>
48 #include <winnls.h>
49 #include <winnt.h>
50 #endif
51
52 #if wxUSE_WCHAR_T
53 size_t WXDLLEXPORT wxMB2WC(wchar_t *buf, const char *psz, size_t n)
54 {
55 if (buf) {
56 if (!n || !*psz) {
57 if (n) *buf = wxT('\0');
58 return 0;
59 }
60 return mbstowcs(buf, psz, n);
61 }
62
63 // assume that we have mbsrtowcs() too if we have wcsrtombs()
64 #ifdef HAVE_WCSRTOMBS
65 mbstate_t mbstate;
66 return mbsrtowcs((wchar_t *) NULL, &psz, 0, &mbstate);
67 #else // !GNU libc
68 return mbstowcs((wchar_t *) NULL, psz, 0);
69 #endif // GNU
70 }
71
72 size_t WXDLLEXPORT wxWC2MB(char *buf, const wchar_t *pwz, size_t n)
73 {
74 if (buf) {
75 if (!n || !*pwz) {
76 // glibc2.1 chokes on null input
77 if (n) *buf = '\0';
78 return 0;
79 }
80 return wcstombs(buf, pwz, n);
81 }
82
83 #if HAVE_WCSRTOMBS
84 mbstate_t mbstate;
85 return wcsrtombs((char *) NULL, &pwz, 0, &mbstate);
86 #else // !GNU libc
87 return wcstombs((char *) NULL, pwz, 0);
88 #endif // GNU
89 }
90 #endif // wxUSE_WCHAR_T
91
92 bool WXDLLEXPORT wxOKlibc()
93 {
94 #if wxUSE_WCHAR_T && defined(__UNIX__) && defined(__GLIBC__)
95 // glibc 2.0 uses UTF-8 even when it shouldn't
96 wchar_t res = 0;
97 if ((MB_CUR_MAX == 2) &&
98 (wxMB2WC(&res, "\xdd\xa5", 1) == 1) &&
99 (res==0x765)) {
100 // this is UTF-8 allright, check whether that's what we want
101 char *cur_locale = setlocale(LC_CTYPE, NULL);
102 if ((strlen(cur_locale) < 4) ||
103 (strcasecmp(cur_locale + strlen(cur_locale) - 4, "utf8")) ||
104 (strcasecmp(cur_locale + strlen(cur_locale) - 5, "utf-8"))) {
105 // nope, don't use libc conversion
106 return FALSE;
107 }
108 }
109 #endif
110 return TRUE;
111 }
112
113 #ifndef HAVE_WCSLEN
114 size_t WXDLLEXPORT wcslen(const wchar_t *s)
115 {
116 size_t len = 0;
117 while (s[len]) len++;
118 return len;
119 }
120 #endif
121
122 #ifdef wxNEED_PRINTF_CONVERSION
123
124 #define CONVERT_FORMAT_1 \
125 wxChar *new_format = (wxChar*) format; \
126 size_t old_len = wxStrlen( format ); \
127 int n = 0; \
128 size_t i; \
129 for (i = 0; i < old_len; i++) \
130 { \
131 if ( (format[i] == L'%') && \
132 ((i < old_len) && ((format[i+1] == L's') || (format[i+1] == L'c'))) && \
133 ((i == 0) || (format[i-1] != L'%')) ) \
134 { \
135 n++; \
136 } \
137 } \
138 \
139 if (n > 0) \
140 { \
141 new_format = new wxChar[old_len+n+1]; \
142 wxChar *s = new_format; \
143 \
144 for (i = 0; i < old_len+1; i++) \
145 { \
146 if ( (format[i] == L'%') && \
147 ((i < old_len) && ((format[i+1] == L's') || (format[i+1] == L'c'))) && \
148 ((i == 0) || (format[i-1] != L'%')) ) \
149 { \
150 *s = L'%'; \
151 s++; \
152 *s = L'l'; \
153 s++; \
154 } \
155 else \
156 { \
157 *s = format[i]; \
158 s++; \
159 } \
160 } \
161 }
162
163 #define CONVERT_FORMAT_2 \
164 if (n > 0) \
165 delete [] new_format;
166
167
168 int wxScanf( const wxChar *format, ... ) ATTRIBUTE_PRINTF_2
169 {
170 CONVERT_FORMAT_1
171
172 va_list argptr;
173 va_start(argptr, format);
174
175 int ret = vwscanf( new_format, argptr );
176
177 CONVERT_FORMAT_2
178
179 va_end(argptr);
180
181 return ret;
182 }
183
184 int wxSscanf( const wxChar *str, const wxChar *format, ... ) ATTRIBUTE_PRINTF_3
185 {
186 CONVERT_FORMAT_1
187
188 va_list argptr;
189 va_start(argptr, format);
190
191 int ret = vswscanf( str, new_format, argptr );
192
193 CONVERT_FORMAT_2
194
195 va_end(argptr);
196
197 return ret;
198 }
199
200 int wxFscanf( FILE *stream, const wxChar *format, ... ) ATTRIBUTE_PRINTF_3
201 {
202 CONVERT_FORMAT_1
203
204 va_list argptr;
205 va_start(argptr, format);
206
207 int ret = vfwscanf(stream, new_format, argptr);
208
209 CONVERT_FORMAT_2
210
211 va_end(argptr);
212
213 return ret;
214 }
215
216 int wxVsscanf( const wxChar *str, const wxChar *format, va_list ap )
217 {
218 CONVERT_FORMAT_1
219
220 int ret = vswscanf( str, new_format, ap );
221
222 CONVERT_FORMAT_2
223
224 return ret;
225 }
226
227 int wxPrintf( const wxChar *format, ... ) ATTRIBUTE_PRINTF_2
228 {
229 CONVERT_FORMAT_1
230
231 va_list argptr;
232 va_start(argptr, format);
233
234 int ret = vwprintf( new_format, argptr );
235
236 CONVERT_FORMAT_2
237
238 va_end(argptr);
239
240 return ret;
241 }
242
243 int wxSnprintf( wxChar *str, size_t size, const wxChar *format, ... ) ATTRIBUTE_PRINTF_4
244 {
245 CONVERT_FORMAT_1
246
247 va_list argptr;
248 va_start(argptr, format);
249
250 int ret = vswprintf( str, size, new_format, argptr );
251
252 CONVERT_FORMAT_2
253
254 va_end(argptr);
255
256 return ret;
257 }
258
259 int wxSprintf( wxChar *str, const wxChar *format, ... ) ATTRIBUTE_PRINTF_3
260 {
261 CONVERT_FORMAT_1
262
263 va_list argptr;
264 va_start(argptr, format);
265
266 // Ugly
267 int ret = vswprintf( str, 10000, new_format, argptr );
268
269 CONVERT_FORMAT_2
270
271 va_end(argptr);
272
273 return ret;
274 }
275
276 int wxFprintf( FILE *stream, const wxChar *format, ... ) ATTRIBUTE_PRINTF_3
277 {
278 CONVERT_FORMAT_1
279
280 va_list argptr;
281 va_start( argptr, format );
282
283 int ret = vfwprintf( stream, new_format, argptr );
284
285 CONVERT_FORMAT_2
286
287 va_end(argptr);
288
289 return ret;
290 }
291
292 int wxVfprint( FILE *stream, const wxChar *format, va_list ap )
293 {
294 CONVERT_FORMAT_1
295
296 int ret = vfwprintf( stream, new_format, ap );
297
298 CONVERT_FORMAT_2
299
300 return ret;
301 }
302
303 int wxVprintf( const wxChar *format, va_list ap )
304 {
305 CONVERT_FORMAT_1
306
307 int ret = vwprintf( new_format, ap );
308
309 CONVERT_FORMAT_2
310
311 return ret;
312 }
313
314 int wxVsnprintf( wxChar *str, size_t size, const wxChar *format, va_list ap )
315 {
316 CONVERT_FORMAT_1
317
318 int ret = vswprintf( str, size, new_format, ap );
319
320 CONVERT_FORMAT_2
321
322 return ret;
323 }
324
325 int wxVsprintf( wxChar *str, const wxChar *format, va_list ap )
326 {
327 CONVERT_FORMAT_1
328
329 // This is so ugly
330 int ret = vswprintf(str, 10000, new_format, ap);
331
332 CONVERT_FORMAT_2
333
334 return ret;
335 }
336 #endif
337
338 #if !defined(wxVsnprintf) && !defined(wxHAS_VSNPRINTF)
339 int WXDLLEXPORT wxVsnprintf(wxChar *buf, size_t len,
340 const wxChar *format, va_list argptr)
341 {
342 #if wxUSE_UNICODE
343 wxString s;
344 int iLen = s.PrintfV(format, argptr);
345 if ( iLen != -1 )
346 {
347 wxStrncpy(buf, s.c_str(), len);
348 buf[len-1] = wxT('\0');
349 }
350
351 return iLen;
352 #else // ANSI
353 // vsnprintf() will not terminate the string with '\0' if there is not
354 // enough place, but we want the string to always be NUL terminated
355 int rc = wxVsnprintfA(buf, len - 1, format, argptr);
356 if ( rc == -1 )
357 {
358 buf[len] = 0;
359 }
360
361 return rc;
362 #endif // Unicode/ANSI
363 }
364 #endif
365
366 #if !defined(wxSnprintf) && !defined(wxHAS_SNPRINTF)
367 int WXDLLEXPORT wxSnprintf(wxChar *buf, size_t len,
368 const wxChar *format, ...)
369 {
370 va_list argptr;
371 va_start(argptr, format);
372
373 int iLen = wxVsnprintf(buf, len, format, argptr);
374
375 va_end(argptr);
376
377 return iLen;
378 }
379 #endif
380
381 #if defined(__WIN32__) && defined(wxNEED_WX_CTYPE_H)
382 inline WORD wxMSW_ctype(wxChar ch)
383 {
384 WORD ret;
385 GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, &ch, 1, &ret);
386 return ret;
387 }
388
389 WXDLLEXPORT int wxIsalnum(wxChar ch) { return IsCharAlphaNumeric(ch); }
390 WXDLLEXPORT int wxIsalpha(wxChar ch) { return IsCharAlpha(ch); }
391 WXDLLEXPORT int wxIsctrl(wxChar ch) { return wxMSW_ctype(ch) & C1_CNTRL; }
392 WXDLLEXPORT int wxIsdigit(wxChar ch) { return wxMSW_ctype(ch) & C1_DIGIT; }
393 WXDLLEXPORT int wxIsgraph(wxChar ch) { return wxMSW_ctype(ch) & (C1_DIGIT|C1_PUNCT|C1_ALPHA); }
394 WXDLLEXPORT int wxIslower(wxChar ch) { return IsCharLower(ch); }
395 WXDLLEXPORT int wxIsprint(wxChar ch) { return wxMSW_ctype(ch) & (C1_DIGIT|C1_SPACE|C1_PUNCT|C1_ALPHA); }
396 WXDLLEXPORT int wxIspunct(wxChar ch) { return wxMSW_ctype(ch) & C1_PUNCT; }
397 WXDLLEXPORT int wxIsspace(wxChar ch) { return wxMSW_ctype(ch) & C1_SPACE; }
398 WXDLLEXPORT int wxIsupper(wxChar ch) { return IsCharUpper(ch); }
399 WXDLLEXPORT int wxIsxdigit(wxChar ch) { return wxMSW_ctype(ch) & C1_XDIGIT; }
400 WXDLLEXPORT int wxTolower(wxChar ch) { return (wxChar)CharLower((LPTSTR)(ch)); }
401 WXDLLEXPORT int wxToupper(wxChar ch) { return (wxChar)CharUpper((LPTSTR)(ch)); }
402 #endif
403
404 #ifndef wxStrdup
405 WXDLLEXPORT wxChar * wxStrdup(const wxChar *psz)
406 {
407 size_t size = (wxStrlen(psz) + 1) * sizeof(wxChar);
408 wxChar *ret = (wxChar *) malloc(size);
409 memcpy(ret, psz, size);
410 return ret;
411 }
412 #endif
413
414 #ifndef wxStricmp
415 int WXDLLEXPORT wxStricmp(const wxChar *psz1, const wxChar *psz2)
416 {
417 register wxChar c1, c2;
418 do {
419 c1 = wxTolower(*psz1++);
420 c2 = wxTolower(*psz2++);
421 } while ( c1 && (c1 == c2) );
422 return c1 - c2;
423 }
424 #endif
425
426 #ifndef wxStricmp
427 int WXDLLEXPORT wxStrnicmp(const wxChar *s1, const wxChar *s2, size_t n)
428 {
429 register wxChar c1, c2;
430 while (n && ((c1 = wxTolower(*s1)) == (c2 = wxTolower(*s2)) ) && c1) n--, s1++, s2++;
431 if (n) {
432 if (c1 < c2) return -1;
433 if (c1 > c2) return 1;
434 }
435 return 0;
436 }
437 #endif
438
439 #ifndef wxStrtok
440 WXDLLEXPORT wxChar * wxStrtok(wxChar *psz, const wxChar *delim, wxChar **save_ptr)
441 {
442 if (!psz) psz = *save_ptr;
443 psz += wxStrspn(psz, delim);
444 if (!*psz) {
445 *save_ptr = (wxChar *)NULL;
446 return (wxChar *)NULL;
447 }
448 wxChar *ret = psz;
449 psz = wxStrpbrk(psz, delim);
450 if (!psz) *save_ptr = (wxChar*)NULL;
451 else {
452 *psz = wxT('\0');
453 *save_ptr = psz + 1;
454 }
455 return ret;
456 }
457 #endif
458
459 #ifndef wxSetlocale
460 WXDLLEXPORT wxWCharBuffer wxSetlocale(int category, const wxChar *locale)
461 {
462 char *localeOld = setlocale(category, wxConvLocal.cWX2MB(locale));
463
464 return wxWCharBuffer(wxConvLocal.cMB2WC(localeOld));
465 }
466 #endif
467
468 #ifdef wxNEED_WX_STRING_H
469 WXDLLEXPORT wxChar * wxStrcat(wxChar *dest, const wxChar *src)
470 {
471 wxChar *ret = dest;
472 while (*dest) dest++;
473 while ((*dest++ = *src++));
474 return ret;
475 }
476
477 WXDLLEXPORT const wxChar * wxStrchr(const wxChar *s, wxChar c)
478 {
479 // be careful here as the terminating NUL makes part of the string
480 while ( *s != c )
481 {
482 if ( !*s++ )
483 return NULL;
484 }
485
486 return s;
487 }
488
489 WXDLLEXPORT int wxStrcmp(const wxChar *s1, const wxChar *s2)
490 {
491 while ((*s1 == *s2) && *s1) s1++, s2++;
492 if ((wxUChar)*s1 < (wxUChar)*s2) return -1;
493 if ((wxUChar)*s1 > (wxUChar)*s2) return 1;
494 return 0;
495 }
496
497 WXDLLEXPORT wxChar * wxStrcpy(wxChar *dest, const wxChar *src)
498 {
499 wxChar *ret = dest;
500 while ((*dest++ = *src++));
501 return ret;
502 }
503
504 WXDLLEXPORT wxChar * wxStrncat(wxChar *dest, const wxChar *src, size_t n)
505 {
506 wxChar *ret = dest;
507 while (*dest) dest++;
508 while (n && (*dest++ = *src++)) n--;
509 return ret;
510 }
511
512 WXDLLEXPORT int wxStrncmp(const wxChar *s1, const wxChar *s2, size_t n)
513 {
514 while (n && (*s1 == *s2) && *s1) n--, s1++, s2++;
515 if (n) {
516 if ((wxUChar)*s1 < (wxUChar)*s2) return -1;
517 if ((wxUChar)*s1 > (wxUChar)*s2) return 1;
518 }
519 return 0;
520 }
521
522 WXDLLEXPORT wxChar * wxStrncpy(wxChar *dest, const wxChar *src, size_t n)
523 {
524 wxChar *ret = dest;
525 while (n && (*dest++ = *src++)) n--;
526 while (n) *dest++=0, n--; // the docs specify padding with zeroes
527 return ret;
528 }
529
530 WXDLLEXPORT const wxChar * wxStrpbrk(const wxChar *s, const wxChar *accept)
531 {
532 while (*s && !wxStrchr(accept, *s))
533 s++;
534
535 return *s ? s : NULL;
536 }
537
538 WXDLLEXPORT const wxChar * wxStrrchr(const wxChar *s, wxChar c)
539 {
540 const wxChar *ret = NULL;
541 do
542 {
543 if ( *s == c )
544 ret = s;
545 s++;
546 }
547 while ( *s );
548
549 return ret;
550 }
551
552 WXDLLEXPORT size_t wxStrspn(const wxChar *s, const wxChar *accept)
553 {
554 size_t len = 0;
555 while (wxStrchr(accept, *s++)) len++;
556 return len;
557 }
558
559 WXDLLEXPORT const wxChar *wxStrstr(const wxChar *haystack, const wxChar *needle)
560 {
561 wxCHECK_RET( needle, NULL, _T("NULL argument in wxStrstr") );
562
563 // VZ: this is not exactly the most efficient string search algorithm...
564
565 const size_t len = wxStrlen(needle);
566
567 while ( const wxChar *fnd = wxStrchr(haystack, *needle) )
568 {
569 if ( !wxStrncmp(fnd, needle, len) )
570 return fnd;
571
572 haystack = fnd + 1;
573 }
574
575 return NULL;
576 }
577
578 WXDLLEXPORT double wxStrtod(const wxChar *nptr, wxChar **endptr)
579 {
580 const wxChar *start = nptr;
581
582 // FIXME: only correct for C locale
583 while (wxIsspace(*nptr)) nptr++;
584 if (*nptr == wxT('+') || *nptr == wxT('-')) nptr++;
585 while (wxIsdigit(*nptr)) nptr++;
586 if (*nptr == wxT('.')) {
587 nptr++;
588 while (wxIsdigit(*nptr)) nptr++;
589 }
590 if (*nptr == wxT('E') || *nptr == wxT('e')) {
591 nptr++;
592 if (*nptr == wxT('+') || *nptr == wxT('-')) nptr++;
593 while (wxIsdigit(*nptr)) nptr++;
594 }
595
596 wxString data(nptr, nptr-start);
597 wxWX2MBbuf dat = data.mb_str(wxConvLocal);
598 char *rdat = wxMBSTRINGCAST dat;
599 double ret = strtod(dat, &rdat);
600
601 if (endptr) *endptr = (wxChar *)(start + (rdat - (const char *)dat));
602
603 return ret;
604 }
605
606 WXDLLEXPORT long int wxStrtol(const wxChar *nptr, wxChar **endptr, int base)
607 {
608 const wxChar *start = nptr;
609
610 // FIXME: only correct for C locale
611 while (wxIsspace(*nptr)) nptr++;
612 if (*nptr == wxT('+') || *nptr == wxT('-')) nptr++;
613 if (((base == 0) || (base == 16)) &&
614 (nptr[0] == wxT('0') && nptr[1] == wxT('x'))) {
615 nptr += 2;
616 base = 16;
617 }
618 else if ((base == 0) && (nptr[0] == wxT('0'))) base = 8;
619 else if (base == 0) base = 10;
620
621 while ((wxIsdigit(*nptr) && (*nptr - wxT('0') < base)) ||
622 (wxIsalpha(*nptr) && (wxToupper(*nptr) - wxT('A') + 10 < base))) nptr++;
623
624 wxString data(nptr, nptr-start);
625 wxWX2MBbuf dat = data.mb_str(wxConvLocal);
626 char *rdat = wxMBSTRINGCAST dat;
627 long int ret = strtol(dat, &rdat, base);
628
629 if (endptr) *endptr = (wxChar *)(start + (rdat - (const char *)dat));
630
631 return ret;
632 }
633 #endif
634
635 #ifdef wxNEED_WX_STDIO_H
636 WXDLLEXPORT FILE * wxFopen(const wxChar *path, const wxChar *mode)
637 {
638 char mode_buffer[10];
639 for (size_t i = 0; i < wxStrlen(mode)+1; i++)
640 mode_buffer[i] = (char) mode[i];
641
642 return fopen( wxConvFile.cWX2MB(path), mode_buffer );
643 }
644
645 WXDLLEXPORT FILE * wxFreopen(const wxChar *path, const wxChar *mode, FILE *stream)
646 {
647 char mode_buffer[10];
648 for (size_t i = 0; i < wxStrlen(mode)+1; i++)
649 mode_buffer[i] = (char) mode[i];
650
651 return freopen( wxConvFile.cWX2MB(path), mode_buffer, stream );
652 }
653
654 WXDLLEXPORT int wxRemove(const wxChar *path)
655 {
656 return remove( wxConvFile.cWX2MB(path) );
657 }
658
659 WXDLLEXPORT int wxRename(const wxChar *oldpath, const wxChar *newpath)
660 {
661 return rename( wxConvFile.cWX2MB(oldpath), wxConvFile.cWX2MB(newpath) );
662 }
663 #endif
664
665 #ifndef wxAtof
666 double WXDLLEXPORT wxAtof(const wxChar *psz)
667 {
668 return atof(wxConvLocal.cWX2MB(psz));
669 }
670 #endif
671
672 #ifdef wxNEED_WX_STDLIB_H
673 int WXDLLEXPORT wxAtoi(const wxChar *psz)
674 {
675 return atoi(wxConvLocal.cWX2MB(psz));
676 }
677
678 long WXDLLEXPORT wxAtol(const wxChar *psz)
679 {
680 return atol(wxConvLocal.cWX2MB(psz));
681 }
682
683 wxChar * WXDLLEXPORT wxGetenv(const wxChar *name)
684 {
685 static wxHashTable env;
686
687 // check if we already have stored the converted env var
688 wxObject *data = env.Get(name);
689 if (!data)
690 {
691 // nope, retrieve it,
692 #if wxUSE_UNICODE
693 wxCharBuffer buffer = wxConvLocal.cWX2MB(name);
694 // printf( "buffer %s\n", (const char*) buffer );
695 const char *val = getenv( (const char *)buffer );
696 #else
697 const char *val = getenv( name );
698 #endif
699
700 if (!val) return (wxChar *)NULL;
701 // printf( "home %s\n", val );
702
703 // convert it,
704 #ifdef wxUSE_UNICODE
705 data = (wxObject *)new wxString(val, wxConvLocal);
706 #else
707 data = (wxObject *)new wxString(val);
708 #endif
709
710 // and store it
711 env.Put(name, data);
712 }
713 // return converted env var
714 return (wxChar *)((wxString *)data)->c_str();
715 }
716
717 int WXDLLEXPORT wxSystem(const wxChar *psz)
718 {
719 return system(wxConvLocal.cWX2MB(psz));
720 }
721
722 #endif
723
724 #ifdef wxNEED_WX_TIME_H
725 WXDLLEXPORT size_t wxStrftime(wxChar *s, size_t max, const wxChar *fmt, const struct tm *tm)
726 {
727 if (!max) return 0;
728
729 char *buf = (char *)malloc(max);
730 size_t ret = strftime(buf, max, wxConvLocal.cWX2MB(fmt), tm);
731 if (ret)
732 {
733 wxStrcpy(s, wxConvLocal.cMB2WX(buf));
734 free(buf);
735 return wxStrlen(s);
736 }
737 else
738 {
739 free(buf);
740 *s = 0;
741 return 0;
742 }
743 }
744 #endif