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
22 #define BUFFER_SIZE (32 + 17)
24 int mbtowc(wchar_t *wchar
, const char *mbchar
, size_t count
);
25 int wctomb(char *mbchar
, wchar_t wchar
);
27 typedef struct _STRING
29 unsigned short Length
;
30 unsigned short MaximumLength
;
36 /* Formatting flags */
37 FLAG_ALIGN_LEFT
= 0x01,
38 FLAG_FORCE_SIGN
= 0x02,
39 FLAG_FORCE_SIGNSP
= 0x04,
43 /* Data format flags */
46 FLAG_WIDECHAR
= FLAG_LONG
,
49 FLAG_INTPTR
= FLAG_INT64
,
53 FLAG_LONGDOUBLE
= 0x800,
56 #define va_arg_f(argptr, flags) \
57 (flags & FLAG_INT64) ? va_arg(argptr, __int64) : \
58 (flags & FLAG_SHORT) ? (short)va_arg(argptr, int) : \
61 #define va_arg_fu(argptr, flags) \
62 (flags & FLAG_INT64) ? va_arg(argptr, unsigned __int64) : \
63 (flags & FLAG_SHORT) ? (unsigned short)va_arg(argptr, int) : \
64 va_arg(argptr, unsigned int)
66 #define va_arg_ffp(argptr, flags) \
67 (flags & FLAG_LONGDOUBLE) ? va_arg(argptr, long double) : \
68 va_arg(argptr, double)
71 # define _flsbuf(chr, stream) 0
74 #define get_exp(f) floor(f > 0 ? log10(f) : log10(-f))
78 /* Due to restrictions in kernel mode regarding the use of floating point,
79 we prevent it from being inlined */
90 static const TCHAR digits_l
[] = _T("0123456789abcdef0x");
91 static const TCHAR digits_u
[] = _T("0123456789ABCDEF0X");
92 static const TCHAR _nan
[] = _T("#QNAN");
93 static const TCHAR _infinity
[] = _T("#INF");
94 const TCHAR
*digits
= digits_l
;
97 int num_digits
, val32
, base
= 10;
100 if (precision
< 0) precision
= 6;
101 else if (precision
> 512) precision
= 512;
103 fpval
= va_arg_ffp(*argptr
, flags
);
104 exponent
= get_exp(fpval
);
111 if (exponent
< -4 || exponent
>= precision
) goto case_e
;
118 fpval
/= pow(10., exponent
);
119 val32
= exponent
>= 0 ? exponent
: -exponent
;
121 // FIXME: handle length of exponent field:
122 // http://msdn.microsoft.com/de-de/library/0fatw238%28VS.80%29.aspx
126 *--(*string
) = digits
[val32
% 10];
130 /* Sign for the exponent */
131 *--(*string
) = exponent
> 0 ? _T('+') : _T('-');
133 /* Add 'e' or 'E' separator */
134 *--(*string
) = digits
[0xe];
147 /* CHECKME: Windows seems to handle a max of 17 digits(?) */
148 num_digits
= precision
<= 17 ? precision
: 17;
156 else if (flags
& FLAG_FORCE_SIGN
)
158 else if (flags
& FLAG_FORCE_SIGNSP
)
161 /* Handle special cases first */
164 (*string
) -= sizeof(_nan
) / sizeof(TCHAR
) - 1;
165 _tcscpy((*string
), _nan
);
168 else if (!_finite(fpval
))
170 (*string
) -= sizeof(_infinity
) / sizeof(TCHAR
) - 1;
171 _tcscpy((*string
), _infinity
);
176 val64
= (__int64
)fpval
;
178 fpval
*= pow(10., precision
);
182 *--(*string
) = digits
[(__int64
)fpval
% 10];
187 *--(*string
) = _T('.');
189 /* Gather digits in reverse order */
192 *--(*string
) = digits
[val64
% base
];
201 streamout_char(FILE *stream
, int chr
)
203 /* Flush the buffer if neccessary */
204 if (stream
->_cnt
< sizeof(TCHAR
))
206 return _flsbuf(chr
, stream
) != EOF
;
209 *(TCHAR
*)stream
->_ptr
= chr
;
210 stream
->_ptr
+= sizeof(TCHAR
);
211 stream
->_cnt
-= sizeof(TCHAR
);
218 streamout_astring(FILE *stream
, const char *string
, int count
)
227 if ((len
= mbtowc(&chr
, string
, MB_CUR_MAX
)) < 1) break;
232 if (streamout_char(stream
, chr
) == 0) return -1;
241 streamout_wstring(FILE *stream
, const wchar_t *string
, int count
)
249 char mbchar
[MB_CUR_MAX
], *ptr
= mbchar
;
252 mblen
= wctomb(mbchar
, *string
++);
253 if (mblen
<= 0) return written
;
255 while (chr
= *ptr
++, mblen
--)
260 if (streamout_char(stream
, chr
) == 0) return -1;
269 #define streamout_string streamout_wstring
271 #define streamout_string streamout_astring
277 streamout(FILE *stream
, const TCHAR
*format
, va_list argptr
)
279 static const TCHAR digits_l
[] = _T("0123456789abcdef0x");
280 static const TCHAR digits_u
[] = _T("0123456789ABCDEF0X");
281 static const char *_nullstring
= "(null)";
282 TCHAR buffer
[BUFFER_SIZE
+ 1];
285 const TCHAR
*digits
, *prefix
;
286 int base
, len
, prefixlen
, fieldwidth
, precision
, padding
;
287 int written
= 1, written_all
= 0;
291 buffer
[BUFFER_SIZE
] = '\0';
297 /* Check for end of format string */
298 if (chr
== _T('\0')) break;
300 /* Check for 'normal' character */
303 /* Write the character to the stream */
304 if ((written
= streamout_char(stream
, chr
)) == -1) return -1;
305 written_all
+= written
;
309 /* Check for escaped % character */
310 if (*format
== _T('%'))
312 /* Write % to the stream */
313 if ((written
= streamout_char(stream
, _T('%'))) == -1) return -1;
314 written_all
+= written
;
323 if (chr
== _T('-')) flags
|= FLAG_ALIGN_LEFT
;
324 else if (chr
== _T('+')) flags
|= FLAG_FORCE_SIGN
;
325 else if (chr
== _T(' ')) flags
|= FLAG_FORCE_SIGNSP
;
326 else if (chr
== _T('0')) flags
|= FLAG_PAD_ZERO
;
327 else if (chr
== _T('#')) flags
|= FLAG_SPECIAL
;
331 /* Handle field width modifier */
334 fieldwidth
= va_arg(argptr
, int);
337 flags
|= FLAG_ALIGN_LEFT
;
338 fieldwidth
= -fieldwidth
;
345 while (chr
>= _T('0') && chr
<= _T('9'))
347 fieldwidth
= fieldwidth
* 10 + (chr
- _T('0'));
352 /* Handle precision modifier */
359 precision
= va_arg(argptr
, int);
365 while (chr
>= _T('0') && chr
<= _T('9'))
367 precision
= precision
* 10 + (chr
- _T('0'));
374 /* Handle argument size prefix */
377 if (chr
== _T('h')) flags
|= FLAG_SHORT
;
378 else if (chr
== _T('w')) flags
|= FLAG_WIDECHAR
;
379 else if (chr
== _T('L')) flags
|= 0; // FIXME: long double
380 else if (chr
== _T('l'))
384 if (format
[0] == _T('l'))
391 else if (chr
== _T('I'))
393 if (format
[0] == _T('3') && format
[1] == _T('2'))
397 else if (format
[0] == _T('6') && format
[1] == _T('4'))
402 else if (format
[0] == _T('x') || format
[0] == _T('X') ||
403 format
[0] == _T('d') || format
[0] == _T('i') ||
404 format
[0] == _T('u') || format
[0] == _T('o'))
406 flags
|= FLAG_INTPTR
;
414 /* Handle the format specifier */
416 string
= &buffer
[BUFFER_SIZE
];
422 if (flags
& FLAG_INT64
)
423 *va_arg(argptr
, __int64
*) = written_all
;
424 else if (flags
& FLAG_SHORT
)
425 *va_arg(argptr
, short*) = written_all
;
427 *va_arg(argptr
, int*) = written_all
;
432 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
438 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
443 if (flags
& FLAG_WIDECHAR
)
445 ((wchar_t*)string
)[0] = va_arg(argptr
, int);
446 ((wchar_t*)string
)[1] = _T('\0');
450 ((char*)string
)[0] = va_arg(argptr
, int);
451 ((char*)string
)[1] = _T('\0');
456 nt_string
= va_arg(argptr
, void*);
457 if (nt_string
&& (string
= nt_string
->Buffer
))
459 len
= nt_string
->Length
;
460 if (flags
& FLAG_WIDECHAR
) len
/= sizeof(wchar_t);
467 string
= va_arg(argptr
, void*);
469 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
474 string
= va_arg(argptr
, void*);
476 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
482 string
= (TCHAR
*)_nullstring
;
483 flags
&= ~FLAG_WIDECHAR
;
486 if (flags
& FLAG_WIDECHAR
)
487 len
= wcslen((wchar_t*)string
);
489 len
= strlen((char*)string
);
490 if (precision
>= 0 && len
> precision
) len
= precision
;
501 flags
|= FLAG_WIDECHAR
;
503 flags
&= ~FLAG_WIDECHAR
;
505 /* Use external function, one for kernel one for user mode */
506 format_float(chr
, flags
, precision
, &string
, &prefix
, &argptr
);
507 len
= _tcslen(string
);
512 val64
= va_arg_f(argptr
, flags
);
519 else if (flags
& FLAG_FORCE_SIGN
)
521 else if (flags
& FLAG_FORCE_SIGNSP
)
528 if (flags
& FLAG_SPECIAL
) prefix
= _T("0");
532 val64
= (unsigned __int64
)va_arg_fu(argptr
, flags
);
536 precision
= 2 * sizeof(void*);
537 flags
&= ~FLAG_PAD_ZERO
;
538 flags
|= FLAG_INTPTR
;
546 val64
= (unsigned __int64
)va_arg_fu(argptr
, flags
);
548 if (flags
& FLAG_SPECIAL
)
550 prefix
= &digits
[16];
555 flags
|= FLAG_WIDECHAR
;
557 flags
&= ~FLAG_WIDECHAR
;
559 /* Gather digits in reverse order */
562 *--string
= digits
[val64
% base
];
568 len
= _tcslen(string
);
572 /* Treat anything else as a new character */
577 /* Calculate padding */
578 prefixlen
= prefix
? _tcslen(prefix
) : 0;
579 if (precision
< 0) precision
= 0;
580 padding
= fieldwidth
- len
- prefixlen
- precision
;
582 /* Optional left space padding */
583 if ((flags
& (FLAG_ALIGN_LEFT
| FLAG_PAD_ZERO
)) == 0)
585 while (padding
-- > 0)
587 if ((written
= streamout_char(stream
, _T(' '))) == -1) return -2;
588 written_all
+= written
;
592 /* Optional prefix */
595 written
= streamout_string(stream
, prefix
, prefixlen
);
596 if (written
== -1) return -3;
597 written_all
+= written
;
600 /* Optional left '0' padding */
601 if ((flags
& FLAG_ALIGN_LEFT
) == 0) precision
+= padding
;
602 while (precision
-- > 0)
604 if ((written
= streamout_char(stream
, _T('0'))) == -1) return -4;
605 written_all
+= written
;
608 /* Output the string */
609 if (flags
& FLAG_WIDECHAR
)
610 written
= streamout_wstring(stream
, (wchar_t*)string
, len
);
612 written
= streamout_astring(stream
, (char*)string
, len
);
613 if (written
== -1) return -5;
614 written_all
+= written
;
616 #if 0 && SUPPORT_FLOAT
617 /* Optional right '0' padding */
618 while (precision
-- > 0)
620 if ((written
= streamout_char(stream
, _T('0'))) == -1) return -6;
621 written_all
+= written
;
626 /* Optional right padding */
627 if (flags
& FLAG_ALIGN_LEFT
)
629 while (padding
-- > 0)
631 if ((written
= streamout_char(stream
, _T(' '))) == -1) return -7;
632 written_all
+= written
;
638 if (written
== -1) return -8;