2 * COPYRIGHT: GNU GPL, see COPYING in the top level directory
3 * PROJECT: ReactOS crt library
4 * FILE: lib/sdk/crt/printf/streamout.c
5 * PURPOSE: Implementation of streamout
6 * PROGRAMMER: Timo Kreuzer
17 # define streamout wstreamout
18 # define format_float format_floatw
19 # define _flsbuf _flswbuf
20 int __cdecl
_flswbuf(int ch
, FILE *stream
);
25 # define _flsbuf(chr, stream) _TEOF
29 #define BUFFER_SIZE (32 + 17)
31 int mbtowc(wchar_t *wchar
, const char *mbchar
, size_t count
);
32 int wctomb(char *mbchar
, wchar_t wchar
);
34 typedef struct _STRING
36 unsigned short Length
;
37 unsigned short MaximumLength
;
43 /* Formatting flags */
44 FLAG_ALIGN_LEFT
= 0x01,
45 FLAG_FORCE_SIGN
= 0x02,
46 FLAG_FORCE_SIGNSP
= 0x04,
50 /* Data format flags */
53 FLAG_WIDECHAR
= FLAG_LONG
,
56 FLAG_INTPTR
= FLAG_INT64
,
60 FLAG_LONGDOUBLE
= 0x800,
63 #define va_arg_f(argptr, flags) \
64 (flags & FLAG_INT64) ? va_arg(argptr, __int64) : \
65 (flags & FLAG_SHORT) ? (short)va_arg(argptr, int) : \
68 #define va_arg_fu(argptr, flags) \
69 (flags & FLAG_INT64) ? va_arg(argptr, unsigned __int64) : \
70 (flags & FLAG_SHORT) ? (unsigned short)va_arg(argptr, int) : \
71 va_arg(argptr, unsigned int)
73 #define va_arg_ffp(argptr, flags) \
74 (flags & FLAG_LONGDOUBLE) ? va_arg(argptr, long double) : \
75 va_arg(argptr, double)
77 #define get_exp(f) floor(f == 0 ? 0 : (f >= 0 ? log10(f) : log10(-f)))
78 #define round(x) floor((x) + 0.5)
82 /* Due to restrictions in kernel mode regarding the use of floating point,
83 we prevent it from being inlined */
94 static const TCHAR digits_l
[] = _T("0123456789abcdef0x");
95 static const TCHAR digits_u
[] = _T("0123456789ABCDEF0X");
96 static const TCHAR _nan
[] = _T("#QNAN");
97 static const TCHAR _infinity
[] = _T("#INF");
98 const TCHAR
*digits
= digits_l
;
99 int exponent
= 0, sign
;
100 long double fpval
, fpval2
;
101 int padding
= 0, num_digits
, val32
, base
= 10;
103 /* Normalize the precision */
104 if (precision
< 0) precision
= 6;
105 else if (precision
> 17)
107 padding
= precision
- 17;
111 /* Get the float value and calculate the exponent */
112 fpval
= va_arg_ffp(*argptr
, flags
);
113 exponent
= get_exp(fpval
);
114 sign
= fpval
< 0 ? -1 : 1;
116 /* Shift the decimal point and round */
117 fpval2
= round(sign
* fpval
* pow(10., precision
- exponent
));
118 if (fpval2
>= (unsigned __int64
)pow(10., precision
+ 1))
121 fpval2
= round(sign
* fpval
* pow(10., precision
- exponent
));
129 if (precision
> 0) precision
--;
130 if (exponent
< -4 || exponent
>= precision
) goto case_e
;
132 /* Skip trailing zeroes */
133 while (precision
&& (unsigned __int64
)fpval2
% 10 == 0)
144 val32
= exponent
>= 0 ? exponent
: -exponent
;
146 // FIXME: handle length of exponent field:
147 // http://msdn.microsoft.com/de-de/library/0fatw238%28VS.80%29.aspx
151 *--(*string
) = digits
[val32
% 10];
155 /* Sign for the exponent */
156 *--(*string
) = exponent
>= 0 ? _T('+') : _T('-');
158 /* Add 'e' or 'E' separator */
159 *--(*string
) = digits
[0xe];
177 else if (flags
& FLAG_FORCE_SIGN
)
179 else if (flags
& FLAG_FORCE_SIGNSP
)
182 /* Handle special cases first */
185 (*string
) -= sizeof(_nan
) / sizeof(TCHAR
) - 1;
186 _tcscpy((*string
), _nan
);
189 else if (!_finite(fpval
))
191 (*string
) -= sizeof(_infinity
) / sizeof(TCHAR
) - 1;
192 _tcscpy((*string
), _infinity
);
198 while (padding
-- > 0) *--(*string
) = _T('0');
200 /* Digits after the decimal point */
201 num_digits
= precision
;
202 while (num_digits
-- > 0)
204 *--(*string
) = digits
[(unsigned __int64
)fpval2
% 10];
209 if (precision
> 0 || flags
& FLAG_SPECIAL
)
210 *--(*string
) = _T('.');
212 /* Digits before the decimal point */
215 *--(*string
) = digits
[(unsigned __int64
)fpval2
% base
];
218 while ((unsigned __int64
)fpval2
);
224 streamout_char(FILE *stream
, int chr
)
226 /* Check if the buffer is full */
227 if (stream
->_cnt
< sizeof(TCHAR
))
229 /* Strings are done now */
230 if (stream
->_flag
& _IOSTRG
) return _TEOF
;
232 /* Flush buffer for files */
233 return _flsbuf(chr
, stream
) != _TEOF
;
236 *(TCHAR
*)stream
->_ptr
= chr
;
237 stream
->_ptr
+= sizeof(TCHAR
);
238 stream
->_cnt
-= sizeof(TCHAR
);
245 streamout_astring(FILE *stream
, const char *string
, int count
)
254 if ((len
= mbtowc(&chr
, string
, MB_CUR_MAX
)) < 1) break;
259 if (streamout_char(stream
, chr
) == 0) return -1;
268 streamout_wstring(FILE *stream
, const wchar_t *string
, int count
)
276 char mbchar
[MB_CUR_MAX
], *ptr
= mbchar
;
279 mblen
= wctomb(mbchar
, *string
++);
280 if (mblen
<= 0) return written
;
282 while (chr
= *ptr
++, mblen
--)
287 if (streamout_char(stream
, chr
) == 0) return -1;
296 #define streamout_string streamout_wstring
298 #define streamout_string streamout_astring
304 streamout(FILE *stream
, const TCHAR
*format
, va_list argptr
)
306 static const TCHAR digits_l
[] = _T("0123456789abcdef0x");
307 static const TCHAR digits_u
[] = _T("0123456789ABCDEF0X");
308 static const char *_nullstring
= "(null)";
309 TCHAR buffer
[BUFFER_SIZE
+ 1];
312 const TCHAR
*digits
, *prefix
;
313 int base
, len
, prefixlen
, fieldwidth
, precision
, padding
;
314 int written
= 1, written_all
= 0;
316 unsigned __int64 val64
;
318 buffer
[BUFFER_SIZE
] = '\0';
324 /* Check for end of format string */
325 if (chr
== _T('\0')) break;
327 /* Check for 'normal' character or double % */
328 if ((chr
!= _T('%')) ||
329 (chr
= *format
++) == _T('%'))
331 /* Write the character to the stream */
332 if ((written
= streamout_char(stream
, chr
)) == 0) return -1;
333 written_all
+= written
;
341 if (chr
== _T('-')) flags
|= FLAG_ALIGN_LEFT
;
342 else if (chr
== _T('+')) flags
|= FLAG_FORCE_SIGN
;
343 else if (chr
== _T(' ')) flags
|= FLAG_FORCE_SIGNSP
;
344 else if (chr
== _T('0')) flags
|= FLAG_PAD_ZERO
;
345 else if (chr
== _T('#')) flags
|= FLAG_SPECIAL
;
350 /* Handle field width modifier */
353 fieldwidth
= va_arg(argptr
, int);
356 flags
|= FLAG_ALIGN_LEFT
;
357 fieldwidth
= -fieldwidth
;
364 while (chr
>= _T('0') && chr
<= _T('9'))
366 fieldwidth
= fieldwidth
* 10 + (chr
- _T('0'));
371 /* Handle precision modifier */
378 precision
= va_arg(argptr
, int);
384 while (chr
>= _T('0') && chr
<= _T('9'))
386 precision
= precision
* 10 + (chr
- _T('0'));
393 /* Handle argument size prefix */
396 if (chr
== _T('h')) flags
|= FLAG_SHORT
;
397 else if (chr
== _T('w')) flags
|= FLAG_WIDECHAR
;
398 else if (chr
== _T('L')) flags
|= 0; // FIXME: long double
399 else if (chr
== _T('F')) flags
|= 0; // FIXME: what is that?
400 else if (chr
== _T('l'))
404 if (format
[0] == _T('l'))
411 else if (chr
== _T('I'))
413 if (format
[0] == _T('3') && format
[1] == _T('2'))
417 else if (format
[0] == _T('6') && format
[1] == _T('4'))
422 else if (format
[0] == _T('x') || format
[0] == _T('X') ||
423 format
[0] == _T('d') || format
[0] == _T('i') ||
424 format
[0] == _T('u') || format
[0] == _T('o'))
426 flags
|= FLAG_INTPTR
;
434 /* Handle the format specifier */
436 string
= &buffer
[BUFFER_SIZE
];
442 if (flags
& FLAG_INT64
)
443 *va_arg(argptr
, __int64
*) = written_all
;
444 else if (flags
& FLAG_SHORT
)
445 *va_arg(argptr
, short*) = written_all
;
447 *va_arg(argptr
, int*) = written_all
;
452 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
458 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
463 if (flags
& FLAG_WIDECHAR
)
465 ((wchar_t*)string
)[0] = va_arg(argptr
, int);
466 ((wchar_t*)string
)[1] = _T('\0');
470 ((char*)string
)[0] = va_arg(argptr
, int);
471 ((char*)string
)[1] = _T('\0');
476 nt_string
= va_arg(argptr
, void*);
477 if (nt_string
&& (string
= nt_string
->Buffer
))
479 len
= nt_string
->Length
;
480 if (flags
& FLAG_WIDECHAR
) len
/= sizeof(wchar_t);
487 string
= va_arg(argptr
, void*);
489 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
494 string
= va_arg(argptr
, void*);
496 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
502 string
= (TCHAR
*)_nullstring
;
503 flags
&= ~FLAG_WIDECHAR
;
506 if (flags
& FLAG_WIDECHAR
)
507 len
= wcslen((wchar_t*)string
);
509 len
= strlen((char*)string
);
510 if (precision
>= 0 && len
> precision
) len
= precision
;
522 flags
|= FLAG_WIDECHAR
;
524 flags
&= ~FLAG_WIDECHAR
;
526 /* Use external function, one for kernel one for user mode */
527 format_float(chr
, flags
, precision
, &string
, &prefix
, &argptr
);
528 len
= _tcslen(string
);
534 val64
= (__int64
)va_arg_f(argptr
, flags
);
536 if ((__int64
)val64
< 0)
541 else if (flags
& FLAG_FORCE_SIGN
)
543 else if (flags
& FLAG_FORCE_SIGNSP
)
550 if (flags
& FLAG_SPECIAL
)
553 if (precision
> 0) precision
--;
558 precision
= 2 * sizeof(void*);
559 flags
&= ~FLAG_PAD_ZERO
;
560 flags
|= FLAG_INTPTR
;
569 if (flags
& FLAG_SPECIAL
)
571 prefix
= &digits
[16];
576 val64
= va_arg_fu(argptr
, flags
);
580 flags
|= FLAG_WIDECHAR
;
582 flags
&= ~FLAG_WIDECHAR
;
584 if (precision
< 0) precision
= 1;
586 /* Gather digits in reverse order */
589 *--string
= digits
[val64
% base
];
594 len
= _tcslen(string
);
598 /* Treat anything else as a new character */
603 /* Calculate padding */
604 prefixlen
= prefix
? _tcslen(prefix
) : 0;
605 if (precision
< 0) precision
= 0;
606 padding
= fieldwidth
- len
- prefixlen
- precision
;
607 if (padding
< 0) padding
= 0;
609 /* Optional left space padding */
610 if ((flags
& (FLAG_ALIGN_LEFT
| FLAG_PAD_ZERO
)) == 0)
612 for (; padding
> 0; padding
--)
614 if ((written
= streamout_char(stream
, _T(' '))) == 0) return -1;
615 written_all
+= written
;
619 /* Optional prefix */
622 written
= streamout_string(stream
, prefix
, prefixlen
);
623 if (written
== -1) return -1;
624 written_all
+= written
;
627 /* Optional left '0' padding */
628 if ((flags
& FLAG_ALIGN_LEFT
) == 0) precision
+= padding
;
629 while (precision
-- > 0)
631 if ((written
= streamout_char(stream
, _T('0'))) == 0) return -1;
632 written_all
+= written
;
635 /* Output the string */
636 if (flags
& FLAG_WIDECHAR
)
637 written
= streamout_wstring(stream
, (wchar_t*)string
, len
);
639 written
= streamout_astring(stream
, (char*)string
, len
);
640 if (written
== -1) return -1;
641 written_all
+= written
;
643 #if 0 && SUPPORT_FLOAT
644 /* Optional right '0' padding */
645 while (precision
-- > 0)
647 if ((written
= streamout_char(stream
, _T('0'))) == 0) return -1;
648 written_all
+= written
;
653 /* Optional right padding */
654 if (flags
& FLAG_ALIGN_LEFT
)
656 while (padding
-- > 0)
658 if ((written
= streamout_char(stream
, _T(' '))) == 0) return -1;
659 written_all
+= written
;
665 if (written
== -1) return -1;