2 * Copyright 2011 Piotr Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define APICHAR MSVCRT_wchar_t
24 #define FUNC_NAME(func) func ## _w
27 #define CONVCHAR MSVCRT_wchar_t
28 #define FUNC_NAME(func) func ## _a
31 struct FUNC_NAME(_str_ctx
) {
36 static int FUNC_NAME(puts_clbk_str
)(void *ctx
, int len
, const APICHAR
*str
)
38 struct FUNC_NAME(_str_ctx
) *out
= ctx
;
44 memmove(out
->buf
, str
, out
->len
*sizeof(APICHAR
));
50 memmove(out
->buf
, str
, len
*sizeof(APICHAR
));
56 static inline const APICHAR
* FUNC_NAME(pf_parse_int
)(const APICHAR
*fmt
, int *val
)
60 while (*fmt
>= '0' && *fmt
<= '9') {
68 /* pf_fill: takes care of signs, alignment, zero and field padding */
69 static inline int FUNC_NAME(pf_fill
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
70 int len
, pf_flags
*flags
, BOOL left
)
72 int i
, r
= 0, written
;
74 if(flags
->Sign
&& !strchr("diaAeEfFgG", flags
->Format
))
77 if(left
&& flags
->Sign
) {
78 APICHAR ch
= flags
->Sign
;
81 r
= pf_puts(puts_ctx
, 1, &ch
);
85 if((!left
&& flags
->LeftAlign
) || (left
&& !flags
->LeftAlign
)) {
88 if(left
&& flags
->PadZero
)
93 for(i
=0; i
<flags
->FieldLength
-len
&& r
>=0; i
++) {
94 r
= pf_puts(puts_ctx
, 1, &ch
);
100 if(r
>=0 && left
&& flags
->Sign
&& !flags
->PadZero
) {
101 APICHAR ch
= flags
->Sign
;
102 r
= pf_puts(puts_ctx
, 1, &ch
);
106 return r
>=0 ? written
: r
;
109 #ifndef PRINTF_HELPERS
110 #define PRINTF_HELPERS
111 static inline int wcstombs_len(char *mbstr
, const MSVCRT_wchar_t
*wcstr
,
112 int len
, MSVCRT__locale_t locale
)
114 char buf
[MSVCRT_MB_LEN_MAX
];
117 for(i
=0; i
<len
; i
++) {
118 r
= MSVCRT__wctomb_l(mbstr
? mbstr
+mblen
: buf
, wcstr
[i
], locale
);
125 static inline int mbstowcs_len(MSVCRT_wchar_t
*wcstr
, const char *mbstr
,
126 int len
, MSVCRT__locale_t locale
)
131 for(i
=0; i
<len
; wlen
++) {
132 r
= MSVCRT_mbtowc_l(wcstr
? wcstr
+wlen
: &buf
, mbstr
+i
, len
-i
, locale
);
140 static inline int FUNC_NAME(pf_output_wstr
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
141 const MSVCRT_wchar_t
*str
, int len
, MSVCRT__locale_t locale
)
144 return pf_puts(puts_ctx
, len
, str
);
147 int len_a
= wcstombs_len(NULL
, str
, len
, locale
);
151 out
= HeapAlloc(GetProcessHeap(), 0, len_a
);
155 wcstombs_len(out
, str
, len
, locale
);
156 len
= pf_puts(puts_ctx
, len_a
, out
);
157 HeapFree(GetProcessHeap(), 0, out
);
162 static inline int FUNC_NAME(pf_output_str
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
163 const char *str
, int len
, MSVCRT__locale_t locale
)
167 int len_w
= mbstowcs_len(NULL
, str
, len
, locale
);
171 out
= HeapAlloc(GetProcessHeap(), 0, len_w
*sizeof(WCHAR
));
175 mbstowcs_len(out
, str
, len
, locale
);
176 len
= pf_puts(puts_ctx
, len_w
, out
);
177 HeapFree(GetProcessHeap(), 0, out
);
180 return pf_puts(puts_ctx
, len
, str
);
184 static inline int FUNC_NAME(pf_output_format_wstr
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
185 const MSVCRT_wchar_t
*str
, int len
, pf_flags
*flags
, MSVCRT__locale_t locale
)
190 /* Do not search past the length specified by the precision. */
191 if(flags
->Precision
>=0)
192 len
= MSVCRT_wcsnlen(str
, flags
->Precision
);
194 len
= MSVCRT_wcslen(str
);
197 if(flags
->Precision
>=0 && flags
->Precision
<len
)
198 len
= flags
->Precision
;
200 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, TRUE
);
203 r
= FUNC_NAME(pf_output_wstr
)(pf_puts
, puts_ctx
, str
, len
, locale
);
207 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, FALSE
);
211 return r
>=0 ? ret
: r
;
214 static inline int FUNC_NAME(pf_output_format_str
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
215 const char *str
, int len
, pf_flags
*flags
, MSVCRT__locale_t locale
)
220 /* Do not search past the length specified by the precision. */
221 if(flags
->Precision
>=0)
222 len
= MSVCRT_strnlen(str
, flags
->Precision
);
227 if(flags
->Precision
>=0 && flags
->Precision
<len
)
228 len
= flags
->Precision
;
230 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, TRUE
);
233 r
= FUNC_NAME(pf_output_str
)(pf_puts
, puts_ctx
, str
, len
, locale
);
237 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, FALSE
);
241 return r
>=0 ? ret
: r
;
244 static inline int FUNC_NAME(pf_handle_string
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
245 const void *str
, int len
, pf_flags
*flags
, MSVCRT__locale_t locale
, BOOL legacy_wide
)
247 BOOL api_is_wide
= sizeof(APICHAR
) == sizeof(MSVCRT_wchar_t
);
248 BOOL complement_is_narrow
= legacy_wide
? api_is_wide
: FALSE
;
250 static const MSVCRT_wchar_t nullW
[] = {'(','n','u','l','l',')',0};
253 return FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, nullW
, 6, flags
, locale
);
256 return FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, "(null)", 6, flags
, locale
);
259 if((flags
->NaturalString
&& api_is_wide
) || flags
->WideString
|| flags
->IntegerLength
== LEN_LONG
)
260 return FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, str
, len
, flags
, locale
);
261 if((flags
->NaturalString
&& !api_is_wide
) || flags
->IntegerLength
== LEN_SHORT
)
262 return FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, str
, len
, flags
, locale
);
264 if((flags
->Format
=='S' || flags
->Format
=='C') == complement_is_narrow
)
265 return FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, str
, len
, flags
, locale
);
267 return FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, str
, len
, flags
, locale
);
270 static inline int FUNC_NAME(pf_output_special_fp
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
271 double v
, pf_flags
*flags
, MSVCRT__locale_t locale
,
272 BOOL legacy_msvcrt_compat
, BOOL three_digit_exp
)
274 APICHAR pfx
[16], sfx
[8], *p
;
275 int len
= 0, r
, frac_len
, pfx_len
, sfx_len
;
277 if(!legacy_msvcrt_compat
) {
281 if(strchr("AEFG", flags
->Format
)) str
= "INF";
284 if(strchr("AEFG", flags
->Format
)) str
= (flags
->Sign
== '-' ? "NAN(IND)" : "NAN");
285 else str
= (flags
->Sign
== '-' ? "nan(ind)" : "nan");
288 flags
->Precision
= -1;
289 flags
->PadZero
= FALSE
;
290 return FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, str
, -1, flags
, locale
);
293 /* workaround a bug in native implementation */
294 if(flags
->Format
=='g' || flags
->Format
=='G')
298 if(flags
->PadZero
&& (flags
->Format
=='a' || flags
->Format
=='A')) {
299 if (flags
->Sign
) *p
++ = flags
->Sign
;
301 *p
++ = (flags
->Format
=='a' ? 'x' : 'X');
302 r
= pf_puts(puts_ctx
, p
-pfx
, pfx
);
306 flags
->FieldLength
-= p
-pfx
;
310 if(!flags
->PadZero
&& (flags
->Format
=='a' || flags
->Format
=='A')) {
312 *p
++ = (flags
->Format
=='a' ? 'x' : 'X');
316 *p
++ = *(locale
? locale
->locinfo
: get_locinfo())->lconv
->decimal_point
;
325 }else if(flags
->Sign
== '-') {
340 if(len
) flags
->Sign
= 0;
342 if(flags
->Precision
>=0 && flags
->Precision
<frac_len
)
343 p
[flags
->Precision
- frac_len
- 1]++;
346 if(strchr("aAeE", flags
->Format
)) {
347 if(flags
->Format
== 'a') *p
++ = 'p';
348 else if(flags
->Format
== 'A') *p
++ = 'P';
349 else if(flags
->Format
== 'e') *p
++ = 'e';
355 if(flags
->Format
== 'e' || flags
->Format
== 'E') {
357 if(three_digit_exp
) *p
++ = '0';
362 if(!flags
->Alternate
&& (flags
->Format
== 'g' || flags
->Format
== 'G')) sfx_len
= frac_len
;
363 else sfx_len
= flags
->Precision
;
366 if(strchr("fFeE", flags
->Format
)) sfx_len
= 6;
367 else if(flags
->Format
== 'a' || flags
->Format
== 'A') sfx_len
= 13;
369 sfx_len
+= p
- sfx
- frac_len
;
371 if(sfx_len
> 0) flags
->FieldLength
-= sfx_len
;
372 if(flags
->Precision
>= 0) {
373 if(!flags
->Precision
) flags
->Precision
--;
374 flags
->Precision
+= pfx_len
- frac_len
;
377 r
= FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, pfx
, -1, flags
, locale
);
379 r
= FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, pfx
, -1, flags
, locale
);
384 flags
->FieldLength
= sfx_len
;
385 flags
->PadZero
= TRUE
;
386 flags
->Precision
= -1;
389 r
= FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, sfx
, -1, flags
, locale
);
391 r
= FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, sfx
, -1, flags
, locale
);
399 static inline int FUNC_NAME(pf_output_hex_fp
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
400 double v
, pf_flags
*flags
, MSVCRT__locale_t locale
, BOOL standard_rounding
)
402 const APICHAR digits
[2][16] = {
403 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' },
404 { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }
407 APICHAR pfx
[4+MANT_BITS
/4+1], sfx
[8], *p
;
409 int len
= 0, sfx_len
= 0, r
, exp
;
411 mant
= (*(ULONGLONG
*)&v
) << 1;
412 exp
= (mant
>> MANT_BITS
);
413 exp
-= (1 << (EXP_BITS
- 1)) - 1;
414 mant
= (mant
<< EXP_BITS
) >> (EXP_BITS
+1);
418 if(flags
->Sign
) *p
++ = flags
->Sign
;
420 *p
++ = (flags
->Format
=='a' ? 'x' : 'X');
421 r
= pf_puts(puts_ctx
, p
-pfx
, pfx
);
425 flags
->FieldLength
-= p
-pfx
;
430 *p
++ = (flags
->Format
=='a' ? 'x' : 'X');
432 if(exp
== -(1 << (EXP_BITS
-1))+1) {
439 *p
++ = *(locale
? locale
->locinfo
: get_locinfo())->lconv
->decimal_point
;
440 for(r
=MANT_BITS
/4-1; r
>=0; r
--) {
441 p
[r
] = digits
[flags
->Format
== 'A'][mant
& 15];
444 if(!flags
->Precision
) {
445 if(p
[0] >= '8') p
[-2]++;
446 if(!flags
->Alternate
) p
--;
447 }else if(flags
->Precision
>0 && flags
->Precision
<MANT_BITS
/4) {
448 BOOL round_up
= FALSE
;
450 if(!standard_rounding
) round_up
= (p
[flags
->Precision
] >= '8');
451 else if(p
[flags
->Precision
] > '8') round_up
= TRUE
;
452 else if(p
[flags
->Precision
] == '8') {
453 for(r
= flags
->Precision
+1; r
<MANT_BITS
/4; r
++) {
461 if(p
[flags
->Precision
-1] <= '9') round_up
= (p
[flags
->Precision
-1] - '0') & 1;
462 else if(p
[flags
->Precision
-1] <= 'F') round_up
= (p
[flags
->Precision
-1] - 'A') & 1;
463 else round_up
= (p
[flags
->Precision
-1] - 'a') & 1;
467 for(r
=flags
->Precision
-1; r
>=0 && round_up
; r
--) {
469 if(p
[r
]=='f' || p
[r
]=='F') {
472 }else if(p
[r
] == '9') {
473 p
[r
] = (flags
->Format
== 'a' ? 'a' : 'A');
478 if(round_up
) p
[-2]++;
479 p
+= flags
->Precision
;
482 if(flags
->Precision
> MANT_BITS
/4) sfx_len
+= flags
->Precision
- MANT_BITS
/4;
487 *p
++ = (flags
->Format
== 'a' ? 'p' : 'P');
494 for(r
=3; r
>=0; r
--) {
499 for(exp
=0; exp
<4-r
; exp
++)
505 flags
->FieldLength
-= sfx_len
;
506 flags
->Precision
= -1;
508 r
= FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, pfx
, -1, flags
, locale
);
510 r
= FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, pfx
, -1, flags
, locale
);
515 flags
->FieldLength
= sfx_len
;
516 flags
->PadZero
= TRUE
;
519 r
= FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, sfx
, -1, flags
, locale
);
521 r
= FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, sfx
, -1, flags
, locale
);
529 /* pf_integer_conv: prints x to buf, including alternate formats and
530 additional precision digits, but not field characters or the sign */
531 static inline void FUNC_NAME(pf_integer_conv
)(APICHAR
*buf
, pf_flags
*flags
, LONGLONG x
)
537 if(flags
->Format
== 'o')
539 else if(flags
->Format
=='x' || flags
->Format
=='X')
544 if(flags
->Format
== 'X')
545 digits
= "0123456789ABCDEFX";
547 digits
= "0123456789abcdefx";
549 if(x
<0 && (flags
->Format
=='d' || flags
->Format
=='i')) {
556 flags
->Alternate
= FALSE
;
561 j
= (ULONGLONG
)x
%base
;
562 x
= (ULONGLONG
)x
/base
;
563 buf
[i
++] = digits
[j
];
566 k
= flags
->Precision
-i
;
569 if(flags
->Alternate
) {
571 buf
[i
++] = digits
[16];
573 } else if(base
==8 && buf
[i
-1]!='0')
577 /* Adjust precision so pf_fill won't truncate the number later */
578 flags
->Precision
= i
;
583 APICHAR tmp
= buf
[j
];
590 static inline int FUNC_NAME(pf_output_fp
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
591 double v
, pf_flags
*flags
, MSVCRT__locale_t locale
, BOOL three_digit_exp
,
592 BOOL standard_rounding
)
594 int e2
, e10
= 0, round_pos
, round_limb
, radix_pos
, first_limb_len
, i
, len
, r
, ret
;
595 BYTE bnum_data
[FIELD_OFFSET(struct bnum
, data
[BNUM_PREC64
])];
596 struct bnum
*b
= (struct bnum
*)bnum_data
;
597 APICHAR buf
[LIMB_DIGITS
+ 1];
598 BOOL trim_tail
= FALSE
, round_up
= FALSE
;
604 TRACE("floating point argument: %.16le\n", v
);
606 if(flags
->Precision
== -1)
607 flags
->Precision
= 6;
609 v
= MSVCRT_frexp(v
, &e2
);
611 m
= (ULONGLONG
)1 << (MANT_BITS
- 1);
612 m
|= (*(ULONGLONG
*)&v
& (((ULONGLONG
)1 << (MANT_BITS
- 1)) - 1));
615 b
->size
= BNUM_PREC64
;
616 b
->data
[0] = m
% LIMB_MAX
;
617 b
->data
[1] = m
/ LIMB_MAX
;
621 int shift
= e2
> 29 ? 29 : e2
;
622 if(bnum_lshift(b
, shift
)) e10
+= LIMB_DIGITS
;
626 int shift
= -e2
> 9 ? 9 : -e2
;
627 if(bnum_rshift(b
, shift
)) e10
-= LIMB_DIGITS
;
633 b
->size
= BNUM_PREC64
;
638 if(!b
->data
[bnum_idx(b
, b
->e
-1)])
641 first_limb_len
= MSVCRT_floor(MSVCRT_log10(b
->data
[bnum_idx(b
, b
->e
- 1)])) + 1;
642 radix_pos
= first_limb_len
+ LIMB_DIGITS
+ e10
;
644 round_pos
= flags
->Precision
;
645 if(flags
->Format
=='f' || flags
->Format
=='F')
646 round_pos
+= radix_pos
;
647 else if(!flags
->Precision
|| flags
->Format
=='e' || flags
->Format
=='E')
649 if (round_pos
<= first_limb_len
)
650 round_limb
= b
->e
+ (first_limb_len
- round_pos
) / LIMB_DIGITS
- 1;
652 round_limb
= b
->e
- (round_pos
- first_limb_len
- 1) / LIMB_DIGITS
- 2;
654 if (b
->b
<=round_limb
&& round_limb
<b
->e
) {
655 if (round_pos
<= first_limb_len
) {
656 round_pos
= first_limb_len
- round_pos
;
658 round_pos
= LIMB_DIGITS
- (round_pos
- first_limb_len
) % LIMB_DIGITS
;
659 if (round_pos
== LIMB_DIGITS
) round_pos
= 0;
663 l
= b
->data
[bnum_idx(b
, round_limb
)] % p10s
[round_pos
];
664 b
->data
[bnum_idx(b
, round_limb
)] -= l
;
665 if(!standard_rounding
) round_up
= (2*l
>= p10s
[round_pos
]);
666 else if(2*l
> p10s
[round_pos
]) round_up
= TRUE
;
667 else if(2*l
== p10s
[round_pos
]) {
668 for(r
= round_limb
-1; r
>= b
->b
; r
--) {
669 if(b
->data
[bnum_idx(b
, r
)]) {
675 if(!round_up
) round_up
= b
->data
[bnum_idx(b
, round_limb
)] / p10s
[round_pos
] & 1;
677 } else if(round_limb
- 1 >= b
->b
) {
678 if(!standard_rounding
) round_up
= (2*b
->data
[bnum_idx(b
, round_limb
-1)] >= LIMB_MAX
);
679 else if(2*b
->data
[bnum_idx(b
, round_limb
-1)] > LIMB_MAX
) round_up
= TRUE
;
680 else if(2*b
->data
[bnum_idx(b
, round_limb
-1)] == LIMB_MAX
) {
681 for(r
= round_limb
-2; r
>= b
->b
; r
--) {
682 if(b
->data
[bnum_idx(b
, r
)]) {
688 if(!round_up
) round_up
= b
->data
[bnum_idx(b
, round_limb
)] & 1;
694 b
->data
[bnum_idx(b
, b
->b
)] += p10s
[round_pos
];
695 for(i
= b
->b
; i
< b
->e
; i
++) {
696 if(b
->data
[bnum_idx(b
, i
)] < LIMB_MAX
) break;
698 b
->data
[bnum_idx(b
, i
)] -= LIMB_MAX
;
699 if(i
+1 < b
->e
) b
->data
[bnum_idx(b
, i
+1)]++;
700 else b
->data
[bnum_idx(b
, i
+1)] = 1;
703 if(!b
->data
[bnum_idx(b
, b
->e
-1)])
706 i
= MSVCRT_floor(MSVCRT_log10(b
->data
[bnum_idx(b
, b
->e
-1)])) + 1;
707 if(i
!= first_limb_len
) {
711 } else if(i
== b
->e
) {
718 else if(b
->e
<= round_limb
) { /* got 0 or 1 after rounding */
719 if(b
->e
== round_limb
) {
720 if(!standard_rounding
) round_up
= b
->data
[bnum_idx(b
, b
->e
-1)] >= LIMB_MAX
/2;
721 else if(b
->data
[bnum_idx(b
, b
->e
-1)] > LIMB_MAX
/2) round_up
= TRUE
;
722 else if(b
->data
[bnum_idx(b
, b
->e
-1)] == LIMB_MAX
/2) {
723 for(r
= b
->e
-2; r
>= b
->b
; r
--) {
724 if(b
->data
[bnum_idx(b
, r
)]) {
732 b
->data
[bnum_idx(b
, round_limb
)] = round_up
;
739 if(flags
->Format
=='g' || flags
->Format
=='G') {
742 if(radix_pos
>=-3 && radix_pos
<=flags
->Precision
) {
744 if(!flags
->Precision
) flags
->Precision
++;
745 flags
->Precision
-= radix_pos
;
748 if(flags
->Precision
> 0) flags
->Precision
--;
752 if(trim_tail
&& !flags
->Alternate
) {
753 for(i
=round_limb
; flags
->Precision
>0 && i
<b
->e
; i
++) {
755 l
= b
->data
[bnum_idx(b
, i
)];
759 if(i
== round_limb
) {
760 if(flags
->Format
=='f' || flags
->Format
=='F')
761 r
= radix_pos
+ flags
->Precision
;
763 r
= flags
->Precision
+ 1;
764 r
= first_limb_len
+ LIMB_DIGITS
* (b
->e
-1 - b
->b
) - r
;
766 if(r
< 0) r
+= LIMB_DIGITS
;
768 limb_len
= LIMB_DIGITS
- r
;
770 limb_len
= LIMB_DIGITS
;
774 flags
->Precision
-= limb_len
;
782 if(flags
->Precision
<= 0) {
783 flags
->Precision
= 0;
791 len
= flags
->Precision
;
792 if(flags
->Precision
|| flags
->Alternate
) len
++;
793 if(flags
->Format
=='f' || flags
->Format
=='F') {
794 len
+= (radix_pos
> 0 ? radix_pos
: 1);
795 } else if(flags
->Format
=='e' || flags
->Format
=='E') {
797 if(!trim_tail
|| radix_pos
) {
798 len
+= 3; /* strlen("1e+") */
799 if(three_digit_exp
|| radix_pos
<-99 || radix_pos
>99) len
+= 3;
806 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, TRUE
);
812 if(flags
->Format
=='f' || flags
->Format
=='F') {
815 r
= pf_puts(puts_ctx
, 1, buf
);
820 limb_len
= LIMB_DIGITS
;
821 for(i
=b
->e
-1; radix_pos
>0 && i
>=b
->b
; i
--) {
822 limb_len
= (i
== b
->e
-1 ? first_limb_len
: LIMB_DIGITS
);
823 l
= b
->data
[bnum_idx(b
, i
)];
824 if(limb_len
> radix_pos
) {
825 f
.Precision
= radix_pos
;
826 l
/= p10s
[limb_len
- radix_pos
];
827 limb_len
= limb_len
- radix_pos
;
829 f
.Precision
= limb_len
;
830 limb_len
= LIMB_DIGITS
;
832 radix_pos
-= f
.Precision
;
833 FUNC_NAME(pf_integer_conv
)(buf
, &f
, l
);
835 r
= pf_puts(puts_ctx
, f
.Precision
, buf
);
841 for(; radix_pos
>0; radix_pos
--) {
842 r
= pf_puts(puts_ctx
, 1, buf
);
847 if(flags
->Precision
|| flags
->Alternate
) {
848 buf
[0] = *(locale
? locale
->locinfo
: get_locinfo())->lconv
->decimal_point
;
849 r
= pf_puts(puts_ctx
, 1, buf
);
854 prec
= flags
->Precision
;
856 for(; prec
>0 && radix_pos
+LIMB_DIGITS
-first_limb_len
<0; radix_pos
++, prec
--) {
857 r
= pf_puts(puts_ctx
, 1, buf
);
862 for(; prec
>0 && i
>=b
->b
; i
--) {
863 l
= b
->data
[bnum_idx(b
, i
)];
864 if(limb_len
!= LIMB_DIGITS
)
866 if(limb_len
> prec
) {
868 l
/= p10s
[limb_len
- prec
];
870 f
.Precision
= limb_len
;
871 limb_len
= LIMB_DIGITS
;
874 FUNC_NAME(pf_integer_conv
)(buf
, &f
, l
);
876 r
= pf_puts(puts_ctx
, f
.Precision
, buf
);
882 for(; prec
>0; prec
--) {
883 r
= pf_puts(puts_ctx
, 1, buf
);
888 l
= b
->data
[bnum_idx(b
, b
->e
- 1)];
889 l
/= p10s
[first_limb_len
- 1];
892 r
= pf_puts(puts_ctx
, 1, buf
);
896 if(flags
->Precision
|| flags
->Alternate
) {
897 buf
[0] = *(locale
? locale
->locinfo
: get_locinfo())->lconv
->decimal_point
;
898 r
= pf_puts(puts_ctx
, 1, buf
);
903 prec
= flags
->Precision
;
904 limb_len
= LIMB_DIGITS
;
905 for(i
=b
->e
-1; prec
>0 && i
>=b
->b
; i
--) {
906 l
= b
->data
[bnum_idx(b
, i
)];
908 limb_len
= first_limb_len
- 1;
912 if(limb_len
> prec
) {
914 l
/= p10s
[limb_len
- prec
];
916 f
.Precision
= limb_len
;
917 limb_len
= LIMB_DIGITS
;
920 FUNC_NAME(pf_integer_conv
)(buf
, &f
, l
);
922 r
= pf_puts(puts_ctx
, f
.Precision
, buf
);
928 for(; prec
>0; prec
--) {
929 r
= pf_puts(puts_ctx
, 1, buf
);
934 if(!trim_tail
|| radix_pos
) {
935 buf
[0] = flags
->Format
;
936 buf
[1] = radix_pos
< 0 ? '-' : '+';
937 r
= pf_puts(puts_ctx
, 2, buf
);
941 f
.Precision
= three_digit_exp
? 3 : 2;
942 FUNC_NAME(pf_integer_conv
)(buf
, &f
, radix_pos
);
943 r
= pf_puts(puts_ctx
, f
.Precision
, buf
);
949 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, FALSE
);
955 int FUNC_NAME(pf_printf
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
, const APICHAR
*fmt
,
956 MSVCRT__locale_t locale
, DWORD options
,
957 args_clbk pf_args
, void *args_ctx
, __ms_va_list
*valist
)
959 const APICHAR
*q
, *p
= fmt
;
961 int written
= 0, pos
, i
;
963 BOOL positional_params
= options
& MSVCRT_PRINTF_POSITIONAL_PARAMS
;
964 BOOL invoke_invalid_param_handler
= options
& MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER
;
965 #if _MSVCR_VER >= 140
966 BOOL legacy_wide
= options
& UCRTBASE_PRINTF_LEGACY_WIDE_SPECIFIERS
;
967 BOOL legacy_msvcrt_compat
= options
& UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY
;
968 BOOL three_digit_exp
= options
& UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS
;
969 BOOL standard_rounding
= options
& UCRTBASE_PRINTF_STANDARD_ROUNDING
;
971 BOOL legacy_wide
= TRUE
, legacy_msvcrt_compat
= TRUE
;
972 BOOL three_digit_exp
= MSVCRT__get_output_format() != MSVCRT__TWO_DIGIT_EXPONENT
;
973 BOOL standard_rounding
= FALSE
;
976 TRACE("Format is: %s\n", FUNC_NAME(debugstr
)(fmt
));
978 if (!MSVCRT_CHECK_PMT(fmt
!= NULL
))
982 /* output characters before '%' */
983 for(q
=p
; *q
&& *q
!='%'; q
++);
985 i
= pf_puts(puts_ctx
, q
-p
, p
);
997 /* output a single '%' character */
999 i
= pf_puts(puts_ctx
, 1, p
++);
1007 /* check parameter position */
1008 if(positional_params
&& (q
= FUNC_NAME(pf_parse_int
)(p
, &pos
)) && *q
=='$')
1013 /* parse the flags */
1014 memset(&flags
, 0, sizeof(flags
));
1016 if(*p
=='+' || *p
==' ') {
1017 if(flags
.Sign
!= '+')
1019 } else if(*p
== '-')
1020 flags
.LeftAlign
= TRUE
;
1022 flags
.PadZero
= TRUE
;
1024 flags
.Alternate
= TRUE
;
1031 /* parse the width */
1034 if(positional_params
&& (q
= FUNC_NAME(pf_parse_int
)(p
, &i
)) && *q
=='$')
1039 flags
.FieldLength
= pf_args(args_ctx
, i
, VT_INT
, valist
).get_int
;
1040 if(flags
.FieldLength
< 0) {
1041 flags
.LeftAlign
= TRUE
;
1042 flags
.FieldLength
= -flags
.FieldLength
;
1044 } else while (*p
>= '0' && *p
<= '9') {
1045 flags
.FieldLength
*= 10;
1046 flags
.FieldLength
+= *p
++ - '0';
1049 /* parse the precision */
1050 flags
.Precision
= -1;
1052 flags
.Precision
= 0;
1056 if(positional_params
&& (q
= FUNC_NAME(pf_parse_int
)(p
, &i
)) && *q
=='$')
1061 flags
.Precision
= pf_args(args_ctx
, i
, VT_INT
, valist
).get_int
;
1062 } else while (*p
>= '0' && *p
<= '9') {
1063 flags
.Precision
*= 10;
1064 flags
.Precision
+= *p
++ - '0';
1068 /* parse argument size modifier */
1070 if(*p
=='l' && *(p
+1)=='l') {
1071 flags
.IntegerDouble
= TRUE
;
1073 } else if(*p
=='l') {
1074 flags
.IntegerLength
= LEN_LONG
;
1075 } else if(*p
== 'h') {
1076 flags
.IntegerLength
= LEN_SHORT
;
1077 } else if(*p
== 'I') {
1078 if(*(p
+1)=='6' && *(p
+2)=='4') {
1079 flags
.IntegerDouble
= TRUE
;
1081 } else if(*(p
+1)=='3' && *(p
+2)=='2')
1083 else if(p
[1] && strchr("diouxX", p
[1]))
1084 flags
.IntegerNative
= TRUE
;
1087 } else if(*p
== 'w')
1088 flags
.WideString
= TRUE
;
1089 #if _MSVCR_VER == 0 || _MSVCR_VER >= 140
1090 else if((*p
== 'z' || *p
== 't') && p
[1] && strchr("diouxX", p
[1]))
1091 flags
.IntegerNative
= TRUE
;
1093 flags
.IntegerDouble
= TRUE
;
1095 #if _MSVCR_VER >= 140
1097 flags
.NaturalString
= TRUE
;
1099 else if(*p
!= 'L' && ((*p
!= 'F' && *p
!= 'N') || !legacy_msvcrt_compat
))
1106 if(flags
.Format
== 's' || flags
.Format
== 'S') {
1107 i
= FUNC_NAME(pf_handle_string
)(pf_puts
, puts_ctx
,
1108 pf_args(args_ctx
, pos
, VT_PTR
, valist
).get_ptr
,
1109 -1, &flags
, locale
, legacy_wide
);
1110 } else if(flags
.Format
== 'c' || flags
.Format
== 'C') {
1111 int ch
= pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
;
1113 i
= FUNC_NAME(pf_handle_string
)(pf_puts
, puts_ctx
, &ch
, 1, &flags
, locale
, legacy_wide
);
1114 if(i
< 0) i
= 0; /* ignore conversion error */
1115 } else if(flags
.Format
== 'p') {
1117 flags
.PadZero
= TRUE
;
1118 i
= flags
.Precision
;
1119 flags
.Precision
= 2*sizeof(void*);
1120 FUNC_NAME(pf_integer_conv
)(buf
, &flags
,
1121 (ULONG_PTR
)pf_args(args_ctx
, pos
, VT_PTR
, valist
).get_ptr
);
1122 flags
.PadZero
= FALSE
;
1123 flags
.Precision
= i
;
1126 i
= FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, buf
, -1, &flags
, locale
);
1128 i
= FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, buf
, -1, &flags
, locale
);
1130 } else if(flags
.Format
== 'n') {
1133 if(!n_format_enabled
) {
1134 MSVCRT_INVALID_PMT("\'n\' format specifier disabled", MSVCRT_EINVAL
);
1138 used
= pf_args(args_ctx
, pos
, VT_PTR
, valist
).get_ptr
;
1141 } else if(flags
.Format
&& strchr("diouxX", flags
.Format
)) {
1145 /* 0 padding is added after '0x' if Alternate flag is in use */
1146 if((flags
.Format
=='x' || flags
.Format
=='X') && flags
.PadZero
&& flags
.Alternate
1147 && !flags
.LeftAlign
&& flags
.Precision
<flags
.FieldLength
-2)
1148 flags
.Precision
= flags
.FieldLength
- 2;
1150 max_len
= (flags
.FieldLength
>flags
.Precision
? flags
.FieldLength
: flags
.Precision
) + 10;
1151 if(max_len
> ARRAY_SIZE(buf
))
1152 tmp
= HeapAlloc(GetProcessHeap(), 0, max_len
);
1156 if(flags
.IntegerDouble
|| (flags
.IntegerNative
&& sizeof(void*) == 8))
1157 FUNC_NAME(pf_integer_conv
)(tmp
, &flags
, pf_args(args_ctx
, pos
,
1158 VT_I8
, valist
).get_longlong
);
1159 else if(flags
.Format
=='d' || flags
.Format
=='i')
1160 FUNC_NAME(pf_integer_conv
)(tmp
, &flags
,
1161 flags
.IntegerLength
!= LEN_SHORT
?
1162 pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
:
1163 (short)pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
);
1165 FUNC_NAME(pf_integer_conv
)(tmp
, &flags
,
1166 flags
.IntegerLength
!= LEN_SHORT
?
1167 (unsigned)pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
:
1168 (unsigned short)pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
);
1171 i
= FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, tmp
, -1, &flags
, locale
);
1173 i
= FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, tmp
, -1, &flags
, locale
);
1176 HeapFree(GetProcessHeap(), 0, tmp
);
1177 } else if(flags
.Format
&& strchr("aAeEfFgG", flags
.Format
)) {
1178 double val
= pf_args(args_ctx
, pos
, VT_R8
, valist
).get_double
;
1185 if(isinf(val
) || isnan(val
))
1186 i
= FUNC_NAME(pf_output_special_fp
)(pf_puts
, puts_ctx
, val
, &flags
,
1187 locale
, legacy_msvcrt_compat
, three_digit_exp
);
1188 else if(flags
.Format
=='a' || flags
.Format
=='A')
1189 i
= FUNC_NAME(pf_output_hex_fp
)(pf_puts
, puts_ctx
, val
, &flags
,
1190 locale
, standard_rounding
);
1192 i
= FUNC_NAME(pf_output_fp
)(pf_puts
, puts_ctx
, val
, &flags
,
1193 locale
, three_digit_exp
, standard_rounding
);
1195 if(invoke_invalid_param_handler
) {
1196 MSVCRT__invalid_parameter(NULL
, NULL
, NULL
, 0, 0);
1197 *MSVCRT__errno() = MSVCRT_EINVAL
;
1214 enum types_clbk_flags
{
1215 TYPE_CLBK_VA_LIST
= 1,
1216 TYPE_CLBK_POSITIONAL
= 2,
1217 TYPE_CLBK_ERROR_POS
= 4,
1218 TYPE_CLBK_ERROR_TYPE
= 8
1221 /* This functions stores types of arguments. It uses args[0] internally */
1222 static printf_arg
arg_clbk_type(void *ctx
, int pos
, int type
, __ms_va_list
*valist
)
1224 static const printf_arg ret
;
1225 printf_arg
*args
= ctx
;
1228 args
[0].get_int
|= TYPE_CLBK_VA_LIST
;
1231 args
[0].get_int
|= TYPE_CLBK_POSITIONAL
;
1233 if(pos
<1 || pos
>MSVCRT__ARGMAX
)
1234 args
[0].get_int
|= TYPE_CLBK_ERROR_POS
;
1235 else if(args
[pos
].get_int
&& args
[pos
].get_int
!=type
)
1236 args
[0].get_int
|= TYPE_CLBK_ERROR_TYPE
;
1238 args
[pos
].get_int
= type
;
1244 int FUNC_NAME(create_positional_ctx
)(void *args_ctx
, const APICHAR
*format
, __ms_va_list valist
)
1246 struct FUNC_NAME(_str_ctx
) puts_ctx
= {INT_MAX
, NULL
};
1247 printf_arg
*args
= args_ctx
;
1250 i
= FUNC_NAME(pf_printf
)(FUNC_NAME(puts_clbk_str
), &puts_ctx
, format
, NULL
,
1251 MSVCRT_PRINTF_POSITIONAL_PARAMS
, arg_clbk_type
, args_ctx
, NULL
);
1255 if(args
[0].get_int
==0 || args
[0].get_int
==TYPE_CLBK_VA_LIST
)
1257 if(args
[0].get_int
!= TYPE_CLBK_POSITIONAL
)
1260 for(i
=MSVCRT__ARGMAX
; i
>0; i
--)
1264 for(j
=1; j
<=i
; j
++) {
1265 switch(args
[j
].get_int
) {
1267 args
[j
].get_longlong
= va_arg(valist
, LONGLONG
);
1270 args
[j
].get_int
= va_arg(valist
, int);
1273 args
[j
].get_double
= va_arg(valist
, double);
1276 args
[j
].get_ptr
= va_arg(valist
, void*);