Merge 51058, 51062-51063
[reactos.git] / reactos / lib / sdk / crt / printf / streamout.c
index 1e1992d..66aae27 100644 (file)
@@ -14,8 +14,8 @@
 #include <float.h>
 
 #ifdef _UNICODE
-#define streamout wstreamout
-#define format_float format_floatw
+# define streamout wstreamout
+# define format_float format_floatw
 #endif
 
 #define MB_CUR_MAX 10
@@ -67,11 +67,10 @@ enum
     (flags & FLAG_LONGDOUBLE) ? va_arg(argptr, long double) : \
     va_arg(argptr, double)
 
-#ifdef _LIBCNT_
-# define _flsbuf(chr, stream) 0
-#endif
+#define get_exp(f) floor(f == 0 ? 0 : (f >= 0 ? log10(f) : log10(-f)))
+#define round(x) floor((x) + 0.5)
 
-#define get_exp(f) floor(f > 0 ? log10(f) : log10(-f))
+#ifndef _USER32_WSPRINTF
 
 void
 #ifdef _LIBCNT
@@ -92,30 +91,56 @@ format_float(
     static const TCHAR _nan[] = _T("#QNAN");
     static const TCHAR _infinity[] = _T("#INF");
     const TCHAR *digits = digits_l;
-    int exponent = 0;
-    long double fpval;
-    int num_digits, val32, base = 10;
-    __int64 val64;
+    int exponent = 0, sign;
+    long double fpval, fpval2;
+    int padding = 0, num_digits, val32, base = 10;
 
+    /* Normalize the precision */
     if (precision < 0) precision = 6;
-    else if (precision > 512) precision = 512;
+    else if (precision > 17)
+    {
+        padding = precision - 17;
+        precision = 17;
+    }
 
+    /* Get the float value and calculate the exponent */
     fpval = va_arg_ffp(*argptr, flags);
     exponent = get_exp(fpval);
+    sign = fpval < 0 ? -1 : 1;
 
     switch (chr)
     {
         case _T('G'):
             digits = digits_u;
         case _T('g'):
+            if (precision > 0) precision--;
             if (exponent < -4 || exponent >= precision) goto case_e;
+
+            /* Shift the decimal point and round */
+            fpval2 = round(sign * fpval * pow(10., precision));
+
+            /* Skip trailing zeroes */
+            while (precision && (unsigned __int64)fpval2 % 10 == 0)
+            {
+                precision--;
+                fpval2 /= 10;
+            }
             break;
 
         case _T('E'):
             digits = digits_u;
         case _T('e'):
         case_e:
-            fpval /= pow(10., exponent);
+            /* Shift the decimal point and round */
+            fpval2 = round(sign * fpval * pow(10., precision - exponent));
+
+            /* Compensate for changed exponent through rounding */
+            if (fpval2 >= (unsigned __int64)pow(10., precision + 1))
+            {
+                exponent++;
+                fpval2 = round(sign * fpval * pow(10., precision - exponent));
+            }
+
             val32 = exponent >= 0 ? exponent : -exponent;
 
             // FIXME: handle length of exponent field:
@@ -128,7 +153,7 @@ format_float(
             }
 
             /* Sign for the exponent */
-            *--(*string) = exponent > 0 ? _T('+') : _T('-');
+            *--(*string) = exponent >= 0 ? _T('+') : _T('-');
 
             /* Add 'e' or 'E' separator */
             *--(*string) = digits[0xe];
@@ -141,16 +166,15 @@ format_float(
             // FIXME: TODO
 
         case _T('f'):
+        default:
+            /* Shift the decimal point and round */
+            fpval2 = round(sign * fpval * pow(10., precision));
             break;
     }
 
-    /* CHECKME: Windows seems to handle a max of 17 digits(?) */
-    num_digits = precision <= 17 ? precision: 17;
-
     /* Handle sign */
     if (fpval < 0)
     {
-        fpval = -fpval;
         *prefix = _T("-");
     }
     else if (flags & FLAG_FORCE_SIGN)
@@ -163,57 +187,59 @@ format_float(
     {
         (*string) -= sizeof(_nan) / sizeof(TCHAR) - 1;
         _tcscpy((*string), _nan);
-        val64 = 1;
+        fpval2 = 1;
     }
     else if (!_finite(fpval))
     {
         (*string) -= sizeof(_infinity) / sizeof(TCHAR) - 1;
         _tcscpy((*string), _infinity);
-        val64 = 1;
+        fpval2 = 1;
     }
     else
     {
-        fpval *= pow(10., precision);
-        val64 = (__int64)(fpval + 0.5);
+        /* Zero padding */
+        while (padding-- > 0) *--(*string) = _T('0');
 
+        /* Digits after the decimal point */
+        num_digits = precision;
         while (num_digits-- > 0)
         {
-            *--(*string) = digits[val64 % 10];
-            val64 /= 10;
+            *--(*string) = digits[(unsigned __int64)fpval2 % 10];
+            fpval2 /= base;
         }
     }
 
-    *--(*string) = _T('.');
+    if (precision > 0 || flags & FLAG_SPECIAL)
+        *--(*string) = _T('.');
 
     /* Digits before the decimal point */
     do
     {
-        *--(*string) = digits[val64 % base];
-        val64 /= base;
+        *--(*string) = digits[(unsigned __int64)fpval2 % base];
+        fpval2 /= base;
     }
-    while (val64);
+    while ((unsigned __int64)fpval2);
 
 }
+#endif
 
 static
 int
 streamout_char(FILE *stream, int chr)
 {
+#if defined(_USER32_WSPRINTF) || defined(_LIBCNT_)
     /* Check if the buffer is full */
     if (stream->_cnt < sizeof(TCHAR))
-    {
-        /* Strings are done now */
-        if (stream->_flag & _IOSTRG) return _TEOF;
-
-        /* Flush buffer for files */
-        return _flsbuf(chr, stream) != _TEOF;
-    }
+        return 0;
 
     *(TCHAR*)stream->_ptr = chr;
     stream->_ptr += sizeof(TCHAR);
     stream->_cnt -= sizeof(TCHAR);
 
     return 1;
+#else
+    return _fputtc((TCHAR)chr, stream) != _TEOF;
+#endif
 }
 
 static
@@ -274,6 +300,11 @@ streamout_wstring(FILE *stream, const wchar_t *string, int count)
 #define streamout_string streamout_astring
 #endif
 
+#ifdef _USER32_WSPRINTF
+# define USE_MULTISIZE 0
+#else
+# define USE_MULTISIZE 1
+#endif
 
 int
 _cdecl
@@ -367,21 +398,17 @@ streamout(FILE *stream, const TCHAR *format, va_list argptr)
         else precision = -1;
 
         /* Handle argument size prefix */
-        while (1)
+        do
         {
                  if (chr == _T('h')) flags |= FLAG_SHORT;
             else if (chr == _T('w')) flags |= FLAG_WIDECHAR;
             else if (chr == _T('L')) flags |= 0; // FIXME: long double
+            else if (chr == _T('F')) flags |= 0; // FIXME: what is that?
             else if (chr == _T('l'))
             {
-                flags |= FLAG_LONG;
-#if SUPPORT_LL
-                if (format[0] == _T('l'))
-                {
-                    format++;
-                    flags |= FLAG_INT64;
-                }
-#endif
+                /* Check if this is the 2nd 'l' in a row */
+                if (format[-2] == 'l') flags |= FLAG_INT64;
+                else flags |= FLAG_LONG;
             }
             else if (chr == _T('I'))
             {
@@ -405,6 +432,7 @@ streamout(FILE *stream, const TCHAR *format, va_list argptr)
             else break;
             chr = *format++;
         }
+        while (USE_MULTISIZE);
 
         /* Handle the format specifier */
         digits = digits_l;
@@ -483,8 +511,10 @@ streamout(FILE *stream, const TCHAR *format, va_list argptr)
                 else
                     len = strlen((char*)string);
                 if (precision >= 0 && len > precision) len = precision;
+                precision = 0;
                 break;
 
+#ifndef _USER32_WSPRINTF
             case _T('G'):
             case _T('E'):
             case _T('A'):
@@ -502,6 +532,7 @@ streamout(FILE *stream, const TCHAR *format, va_list argptr)
                 len = _tcslen(string);
                 precision = 0;
                 break;
+#endif
 
             case _T('d'):
             case _T('i'):
@@ -521,9 +552,12 @@ streamout(FILE *stream, const TCHAR *format, va_list argptr)
 
             case _T('o'):
                 base = 8;
-                if (flags & FLAG_SPECIAL) prefix = _T("0");
+                if (flags & FLAG_SPECIAL)
+                {
+                    prefix = _T("0");
+                    if (precision > 0) precision--;
+                }
                 goto case_unsigned;
-                /* Fall through */
 
             case _T('p'):
                 precision = 2 * sizeof(void*);
@@ -540,6 +574,9 @@ streamout(FILE *stream, const TCHAR *format, va_list argptr)
                 if (flags & FLAG_SPECIAL)
                 {
                     prefix = &digits[16];
+#ifdef _USER32_WSPRINTF
+                    fieldwidth += 2;
+#endif
                 }
 
             case _T('u'):