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 * PROGRAMMERS: Timo Kreuzer
18 # define streamout wstreamout
19 # define format_float format_floatw
23 #define BUFFER_SIZE (32 + 17)
25 int mbtowc(wchar_t *wchar
, const char *mbchar
, size_t count
);
26 int wctomb(char *mbchar
, wchar_t wchar
);
28 typedef struct _STRING
30 unsigned short Length
;
31 unsigned short MaximumLength
;
37 /* Formatting flags */
38 FLAG_ALIGN_LEFT
= 0x01,
39 FLAG_FORCE_SIGN
= 0x02,
40 FLAG_FORCE_SIGNSP
= 0x04,
44 /* Data format flags */
47 FLAG_WIDECHAR
= FLAG_LONG
,
50 FLAG_INTPTR
= FLAG_INT64
,
54 FLAG_LONGDOUBLE
= 0x800,
57 #define va_arg_f(argptr, flags) \
58 (flags & FLAG_INT64) ? va_arg(argptr, __int64) : \
59 (flags & FLAG_SHORT) ? (short)va_arg(argptr, int) : \
62 #define va_arg_fu(argptr, flags) \
63 (flags & FLAG_INT64) ? va_arg(argptr, unsigned __int64) : \
64 (flags & FLAG_SHORT) ? (unsigned short)va_arg(argptr, int) : \
65 va_arg(argptr, unsigned int)
67 #define va_arg_ffp(argptr, flags) \
68 (flags & FLAG_LONGDOUBLE) ? va_arg(argptr, long double) : \
69 va_arg(argptr, double)
71 #define get_exp(f) (int)floor(f == 0 ? 0 : (f >= 0 ? log10(f) : log10(-f)))
72 #define round(x) floor((x) + 0.5)
74 #ifndef _USER32_WSPRINTF
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
;
95 int exponent
= 0, sign
;
96 long double fpval
, fpval2
;
97 int padding
= 0, num_digits
, val32
, base
= 10;
99 /* Normalize the precision */
100 if (precision
< 0) precision
= 6;
101 else if (precision
> 17)
103 padding
= precision
- 17;
107 /* Get the float value and calculate the exponent */
108 fpval
= va_arg_ffp(*argptr
, flags
);
109 exponent
= get_exp(fpval
);
110 sign
= fpval
< 0 ? -1 : 1;
117 if (precision
> 0) precision
--;
118 if (exponent
< -4 || exponent
>= precision
) goto case_e
;
120 /* Shift the decimal point and round */
121 fpval2
= round(sign
* fpval
* pow(10., precision
));
123 /* Skip trailing zeroes */
124 while (precision
&& (unsigned __int64
)fpval2
% 10 == 0)
135 /* Shift the decimal point and round */
136 fpval2
= round(sign
* fpval
* pow(10., precision
- exponent
));
138 /* Compensate for changed exponent through rounding */
139 if (fpval2
>= (unsigned __int64
)pow(10., precision
+ 1))
142 fpval2
= round(sign
* fpval
* pow(10., precision
- exponent
));
145 val32
= exponent
>= 0 ? exponent
: -exponent
;
147 // FIXME: handle length of exponent field:
148 // http://msdn.microsoft.com/de-de/library/0fatw238%28VS.80%29.aspx
152 *--(*string
) = digits
[val32
% 10];
156 /* Sign for the exponent */
157 *--(*string
) = exponent
>= 0 ? _T('+') : _T('-');
159 /* Add 'e' or 'E' separator */
160 *--(*string
) = digits
[0xe];
171 /* Shift the decimal point and round */
172 fpval2
= round(sign
* fpval
* pow(10., precision
));
181 else if (flags
& FLAG_FORCE_SIGN
)
183 else if (flags
& FLAG_FORCE_SIGNSP
)
186 /* Handle special cases first */
189 (*string
) -= sizeof(_nan
) / sizeof(TCHAR
) - 1;
190 _tcscpy((*string
), _nan
);
193 else if (!_finite(fpval
))
195 (*string
) -= sizeof(_infinity
) / sizeof(TCHAR
) - 1;
196 _tcscpy((*string
), _infinity
);
202 while (padding
-- > 0) *--(*string
) = _T('0');
204 /* Digits after the decimal point */
205 num_digits
= precision
;
206 while (num_digits
-- > 0)
208 *--(*string
) = digits
[(unsigned __int64
)fpval2
% 10];
213 if (precision
> 0 || flags
& FLAG_SPECIAL
)
214 *--(*string
) = _T('.');
216 /* Digits before the decimal point */
219 *--(*string
) = digits
[(unsigned __int64
)fpval2
% base
];
222 while ((unsigned __int64
)fpval2
);
229 streamout_char(FILE *stream
, int chr
)
231 #if !defined(_USER32_WSPRINTF)
232 if ((stream
->_flag
& _IOSTRG
) && (stream
->_base
== NULL
))
235 #if defined(_USER32_WSPRINTF) || defined(_LIBCNT_)
236 /* Check if the buffer is full */
237 if (stream
->_cnt
< sizeof(TCHAR
))
240 *(TCHAR
*)stream
->_ptr
= chr
;
241 stream
->_ptr
+= sizeof(TCHAR
);
242 stream
->_cnt
-= sizeof(TCHAR
);
246 return _fputtc((TCHAR
)chr
, stream
) != _TEOF
;
252 streamout_astring(FILE *stream
, const char *string
, size_t count
)
257 #if !defined(_USER32_WSPRINTF)
258 if ((stream
->_flag
& _IOSTRG
) && (stream
->_base
== NULL
))
266 if ((len
= mbtowc(&chr
, string
, MB_CUR_MAX
)) < 1) break;
271 if (streamout_char(stream
, chr
) == 0) return -1;
280 streamout_wstring(FILE *stream
, const wchar_t *string
, size_t count
)
285 #if defined(_UNICODE) && !defined(_USER32_WSPRINTF)
286 if ((stream
->_flag
& _IOSTRG
) && (stream
->_base
== NULL
))
293 char mbchar
[MB_CUR_MAX
], *ptr
= mbchar
;
296 mblen
= wctomb(mbchar
, *string
++);
297 if (mblen
<= 0) return written
;
299 while (chr
= *ptr
++, mblen
--)
304 if (streamout_char(stream
, chr
) == 0) return -1;
313 #define streamout_string streamout_wstring
315 #define streamout_string streamout_astring
318 #ifdef _USER32_WSPRINTF
319 # define USE_MULTISIZE 0
321 # define USE_MULTISIZE 1
326 streamout(FILE *stream
, const TCHAR
*format
, va_list argptr
)
328 static const TCHAR digits_l
[] = _T("0123456789abcdef0x");
329 static const TCHAR digits_u
[] = _T("0123456789ABCDEF0X");
330 static const char *_nullstring
= "(null)";
331 TCHAR buffer
[BUFFER_SIZE
+ 1];
334 const TCHAR
*digits
, *prefix
;
335 int base
, fieldwidth
, precision
, padding
;
336 size_t prefixlen
, len
;
337 int written
= 1, written_all
= 0;
339 unsigned __int64 val64
;
341 buffer
[BUFFER_SIZE
] = '\0';
347 /* Check for end of format string */
348 if (chr
== _T('\0')) break;
350 /* Check for 'normal' character or double % */
351 if ((chr
!= _T('%')) ||
352 (chr
= *format
++) == _T('%'))
354 /* Write the character to the stream */
355 if ((written
= streamout_char(stream
, chr
)) == 0) return -1;
356 written_all
+= written
;
364 if (chr
== _T('-')) flags
|= FLAG_ALIGN_LEFT
;
365 else if (chr
== _T('+')) flags
|= FLAG_FORCE_SIGN
;
366 else if (chr
== _T(' ')) flags
|= FLAG_FORCE_SIGNSP
;
367 else if (chr
== _T('0')) flags
|= FLAG_PAD_ZERO
;
368 else if (chr
== _T('#')) flags
|= FLAG_SPECIAL
;
373 /* Handle field width modifier */
376 #ifdef _USER32_WSPRINTF
377 if ((written
= streamout_char(stream
, chr
)) == 0) return -1;
378 written_all
+= written
;
381 fieldwidth
= va_arg(argptr
, int);
384 flags
|= FLAG_ALIGN_LEFT
;
385 fieldwidth
= -fieldwidth
;
393 while (chr
>= _T('0') && chr
<= _T('9'))
395 fieldwidth
= fieldwidth
* 10 + (chr
- _T('0'));
400 /* Handle precision modifier */
407 #ifdef _USER32_WSPRINTF
408 if ((written
= streamout_char(stream
, chr
)) == 0) return -1;
409 written_all
+= written
;
412 precision
= va_arg(argptr
, int);
419 while (chr
>= _T('0') && chr
<= _T('9'))
421 precision
= precision
* 10 + (chr
- _T('0'));
428 /* Handle argument size prefix */
431 if (chr
== _T('h')) flags
|= FLAG_SHORT
;
432 else if (chr
== _T('w')) flags
|= FLAG_WIDECHAR
;
433 else if (chr
== _T('L')) flags
|= 0; // FIXME: long double
434 else if (chr
== _T('F')) flags
|= 0; // FIXME: what is that?
435 else if (chr
== _T('l'))
437 /* Check if this is the 2nd 'l' in a row */
438 if (format
[-2] == 'l') flags
|= FLAG_INT64
;
439 else flags
|= FLAG_LONG
;
441 else if (chr
== _T('I'))
443 if (format
[0] == _T('3') && format
[1] == _T('2'))
447 else if (format
[0] == _T('6') && format
[1] == _T('4'))
452 else if (format
[0] == _T('x') || format
[0] == _T('X') ||
453 format
[0] == _T('d') || format
[0] == _T('i') ||
454 format
[0] == _T('u') || format
[0] == _T('o'))
456 flags
|= FLAG_INTPTR
;
463 while (USE_MULTISIZE
);
465 /* Handle the format specifier */
467 string
= &buffer
[BUFFER_SIZE
];
473 if (flags
& FLAG_INT64
)
474 *va_arg(argptr
, __int64
*) = written_all
;
475 else if (flags
& FLAG_SHORT
)
476 *va_arg(argptr
, short*) = written_all
;
478 *va_arg(argptr
, int*) = written_all
;
483 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
489 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
494 if (flags
& FLAG_WIDECHAR
)
496 ((wchar_t*)string
)[0] = va_arg(argptr
, int);
497 ((wchar_t*)string
)[1] = _T('\0');
501 ((char*)string
)[0] = va_arg(argptr
, int);
502 ((char*)string
)[1] = _T('\0');
507 nt_string
= va_arg(argptr
, void*);
508 if (nt_string
&& (string
= nt_string
->Buffer
))
510 len
= nt_string
->Length
;
511 if (flags
& FLAG_WIDECHAR
) len
/= sizeof(wchar_t);
518 string
= va_arg(argptr
, void*);
520 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
525 string
= va_arg(argptr
, void*);
527 if (!(flags
& FLAG_SHORT
)) flags
|= FLAG_WIDECHAR
;
533 string
= (TCHAR
*)_nullstring
;
534 flags
&= ~FLAG_WIDECHAR
;
537 if (flags
& FLAG_WIDECHAR
)
538 len
= wcsnlen((wchar_t*)string
, (unsigned)precision
);
540 len
= strnlen((char*)string
, (unsigned)precision
);
544 #ifndef _USER32_WSPRINTF
553 flags
|= FLAG_WIDECHAR
;
555 flags
&= ~FLAG_WIDECHAR
;
557 /* Use external function, one for kernel one for user mode */
558 format_float(chr
, flags
, precision
, &string
, &prefix
, &argptr
);
559 len
= _tcslen(string
);
566 val64
= (__int64
)va_arg_f(argptr
, flags
);
568 if ((__int64
)val64
< 0)
570 val64
= -(__int64
)val64
;
573 else if (flags
& FLAG_FORCE_SIGN
)
575 else if (flags
& FLAG_FORCE_SIGNSP
)
582 if (flags
& FLAG_SPECIAL
)
585 if (precision
> 0) precision
--;
590 precision
= 2 * sizeof(void*);
591 flags
&= ~FLAG_PAD_ZERO
;
592 flags
|= FLAG_INTPTR
;
601 if (flags
& FLAG_SPECIAL
)
603 prefix
= &digits
[16];
604 #ifdef _USER32_WSPRINTF
611 val64
= va_arg_fu(argptr
, flags
);
615 flags
|= FLAG_WIDECHAR
;
617 flags
&= ~FLAG_WIDECHAR
;
619 if (precision
< 0) precision
= 1;
621 /* Gather digits in reverse order */
624 *--string
= digits
[val64
% base
];
629 len
= _tcslen(string
);
633 /* Treat anything else as a new character */
638 /* Calculate padding */
639 prefixlen
= prefix
? _tcslen(prefix
) : 0;
640 if (precision
< 0) precision
= 0;
641 padding
= (int)(fieldwidth
- len
- prefixlen
- precision
);
642 if (padding
< 0) padding
= 0;
644 /* Optional left space padding */
645 if ((flags
& (FLAG_ALIGN_LEFT
| FLAG_PAD_ZERO
)) == 0)
647 for (; padding
> 0; padding
--)
649 if ((written
= streamout_char(stream
, _T(' '))) == 0) return -1;
650 written_all
+= written
;
654 /* Optional prefix */
657 written
= streamout_string(stream
, prefix
, prefixlen
);
658 if (written
== -1) return -1;
659 written_all
+= written
;
662 /* Optional left '0' padding */
663 if ((flags
& FLAG_ALIGN_LEFT
) == 0) precision
+= padding
;
664 while (precision
-- > 0)
666 if ((written
= streamout_char(stream
, _T('0'))) == 0) return -1;
667 written_all
+= written
;
670 /* Output the string */
671 if (flags
& FLAG_WIDECHAR
)
672 written
= streamout_wstring(stream
, (wchar_t*)string
, len
);
674 written
= streamout_astring(stream
, (char*)string
, len
);
675 if (written
== -1) return -1;
676 written_all
+= written
;
678 #if 0 && SUPPORT_FLOAT
679 /* Optional right '0' padding */
680 while (precision
-- > 0)
682 if ((written
= streamout_char(stream
, _T('0'))) == 0) return -1;
683 written_all
+= written
;
688 /* Optional right padding */
689 if (flags
& FLAG_ALIGN_LEFT
)
691 while (padding
-- > 0)
693 if ((written
= streamout_char(stream
, _T(' '))) == 0) return -1;
694 written_all
+= written
;
700 if (written
== -1) return -1;