msvcrt: Added wcstombs_s implementation.
[wine] / dlls / msvcrt / wcs.c
1 /*
2  * msvcrt.dll wide-char functions
3  *
4  * Copyright 1999 Alexandre Julliard
5  * Copyright 2000 Jon Griffiths
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <limits.h>
25 #include <stdio.h>
26 #include <math.h>
27 #include <assert.h>
28 #include "msvcrt.h"
29 #include "winnls.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
34
35
36 /*********************************************************************
37  *              _wcsdup (MSVCRT.@)
38  */
39 MSVCRT_wchar_t* CDECL _wcsdup( const MSVCRT_wchar_t* str )
40 {
41   MSVCRT_wchar_t* ret = NULL;
42   if (str)
43   {
44     int size = (strlenW(str) + 1) * sizeof(MSVCRT_wchar_t);
45     ret = MSVCRT_malloc( size );
46     if (ret) memcpy( ret, str, size );
47   }
48   return ret;
49 }
50
51 /*********************************************************************
52  *              _wcsicoll (MSVCRT.@)
53  */
54 INT CDECL _wcsicoll( const MSVCRT_wchar_t* str1, const MSVCRT_wchar_t* str2 )
55 {
56   /* FIXME: handle collates */
57   return strcmpiW( str1, str2 );
58 }
59
60 /*********************************************************************
61  *              _wcsnset (MSVCRT.@)
62  */
63 MSVCRT_wchar_t* CDECL MSVCRT__wcsnset( MSVCRT_wchar_t* str, MSVCRT_wchar_t c, MSVCRT_size_t n )
64 {
65   MSVCRT_wchar_t* ret = str;
66   while ((n-- > 0) && *str) *str++ = c;
67   return ret;
68 }
69
70 /*********************************************************************
71  *              _wcsrev (MSVCRT.@)
72  */
73 MSVCRT_wchar_t* CDECL _wcsrev( MSVCRT_wchar_t* str )
74 {
75   MSVCRT_wchar_t* ret = str;
76   MSVCRT_wchar_t* end = str + strlenW(str) - 1;
77   while (end > str)
78   {
79     MSVCRT_wchar_t t = *end;
80     *end--  = *str;
81     *str++  = t;
82   }
83   return ret;
84 }
85
86 /*********************************************************************
87  *              _wcsset (MSVCRT.@)
88  */
89 MSVCRT_wchar_t* CDECL _wcsset( MSVCRT_wchar_t* str, MSVCRT_wchar_t c )
90 {
91   MSVCRT_wchar_t* ret = str;
92   while (*str) *str++ = c;
93   return ret;
94 }
95
96 /******************************************************************
97  *              _wcsupr_s (MSVCRT.@)
98  *
99  */
100 INT CDECL MSVCRT__wcsupr_s( MSVCRT_wchar_t* str, MSVCRT_size_t n )
101 {
102   MSVCRT_wchar_t* ptr = str;
103
104   if (!str || !n)
105   {
106     if (str) *str = '\0';
107     *MSVCRT__errno() = MSVCRT_EINVAL;
108     return MSVCRT_EINVAL;
109   }
110
111   while (n--)
112   {
113     if (!*ptr) return 0;
114     *ptr = toupperW(*ptr);
115     ptr++;
116   }
117
118   /* MSDN claims that the function should return and set errno to
119    * ERANGE, which doesn't seem to be true based on the tests. */
120   *str = '\0';
121   *MSVCRT__errno() = MSVCRT_EINVAL;
122   return MSVCRT_EINVAL;
123 }
124
125 /*********************************************************************
126  * _wcstod_l - not exported in native msvcrt
127  */
128 double CDECL MSVCRT__wcstod_l(const MSVCRT_wchar_t* str, MSVCRT_wchar_t** end,
129         MSVCRT__locale_t locale)
130 {
131     unsigned __int64 d=0, hlp;
132     int exp=0, sign=1;
133     const MSVCRT_wchar_t *p;
134     double ret;
135
136     if(!str) {
137         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
138         *MSVCRT__errno() = MSVCRT_EINVAL;
139         return 0;
140     }
141
142     if(!locale)
143         locale = get_locale();
144
145     p = str;
146     while(isspaceW(*p))
147         p++;
148
149     if(*p == '-') {
150         sign = -1;
151         p++;
152     } else  if(*p == '+')
153         p++;
154
155     while(isdigitW(*p)) {
156         hlp = d*10+*(p++)-'0';
157         if(d>MSVCRT_UI64_MAX/10 || hlp<d) {
158             exp++;
159             break;
160         } else
161             d = hlp;
162     }
163     while(isdigitW(*p)) {
164         exp++;
165         p++;
166     }
167     if(*p == *locale->locinfo->lconv->decimal_point)
168         p++;
169
170     while(isdigitW(*p)) {
171         hlp = d*10+*(p++)-'0';
172         if(d>MSVCRT_UI64_MAX/10 || hlp<d)
173             break;
174
175         d = hlp;
176         exp--;
177     }
178     while(isdigitW(*p))
179         p++;
180
181     if(p == str) {
182         if(end)
183             *end = (MSVCRT_wchar_t*)str;
184         return 0.0;
185     }
186
187     if(*p=='e' || *p=='E' || *p=='d' || *p=='D') {
188         int e=0, s=1;
189
190         p++;
191         if(*p == '-') {
192             s = -1;
193             p++;
194         } else if(*p == '+')
195             p++;
196
197         if(isdigitW(*p)) {
198             while(isdigitW(*p)) {
199                 if(e>INT_MAX/10 || (e=e*10+*p-'0')<0)
200                     e = INT_MAX;
201                 p++;
202             }
203             e *= s;
204
205             if(exp<0 && e<0 && exp+e>=0) exp = INT_MIN;
206             else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
207             else exp += e;
208         } else {
209             if(*p=='-' || *p=='+')
210                 p--;
211             p--;
212         }
213     }
214
215     if(exp>0)
216         ret = (double)sign*d*pow(10, exp);
217     else
218         ret = (double)sign*d/pow(10, -exp);
219
220     if((d && ret==0.0) || isinf(ret))
221         *MSVCRT__errno() = MSVCRT_ERANGE;
222
223     if(end)
224         *end = (MSVCRT_wchar_t*)p;
225
226     return ret;
227 }
228
229 /*********************************************************************
230  *              _wcstombs_s_l (MSVCRT.@)
231  */
232 int CDECL MSVCRT__wcstombs_s_l(MSVCRT_size_t *ret, char *mbstr,
233         MSVCRT_size_t size, const MSVCRT_wchar_t *wcstr,
234         MSVCRT_size_t count, MSVCRT__locale_t locale)
235 {
236     char default_char = '\0', *p;
237     int hlp, len;
238
239     if(!size)
240         return 0;
241
242     if(!mbstr || !wcstr) {
243         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
244         if(mbstr)
245             *mbstr = '\0';
246         *MSVCRT__errno() = MSVCRT_EINVAL;
247         return MSVCRT_EINVAL;
248     }
249
250     if(!locale)
251         locale = get_locale();
252
253     if(size<=count)
254         len = size;
255     else if(count==_TRUNCATE)
256         len = size-1;
257     else
258         len = count;
259
260     p = mbstr;
261     *ret = 0;
262     while(1) {
263         if(!len)
264             break;
265
266         if(*wcstr == '\0') {
267             *p = '\0';
268             break;
269         }
270
271         hlp = WideCharToMultiByte(locale->locinfo->lc_codepage,
272                 WC_NO_BEST_FIT_CHARS, wcstr, 1, p, len, &default_char, NULL);
273         if(!hlp || *p=='\0')
274             break;
275
276         p += hlp;
277         len -= hlp;
278
279         wcstr++;
280         *ret += 1;
281     }
282
283     if(!len && size<=count) {
284         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
285         *mbstr = '\0';
286         *MSVCRT__errno() = MSVCRT_ERANGE;
287         return MSVCRT_ERANGE;
288     }
289
290     if(*wcstr == '\0')
291         *ret += 1;
292     *p = '\0';
293     return 0;
294 }
295
296 /*********************************************************************
297  *              wcstombs_s (MSVCRT.@)
298  */
299 int CDECL MSVCRT_wcstombs_s(MSVCRT_size_t *ret, char *mbstr,
300         MSVCRT_size_t size, const MSVCRT_wchar_t *wcstr, MSVCRT_size_t count)
301 {
302     return MSVCRT__wcstombs_s_l(ret, mbstr, size, wcstr, count, NULL);
303 }
304
305 /*********************************************************************
306  *              wcstod (MSVCRT.@)
307  */
308 double CDECL MSVCRT_wcstod(const MSVCRT_wchar_t* lpszStr, MSVCRT_wchar_t** end)
309 {
310     return MSVCRT__wcstod_l(lpszStr, end, NULL);
311 }
312
313 /*********************************************************************
314  *              _wtof (MSVCRT.@)
315  */
316 double CDECL MSVCRT__wtof(const MSVCRT_wchar_t *str)
317 {
318     return MSVCRT__wcstod_l(str, NULL, NULL);
319 }
320
321 /*********************************************************************
322  *              _wtof_l (MSVCRT.@)
323  */
324 double CDECL MSVCRT__wtof_l(const MSVCRT_wchar_t *str, MSVCRT__locale_t locale)
325 {
326     return MSVCRT__wcstod_l(str, NULL, locale);
327 }
328
329 typedef struct pf_output_t
330 {
331     int used;
332     int len;
333     BOOL unicode;
334     union {
335         LPWSTR W;
336         LPSTR  A;
337     } buf;
338 } pf_output;
339
340 typedef struct pf_flags_t
341 {
342     char Sign, LeftAlign, Alternate, PadZero;
343     int FieldLength, Precision;
344     char IntegerLength, IntegerDouble;
345     char WideString;
346     char Format;
347 } pf_flags;
348
349 /*
350  * writes a string of characters to the output
351  * returns -1 if the string doesn't fit in the output buffer
352  * return the length of the string if all characters were written
353  */
354 static inline int pf_output_stringW( pf_output *out, LPCWSTR str, int len )
355 {
356     int space = out->len - out->used;
357
358     if( len < 0 )
359         len = strlenW( str );
360     if( out->unicode )
361     {
362         LPWSTR p = out->buf.W + out->used;
363
364         if( space >= len )
365         {
366             if (out->buf.W) memcpy( p, str, len*sizeof(WCHAR) );
367             out->used += len;
368             return len;
369         }
370         if( space > 0 && out->buf.W )
371             memcpy( p, str, space*sizeof(WCHAR) );
372         out->used += len;
373     }
374     else
375     {
376         int n = WideCharToMultiByte( CP_ACP, 0, str, len, NULL, 0, NULL, NULL );
377         LPSTR p = out->buf.A + out->used;
378
379         if( space >= n )
380         {
381             if (out->buf.A) WideCharToMultiByte( CP_ACP, 0, str, len, p, n, NULL, NULL );
382             out->used += n;
383             return len;
384         }
385         if( space > 0 && out->buf.A )
386             WideCharToMultiByte( CP_ACP, 0, str, len, p, space, NULL, NULL );
387         out->used += n;
388     }
389     return -1;
390 }
391
392 static inline int pf_output_stringA( pf_output *out, LPCSTR str, int len )
393 {
394     int space = out->len - out->used;
395
396     if( len < 0 )
397         len = strlen( str );
398     if( !out->unicode )
399     {
400         LPSTR p = out->buf.A + out->used;
401
402         if( space >= len )
403         {
404             if (out->buf.A) memcpy( p, str, len );
405             out->used += len;
406             return len;
407         }
408         if( space > 0 && out->buf.A )
409             memcpy( p, str, space );
410         out->used += len;
411     }
412     else
413     {
414         int n = MultiByteToWideChar( CP_ACP, 0, str, len, NULL, 0 );
415         LPWSTR p = out->buf.W + out->used;
416
417         if( space >= n )
418         {
419             if (out->buf.W) MultiByteToWideChar( CP_ACP, 0, str, len, p, n );
420             out->used += n;
421             return len;
422         }
423         if( space > 0 && out->buf.W )
424             MultiByteToWideChar( CP_ACP, 0, str, len, p, space );
425         out->used += n;
426     }
427     return -1;
428 }
429
430 /* pf_fill: takes care of signs, alignment, zero and field padding */
431 static inline int pf_fill( pf_output *out, int len, pf_flags *flags, char left )
432 {
433     int i, r = 0;
434
435     if( flags->Sign && !( flags->Format == 'd' || flags->Format == 'i' ) )
436         flags->Sign = 0;
437
438     if( left && flags->Sign )
439     {
440         flags->FieldLength--;
441         if( flags->PadZero )
442             r = pf_output_stringA( out, &flags->Sign, 1 );
443     }
444
445     if( ( !left &&  flags->LeftAlign ) || 
446         (  left && !flags->LeftAlign ))
447     {
448         for( i=0; (i<(flags->FieldLength-len)) && (r>=0); i++ )
449         {
450             if( left && flags->PadZero )
451                 r = pf_output_stringA( out, "0", 1 );
452             else
453                 r = pf_output_stringA( out, " ", 1 );
454         }
455     }
456
457     if( left && flags->Sign && !flags->PadZero )
458         r = pf_output_stringA( out, &flags->Sign, 1 );
459
460     return r;
461 }
462
463 static inline int pf_output_format_W( pf_output *out, LPCWSTR str,
464                                       int len, pf_flags *flags )
465 {
466     int r = 0;
467
468     if( len < 0 )
469         len = strlenW( str );
470
471     if (flags->Precision >= 0 && flags->Precision < len)
472         len = flags->Precision;
473
474     r = pf_fill( out, len, flags, 1 );
475
476     if( r>=0 )
477         r = pf_output_stringW( out, str, len );
478
479     if( r>=0 )
480         r = pf_fill( out, len, flags, 0 );
481
482     return r;
483 }
484
485 static inline int pf_output_format_A( pf_output *out, LPCSTR str,
486                                       int len, pf_flags *flags )
487 {
488     int r = 0;
489
490     if( len < 0 )
491         len = strlen( str );
492
493     if (flags->Precision >= 0 && flags->Precision < len)
494         len = flags->Precision;
495
496     r = pf_fill( out, len, flags, 1 );
497
498     if( r>=0 )
499         r = pf_output_stringA( out, str, len );
500
501     if( r>=0 )
502         r = pf_fill( out, len, flags, 0 );
503
504     return r;
505 }
506
507 static int pf_handle_string_format( pf_output *out, const void* str, int len,
508                              pf_flags *flags, BOOL capital_letter)
509 {
510      if(str == NULL)  /* catch NULL pointer */
511         return pf_output_format_A( out, "(null)", -1, flags);
512
513      /* prefixes take priority over %c,%s vs. %C,%S, so we handle them first */
514     if(flags->WideString || flags->IntegerLength == 'l')
515         return pf_output_format_W( out, str, len, flags);
516     if(flags->IntegerLength == 'h')
517         return pf_output_format_A( out, str, len, flags);
518
519     /* %s,%c ->  chars in ansi functions & wchars in unicode
520      * %S,%C -> wchars in ansi functions &  chars in unicode */
521     if( capital_letter == out->unicode) /* either both TRUE or both FALSE */
522         return pf_output_format_A( out, str, len, flags);
523     else
524         return pf_output_format_W( out, str, len, flags);
525 }
526
527 static inline BOOL pf_is_integer_format( char fmt )
528 {
529     static const char float_fmts[] = "diouxX";
530     if (!fmt)
531         return FALSE;
532     return strchr( float_fmts, fmt ) ? TRUE : FALSE;
533 }
534
535 static inline BOOL pf_is_double_format( char fmt )
536 {
537     static const char float_fmts[] = "aeEfgG";
538     if (!fmt)
539         return FALSE;
540     return strchr( float_fmts, fmt ) ? TRUE : FALSE;
541 }
542
543 static inline BOOL pf_is_valid_format( char fmt )
544 {
545     static const char float_fmts[] = "acCdeEfgGinouxX";
546     if (!fmt)
547         return FALSE;
548     return strchr( float_fmts, fmt ) ? TRUE : FALSE;
549 }
550
551 static void pf_rebuild_format_string( char *p, pf_flags *flags )
552 {
553     *p++ = '%';
554     if( flags->Sign )
555         *p++ = flags->Sign;
556     if( flags->LeftAlign )
557         *p++ = flags->LeftAlign;
558     if( flags->Alternate )
559         *p++ = flags->Alternate;
560     if( flags->PadZero )
561         *p++ = flags->PadZero;
562     if( flags->FieldLength )
563     {
564         sprintf(p, "%d", flags->FieldLength);
565         p += strlen(p);
566     }
567     if( flags->Precision >= 0 )
568     {
569         sprintf(p, ".%d", flags->Precision);
570         p += strlen(p);
571     }
572     *p++ = flags->Format;
573     *p++ = 0;
574 }
575
576 /* pf_integer_conv:  prints x to buf, including alternate formats and
577    additional precision digits, but not field characters or the sign */
578 static void pf_integer_conv( char *buf, int buf_len, pf_flags *flags,
579                              LONGLONG x )
580 {
581     unsigned int base;
582     const char *digits;
583
584     int i, j, k;
585     char number[40], *tmp = number;
586
587     if( buf_len > sizeof number )
588         tmp = HeapAlloc( GetProcessHeap(), 0, buf_len );
589
590     base = 10;
591     if( flags->Format == 'o' )
592         base = 8;
593     else if( flags->Format == 'x' || flags->Format == 'X' )
594         base = 16;
595
596     if( flags->Format == 'X' )
597         digits = "0123456789ABCDEFX";
598     else
599         digits = "0123456789abcdefx";
600
601     if( x < 0 && ( flags->Format == 'd' || flags->Format == 'i' ) )
602     {
603         x = -x;
604         flags->Sign = '-';
605     }
606
607     /* Do conversion (backwards) */
608     i = 0;
609     if( x == 0 && flags->Precision )
610         tmp[i++] = '0';
611     else
612         while( x != 0 )
613         {
614             j = (ULONGLONG) x % base;
615             x = (ULONGLONG) x / base;
616             tmp[i++] = digits[j];
617         }
618     k = flags->Precision - i;
619     while( k-- > 0 )
620         tmp[i++] = '0';
621     if( flags->Alternate )
622     {
623         if( base == 16 )
624         {
625             tmp[i++] = digits[16];
626             tmp[i++] = '0';
627         }
628         else if( base == 8 && tmp[i-1] != '0' )
629             tmp[i++] = '0';
630     }
631
632     /* Reverse for buf */
633     j = 0;
634     while( i-- > 0 )
635         buf[j++] = tmp[i];
636     buf[j] = '\0';
637
638     /* Adjust precision so pf_fill won't truncate the number later */
639     flags->Precision = strlen( buf );
640
641     if( tmp != number )
642         HeapFree( GetProcessHeap(), 0, tmp );
643
644     return;
645 }
646
647 /* pf_fixup_exponent: convert a string containing a 2 digit exponent
648    to 3 digits, accounting for padding, in place. Needed to match
649    the native printf's which always use 3 digits. */
650 static void pf_fixup_exponent( char *buf )
651 {
652     char* tmp = buf;
653
654     while (tmp[0] && toupper(tmp[0]) != 'E')
655         tmp++;
656
657     if (tmp[0] && (tmp[1] == '+' || tmp[1] == '-') &&
658         isdigit(tmp[2]) && isdigit(tmp[3]))
659     {
660         char final;
661
662         if (isdigit(tmp[4]))
663             return; /* Exponent already 3 digits */
664
665         /* We have a 2 digit exponent. Prepend '0' to make it 3 */
666         tmp += 2;
667         final = tmp[2];
668         tmp[2] = tmp[1];
669         tmp[1] = tmp[0];
670         tmp[0] = '0';
671         if (final == '\0')
672         {
673             /* We didn't expand into trailing space, so this string isn't left
674              * justified. Terminate the string and strip a ' ' at the start of
675              * the string if there is one (as there may be if the string is
676              * right justified).
677              */
678             tmp[3] = '\0';
679             if (buf[0] == ' ')
680                 memmove(buf, buf + 1, (tmp - buf) + 3);
681         }
682         /* Otherwise, we expanded into trailing space -> nothing to do */
683     }
684 }
685
686 /*********************************************************************
687  *  pf_vsnprintf  (INTERNAL)
688  *
689  *  implements both A and W vsnprintf functions
690  */
691 static int pf_vsnprintf( pf_output *out, const WCHAR *format,
692         MSVCRT__locale_t locale, BOOL valid, __ms_va_list valist )
693 {
694     int r;
695     LPCWSTR q, p = format;
696     pf_flags flags;
697
698     if(!locale)
699         locale = get_locale();
700
701     TRACE("format is %s\n",debugstr_w(format));
702     while (*p)
703     {
704         q = strchrW( p, '%' );
705
706         /* there's no % characters left, output the rest of the string */
707         if( !q )
708         {
709             r = pf_output_stringW(out, p, -1);
710             if( r<0 )
711                 return r;
712             p += r;
713             continue;
714         }
715
716         /* there's characters before the %, output them */
717         if( q != p )
718         {
719             r = pf_output_stringW(out, p, q - p);
720             if( r<0 )
721                 return r;
722             p = q;
723         }
724
725         /* we must be at a % now, skip over it */
726         assert( *p == '%' );
727         p++;
728
729         /* output a single % character */
730         if( *p == '%' )
731         {
732             r = pf_output_stringW(out, p++, 1);
733             if( r<0 )
734                 return r;
735             continue;
736         }
737
738         /* parse the flags */
739         memset( &flags, 0, sizeof flags );
740         while (*p)
741         {
742             if( *p == '+' || *p == ' ' )
743             {
744                 if ( flags.Sign != '+' )
745                     flags.Sign = *p;
746             }
747             else if( *p == '-' )
748                 flags.LeftAlign = *p;
749             else if( *p == '0' )
750                 flags.PadZero = *p;
751             else if( *p == '#' )
752                 flags.Alternate = *p;
753             else
754                 break;
755             p++;
756         }
757
758         /* deal with the field width specifier */
759         flags.FieldLength = 0;
760         if( *p == '*' )
761         {
762             flags.FieldLength = va_arg( valist, int );
763             if (flags.FieldLength < 0)
764             {
765                 flags.LeftAlign = '-';
766                 flags.FieldLength = -flags.FieldLength;
767             }
768             p++;
769         }
770         else while( isdigit(*p) )
771         {
772             flags.FieldLength *= 10;
773             flags.FieldLength += *p++ - '0';
774         }
775
776         /* deal with precision */
777         flags.Precision = -1;
778         if( *p == '.' )
779         {
780             flags.Precision = 0;
781             p++;
782             if( *p == '*' )
783             {
784                 flags.Precision = va_arg( valist, int );
785                 p++;
786             }
787             else while( isdigit(*p) )
788             {
789                 flags.Precision *= 10;
790                 flags.Precision += *p++ - '0';
791             }
792         }
793
794         /* deal with integer width modifier */
795         while( *p )
796         {
797             if( *p == 'h' || *p == 'l' || *p == 'L' )
798             {
799                 flags.IntegerLength = *p;
800                 p++;
801             }
802             else if( *p == 'I' )
803             {
804                 if( *(p+1) == '6' && *(p+2) == '4' )
805                 {
806                     flags.IntegerDouble++;
807                     p += 3;
808                 }
809                 else if( *(p+1) == '3' && *(p+2) == '2' )
810                     p += 3;
811                 else if( isdigit(*(p+1)) || *(p+1) == 0 )
812                     break;
813                 else
814                     p++;
815             }
816             else if( *p == 'w' )
817                 flags.WideString = *p++;
818             else if( *p == 'F' )
819                 p++; /* ignore */
820             else
821                 break;
822         }
823
824         flags.Format = *p;
825         r = 0;
826
827         if (flags.Format == '$')
828         {
829             FIXME("Positional parameters are not supported (%s)\n", wine_dbgstr_w(format));
830             return -1;
831         }
832         /* output a string */
833         if(  flags.Format == 's' || flags.Format == 'S' )
834             r = pf_handle_string_format( out, va_arg(valist, const void*), -1,
835                                          &flags, (flags.Format == 'S') );
836
837         /* output a single character */
838         else if( flags.Format == 'c' || flags.Format == 'C' )
839         {
840             INT ch = va_arg( valist, int );
841
842             r = pf_handle_string_format( out, &ch, 1, &flags, (flags.Format == 'C') );
843         }
844
845         /* output a pointer */
846         else if( flags.Format == 'p' )
847         {
848             char pointer[32];
849             void *ptr = va_arg( valist, void * );
850
851             flags.PadZero = 0;
852             if( flags.Alternate )
853                 sprintf(pointer, "0X%0*lX", 2 * (int)sizeof(ptr), (ULONG_PTR)ptr);
854             else
855                 sprintf(pointer, "%0*lX", 2 * (int)sizeof(ptr), (ULONG_PTR)ptr);
856             r = pf_output_format_A( out, pointer, -1, &flags );
857         }
858
859         /* deal with %n */
860         else if( flags.Format == 'n' )
861         {
862             int *x = va_arg(valist, int *);
863             *x = out->used;
864         }
865
866         /* deal with 64-bit integers */
867         else if( pf_is_integer_format( flags.Format ) && flags.IntegerDouble )
868         {
869             char number[40], *x = number;
870
871             /* Estimate largest possible required buffer size:
872                * Chooses the larger of the field or precision
873                * Includes extra bytes: 1 byte for null, 1 byte for sign,
874                  4 bytes for exponent, 2 bytes for alternate formats, 1 byte 
875                  for a decimal, and 1 byte for an additional float digit. */
876             int x_len = ((flags.FieldLength > flags.Precision) ? 
877                         flags.FieldLength : flags.Precision) + 10;
878
879             if( x_len >= sizeof number)
880                 x = HeapAlloc( GetProcessHeap(), 0, x_len );
881
882             pf_integer_conv( x, x_len, &flags, va_arg(valist, LONGLONG) );
883
884             r = pf_output_format_A( out, x, -1, &flags );
885             if( x != number )
886                 HeapFree( GetProcessHeap(), 0, x );
887         }
888
889         /* deal with integers and floats using libc's printf */
890         else if( pf_is_valid_format( flags.Format ) )
891         {
892             char fmt[20], number[40], *x = number, *decimal_point;
893
894             /* Estimate largest possible required buffer size:
895                * Chooses the larger of the field or precision
896                * Includes extra bytes: 1 byte for null, 1 byte for sign,
897                  4 bytes for exponent, 2 bytes for alternate formats, 1 byte 
898                  for a decimal, and 1 byte for an additional float digit. */
899             int x_len = ((flags.FieldLength > flags.Precision) ? 
900                         flags.FieldLength : flags.Precision) + 10;
901
902             if( x_len >= sizeof number)
903                 x = HeapAlloc( GetProcessHeap(), 0, x_len );
904
905             pf_rebuild_format_string( fmt, &flags );
906
907             if( pf_is_double_format( flags.Format ) )
908             {
909                 sprintf( x, fmt, va_arg(valist, double) );
910                 if (toupper(flags.Format) == 'E' || toupper(flags.Format) == 'G')
911                     pf_fixup_exponent( x );
912             }
913             else
914                 sprintf( x, fmt, va_arg(valist, int) );
915
916             decimal_point = strchr(x, '.');
917             if(decimal_point)
918                 *decimal_point = *locale->locinfo->lconv->decimal_point;
919
920             r = pf_output_stringA( out, x, -1 );
921             if( x != number )
922                 HeapFree( GetProcessHeap(), 0, x );
923             if(r < 0)
924                 return r;
925         }
926         else
927         {
928             if( valid )
929             {
930                 MSVCRT__invalid_parameter( NULL, NULL, NULL, 0, 0 );
931                 *MSVCRT__errno() = MSVCRT_EINVAL;
932                 return -1;
933             }
934
935             continue;
936         }
937
938         if( r<0 )
939             return r;
940         p++;
941     }
942
943     /* check we reached the end, and null terminate the string */
944     assert( *p == 0 );
945     pf_output_stringW( out, p, 1 );
946
947     return out->used - 1;
948 }
949
950 /*********************************************************************
951  * vsnprintf_internal (INTERNAL)
952  */
953 static inline int vsnprintf_internal( char *str, MSVCRT_size_t len, const char *format,
954         MSVCRT__locale_t locale, BOOL valid, __ms_va_list valist )
955 {
956     DWORD sz;
957     LPWSTR formatW = NULL;
958     pf_output out;
959     int r;
960
961     out.unicode = FALSE;
962     out.buf.A = str;
963     out.used = 0;
964     out.len = len;
965
966     sz = MultiByteToWideChar( CP_ACP, 0, format, -1, NULL, 0 );
967     formatW = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
968     MultiByteToWideChar( CP_ACP, 0, format, -1, formatW, sz );
969
970     r = pf_vsnprintf( &out, formatW, locale, valid, valist );
971
972     HeapFree( GetProcessHeap(), 0, formatW );
973
974     return r;
975 }
976
977 /*********************************************************************
978  *              _vsnprintf (MSVCRT.@)
979  */
980 int CDECL MSVCRT_vsnprintf( char *str, MSVCRT_size_t len,
981                             const char *format, __ms_va_list valist )
982 {
983     return vsnprintf_internal(str, len, format, NULL, FALSE, valist);
984 }
985
986 /*********************************************************************
987 *               _vsnprintf_l (MSVCRT.@)
988  */
989 int CDECL MSVCRT_vsnprintf_l( char *str, MSVCRT_size_t len, const char *format,
990                             MSVCRT__locale_t locale, __ms_va_list valist )
991 {
992     return vsnprintf_internal(str, len, format, locale, FALSE, valist);
993 }
994
995 /*********************************************************************
996  *              _vsnprintf_s_l (MSVCRT.@)
997  */
998 int CDECL MSVCRT_vsnprintf_s_l( char *str, MSVCRT_size_t sizeOfBuffer,
999         MSVCRT_size_t count, const char *format,
1000         MSVCRT__locale_t locale, __ms_va_list valist )
1001 {
1002     int len, ret;
1003
1004     if(sizeOfBuffer<count+1 || count==-1)
1005         len = sizeOfBuffer;
1006     else
1007         len = count+1;
1008
1009     ret = vsnprintf_internal(str, len, format, locale, TRUE, valist);
1010
1011     if(ret<0 || ret==len) {
1012         if(count!=_TRUNCATE && count>sizeOfBuffer) {
1013             MSVCRT__invalid_parameter( NULL, NULL, NULL, 0, 0 );
1014             *MSVCRT__errno() = MSVCRT_ERANGE;
1015             memset(str, 0, sizeOfBuffer);
1016         } else
1017             str[len-1] = '\0';
1018
1019         return -1;
1020     }
1021
1022     return ret;
1023 }
1024
1025 /*********************************************************************
1026  *              _vsnprintf_s (MSVCRT.@)
1027  */
1028 int CDECL MSVCRT_vsnprintf_s( char *str, MSVCRT_size_t sizeOfBuffer,
1029         MSVCRT_size_t count, const char *format, __ms_va_list valist )
1030 {
1031     return MSVCRT_vsnprintf_s_l(str,sizeOfBuffer, count, format, NULL, valist);
1032 }
1033
1034 /*********************************************************************
1035  *              vsprintf (MSVCRT.@)
1036  */
1037 int CDECL MSVCRT_vsprintf( char *str, const char *format, __ms_va_list valist)
1038 {
1039     return MSVCRT_vsnprintf(str, INT_MAX, format, valist);
1040 }
1041
1042 /*********************************************************************
1043  *              vsprintf_s (MSVCRT.@)
1044  */
1045 int CDECL MSVCRT_vsprintf_s( char *str, MSVCRT_size_t num, const char *format, __ms_va_list valist)
1046 {
1047     return MSVCRT_vsnprintf(str, num, format, valist);
1048 }
1049
1050 /*********************************************************************
1051  *              _vscprintf (MSVCRT.@)
1052  */
1053 int CDECL _vscprintf( const char *format, __ms_va_list valist )
1054 {
1055     return MSVCRT_vsnprintf( NULL, INT_MAX, format, valist );
1056 }
1057
1058 /*********************************************************************
1059  *              _snprintf (MSVCRT.@)
1060  */
1061 int CDECL MSVCRT__snprintf(char *str, unsigned int len, const char *format, ...)
1062 {
1063     int retval;
1064     __ms_va_list valist;
1065     __ms_va_start(valist, format);
1066     retval = MSVCRT_vsnprintf(str, len, format, valist);
1067     __ms_va_end(valist);
1068     return retval;
1069 }
1070
1071 /*********************************************************************
1072  * vsnwsprintf_internal (INTERNAL)
1073  */
1074 static inline int vsnwprintf_internal(MSVCRT_wchar_t *str, MSVCRT_size_t len,
1075         const MSVCRT_wchar_t *format, MSVCRT__locale_t locale, BOOL valid,
1076         __ms_va_list valist)
1077 {
1078     pf_output out;
1079
1080     out.unicode = TRUE;
1081     out.buf.W = str;
1082     out.used = 0;
1083     out.len = len;
1084
1085     return pf_vsnprintf( &out, format, locale, valid, valist );
1086 }
1087
1088 /*********************************************************************
1089  *              _vsnwsprintf (MSVCRT.@)
1090  */
1091 int CDECL MSVCRT_vsnwprintf(MSVCRT_wchar_t *str, MSVCRT_size_t len,
1092         const MSVCRT_wchar_t *format, __ms_va_list valist)
1093 {
1094     return vsnwprintf_internal(str, len, format, NULL, FALSE, valist);
1095 }
1096
1097 /*********************************************************************
1098  *              _vsnwsprintf_l (MSVCRT.@)
1099  */
1100 int CDECL MSVCRT_vsnwprintf_l(MSVCRT_wchar_t *str, MSVCRT_size_t len,
1101         const MSVCRT_wchar_t *format, MSVCRT__locale_t locale,
1102         __ms_va_list valist)
1103 {
1104         return vsnwprintf_internal(str, len, format, locale, FALSE, valist);
1105 }
1106
1107 /*********************************************************************
1108  *              _vsnwsprintf_s_l (MSVCRT.@)
1109  */
1110 int CDECL MSVCRT_vsnwprintf_s_l( MSVCRT_wchar_t *str, MSVCRT_size_t sizeOfBuffer,
1111         MSVCRT_size_t count, const MSVCRT_wchar_t *format,
1112         MSVCRT__locale_t locale, __ms_va_list valist)
1113 {
1114     int len, ret;
1115
1116     len = sizeOfBuffer/sizeof(MSVCRT_wchar_t);
1117     if(count!=-1 && len>count+1)
1118         len = count+1;
1119
1120     ret = vsnwprintf_internal(str, len, format, locale, TRUE, valist);
1121
1122     if(ret<0 || ret==len) {
1123         if(count!=_TRUNCATE && count>sizeOfBuffer/sizeof(MSVCRT_wchar_t)) {
1124             MSVCRT__invalid_parameter( NULL, NULL, NULL, 0, 0 );
1125             *MSVCRT__errno() = MSVCRT_ERANGE;
1126             memset(str, 0, sizeOfBuffer);
1127         } else
1128             str[len-1] = '\0';
1129
1130         return -1;
1131     }
1132
1133     return ret;
1134 }
1135
1136 /*********************************************************************
1137  *              _vsnwsprintf_s (MSVCRT.@)
1138  */
1139 int CDECL MSVCRT_vsnwprintf_s(MSVCRT_wchar_t *str, MSVCRT_size_t sizeOfBuffer,
1140         MSVCRT_size_t count, const MSVCRT_wchar_t *format, __ms_va_list valist)
1141 {
1142     return MSVCRT_vsnwprintf_s_l(str, sizeOfBuffer, count,
1143             format, NULL, valist);
1144 }
1145
1146 /*********************************************************************
1147  *              _snwprintf (MSVCRT.@)
1148  */
1149 int CDECL MSVCRT__snwprintf( MSVCRT_wchar_t *str, unsigned int len, const MSVCRT_wchar_t *format, ...)
1150 {
1151     int retval;
1152     __ms_va_list valist;
1153     __ms_va_start(valist, format);
1154     retval = MSVCRT_vsnwprintf(str, len, format, valist);
1155     __ms_va_end(valist);
1156     return retval;
1157 }
1158
1159 /*********************************************************************
1160  *              sprintf (MSVCRT.@)
1161  */
1162 int CDECL MSVCRT_sprintf( char *str, const char *format, ... )
1163 {
1164     __ms_va_list ap;
1165     int r;
1166
1167     __ms_va_start( ap, format );
1168     r = MSVCRT_vsnprintf( str, INT_MAX, format, ap );
1169     __ms_va_end( ap );
1170     return r;
1171 }
1172
1173 /*********************************************************************
1174  *              sprintf_s (MSVCRT.@)
1175  */
1176 int CDECL MSVCRT_sprintf_s( char *str, MSVCRT_size_t num, const char *format, ... )
1177 {
1178     __ms_va_list ap;
1179     int r;
1180
1181     __ms_va_start( ap, format );
1182     r = MSVCRT_vsnprintf( str, num, format, ap );
1183     __ms_va_end( ap );
1184     return r;
1185 }
1186
1187 /*********************************************************************
1188  *              swprintf (MSVCRT.@)
1189  */
1190 int CDECL MSVCRT_swprintf( MSVCRT_wchar_t *str, const MSVCRT_wchar_t *format, ... )
1191 {
1192     __ms_va_list ap;
1193     int r;
1194
1195     __ms_va_start( ap, format );
1196     r = MSVCRT_vsnwprintf( str, INT_MAX, format, ap );
1197     __ms_va_end( ap );
1198     return r;
1199 }
1200
1201 /*********************************************************************
1202  *              swprintf_s (MSVCRT.@)
1203  */
1204 int CDECL MSVCRT_swprintf_s(MSVCRT_wchar_t *str, MSVCRT_size_t numberOfElements,
1205         const MSVCRT_wchar_t *format, ... )
1206 {
1207     __ms_va_list ap;
1208     int r;
1209
1210     __ms_va_start(ap, format);
1211     r = MSVCRT_vsnwprintf_s(str, numberOfElements*sizeof(MSVCRT_wchar_t),
1212             INT_MAX, format, ap);
1213     __ms_va_end(ap);
1214
1215     return r;
1216 }
1217
1218 /*********************************************************************
1219  *              vswprintf (MSVCRT.@)
1220  */
1221 int CDECL MSVCRT_vswprintf( MSVCRT_wchar_t* str, const MSVCRT_wchar_t* format, __ms_va_list args )
1222 {
1223     return MSVCRT_vsnwprintf( str, INT_MAX, format, args );
1224 }
1225
1226 /*********************************************************************
1227  *              _vscwprintf (MSVCRT.@)
1228  */
1229 int CDECL _vscwprintf( const MSVCRT_wchar_t *format, __ms_va_list args )
1230 {
1231     return MSVCRT_vsnwprintf( NULL, INT_MAX, format, args );
1232 }
1233
1234 /*********************************************************************
1235  *              vswprintf_s (MSVCRT.@)
1236  */
1237 int CDECL MSVCRT_vswprintf_s(MSVCRT_wchar_t* str, MSVCRT_size_t numberOfElements,
1238         const MSVCRT_wchar_t* format, __ms_va_list args)
1239 {
1240     return MSVCRT_vsnwprintf_s(str, numberOfElements*sizeof(MSVCRT_wchar_t),
1241             INT_MAX, format, args);
1242 }
1243
1244 /*********************************************************************
1245  *              _vswprintf_s_l (MSVCRT.@)
1246  */
1247 int CDECL MSVCRT_vswprintf_s_l(MSVCRT_wchar_t* str, MSVCRT_size_t numberOfElements,
1248         const MSVCRT_wchar_t* format, MSVCRT__locale_t locale, __ms_va_list args)
1249 {
1250     return MSVCRT_vsnwprintf_s_l(str, numberOfElements*sizeof(MSVCRT_wchar_t),
1251             INT_MAX, format, locale, args );
1252 }
1253
1254 /*********************************************************************
1255  *              wcscoll (MSVCRT.@)
1256  */
1257 int CDECL MSVCRT_wcscoll( const MSVCRT_wchar_t* str1, const MSVCRT_wchar_t* str2 )
1258 {
1259   /* FIXME: handle collates */
1260   return strcmpW( str1, str2 );
1261 }
1262
1263 /*********************************************************************
1264  *              wcspbrk (MSVCRT.@)
1265  */
1266 MSVCRT_wchar_t* CDECL MSVCRT_wcspbrk( const MSVCRT_wchar_t* str, const MSVCRT_wchar_t* accept )
1267 {
1268   const MSVCRT_wchar_t* p;
1269   while (*str)
1270   {
1271     for (p = accept; *p; p++) if (*p == *str) return (MSVCRT_wchar_t*)str;
1272       str++;
1273   }
1274   return NULL;
1275 }
1276
1277 /*********************************************************************
1278  *              wcstok  (MSVCRT.@)
1279  */
1280 MSVCRT_wchar_t * CDECL MSVCRT_wcstok( MSVCRT_wchar_t *str, const MSVCRT_wchar_t *delim )
1281 {
1282     thread_data_t *data = msvcrt_get_thread_data();
1283     MSVCRT_wchar_t *ret;
1284
1285     if (!str)
1286         if (!(str = data->wcstok_next)) return NULL;
1287
1288     while (*str && strchrW( delim, *str )) str++;
1289     if (!*str) return NULL;
1290     ret = str++;
1291     while (*str && !strchrW( delim, *str )) str++;
1292     if (*str) *str++ = 0;
1293     data->wcstok_next = str;
1294     return ret;
1295 }
1296
1297
1298 /*********************************************************************
1299  *              wctomb (MSVCRT.@)
1300  */
1301 INT CDECL MSVCRT_wctomb( char *dst, MSVCRT_wchar_t ch )
1302 {
1303   return WideCharToMultiByte( CP_ACP, 0, &ch, 1, dst, 6, NULL, NULL );
1304 }
1305
1306 /*********************************************************************
1307  *              iswalnum (MSVCRT.@)
1308  */
1309 INT CDECL MSVCRT_iswalnum( MSVCRT_wchar_t wc )
1310 {
1311     return isalnumW( wc );
1312 }
1313
1314 /*********************************************************************
1315  *              iswalpha (MSVCRT.@)
1316  */
1317 INT CDECL MSVCRT_iswalpha( MSVCRT_wchar_t wc )
1318 {
1319     return isalphaW( wc );
1320 }
1321
1322 /*********************************************************************
1323  *              iswalpha_l (MSVCRT.@)
1324  */
1325 INT CDECL MSVCRT__iswalpha_l( MSVCRT_wchar_t wc, MSVCRT__locale_t locale )
1326 {
1327     return isalphaW( wc );
1328 }
1329
1330 /*********************************************************************
1331  *              iswcntrl (MSVCRT.@)
1332  */
1333 INT CDECL MSVCRT_iswcntrl( MSVCRT_wchar_t wc )
1334 {
1335     return iscntrlW( wc );
1336 }
1337
1338 /*********************************************************************
1339  *              iswdigit (MSVCRT.@)
1340  */
1341 INT CDECL MSVCRT_iswdigit( MSVCRT_wchar_t wc )
1342 {
1343     return isdigitW( wc );
1344 }
1345
1346 /*********************************************************************
1347  *              iswgraph (MSVCRT.@)
1348  */
1349 INT CDECL MSVCRT_iswgraph( MSVCRT_wchar_t wc )
1350 {
1351     return isgraphW( wc );
1352 }
1353
1354 /*********************************************************************
1355  *              iswlower (MSVCRT.@)
1356  */
1357 INT CDECL MSVCRT_iswlower( MSVCRT_wchar_t wc )
1358 {
1359     return islowerW( wc );
1360 }
1361
1362 /*********************************************************************
1363  *              iswprint (MSVCRT.@)
1364  */
1365 INT CDECL MSVCRT_iswprint( MSVCRT_wchar_t wc )
1366 {
1367     return isprintW( wc );
1368 }
1369
1370 /*********************************************************************
1371  *              iswpunct (MSVCRT.@)
1372  */
1373 INT CDECL MSVCRT_iswpunct( MSVCRT_wchar_t wc )
1374 {
1375     return ispunctW( wc );
1376 }
1377
1378 /*********************************************************************
1379  *              iswspace (MSVCRT.@)
1380  */
1381 INT CDECL MSVCRT_iswspace( MSVCRT_wchar_t wc )
1382 {
1383     return isspaceW( wc );
1384 }
1385
1386 /*********************************************************************
1387  *              iswupper (MSVCRT.@)
1388  */
1389 INT CDECL MSVCRT_iswupper( MSVCRT_wchar_t wc )
1390 {
1391     return isupperW( wc );
1392 }
1393
1394 /*********************************************************************
1395  *              iswxdigit (MSVCRT.@)
1396  */
1397 INT CDECL MSVCRT_iswxdigit( MSVCRT_wchar_t wc )
1398 {
1399     return isxdigitW( wc );
1400 }
1401
1402 /*********************************************************************
1403  *              wcscpy_s (MSVCRT.@)
1404  */
1405 INT CDECL MSVCRT_wcscpy_s( MSVCRT_wchar_t* wcDest, MSVCRT_size_t numElement, const  MSVCRT_wchar_t *wcSrc)
1406 {
1407     MSVCRT_size_t size = 0;
1408
1409     if(!wcDest || !numElement)
1410         return MSVCRT_EINVAL;
1411
1412     wcDest[0] = 0;
1413
1414     if(!wcSrc)
1415     {
1416         return MSVCRT_EINVAL;
1417     }
1418
1419     size = strlenW(wcSrc) + 1;
1420
1421     if(size > numElement)
1422     {
1423         return MSVCRT_ERANGE;
1424     }
1425
1426     memcpy( wcDest, wcSrc, size*sizeof(WCHAR) );
1427
1428     return 0;
1429 }
1430
1431 /******************************************************************
1432  *              wcsncpy_s (MSVCRT.@)
1433  */
1434 INT CDECL MSVCRT_wcsncpy_s( MSVCRT_wchar_t* wcDest, MSVCRT_size_t numElement, const MSVCRT_wchar_t *wcSrc,
1435                             MSVCRT_size_t count )
1436 {
1437     MSVCRT_size_t size = 0;
1438
1439     if (!wcDest || !numElement)
1440         return MSVCRT_EINVAL;
1441
1442     wcDest[0] = 0;
1443
1444     if (!wcSrc)
1445     {
1446         return MSVCRT_EINVAL;
1447     }
1448
1449     size = min(strlenW(wcSrc), count);
1450
1451     if (size >= numElement)
1452     {
1453         return MSVCRT_ERANGE;
1454     }
1455
1456     memcpy( wcDest, wcSrc, size*sizeof(WCHAR) );
1457     wcDest[size] = '\0';
1458
1459     return 0;
1460 }
1461
1462 /******************************************************************
1463  *              wcscat_s (MSVCRT.@)
1464  *
1465  */
1466 INT CDECL MSVCRT_wcscat_s(MSVCRT_wchar_t* dst, MSVCRT_size_t elem, const MSVCRT_wchar_t* src)
1467 {
1468     MSVCRT_wchar_t* ptr = dst;
1469
1470     if (!dst || elem == 0) return MSVCRT_EINVAL;
1471     if (!src)
1472     {
1473         dst[0] = '\0';
1474         return MSVCRT_EINVAL;
1475     }
1476
1477     /* seek to end of dst string (or elem if no end of string is found */
1478     while (ptr < dst + elem && *ptr != '\0') ptr++;
1479     while (ptr < dst + elem)
1480     {
1481         if ((*ptr++ = *src++) == '\0') return 0;
1482     }
1483     /* not enough space */
1484     dst[0] = '\0';
1485     return MSVCRT_ERANGE;
1486 }
1487
1488 /*********************************************************************
1489  *  _wctoi64_l (MSVCR90.@)
1490  *
1491  * FIXME: locale parameter is ignored
1492  */
1493 __int64 CDECL MSVCRT__wcstoi64_l(const MSVCRT_wchar_t *nptr,
1494         MSVCRT_wchar_t **endptr, int base, MSVCRT__locale_t locale)
1495 {
1496     BOOL negative = FALSE;
1497     __int64 ret = 0;
1498
1499     TRACE("(%s %p %d %p)\n", debugstr_w(nptr), endptr, base, locale);
1500
1501     if(!nptr || base<0 || base>36 || base==1) {
1502         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
1503         return 0;
1504     }
1505
1506     while(isspaceW(*nptr)) nptr++;
1507
1508     if(*nptr == '-') {
1509         negative = TRUE;
1510         nptr++;
1511     } else if(*nptr == '+')
1512         nptr++;
1513
1514     if((base==0 || base==16) && *nptr=='0' && tolowerW(*(nptr+1))=='x') {
1515         base = 16;
1516         nptr += 2;
1517     }
1518
1519     if(base == 0) {
1520         if(*nptr=='0')
1521             base = 8;
1522         else
1523             base = 10;
1524     }
1525
1526     while(*nptr) {
1527         char cur = tolowerW(*nptr);
1528         int v;
1529
1530         if(isdigitW(cur)) {
1531             if(cur >= '0'+base)
1532                 break;
1533             v = cur-'0';
1534         } else {
1535             if(cur<'a' || cur>='a'+base-10)
1536                 break;
1537             v = cur-'a'+10;
1538         }
1539
1540         if(negative)
1541             v = -v;
1542
1543         nptr++;
1544
1545         if(!negative && (ret>MSVCRT_I64_MAX/base || ret*base>MSVCRT_I64_MAX-v)) {
1546             ret = MSVCRT_I64_MAX;
1547             *MSVCRT__errno() = MSVCRT_ERANGE;
1548         } else if(negative && (ret<MSVCRT_I64_MIN/base || ret*base<MSVCRT_I64_MIN-v)) {
1549             ret = MSVCRT_I64_MIN;
1550             *MSVCRT__errno() = MSVCRT_ERANGE;
1551         } else
1552             ret = ret*base + v;
1553     }
1554
1555     if(endptr)
1556         *endptr = (MSVCRT_wchar_t*)nptr;
1557
1558     return ret;
1559 }
1560
1561 /*********************************************************************
1562  *  _wcstoi64 (MSVCR90.@)
1563  */
1564 __int64 CDECL MSVCRT__wcstoi64(const MSVCRT_wchar_t *nptr,
1565         MSVCRT_wchar_t **endptr, int base)
1566 {
1567     return MSVCRT__wcstoi64_l(nptr, endptr, base, NULL);
1568 }
1569
1570 /*********************************************************************
1571  *  _wcstoui64_l (MSVCR90.@)
1572  *
1573  * FIXME: locale parameter is ignored
1574  */
1575 unsigned __int64 CDECL MSVCRT__wcstoui64_l(const MSVCRT_wchar_t *nptr,
1576         MSVCRT_wchar_t **endptr, int base, MSVCRT__locale_t locale)
1577 {
1578     BOOL negative = FALSE;
1579     unsigned __int64 ret = 0;
1580
1581     TRACE("(%s %p %d %p)\n", debugstr_w(nptr), endptr, base, locale);
1582
1583     if(!nptr || base<0 || base>36 || base==1) {
1584         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
1585         return 0;
1586     }
1587
1588     while(isspaceW(*nptr)) nptr++;
1589
1590     if(*nptr == '-') {
1591         negative = TRUE;
1592         nptr++;
1593     } else if(*nptr == '+')
1594         nptr++;
1595
1596     if((base==0 || base==16) && *nptr=='0' && tolowerW(*(nptr+1))=='x') {
1597         base = 16;
1598         nptr += 2;
1599     }
1600
1601     if(base == 0) {
1602         if(*nptr=='0')
1603             base = 8;
1604         else
1605             base = 10;
1606     }
1607
1608     while(*nptr) {
1609         char cur = tolowerW(*nptr);
1610         int v;
1611
1612         if(isdigit(cur)) {
1613             if(cur >= '0'+base)
1614                 break;
1615             v = *nptr-'0';
1616         } else {
1617             if(cur<'a' || cur>='a'+base-10)
1618                 break;
1619             v = cur-'a'+10;
1620         }
1621
1622         nptr++;
1623
1624         if(ret>MSVCRT_UI64_MAX/base || ret*base>MSVCRT_UI64_MAX-v) {
1625             ret = MSVCRT_UI64_MAX;
1626             *MSVCRT__errno() = MSVCRT_ERANGE;
1627         } else
1628             ret = ret*base + v;
1629     }
1630
1631     if(endptr)
1632         *endptr = (MSVCRT_wchar_t*)nptr;
1633
1634     return negative ? -ret : ret;
1635 }
1636
1637 /*********************************************************************
1638  *  _wcstoui64 (MSVCR90.@)
1639  */
1640 unsigned __int64 CDECL MSVCRT__wcstoui64(const MSVCRT_wchar_t *nptr,
1641         MSVCRT_wchar_t **endptr, int base)
1642 {
1643     return MSVCRT__wcstoui64_l(nptr, endptr, base, NULL);
1644 }