msvcrt: Added qsort_s implementation.
[wine] / dlls / msvcrt / string.c
1 /*
2  * MSVCRT string functions
3  *
4  * Copyright 1996,1998 Marcus Meissner
5  * Copyright 1996 Jukka Iivonen
6  * Copyright 1997,2000 Uwe Bonnes
7  * Copyright 2000 Jon Griffiths
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #define _ISOC99_SOURCE
25 #include "config.h"
26 #include "wine/port.h"
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <math.h>
31 #include <limits.h>
32 #include <errno.h>
33 #include "msvcrt.h"
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
37
38 /*********************************************************************
39  *              _mbsdup (MSVCRT.@)
40  *              _strdup (MSVCRT.@)
41  */
42 char* CDECL _strdup(const char* str)
43 {
44     if(str)
45     {
46       char * ret = MSVCRT_malloc(strlen(str)+1);
47       if (ret) strcpy( ret, str );
48       return ret;
49     }
50     else return 0;
51 }
52
53 /*********************************************************************
54  *              _strlwr_s (MSVCRT.@)
55  */
56 int CDECL _strlwr_s(char *str, MSVCRT_size_t len)
57 {
58     char *ptr = str;
59
60     if (!str || !len)
61     {
62         *MSVCRT__errno() = MSVCRT_EINVAL;
63         return MSVCRT_EINVAL;
64     }
65
66     while (len && *ptr)
67     {
68         len--;
69         ptr++;
70     }
71
72     if (!len)
73     {
74         str[0] = '\0';
75         *MSVCRT__errno() = MSVCRT_EINVAL;
76         return MSVCRT_EINVAL;
77     }
78
79     while (*str)
80     {
81         *str = tolower(*str);
82         str++;
83     }
84
85     return 0;
86 }
87
88 /*********************************************************************
89  *              _strnset (MSVCRT.@)
90  */
91 char* CDECL MSVCRT__strnset(char* str, int value, MSVCRT_size_t len)
92 {
93   if (len > 0 && str)
94     while (*str && len--)
95       *str++ = value;
96   return str;
97 }
98
99 /*********************************************************************
100  *              _strrev (MSVCRT.@)
101  */
102 char* CDECL _strrev(char* str)
103 {
104   char * p1;
105   char * p2;
106
107   if (str && *str)
108     for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
109     {
110       *p1 ^= *p2;
111       *p2 ^= *p1;
112       *p1 ^= *p2;
113     }
114
115   return str;
116 }
117
118 /*********************************************************************
119  *              _strset (MSVCRT.@)
120  */
121 char* CDECL _strset(char* str, int value)
122 {
123   char *ptr = str;
124   while (*ptr)
125     *ptr++ = value;
126
127   return str;
128 }
129
130 /*********************************************************************
131  *              strtok  (MSVCRT.@)
132  */
133 char * CDECL MSVCRT_strtok( char *str, const char *delim )
134 {
135     thread_data_t *data = msvcrt_get_thread_data();
136     char *ret;
137
138     if (!str)
139         if (!(str = data->strtok_next)) return NULL;
140
141     while (*str && strchr( delim, *str )) str++;
142     if (!*str) return NULL;
143     ret = str++;
144     while (*str && !strchr( delim, *str )) str++;
145     if (*str) *str++ = 0;
146     data->strtok_next = str;
147     return ret;
148 }
149
150 /*********************************************************************
151  *              strtok_s  (MSVCRT.@)
152  */
153 char * CDECL MSVCRT_strtok_s(char *str, const char *delim, char **ctx)
154 {
155     if (!MSVCRT_CHECK_PMT(delim != NULL) || !MSVCRT_CHECK_PMT(ctx != NULL) ||
156         !MSVCRT_CHECK_PMT(str != NULL || *ctx != NULL)) {
157         *MSVCRT__errno() = MSVCRT_EINVAL;
158         return NULL;
159     }
160
161     if(!str)
162         str = *ctx;
163
164     while(*str && strchr(delim, *str))
165         str++;
166     if(!*str)
167         return NULL;
168
169     *ctx = str+1;
170     while(**ctx && !strchr(delim, **ctx))
171         (*ctx)++;
172     if(**ctx)
173         *(*ctx)++ = 0;
174
175     return str;
176 }
177
178 /*********************************************************************
179  *              _swab (MSVCRT.@)
180  */
181 void CDECL MSVCRT__swab(char* src, char* dst, int len)
182 {
183   if (len > 1)
184   {
185     len = (unsigned)len >> 1;
186
187     while (len--) {
188       char s0 = src[0];
189       char s1 = src[1];
190       *dst++ = s1;
191       *dst++ = s0;
192       src = src + 2;
193     }
194   }
195 }
196
197 /*********************************************************************
198  *              strtod_l  (MSVCRT.@)
199  */
200 double CDECL MSVCRT_strtod_l( const char *str, char **end, MSVCRT__locale_t locale)
201 {
202     unsigned __int64 d=0, hlp;
203     unsigned fpcontrol;
204     int exp=0, sign=1;
205     const char *p;
206     double ret;
207     BOOL found_digit = FALSE;
208
209     if (!MSVCRT_CHECK_PMT(str != NULL)) {
210         *MSVCRT__errno() = MSVCRT_EINVAL;
211         return 0;
212     }
213
214     if(!locale)
215         locale = get_locale();
216
217     /* FIXME: use *_l functions */
218     p = str;
219     while(isspace(*p))
220         p++;
221
222     if(*p == '-') {
223         sign = -1;
224         p++;
225     } else  if(*p == '+')
226         p++;
227
228     while(isdigit(*p)) {
229         found_digit = TRUE;
230         hlp = d*10+*(p++)-'0';
231         if(d>MSVCRT_UI64_MAX/10 || hlp<d) {
232             exp++;
233             break;
234         } else
235             d = hlp;
236     }
237     while(isdigit(*p)) {
238         exp++;
239         p++;
240     }
241
242     if(*p == *locale->locinfo->lconv->decimal_point)
243         p++;
244
245     while(isdigit(*p)) {
246         found_digit = TRUE;
247         hlp = d*10+*(p++)-'0';
248         if(d>MSVCRT_UI64_MAX/10 || hlp<d)
249             break;
250
251         d = hlp;
252         exp--;
253     }
254     while(isdigit(*p))
255         p++;
256
257     if(!found_digit) {
258         if(end)
259             *end = (char*)str;
260         return 0.0;
261     }
262
263     if(*p=='e' || *p=='E' || *p=='d' || *p=='D') {
264         int e=0, s=1;
265
266         p++;
267         if(*p == '-') {
268             s = -1;
269             p++;
270         } else if(*p == '+')
271             p++;
272
273         if(isdigit(*p)) {
274             while(isdigit(*p)) {
275                 if(e>INT_MAX/10 || (e=e*10+*p-'0')<0)
276                     e = INT_MAX;
277                 p++;
278             }
279             e *= s;
280
281             if(exp<0 && e<0 && exp+e>=0) exp = INT_MIN;
282             else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
283             else exp += e;
284         } else {
285             if(*p=='-' || *p=='+')
286                 p--;
287             p--;
288         }
289     }
290
291     fpcontrol = _control87(0, 0);
292     _control87(MSVCRT__EM_DENORMAL|MSVCRT__EM_INVALID|MSVCRT__EM_ZERODIVIDE
293             |MSVCRT__EM_OVERFLOW|MSVCRT__EM_UNDERFLOW|MSVCRT__EM_INEXACT, 0xffffffff);
294
295     if(exp>0)
296         ret = (double)sign*d*pow(10, exp);
297     else
298         ret = (double)sign*d/pow(10, -exp);
299
300     _control87(fpcontrol, 0xffffffff);
301
302     if((d && ret==0.0) || isinf(ret))
303         *MSVCRT__errno() = MSVCRT_ERANGE;
304
305     if(end)
306         *end = (char*)p;
307
308     return ret;
309 }
310
311 /*********************************************************************
312  *              strtod  (MSVCRT.@)
313  */
314 double CDECL MSVCRT_strtod( const char *str, char **end )
315 {
316     return MSVCRT_strtod_l( str, end, NULL );
317 }
318
319 /*********************************************************************
320  *              atof  (MSVCRT.@)
321  */
322 double CDECL MSVCRT_atof( const char *str )
323 {
324     return MSVCRT_strtod_l(str, NULL, NULL);
325 }
326
327 /*********************************************************************
328  *              _atof_l  (MSVCRT.@)
329  */
330 double CDECL MSVCRT__atof_l( const char *str, MSVCRT__locale_t locale)
331 {
332     return MSVCRT_strtod_l(str, NULL, locale);
333 }
334
335 /*********************************************************************
336  *              strcoll (MSVCRT.@)
337  */
338 int CDECL MSVCRT_strcoll( const char* str1, const char* str2 )
339 {
340     /* FIXME: handle Windows locale */
341     return strcoll( str1, str2 );
342 }
343
344 /*********************************************************************
345  *      strcpy_s (MSVCRT.@)
346  */
347 int CDECL MSVCRT_strcpy_s( char* dst, MSVCRT_size_t elem, const char* src )
348 {
349     MSVCRT_size_t i;
350     if(!elem) return MSVCRT_EINVAL;
351     if(!dst) return MSVCRT_EINVAL;
352     if(!src)
353     {
354         dst[0] = '\0';
355         return MSVCRT_EINVAL;
356     }
357
358     for(i = 0; i < elem; i++)
359     {
360         if((dst[i] = src[i]) == '\0') return 0;
361     }
362     dst[0] = '\0';
363     return MSVCRT_ERANGE;
364 }
365
366 /*********************************************************************
367  *      strcat_s (MSVCRT.@)
368  */
369 int CDECL MSVCRT_strcat_s( char* dst, MSVCRT_size_t elem, const char* src )
370 {
371     MSVCRT_size_t i, j;
372     if(!dst) return MSVCRT_EINVAL;
373     if(elem == 0) return MSVCRT_EINVAL;
374     if(!src)
375     {
376         dst[0] = '\0';
377         return MSVCRT_EINVAL;
378     }
379
380     for(i = 0; i < elem; i++)
381     {
382         if(dst[i] == '\0')
383         {
384             for(j = 0; (j + i) < elem; j++)
385             {
386                 if((dst[j + i] = src[j]) == '\0') return 0;
387             }
388         }
389     }
390     /* Set the first element to 0, not the first element after the skipped part */
391     dst[0] = '\0';
392     return MSVCRT_ERANGE;
393 }
394
395 /*********************************************************************
396  *      strncat_s (MSVCRT.@)
397  */
398 int CDECL MSVCRT_strncat_s( char* dst, MSVCRT_size_t elem, const char* src, MSVCRT_size_t count )
399 {
400     MSVCRT_size_t i, j;
401     if(!MSVCRT_CHECK_PMT(dst != 0) || !MSVCRT_CHECK_PMT(elem != 0))
402         return MSVCRT_EINVAL;
403     if(!MSVCRT_CHECK_PMT(src != 0))
404     {
405         dst[0] = '\0';
406         return MSVCRT_EINVAL;
407     }
408
409     for(i = 0; i < elem; i++)
410     {
411         if(dst[i] == '\0')
412         {
413             for(j = 0; (j + i) < elem; j++)
414             {
415                 if(count == MSVCRT__TRUNCATE && j + i == elem - 1)
416                 {
417                     dst[j + i] = '\0';
418                     return MSVCRT_STRUNCATE;
419                 }
420                 if(j == count || (dst[j + i] = src[j]) == '\0')
421                 {
422                     dst[j + i] = '\0';
423                     return 0;
424                 }
425             }
426         }
427     }
428     /* Set the first element to 0, not the first element after the skipped part */
429     dst[0] = '\0';
430     return MSVCRT_ERANGE;
431 }
432
433 /*********************************************************************
434  *              strxfrm (MSVCRT.@)
435  */
436 MSVCRT_size_t CDECL MSVCRT_strxfrm( char *dest, const char *src, MSVCRT_size_t len )
437 {
438     /* FIXME: handle Windows locale */
439     return strxfrm( dest, src, len );
440 }
441
442 /*********************************************************************
443  *              _stricoll (MSVCRT.@)
444  */
445 int CDECL MSVCRT__stricoll( const char* str1, const char* str2 )
446 {
447   /* FIXME: handle collates */
448   TRACE("str1 %s str2 %s\n", debugstr_a(str1), debugstr_a(str2));
449   return lstrcmpiA( str1, str2 );
450 }
451
452 /********************************************************************
453  *              _atoldbl (MSVCRT.@)
454  */
455 int CDECL MSVCRT__atoldbl(MSVCRT__LDOUBLE *value, const char *str)
456 {
457   /* FIXME needs error checking for huge/small values */
458 #ifdef HAVE_STRTOLD
459   TRACE("str %s value %p\n",str,value);
460   value->x = strtold(str,0);
461 #else
462   FIXME("stub, str %s value %p\n",str,value);
463 #endif
464   return 0;
465 }
466
467 /********************************************************************
468  *              __STRINGTOLD (MSVCRT.@)
469  */
470 int CDECL __STRINGTOLD( MSVCRT__LDOUBLE *value, char **endptr, const char *str, int flags )
471 {
472 #ifdef HAVE_STRTOLD
473     FIXME("%p %p %s %x partial stub\n", value, endptr, str, flags );
474     value->x = strtold(str,endptr);
475 #else
476     FIXME("%p %p %s %x stub\n", value, endptr, str, flags );
477 #endif
478     return 0;
479 }
480
481 /******************************************************************
482  *              strtol (MSVCRT.@)
483  */
484 MSVCRT_long CDECL MSVCRT_strtol(const char* nptr, char** end, int base)
485 {
486     /* wrapper to forward libc error code to msvcrt's error codes */
487     long ret;
488
489     errno = 0;
490     ret = strtol(nptr, end, base);
491     switch (errno)
492     {
493     case ERANGE:        *MSVCRT__errno() = MSVCRT_ERANGE;       break;
494     case EINVAL:        *MSVCRT__errno() = MSVCRT_EINVAL;       break;
495     default:
496         /* cope with the fact that we may use 64bit long integers on libc
497          * while msvcrt always uses 32bit long integers
498          */
499         if (ret > MSVCRT_LONG_MAX)
500         {
501             ret = MSVCRT_LONG_MAX;
502             *MSVCRT__errno() = MSVCRT_ERANGE;
503         }
504         else if (ret < -MSVCRT_LONG_MAX - 1)
505         {
506             ret = -MSVCRT_LONG_MAX - 1;
507             *MSVCRT__errno() = MSVCRT_ERANGE;
508         }
509         break;
510     }
511
512     return ret;
513 }
514
515 /******************************************************************
516  *              strtoul (MSVCRT.@)
517  */
518 MSVCRT_ulong CDECL MSVCRT_strtoul(const char* nptr, char** end, int base)
519 {
520     /* wrapper to forward libc error code to msvcrt's error codes */
521     unsigned long ret;
522
523     errno = 0;
524     ret = strtoul(nptr, end, base);
525     switch (errno)
526     {
527     case ERANGE:        *MSVCRT__errno() = MSVCRT_ERANGE;       break;
528     case EINVAL:        *MSVCRT__errno() = MSVCRT_EINVAL;       break;
529     default:
530         /* cope with the fact that we may use 64bit long integers on libc
531          * while msvcrt always uses 32bit long integers
532          */
533         if (ret > MSVCRT_ULONG_MAX)
534         {
535             ret = MSVCRT_ULONG_MAX;
536             *MSVCRT__errno() = MSVCRT_ERANGE;
537         }
538         break;
539     }
540
541     return ret;
542 }
543
544 /******************************************************************
545  *              strnlen (MSVCRT.@)
546  */
547 MSVCRT_size_t CDECL MSVCRT_strnlen(const char *s, MSVCRT_size_t maxlen)
548 {
549     MSVCRT_size_t i;
550
551     for(i=0; i<maxlen; i++)
552         if(!s[i]) break;
553
554     return i;
555 }
556
557 /*********************************************************************
558  *  _strtoi64_l (MSVCRT.@)
559  *
560  * FIXME: locale parameter is ignored
561  */
562 __int64 CDECL MSVCRT_strtoi64_l(const char *nptr, char **endptr, int base, MSVCRT__locale_t locale)
563 {
564     BOOL negative = FALSE;
565     __int64 ret = 0;
566
567     TRACE("(%s %p %d %p)\n", nptr, endptr, base, locale);
568
569     if (!MSVCRT_CHECK_PMT(nptr != NULL) || !MSVCRT_CHECK_PMT(base == 0 || base >= 2) ||
570         !MSVCRT_CHECK_PMT(base <= 36)) {
571         return 0;
572     }
573
574     while(isspace(*nptr)) nptr++;
575
576     if(*nptr == '-') {
577         negative = TRUE;
578         nptr++;
579     } else if(*nptr == '+')
580         nptr++;
581
582     if((base==0 || base==16) && *nptr=='0' && tolower(*(nptr+1))=='x') {
583         base = 16;
584         nptr += 2;
585     }
586
587     if(base == 0) {
588         if(*nptr=='0')
589             base = 8;
590         else
591             base = 10;
592     }
593
594     while(*nptr) {
595         char cur = tolower(*nptr);
596         int v;
597
598         if(isdigit(cur)) {
599             if(cur >= '0'+base)
600                 break;
601             v = cur-'0';
602         } else {
603             if(cur<'a' || cur>='a'+base-10)
604                 break;
605             v = cur-'a'+10;
606         }
607
608         if(negative)
609             v = -v;
610
611         nptr++;
612
613         if(!negative && (ret>MSVCRT_I64_MAX/base || ret*base>MSVCRT_I64_MAX-v)) {
614             ret = MSVCRT_I64_MAX;
615             *MSVCRT__errno() = MSVCRT_ERANGE;
616         } else if(negative && (ret<MSVCRT_I64_MIN/base || ret*base<MSVCRT_I64_MIN-v)) {
617             ret = MSVCRT_I64_MIN;
618             *MSVCRT__errno() = MSVCRT_ERANGE;
619         } else
620             ret = ret*base + v;
621     }
622
623     if(endptr)
624         *endptr = (char*)nptr;
625
626     return ret;
627 }
628
629 /*********************************************************************
630  *  _strtoi64 (MSVCRT.@)
631  */
632 __int64 CDECL MSVCRT_strtoi64(const char *nptr, char **endptr, int base)
633 {
634     return MSVCRT_strtoi64_l(nptr, endptr, base, NULL);
635 }
636
637 /*********************************************************************
638  *  _strtoui64_l (MSVCRT.@)
639  *
640  * FIXME: locale parameter is ignored
641  */
642 unsigned __int64 CDECL MSVCRT_strtoui64_l(const char *nptr, char **endptr, int base, MSVCRT__locale_t locale)
643 {
644     BOOL negative = FALSE;
645     unsigned __int64 ret = 0;
646
647     TRACE("(%s %p %d %p)\n", nptr, endptr, base, locale);
648
649     if (!MSVCRT_CHECK_PMT(nptr != NULL) || !MSVCRT_CHECK_PMT(base == 0 || base >= 2) ||
650         !MSVCRT_CHECK_PMT(base <= 36)) {
651         return 0;
652     }
653
654     while(isspace(*nptr)) nptr++;
655
656     if(*nptr == '-') {
657         negative = TRUE;
658         nptr++;
659     } else if(*nptr == '+')
660         nptr++;
661
662     if((base==0 || base==16) && *nptr=='0' && tolower(*(nptr+1))=='x') {
663         base = 16;
664         nptr += 2;
665     }
666
667     if(base == 0) {
668         if(*nptr=='0')
669             base = 8;
670         else
671             base = 10;
672     }
673
674     while(*nptr) {
675         char cur = tolower(*nptr);
676         int v;
677
678         if(isdigit(cur)) {
679             if(cur >= '0'+base)
680                 break;
681             v = *nptr-'0';
682         } else {
683             if(cur<'a' || cur>='a'+base-10)
684                 break;
685             v = cur-'a'+10;
686         }
687
688         nptr++;
689
690         if(ret>MSVCRT_UI64_MAX/base || ret*base>MSVCRT_UI64_MAX-v) {
691             ret = MSVCRT_UI64_MAX;
692             *MSVCRT__errno() = MSVCRT_ERANGE;
693         } else
694             ret = ret*base + v;
695     }
696
697     if(endptr)
698         *endptr = (char*)nptr;
699
700     return negative ? -ret : ret;
701 }
702
703 /*********************************************************************
704  *  _strtoui64 (MSVCRT.@)
705  */
706 unsigned __int64 CDECL MSVCRT_strtoui64(const char *nptr, char **endptr, int base)
707 {
708     return MSVCRT_strtoui64_l(nptr, endptr, base, NULL);
709 }
710
711 /*********************************************************************
712  *  _ltoa_s (MSVCRT.@)
713  */
714 int CDECL _ltoa_s(MSVCRT_long value, char *str, MSVCRT_size_t size, int radix)
715 {
716     MSVCRT_ulong val;
717     unsigned int digit;
718     int is_negative;
719     char buffer[33], *pos;
720     size_t len;
721
722     if (!MSVCRT_CHECK_PMT(str != NULL) || !MSVCRT_CHECK_PMT(size > 0) ||
723         !MSVCRT_CHECK_PMT(radix >= 2) || !MSVCRT_CHECK_PMT(radix <= 36))
724     {
725         if (str && size)
726             str[0] = '\0';
727
728         *MSVCRT__errno() = MSVCRT_EINVAL;
729         return MSVCRT_EINVAL;
730     }
731
732     if (value < 0 && radix == 10)
733     {
734         is_negative = 1;
735         val = -value;
736     }
737     else
738     {
739         is_negative = 0;
740         val = value;
741     }
742
743     pos = buffer + 32;
744     *pos = '\0';
745
746     do
747     {
748         digit = val % radix;
749         val /= radix;
750
751         if (digit < 10)
752             *--pos = '0' + digit;
753         else
754             *--pos = 'a' + digit - 10;
755     }
756     while (val != 0);
757
758     if (is_negative)
759         *--pos = '-';
760
761     len = buffer + 33 - pos;
762     if (len > size)
763     {
764         size_t i;
765         char *p = str;
766
767         /* Copy the temporary buffer backwards up to the available number of
768          * characters. Don't copy the negative sign if present. */
769
770         if (is_negative)
771         {
772             p++;
773             size--;
774         }
775
776         for (pos = buffer + 31, i = 0; i < size; i++)
777             *p++ = *pos--;
778
779         str[0] = '\0';
780         MSVCRT_INVALID_PMT("str[size] is too small");
781         *MSVCRT__errno() = MSVCRT_ERANGE;
782         return MSVCRT_ERANGE;
783     }
784
785     memcpy(str, pos, len);
786     return 0;
787 }
788
789 /*********************************************************************
790  *  _ltow_s (MSVCRT.@)
791  */
792 int CDECL _ltow_s(MSVCRT_long value, MSVCRT_wchar_t *str, MSVCRT_size_t size, int radix)
793 {
794     MSVCRT_ulong val;
795     unsigned int digit;
796     int is_negative;
797     MSVCRT_wchar_t buffer[33], *pos;
798     size_t len;
799
800     if (!MSVCRT_CHECK_PMT(str != NULL) || !MSVCRT_CHECK_PMT(size > 0) ||
801         !MSVCRT_CHECK_PMT(radix >= 2) || !MSVCRT_CHECK_PMT(radix <= 36))
802     {
803         if (str && size)
804             str[0] = '\0';
805
806         *MSVCRT__errno() = MSVCRT_EINVAL;
807         return MSVCRT_EINVAL;
808     }
809
810     if (value < 0 && radix == 10)
811     {
812         is_negative = 1;
813         val = -value;
814     }
815     else
816     {
817         is_negative = 0;
818         val = value;
819     }
820
821     pos = buffer + 32;
822     *pos = '\0';
823
824     do
825     {
826         digit = val % radix;
827         val /= radix;
828
829         if (digit < 10)
830             *--pos = '0' + digit;
831         else
832             *--pos = 'a' + digit - 10;
833     }
834     while (val != 0);
835
836     if (is_negative)
837         *--pos = '-';
838
839     len = buffer + 33 - pos;
840     if (len > size)
841     {
842         size_t i;
843         MSVCRT_wchar_t *p = str;
844
845         /* Copy the temporary buffer backwards up to the available number of
846          * characters. Don't copy the negative sign if present. */
847
848         if (is_negative)
849         {
850             p++;
851             size--;
852         }
853
854         for (pos = buffer + 31, i = 0; i < size; i++)
855             *p++ = *pos--;
856
857         MSVCRT_INVALID_PMT("str[size] is too small");
858         str[0] = '\0';
859         *MSVCRT__errno() = MSVCRT_ERANGE;
860         return MSVCRT_ERANGE;
861     }
862
863     memcpy(str, pos, len * sizeof(MSVCRT_wchar_t));
864     return 0;
865 }
866
867 /*********************************************************************
868  *  _itoa_s (MSVCRT.@)
869  */
870 int CDECL _itoa_s(int value, char *str, MSVCRT_size_t size, int radix)
871 {
872     return _ltoa_s(value, str, size, radix);
873 }
874
875 /*********************************************************************
876  *  _itow_s (MSVCRT.@)
877  */
878 int CDECL _itow_s(int value, MSVCRT_wchar_t *str, MSVCRT_size_t size, int radix)
879 {
880     return _ltow_s(value, str, size, radix);
881 }
882
883 /*********************************************************************
884  *  _ui64toa_s (MSVCRT.@)
885  */
886 int CDECL MSVCRT__ui64toa_s(unsigned __int64 value, char *str,
887         MSVCRT_size_t size, int radix)
888 {
889     char buffer[65], *pos;
890     int digit;
891
892     if (!MSVCRT_CHECK_PMT(str != NULL) || !MSVCRT_CHECK_PMT(size > 0) ||
893         !MSVCRT_CHECK_PMT(radix>=2) || !MSVCRT_CHECK_PMT(radix<=36)) {
894         *MSVCRT__errno() = MSVCRT_EINVAL;
895         return MSVCRT_EINVAL;
896     }
897
898     pos = buffer+64;
899     *pos = '\0';
900
901     do {
902         digit = value%radix;
903         value /= radix;
904
905         if(digit < 10)
906             *--pos = '0'+digit;
907         else
908             *--pos = 'a'+digit-10;
909     }while(value != 0);
910
911     if(buffer-pos+65 > size) {
912         MSVCRT_INVALID_PMT("str[size] is too small");
913         *MSVCRT__errno() = MSVCRT_EINVAL;
914         return MSVCRT_EINVAL;
915     }
916
917     memcpy(str, pos, buffer-pos+65);
918     return 0;
919 }
920
921 /*********************************************************************
922  *  _ultoa_s (MSVCRT.@)
923  */
924 int CDECL _ultoa_s(MSVCRT_ulong value, char *str, MSVCRT_size_t size, int radix)
925 {
926     MSVCRT_ulong digit;
927     char buffer[33], *pos;
928     size_t len;
929
930     if (!str || !size || radix < 2 || radix > 36)
931     {
932         if (str && size)
933             str[0] = '\0';
934
935         *MSVCRT__errno() = MSVCRT_EINVAL;
936         return MSVCRT_EINVAL;
937     }
938
939     pos = buffer + 32;
940     *pos = '\0';
941
942     do
943     {
944         digit = value % radix;
945         value /= radix;
946
947         if (digit < 10)
948             *--pos = '0' + digit;
949         else
950             *--pos = 'a' + digit - 10;
951     }
952     while (value != 0);
953
954     len = buffer + 33 - pos;
955     if (len > size)
956     {
957         size_t i;
958         char *p = str;
959
960         /* Copy the temporary buffer backwards up to the available number of
961          * characters. */
962
963         for (pos = buffer + 31, i = 0; i < size; i++)
964             *p++ = *pos--;
965
966         str[0] = '\0';
967         *MSVCRT__errno() = MSVCRT_ERANGE;
968         return MSVCRT_ERANGE;
969     }
970
971     memcpy(str, pos, len);
972     return 0;
973 }
974
975 /*********************************************************************
976  *  _i64toa_s (MSVCRT.@)
977  */
978 int CDECL _i64toa_s(__int64 value, char *str, MSVCRT_size_t size, int radix)
979 {
980     unsigned __int64 val;
981     unsigned int digit;
982     int is_negative;
983     char buffer[65], *pos;
984     size_t len;
985
986     if (!MSVCRT_CHECK_PMT(str != NULL) || !MSVCRT_CHECK_PMT(size > 0) ||
987         !MSVCRT_CHECK_PMT(radix >= 2) || !MSVCRT_CHECK_PMT(radix <= 36))
988     {
989         if (str && size)
990             str[0] = '\0';
991
992         *MSVCRT__errno() = MSVCRT_EINVAL;
993         return MSVCRT_EINVAL;
994     }
995
996     if (value < 0 && radix == 10)
997     {
998         is_negative = 1;
999         val = -value;
1000     }
1001     else
1002     {
1003         is_negative = 0;
1004         val = value;
1005     }
1006
1007     pos = buffer + 64;
1008     *pos = '\0';
1009
1010     do
1011     {
1012         digit = val % radix;
1013         val /= radix;
1014
1015         if (digit < 10)
1016             *--pos = '0' + digit;
1017         else
1018             *--pos = 'a' + digit - 10;
1019     }
1020     while (val != 0);
1021
1022     if (is_negative)
1023         *--pos = '-';
1024
1025     len = buffer + 65 - pos;
1026     if (len > size)
1027     {
1028         size_t i;
1029         char *p = str;
1030
1031         /* Copy the temporary buffer backwards up to the available number of
1032          * characters. Don't copy the negative sign if present. */
1033
1034         if (is_negative)
1035         {
1036             p++;
1037             size--;
1038         }
1039
1040         for (pos = buffer + 63, i = 0; i < size; i++)
1041             *p++ = *pos--;
1042
1043         str[0] = '\0';
1044         MSVCRT_INVALID_PMT("str[size] is too small");
1045         *MSVCRT__errno() = MSVCRT_ERANGE;
1046         return MSVCRT_ERANGE;
1047     }
1048
1049     memcpy(str, pos, len);
1050     return 0;
1051 }
1052
1053 /*********************************************************************
1054  *  _i64tow_s (MSVCRT.@)
1055  */
1056 int CDECL _i64tow_s(__int64 value, MSVCRT_wchar_t *str, MSVCRT_size_t size, int radix)
1057 {
1058     unsigned __int64 val;
1059     unsigned int digit;
1060     int is_negative;
1061     MSVCRT_wchar_t buffer[65], *pos;
1062     size_t len;
1063
1064     if (!MSVCRT_CHECK_PMT(str != NULL) || !MSVCRT_CHECK_PMT(size > 0) ||
1065         !MSVCRT_CHECK_PMT(radix >= 2) || !MSVCRT_CHECK_PMT(radix <= 36))
1066     {
1067         if (str && size)
1068             str[0] = '\0';
1069
1070         *MSVCRT__errno() = MSVCRT_EINVAL;
1071         return MSVCRT_EINVAL;
1072     }
1073
1074     if (value < 0 && radix == 10)
1075     {
1076         is_negative = 1;
1077         val = -value;
1078     }
1079     else
1080     {
1081         is_negative = 0;
1082         val = value;
1083     }
1084
1085     pos = buffer + 64;
1086     *pos = '\0';
1087
1088     do
1089     {
1090         digit = val % radix;
1091         val /= radix;
1092
1093         if (digit < 10)
1094             *--pos = '0' + digit;
1095         else
1096             *--pos = 'a' + digit - 10;
1097     }
1098     while (val != 0);
1099
1100     if (is_negative)
1101         *--pos = '-';
1102
1103     len = buffer + 65 - pos;
1104     if (len > size)
1105     {
1106         size_t i;
1107         MSVCRT_wchar_t *p = str;
1108
1109         /* Copy the temporary buffer backwards up to the available number of
1110          * characters. Don't copy the negative sign if present. */
1111
1112         if (is_negative)
1113         {
1114             p++;
1115             size--;
1116         }
1117
1118         for (pos = buffer + 63, i = 0; i < size; i++)
1119             *p++ = *pos--;
1120
1121         MSVCRT_INVALID_PMT("str[size] is too small");
1122         str[0] = '\0';
1123         *MSVCRT__errno() = MSVCRT_ERANGE;
1124         return MSVCRT_ERANGE;
1125     }
1126
1127     memcpy(str, pos, len * sizeof(MSVCRT_wchar_t));
1128     return 0;
1129 }
1130
1131 #define I10_OUTPUT_MAX_PREC 21
1132 /* Internal structure used by $I10_OUTPUT */
1133 struct _I10_OUTPUT_DATA {
1134     short pos;
1135     char sign;
1136     BYTE len;
1137     char str[I10_OUTPUT_MAX_PREC+1]; /* add space for '\0' */
1138 };
1139
1140 /*********************************************************************
1141  *              $I10_OUTPUT (MSVCRT.@)
1142  * ld - long double to be printed to data
1143  * prec - precision of part, we're interested in
1144  * flag - 0 for first prec digits, 1 for fractional part
1145  * data - data to be populated
1146  *
1147  * return value
1148  *      0 if given double is NaN or INF
1149  *      1 otherwise
1150  *
1151  * FIXME
1152  *      Native sets last byte of data->str to '0' or '9', I don't know what
1153  *      it means. Current implementation sets it always to '0'.
1154  */
1155 int CDECL MSVCRT_I10_OUTPUT(MSVCRT__LDOUBLE ld, int prec, int flag, struct _I10_OUTPUT_DATA *data)
1156 {
1157     static const char inf_str[] = "1#INF";
1158     static const char nan_str[] = "1#QNAN";
1159
1160     double d = ld.x;
1161     char format[8];
1162     char buf[I10_OUTPUT_MAX_PREC+9]; /* 9 = strlen("0.e+0000") + '\0' */
1163     char *p;
1164
1165     TRACE("(%lf %d %x %p)\n", d, prec, flag, data);
1166
1167     if(d<0) {
1168         data->sign = '-';
1169         d = -d;
1170     } else
1171         data->sign = ' ';
1172
1173     if(isinf(d)) {
1174         data->pos = 1;
1175         data->len = 5;
1176         memcpy(data->str, inf_str, sizeof(inf_str));
1177
1178         return 0;
1179     }
1180
1181     if(isnan(d)) {
1182         data->pos = 1;
1183         data->len = 6;
1184         memcpy(data->str, nan_str, sizeof(nan_str));
1185
1186         return 0;
1187     }
1188
1189     if(flag&1) {
1190         int exp = 1+floor(log10(d));
1191
1192         prec += exp;
1193         if(exp < 0)
1194             prec--;
1195     }
1196     prec--;
1197
1198     if(prec+1 > I10_OUTPUT_MAX_PREC)
1199         prec = I10_OUTPUT_MAX_PREC-1;
1200     else if(prec < 0) {
1201         d = 0.0;
1202         prec = 0;
1203     }
1204
1205     sprintf(format, "%%.%dle", prec);
1206     sprintf(buf, format, d);
1207
1208     buf[1] = buf[0];
1209     data->pos = atoi(buf+prec+3);
1210     if(buf[1] != '0')
1211         data->pos++;
1212
1213     for(p = buf+prec+1; p>buf+1 && *p=='0'; p--);
1214     data->len = p-buf;
1215
1216     memcpy(data->str, buf+1, data->len);
1217     data->str[data->len] = '\0';
1218
1219     if(buf[1]!='0' && prec-data->len+1>0)
1220         memcpy(data->str+data->len+1, buf+data->len+1, prec-data->len+1);
1221
1222     return 1;
1223 }
1224 #undef I10_OUTPUT_MAX_PREC