msvcrt: Implement _ultoa_s.
[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(!delim || !ctx || (!str && !*ctx)) {
156         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
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(!str) {
210         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
211         *MSVCRT__errno() = MSVCRT_EINVAL;
212         return 0;
213     }
214
215     if(!locale)
216         locale = get_locale();
217
218     /* FIXME: use *_l functions */
219     p = str;
220     while(isspace(*p))
221         p++;
222
223     if(*p == '-') {
224         sign = -1;
225         p++;
226     } else  if(*p == '+')
227         p++;
228
229     while(isdigit(*p)) {
230         found_digit = TRUE;
231         hlp = d*10+*(p++)-'0';
232         if(d>MSVCRT_UI64_MAX/10 || hlp<d) {
233             exp++;
234             break;
235         } else
236             d = hlp;
237     }
238     while(isdigit(*p)) {
239         exp++;
240         p++;
241     }
242
243     if(*p == *locale->locinfo->lconv->decimal_point)
244         p++;
245
246     while(isdigit(*p)) {
247         found_digit = TRUE;
248         hlp = d*10+*(p++)-'0';
249         if(d>MSVCRT_UI64_MAX/10 || hlp<d)
250             break;
251
252         d = hlp;
253         exp--;
254     }
255     while(isdigit(*p))
256         p++;
257
258     if(!found_digit) {
259         if(end)
260             *end = (char*)str;
261         return 0.0;
262     }
263
264     if(*p=='e' || *p=='E' || *p=='d' || *p=='D') {
265         int e=0, s=1;
266
267         p++;
268         if(*p == '-') {
269             s = -1;
270             p++;
271         } else if(*p == '+')
272             p++;
273
274         if(isdigit(*p)) {
275             while(isdigit(*p)) {
276                 if(e>INT_MAX/10 || (e=e*10+*p-'0')<0)
277                     e = INT_MAX;
278                 p++;
279             }
280             e *= s;
281
282             if(exp<0 && e<0 && exp+e>=0) exp = INT_MIN;
283             else if(exp>0 && e>0 && exp+e<0) exp = INT_MAX;
284             else exp += e;
285         } else {
286             if(*p=='-' || *p=='+')
287                 p--;
288             p--;
289         }
290     }
291
292     fpcontrol = _control87(0, 0);
293     _control87(MSVCRT__EM_DENORMAL|MSVCRT__EM_INVALID|MSVCRT__EM_ZERODIVIDE
294             |MSVCRT__EM_OVERFLOW|MSVCRT__EM_UNDERFLOW|MSVCRT__EM_INEXACT, 0xffffffff);
295
296     if(exp>0)
297         ret = (double)sign*d*pow(10, exp);
298     else
299         ret = (double)sign*d/pow(10, -exp);
300
301     _control87(fpcontrol, 0xffffffff);
302
303     if((d && ret==0.0) || isinf(ret))
304         *MSVCRT__errno() = MSVCRT_ERANGE;
305
306     if(end)
307         *end = (char*)p;
308
309     return ret;
310 }
311
312 /*********************************************************************
313  *              strtod  (MSVCRT.@)
314  */
315 double CDECL MSVCRT_strtod( const char *str, char **end )
316 {
317     return MSVCRT_strtod_l( str, end, NULL );
318 }
319
320 /*********************************************************************
321  *              atof  (MSVCRT.@)
322  */
323 double CDECL MSVCRT_atof( const char *str )
324 {
325     return MSVCRT_strtod_l(str, NULL, NULL);
326 }
327
328 /*********************************************************************
329  *              _atof_l  (MSVCRT.@)
330  */
331 double CDECL MSVCRT__atof_l( const char *str, MSVCRT__locale_t locale)
332 {
333     return MSVCRT_strtod_l(str, NULL, locale);
334 }
335
336 /*********************************************************************
337  *              strcoll (MSVCRT.@)
338  */
339 int CDECL MSVCRT_strcoll( const char* str1, const char* str2 )
340 {
341     /* FIXME: handle Windows locale */
342     return strcoll( str1, str2 );
343 }
344
345 /*********************************************************************
346  *      strcpy_s (MSVCRT.@)
347  */
348 int CDECL MSVCRT_strcpy_s( char* dst, MSVCRT_size_t elem, const char* src )
349 {
350     MSVCRT_size_t i;
351     if(!elem) return MSVCRT_EINVAL;
352     if(!dst) return MSVCRT_EINVAL;
353     if(!src)
354     {
355         dst[0] = '\0';
356         return MSVCRT_EINVAL;
357     }
358
359     for(i = 0; i < elem; i++)
360     {
361         if((dst[i] = src[i]) == '\0') return 0;
362     }
363     dst[0] = '\0';
364     return MSVCRT_ERANGE;
365 }
366
367 /*********************************************************************
368  *      strcat_s (MSVCRT.@)
369  */
370 int CDECL MSVCRT_strcat_s( char* dst, MSVCRT_size_t elem, const char* src )
371 {
372     MSVCRT_size_t i, j;
373     if(!dst) return MSVCRT_EINVAL;
374     if(elem == 0) return MSVCRT_EINVAL;
375     if(!src)
376     {
377         dst[0] = '\0';
378         return MSVCRT_EINVAL;
379     }
380
381     for(i = 0; i < elem; i++)
382     {
383         if(dst[i] == '\0')
384         {
385             for(j = 0; (j + i) < elem; j++)
386             {
387                 if((dst[j + i] = src[j]) == '\0') return 0;
388             }
389         }
390     }
391     /* Set the first element to 0, not the first element after the skipped part */
392     dst[0] = '\0';
393     return MSVCRT_ERANGE;
394 }
395
396 /*********************************************************************
397  *              strxfrm (MSVCRT.@)
398  */
399 MSVCRT_size_t CDECL MSVCRT_strxfrm( char *dest, const char *src, MSVCRT_size_t len )
400 {
401     /* FIXME: handle Windows locale */
402     return strxfrm( dest, src, len );
403 }
404
405 /*********************************************************************
406  *              _stricoll (MSVCRT.@)
407  */
408 int CDECL MSVCRT__stricoll( const char* str1, const char* str2 )
409 {
410   /* FIXME: handle collates */
411   TRACE("str1 %s str2 %s\n", debugstr_a(str1), debugstr_a(str2));
412   return lstrcmpiA( str1, str2 );
413 }
414
415 /********************************************************************
416  *              _atoldbl (MSVCRT.@)
417  */
418 int CDECL MSVCRT__atoldbl(MSVCRT__LDOUBLE *value, const char *str)
419 {
420   /* FIXME needs error checking for huge/small values */
421 #ifdef HAVE_STRTOLD
422   TRACE("str %s value %p\n",str,value);
423   value->x = strtold(str,0);
424 #else
425   FIXME("stub, str %s value %p\n",str,value);
426 #endif
427   return 0;
428 }
429
430 /********************************************************************
431  *              __STRINGTOLD (MSVCRT.@)
432  */
433 int CDECL __STRINGTOLD( MSVCRT__LDOUBLE *value, char **endptr, const char *str, int flags )
434 {
435 #ifdef HAVE_STRTOLD
436     FIXME("%p %p %s %x partial stub\n", value, endptr, str, flags );
437     value->x = strtold(str,endptr);
438 #else
439     FIXME("%p %p %s %x stub\n", value, endptr, str, flags );
440 #endif
441     return 0;
442 }
443
444 /******************************************************************
445  *              strtol (MSVCRT.@)
446  */
447 MSVCRT_long CDECL MSVCRT_strtol(const char* nptr, char** end, int base)
448 {
449     /* wrapper to forward libc error code to msvcrt's error codes */
450     long ret;
451
452     errno = 0;
453     ret = strtol(nptr, end, base);
454     switch (errno)
455     {
456     case ERANGE:        *MSVCRT__errno() = MSVCRT_ERANGE;       break;
457     case EINVAL:        *MSVCRT__errno() = MSVCRT_EINVAL;       break;
458     default:
459         /* cope with the fact that we may use 64bit long integers on libc
460          * while msvcrt always uses 32bit long integers
461          */
462         if (ret > MSVCRT_LONG_MAX)
463         {
464             ret = MSVCRT_LONG_MAX;
465             *MSVCRT__errno() = MSVCRT_ERANGE;
466         }
467         else if (ret < -MSVCRT_LONG_MAX - 1)
468         {
469             ret = -MSVCRT_LONG_MAX - 1;
470             *MSVCRT__errno() = MSVCRT_ERANGE;
471         }
472         break;
473     }
474
475     return ret;
476 }
477
478 /******************************************************************
479  *              strtoul (MSVCRT.@)
480  */
481 MSVCRT_ulong CDECL MSVCRT_strtoul(const char* nptr, char** end, int base)
482 {
483     /* wrapper to forward libc error code to msvcrt's error codes */
484     unsigned long ret;
485
486     errno = 0;
487     ret = strtoul(nptr, end, base);
488     switch (errno)
489     {
490     case ERANGE:        *MSVCRT__errno() = MSVCRT_ERANGE;       break;
491     case EINVAL:        *MSVCRT__errno() = MSVCRT_EINVAL;       break;
492     default:
493         /* cope with the fact that we may use 64bit long integers on libc
494          * while msvcrt always uses 32bit long integers
495          */
496         if (ret > MSVCRT_ULONG_MAX)
497         {
498             ret = MSVCRT_ULONG_MAX;
499             *MSVCRT__errno() = MSVCRT_ERANGE;
500         }
501         break;
502     }
503
504     return ret;
505 }
506
507 /******************************************************************
508  *              strnlen (MSVCRT.@)
509  */
510 MSVCRT_size_t CDECL MSVCRT_strnlen(const char *s, MSVCRT_size_t maxlen)
511 {
512     MSVCRT_size_t i;
513
514     for(i=0; i<maxlen; i++)
515         if(!s[i]) break;
516
517     return i;
518 }
519
520 /*********************************************************************
521  *  _strtoi64_l (MSVCRT.@)
522  *
523  * FIXME: locale parameter is ignored
524  */
525 __int64 CDECL MSVCRT_strtoi64_l(const char *nptr, char **endptr, int base, MSVCRT__locale_t locale)
526 {
527     BOOL negative = FALSE;
528     __int64 ret = 0;
529
530     TRACE("(%s %p %d %p)\n", nptr, endptr, base, locale);
531
532     if(!nptr || base<0 || base>36 || base==1) {
533         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
534         return 0;
535     }
536
537     while(isspace(*nptr)) nptr++;
538
539     if(*nptr == '-') {
540         negative = TRUE;
541         nptr++;
542     } else if(*nptr == '+')
543         nptr++;
544
545     if((base==0 || base==16) && *nptr=='0' && tolower(*(nptr+1))=='x') {
546         base = 16;
547         nptr += 2;
548     }
549
550     if(base == 0) {
551         if(*nptr=='0')
552             base = 8;
553         else
554             base = 10;
555     }
556
557     while(*nptr) {
558         char cur = tolower(*nptr);
559         int v;
560
561         if(isdigit(cur)) {
562             if(cur >= '0'+base)
563                 break;
564             v = cur-'0';
565         } else {
566             if(cur<'a' || cur>='a'+base-10)
567                 break;
568             v = cur-'a'+10;
569         }
570
571         if(negative)
572             v = -v;
573
574         nptr++;
575
576         if(!negative && (ret>MSVCRT_I64_MAX/base || ret*base>MSVCRT_I64_MAX-v)) {
577             ret = MSVCRT_I64_MAX;
578             *MSVCRT__errno() = MSVCRT_ERANGE;
579         } else if(negative && (ret<MSVCRT_I64_MIN/base || ret*base<MSVCRT_I64_MIN-v)) {
580             ret = MSVCRT_I64_MIN;
581             *MSVCRT__errno() = MSVCRT_ERANGE;
582         } else
583             ret = ret*base + v;
584     }
585
586     if(endptr)
587         *endptr = (char*)nptr;
588
589     return ret;
590 }
591
592 /*********************************************************************
593  *  _strtoi64 (MSVCRT.@)
594  */
595 __int64 CDECL MSVCRT_strtoi64(const char *nptr, char **endptr, int base)
596 {
597     return MSVCRT_strtoi64_l(nptr, endptr, base, NULL);
598 }
599
600 /*********************************************************************
601  *  _strtoui64_l (MSVCRT.@)
602  *
603  * FIXME: locale parameter is ignored
604  */
605 unsigned __int64 CDECL MSVCRT_strtoui64_l(const char *nptr, char **endptr, int base, MSVCRT__locale_t locale)
606 {
607     BOOL negative = FALSE;
608     unsigned __int64 ret = 0;
609
610     TRACE("(%s %p %d %p)\n", nptr, endptr, base, locale);
611
612     if(!nptr || base<0 || base>36 || base==1) {
613         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
614         return 0;
615     }
616
617     while(isspace(*nptr)) nptr++;
618
619     if(*nptr == '-') {
620         negative = TRUE;
621         nptr++;
622     } else if(*nptr == '+')
623         nptr++;
624
625     if((base==0 || base==16) && *nptr=='0' && tolower(*(nptr+1))=='x') {
626         base = 16;
627         nptr += 2;
628     }
629
630     if(base == 0) {
631         if(*nptr=='0')
632             base = 8;
633         else
634             base = 10;
635     }
636
637     while(*nptr) {
638         char cur = tolower(*nptr);
639         int v;
640
641         if(isdigit(cur)) {
642             if(cur >= '0'+base)
643                 break;
644             v = *nptr-'0';
645         } else {
646             if(cur<'a' || cur>='a'+base-10)
647                 break;
648             v = cur-'a'+10;
649         }
650
651         nptr++;
652
653         if(ret>MSVCRT_UI64_MAX/base || ret*base>MSVCRT_UI64_MAX-v) {
654             ret = MSVCRT_UI64_MAX;
655             *MSVCRT__errno() = MSVCRT_ERANGE;
656         } else
657             ret = ret*base + v;
658     }
659
660     if(endptr)
661         *endptr = (char*)nptr;
662
663     return negative ? -ret : ret;
664 }
665
666 /*********************************************************************
667  *  _strtoui64 (MSVCRT.@)
668  */
669 unsigned __int64 CDECL MSVCRT_strtoui64(const char *nptr, char **endptr, int base)
670 {
671     return MSVCRT_strtoui64_l(nptr, endptr, base, NULL);
672 }
673
674 /*********************************************************************
675  *  _itoa_s (MSVCRT.@)
676  */
677 int CDECL _itoa_s(int value, char *str, MSVCRT_size_t size, int radix)
678 {
679     unsigned int val, digit;
680     int is_negative;
681     char buffer[33], *pos;
682     size_t len;
683
684     if (!str || !size || radix < 2 || radix > 36)
685     {
686         if (str && size)
687             str[0] = '\0';
688
689         *MSVCRT__errno() = MSVCRT_EINVAL;
690         return MSVCRT_EINVAL;
691     }
692
693     if (value < 0 && radix == 10)
694     {
695         is_negative = 1;
696         val = -value;
697     }
698     else
699     {
700         is_negative = 0;
701         val = value;
702     }
703
704     pos = buffer + 32;
705     *pos = '\0';
706
707     do
708     {
709         digit = val % radix;
710         val /= radix;
711
712         if (digit < 10)
713             *--pos = '0' + digit;
714         else
715             *--pos = 'a' + digit - 10;
716     }
717     while (val != 0);
718
719     if (is_negative)
720         *--pos = '-';
721
722     len = buffer + 33 - pos;
723     if (len > size)
724     {
725         size_t i;
726         char *p = str;
727
728         /* Copy the temporary buffer backwards up to the available number of
729          * characters. Don't copy the negative sign if present. */
730
731         if (is_negative)
732         {
733             p++;
734             size--;
735         }
736
737         for (pos = buffer + 31, i = 0; i < size; i++)
738             *p++ = *pos--;
739
740         str[0] = '\0';
741         *MSVCRT__errno() = MSVCRT_ERANGE;
742         return MSVCRT_ERANGE;
743     }
744
745     memcpy(str, pos, len);
746     return 0;
747 }
748
749 /*********************************************************************
750  *  _ui64toa_s (MSVCRT.@)
751  */
752 int CDECL MSVCRT__ui64toa_s(unsigned __int64 value, char *str,
753         MSVCRT_size_t size, int radix)
754 {
755     char buffer[65], *pos;
756     int digit;
757
758     if(!str || radix<2 || radix>36) {
759         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
760         *MSVCRT__errno() = MSVCRT_EINVAL;
761         return MSVCRT_EINVAL;
762     }
763
764     pos = buffer+64;
765     *pos = '\0';
766
767     do {
768         digit = value%radix;
769         value /= radix;
770
771         if(digit < 10)
772             *--pos = '0'+digit;
773         else
774             *--pos = 'a'+digit-10;
775     }while(value != 0);
776
777     if(buffer-pos+65 > size) {
778         MSVCRT__invalid_parameter(NULL, NULL, NULL, 0, 0);
779         *MSVCRT__errno() = MSVCRT_EINVAL;
780         return MSVCRT_EINVAL;
781     }
782
783     memcpy(str, pos, buffer-pos+65);
784     return 0;
785 }
786
787 /*********************************************************************
788  *  _ultoa_s (MSVCRT.@)
789  */
790 int CDECL _ultoa_s(MSVCRT_ulong value, char *str, MSVCRT_size_t size, int radix)
791 {
792     MSVCRT_ulong digit;
793     char buffer[33], *pos;
794     size_t len;
795
796     if (!str || !size || radix < 2 || radix > 36)
797     {
798         if (str && size)
799             str[0] = '\0';
800
801         *MSVCRT__errno() = MSVCRT_EINVAL;
802         return MSVCRT_EINVAL;
803     }
804
805     pos = buffer + 32;
806     *pos = '\0';
807
808     do
809     {
810         digit = value % radix;
811         value /= radix;
812
813         if (digit < 10)
814             *--pos = '0' + digit;
815         else
816             *--pos = 'a' + digit - 10;
817     }
818     while (value != 0);
819
820     len = buffer + 33 - pos;
821     if (len > size)
822     {
823         size_t i;
824         char *p = str;
825
826         /* Copy the temporary buffer backwards up to the available number of
827          * characters. */
828
829         for (pos = buffer + 31, i = 0; i < size; i++)
830             *p++ = *pos--;
831
832         str[0] = '\0';
833         *MSVCRT__errno() = MSVCRT_ERANGE;
834         return MSVCRT_ERANGE;
835     }
836
837     memcpy(str, pos, len);
838     return 0;
839 }
840
841 #define I10_OUTPUT_MAX_PREC 21
842 /* Internal structure used by $I10_OUTPUT */
843 struct _I10_OUTPUT_DATA {
844     short pos;
845     char sign;
846     BYTE len;
847     char str[I10_OUTPUT_MAX_PREC+1]; /* add space for '\0' */
848 };
849
850 /*********************************************************************
851  *              $I10_OUTPUT (MSVCRT.@)
852  * ld - long double to be printed to data
853  * prec - precision of part, we're interested in
854  * flag - 0 for first prec digits, 1 for fractional part
855  * data - data to be populated
856  *
857  * return value
858  *      0 if given double is NaN or INF
859  *      1 otherwise
860  *
861  * FIXME
862  *      Native sets last byte of data->str to '0' or '9', I don't know what
863  *      it means. Current implementation sets it always to '0'.
864  */
865 int CDECL MSVCRT_I10_OUTPUT(MSVCRT__LDOUBLE ld, int prec, int flag, struct _I10_OUTPUT_DATA *data)
866 {
867     static const char inf_str[] = "1#INF";
868     static const char nan_str[] = "1#QNAN";
869
870     double d = ld.x;
871     char format[8];
872     char buf[I10_OUTPUT_MAX_PREC+9]; /* 9 = strlen("0.e+0000") + '\0' */
873     char *p;
874
875     TRACE("(%lf %d %x %p)\n", d, prec, flag, data);
876
877     if(d<0) {
878         data->sign = '-';
879         d = -d;
880     } else
881         data->sign = ' ';
882
883     if(isinf(d)) {
884         data->pos = 1;
885         data->len = 5;
886         memcpy(data->str, inf_str, sizeof(inf_str));
887
888         return 0;
889     }
890
891     if(isnan(d)) {
892         data->pos = 1;
893         data->len = 6;
894         memcpy(data->str, nan_str, sizeof(nan_str));
895
896         return 0;
897     }
898
899     if(flag&1) {
900         int exp = 1+floor(log10(d));
901
902         prec += exp;
903         if(exp < 0)
904             prec--;
905     }
906     prec--;
907
908     if(prec+1 > I10_OUTPUT_MAX_PREC)
909         prec = I10_OUTPUT_MAX_PREC-1;
910     else if(prec < 0) {
911         d = 0.0;
912         prec = 0;
913     }
914
915     sprintf(format, "%%.%dle", prec);
916     sprintf(buf, format, d);
917
918     buf[1] = buf[0];
919     data->pos = atoi(buf+prec+3);
920     if(buf[1] != '0')
921         data->pos++;
922
923     for(p = buf+prec+1; p>buf+1 && *p=='0'; p--);
924     data->len = p-buf;
925
926     memcpy(data->str, buf+1, data->len);
927     data->str[data->len] = '\0';
928
929     if(buf[1]!='0' && prec-data->len+1>0)
930         memcpy(data->str+data->len+1, buf+data->len+1, prec-data->len+1);
931
932     return 1;
933 }
934 #undef I10_OUTPUT_MAX_PREC