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
20 #define APICHAR MSVCRT_wchar_t
22 #define FUNC_NAME(func) func ## _w
25 #define CONVCHAR MSVCRT_wchar_t
26 #define FUNC_NAME(func) func ## _a
30 #define signbit(x) ((x) < 0)
33 typedef struct FUNC_NAME(pf_flags_t
)
35 APICHAR Sign
, LeftAlign
, Alternate
, PadZero
;
36 int FieldLength
, Precision
;
37 APICHAR IntegerLength
, IntegerDouble
, IntegerNative
;
38 APICHAR WideString
, NaturalString
;
40 } FUNC_NAME(pf_flags
);
42 struct FUNC_NAME(_str_ctx
) {
47 static int FUNC_NAME(puts_clbk_str
)(void *ctx
, int len
, const APICHAR
*str
)
49 struct FUNC_NAME(_str_ctx
) *out
= ctx
;
55 memcpy(out
->buf
, str
, out
->len
*sizeof(APICHAR
));
61 memcpy(out
->buf
, str
, len
*sizeof(APICHAR
));
67 static inline const APICHAR
* FUNC_NAME(pf_parse_int
)(const APICHAR
*fmt
, int *val
)
71 while(isdigit(*fmt
)) {
79 /* pf_fill: takes care of signs, alignment, zero and field padding */
80 static inline int FUNC_NAME(pf_fill
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
81 int len
, FUNC_NAME(pf_flags
) *flags
, BOOL left
)
83 int i
, r
= 0, written
;
85 if(flags
->Sign
&& !strchr("diaeEfFgG", flags
->Format
))
88 if(left
&& flags
->Sign
) {
91 r
= pf_puts(puts_ctx
, 1, &flags
->Sign
);
95 if((!left
&& flags
->LeftAlign
) || (left
&& !flags
->LeftAlign
)) {
98 if(left
&& flags
->PadZero
)
103 for(i
=0; i
<flags
->FieldLength
-len
&& r
>=0; i
++) {
104 r
= pf_puts(puts_ctx
, 1, &ch
);
110 if(r
>=0 && left
&& flags
->Sign
&& !flags
->PadZero
) {
111 r
= pf_puts(puts_ctx
, 1, &flags
->Sign
);
115 return r
>=0 ? written
: r
;
118 static inline int FUNC_NAME(pf_output_wstr
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
119 const MSVCRT_wchar_t
*str
, int len
, MSVCRT_pthreadlocinfo locinfo
)
122 return pf_puts(puts_ctx
, len
, str
);
126 int len_a
= WideCharToMultiByte(locinfo
->lc_codepage
, WC_NO_BEST_FIT_CHARS
,
127 str
, len
, NULL
, 0, NULL
, &def_char
);
131 out
= HeapAlloc(GetProcessHeap(), 0, len_a
);
135 WideCharToMultiByte(locinfo
->lc_codepage
, WC_NO_BEST_FIT_CHARS
,
136 str
, len
, out
, len_a
, NULL
, NULL
);
137 len
= pf_puts(puts_ctx
, len_a
, out
);
138 HeapFree(GetProcessHeap(), 0, out
);
143 static inline int FUNC_NAME(pf_output_str
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
144 const char *str
, int len
, MSVCRT_pthreadlocinfo locinfo
)
148 int len_w
= MultiByteToWideChar(locinfo
->lc_codepage
, 0, str
, len
, NULL
, 0);
150 out
= HeapAlloc(GetProcessHeap(), 0, len_w
*sizeof(WCHAR
));
154 MultiByteToWideChar(locinfo
->lc_codepage
, 0, str
, len
, out
, len_w
);
155 len
= pf_puts(puts_ctx
, len_w
, out
);
156 HeapFree(GetProcessHeap(), 0, out
);
159 return pf_puts(puts_ctx
, len
, str
);
163 static inline int FUNC_NAME(pf_output_format_wstr
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
164 const MSVCRT_wchar_t
*str
, int len
, FUNC_NAME(pf_flags
) *flags
, MSVCRT_pthreadlocinfo locinfo
)
169 /* Do not search past the length specified by the precision. */
170 if(flags
->Precision
>=0)
171 len
= MSVCRT_wcsnlen(str
, flags
->Precision
);
176 if(flags
->Precision
>=0 && flags
->Precision
<len
)
177 len
= flags
->Precision
;
179 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, TRUE
);
182 r
= FUNC_NAME(pf_output_wstr
)(pf_puts
, puts_ctx
, str
, len
, locinfo
);
186 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, FALSE
);
190 return r
>=0 ? ret
: r
;
193 static inline int FUNC_NAME(pf_output_format_str
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
194 const char *str
, int len
, FUNC_NAME(pf_flags
) *flags
, MSVCRT_pthreadlocinfo locinfo
)
199 /* Do not search past the length specified by the precision. */
200 if(flags
->Precision
>=0)
201 len
= MSVCRT_strnlen(str
, flags
->Precision
);
206 if(flags
->Precision
>=0 && flags
->Precision
<len
)
207 len
= flags
->Precision
;
209 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, TRUE
);
212 r
= FUNC_NAME(pf_output_str
)(pf_puts
, puts_ctx
, str
, len
, locinfo
);
216 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, flags
, FALSE
);
220 return r
>=0 ? ret
: r
;
223 static inline int FUNC_NAME(pf_handle_string
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
,
224 const void *str
, int len
, FUNC_NAME(pf_flags
) *flags
, MSVCRT_pthreadlocinfo locinfo
, BOOL legacy_wide
)
226 BOOL api_is_wide
= sizeof(APICHAR
) == sizeof(MSVCRT_wchar_t
);
227 BOOL complement_is_narrow
= legacy_wide
? api_is_wide
: FALSE
;
229 static const MSVCRT_wchar_t nullW
[] = {'(','n','u','l','l',')',0};
232 return FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, nullW
, 6, flags
, locinfo
);
235 return FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, "(null)", 6, flags
, locinfo
);
238 if((flags
->NaturalString
&& api_is_wide
) || flags
->WideString
|| flags
->IntegerLength
=='l')
239 return FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, str
, len
, flags
, locinfo
);
240 if((flags
->NaturalString
&& !api_is_wide
) || flags
->IntegerLength
== 'h')
241 return FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, str
, len
, flags
, locinfo
);
243 if((flags
->Format
=='S' || flags
->Format
=='C') == complement_is_narrow
)
244 return FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, str
, len
, flags
, locinfo
);
246 return FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, str
, len
, flags
, locinfo
);
249 static inline void FUNC_NAME(pf_rebuild_format_string
)(char *p
, FUNC_NAME(pf_flags
) *flags
)
253 *p
++ = flags
->Alternate
;
254 if(flags
->Precision
>= 0) {
255 p
+= sprintf(p
, ".%d", flags
->Precision
);
257 *p
++ = flags
->Format
;
261 /* pf_integer_conv: prints x to buf, including alternate formats and
262 additional precision digits, but not field characters or the sign */
263 static inline void FUNC_NAME(pf_integer_conv
)(APICHAR
*buf
, int buf_len
,
264 FUNC_NAME(pf_flags
) *flags
, LONGLONG x
)
270 if(flags
->Format
== 'o')
272 else if(flags
->Format
=='x' || flags
->Format
=='X')
277 if(flags
->Format
== 'X')
278 digits
= "0123456789ABCDEFX";
280 digits
= "0123456789abcdefx";
282 if(x
<0 && (flags
->Format
=='d' || flags
->Format
=='i')) {
289 flags
->Alternate
= 0;
294 j
= (ULONGLONG
)x
%base
;
295 x
= (ULONGLONG
)x
/base
;
296 buf
[i
++] = digits
[j
];
299 k
= flags
->Precision
-i
;
302 if(flags
->Alternate
) {
304 buf
[i
++] = digits
[16];
306 } else if(base
==8 && buf
[i
-1]!='0')
310 /* Adjust precision so pf_fill won't truncate the number later */
311 flags
->Precision
= i
;
316 APICHAR tmp
= buf
[j
];
323 static inline void FUNC_NAME(pf_fixup_exponent
)(char *buf
, BOOL three_digit_exp
)
327 while(tmp
[0] && toupper(tmp
[0])!='E')
330 if(tmp
[0] && (tmp
[1]=='+' || tmp
[1]=='-') &&
331 isdigit(tmp
[2]) && isdigit(tmp
[3])) {
332 #if _MSVCR_VER >= 140
333 BOOL two_digit_exp
= !three_digit_exp
;
335 BOOL two_digit_exp
= (MSVCRT__get_output_format() == MSVCRT__TWO_DIGIT_EXPONENT
);
339 if(isdigit(tmp
[2])) {
340 if(two_digit_exp
&& tmp
[0]=='0') {
346 return; /* Exponent already 3 digits */
347 }else if(two_digit_exp
) {
358 int FUNC_NAME(pf_printf
)(FUNC_NAME(puts_clbk
) pf_puts
, void *puts_ctx
, const APICHAR
*fmt
,
359 MSVCRT__locale_t locale
, DWORD options
,
360 args_clbk pf_args
, void *args_ctx
, __ms_va_list
*valist
)
362 MSVCRT_pthreadlocinfo locinfo
;
363 const APICHAR
*q
, *p
= fmt
;
365 int written
= 0, pos
, i
;
366 FUNC_NAME(pf_flags
) flags
;
367 BOOL positional_params
= options
& MSVCRT_PRINTF_POSITIONAL_PARAMS
;
368 BOOL invoke_invalid_param_handler
= options
& MSVCRT_PRINTF_INVOKE_INVALID_PARAM_HANDLER
;
369 #if _MSVCR_VER >= 140
370 BOOL legacy_wide
= options
& UCRTBASE_PRINTF_LEGACY_WIDE_SPECIFIERS
;
371 BOOL legacy_msvcrt_compat
= options
& UCRTBASE_PRINTF_LEGACY_MSVCRT_COMPATIBILITY
;
372 BOOL three_digit_exp
= options
& UCRTBASE_PRINTF_LEGACY_THREE_DIGIT_EXPONENTS
;
374 BOOL legacy_wide
= TRUE
, legacy_msvcrt_compat
= TRUE
, three_digit_exp
= TRUE
;
377 TRACE("Format is: %s\n", FUNC_NAME(debugstr
)(fmt
));
379 if (!MSVCRT_CHECK_PMT(fmt
!= NULL
))
383 locinfo
= get_locinfo();
385 locinfo
= locale
->locinfo
;
388 /* output characters before '%' */
389 for(q
=p
; *q
&& *q
!='%'; q
++);
391 i
= pf_puts(puts_ctx
, q
-p
, p
);
403 /* output a single '%' character */
405 i
= pf_puts(puts_ctx
, 1, p
++);
413 /* check parameter position */
414 if(positional_params
&& (q
= FUNC_NAME(pf_parse_int
)(p
, &pos
)) && *q
=='$')
419 /* parse the flags */
420 memset(&flags
, 0, sizeof(flags
));
422 if(*p
=='+' || *p
==' ') {
423 if(flags
.Sign
!= '+')
426 flags
.LeftAlign
= *p
;
430 flags
.Alternate
= *p
;
437 /* parse the width */
440 if(positional_params
&& (q
= FUNC_NAME(pf_parse_int
)(p
, &i
)) && *q
=='$')
445 flags
.FieldLength
= pf_args(args_ctx
, i
, VT_INT
, valist
).get_int
;
446 if(flags
.FieldLength
< 0) {
447 flags
.LeftAlign
= '-';
448 flags
.FieldLength
= -flags
.FieldLength
;
450 } else while(isdigit(*p
)) {
451 flags
.FieldLength
*= 10;
452 flags
.FieldLength
+= *p
++ - '0';
455 /* parse the precision */
456 flags
.Precision
= -1;
462 if(positional_params
&& (q
= FUNC_NAME(pf_parse_int
)(p
, &i
)) && *q
=='$')
467 flags
.Precision
= pf_args(args_ctx
, i
, VT_INT
, valist
).get_int
;
468 } else while(isdigit(*p
)) {
469 flags
.Precision
*= 10;
470 flags
.Precision
+= *p
++ - '0';
474 /* parse argument size modifier */
476 if(*p
=='l' && *(p
+1)=='l') {
477 flags
.IntegerDouble
++;
479 } else if(*p
=='h' || *p
=='l' || *p
=='L') {
480 flags
.IntegerLength
= *p
;
482 } else if(*p
== 'I') {
483 if(*(p
+1)=='6' && *(p
+2)=='4') {
484 flags
.IntegerDouble
++;
486 } else if(*(p
+1)=='3' && *(p
+2)=='2')
488 else if(isdigit(*(p
+1)) || !*(p
+1))
491 flags
.IntegerNative
= *p
++;
493 flags
.WideString
= *p
++;
494 #if _MSVCR_VER >= 140
496 flags
.IntegerNative
= *p
++;
498 flags
.NaturalString
= *p
++;
500 else if((*p
== 'F' || *p
== 'N') && legacy_msvcrt_compat
)
508 if(flags
.Format
== 's' || flags
.Format
== 'S') {
509 i
= FUNC_NAME(pf_handle_string
)(pf_puts
, puts_ctx
,
510 pf_args(args_ctx
, pos
, VT_PTR
, valist
).get_ptr
,
511 -1, &flags
, locinfo
, legacy_wide
);
512 } else if(flags
.Format
== 'c' || flags
.Format
== 'C') {
513 int ch
= pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
;
515 i
= FUNC_NAME(pf_handle_string
)(pf_puts
, puts_ctx
, &ch
, 1, &flags
, locinfo
, legacy_wide
);
516 } else if(flags
.Format
== 'p') {
520 flags
.Precision
= 2*sizeof(void*);
521 FUNC_NAME(pf_integer_conv
)(buf
, sizeof(buf
)/sizeof(APICHAR
), &flags
,
522 (ULONG_PTR
)pf_args(args_ctx
, pos
, VT_PTR
, valist
).get_ptr
);
527 i
= FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, buf
, -1, &flags
, locinfo
);
529 i
= FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, buf
, -1, &flags
, locinfo
);
531 } else if(flags
.Format
== 'n') {
534 if(!n_format_enabled
) {
535 MSVCRT_INVALID_PMT("\'n\' format specifier disabled", MSVCRT_EINVAL
);
539 used
= pf_args(args_ctx
, pos
, VT_PTR
, valist
).get_ptr
;
542 } else if(flags
.Format
&& strchr("diouxX", flags
.Format
)) {
546 /* 0 padding is added after '0x' if Alternate flag is in use */
547 if((flags
.Format
=='x' || flags
.Format
=='X') && flags
.PadZero
&& flags
.Alternate
548 && !flags
.LeftAlign
&& flags
.Precision
<flags
.FieldLength
-2)
549 flags
.Precision
= flags
.FieldLength
- 2;
551 max_len
= (flags
.FieldLength
>flags
.Precision
? flags
.FieldLength
: flags
.Precision
) + 10;
552 if(max_len
> sizeof(buf
)/sizeof(APICHAR
))
553 tmp
= HeapAlloc(GetProcessHeap(), 0, max_len
);
557 if(flags
.IntegerDouble
|| (flags
.IntegerNative
&& sizeof(void*) == 8))
558 FUNC_NAME(pf_integer_conv
)(tmp
, max_len
, &flags
, pf_args(args_ctx
, pos
,
559 VT_I8
, valist
).get_longlong
);
560 else if(flags
.Format
=='d' || flags
.Format
=='i')
561 FUNC_NAME(pf_integer_conv
)(tmp
, max_len
, &flags
, flags
.IntegerLength
!='h' ?
562 pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
:
563 (short)pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
);
565 FUNC_NAME(pf_integer_conv
)(tmp
, max_len
, &flags
, flags
.IntegerLength
!='h' ?
566 (unsigned)pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
:
567 (unsigned short)pf_args(args_ctx
, pos
, VT_INT
, valist
).get_int
);
570 i
= FUNC_NAME(pf_output_format_wstr
)(pf_puts
, puts_ctx
, tmp
, -1, &flags
, locinfo
);
572 i
= FUNC_NAME(pf_output_format_str
)(pf_puts
, puts_ctx
, tmp
, -1, &flags
, locinfo
);
575 HeapFree(GetProcessHeap(), 0, tmp
);
576 } else if(flags
.Format
&& strchr("aeEfFgG", flags
.Format
)) {
577 char float_fmt
[20], buf_a
[32], *tmp
= buf_a
, *decimal_point
;
578 int len
= flags
.Precision
+ 10;
579 double val
= pf_args(args_ctx
, pos
, VT_R8
, valist
).get_double
;
581 BOOL inf
= FALSE
, nan
= FALSE
, ind
= FALSE
;
585 else if(isnan(val
)) {
592 if(inf
|| nan
|| ind
) {
596 if(flags
.Format
=='g' || flags
.Format
=='G')
597 val
= (nan
? 1.12345 : 1.1234); /* fraction will be overwritten with #INF, #IND or #QNAN string */
601 if(flags
.Format
=='a') {
602 if(flags
.Precision
==-1)
603 flags
.Precision
= 6; /* strlen("#INF00") */
607 if(flags
.Format
=='f' || flags
.Format
=='F') {
608 if(val
>-10.0 && val
<10.0)
611 i
= 1 + log10(val
<0 ? -val
: val
);
612 /* Default precision is 6, additional space for sign, separator and nullbyte is required */
613 i
+= (flags
.Precision
==-1 ? 6 : flags
.Precision
) + 3;
619 if(len
> sizeof(buf_a
))
620 tmp
= HeapAlloc(GetProcessHeap(), 0, len
);
624 FUNC_NAME(pf_rebuild_format_string
)(float_fmt
, &flags
);
630 if((inf
|| nan
|| ind
) && !legacy_msvcrt_compat
) {
631 static const char inf_str
[] = "inf";
632 static const char ind_str
[] = "nan(ind)";
633 static const char nan_str
[] = "nan";
635 sprintf(tmp
, inf_str
);
637 sprintf(tmp
, ind_str
);
639 sprintf(tmp
, nan_str
);
640 if (strchr("EFG", flags
.Format
))
641 for(i
=0; tmp
[i
]; i
++)
642 tmp
[i
] = toupper(tmp
[i
]);
644 sprintf(tmp
, float_fmt
, val
);
645 if(toupper(flags
.Format
)=='E' || toupper(flags
.Format
)=='G')
646 FUNC_NAME(pf_fixup_exponent
)(tmp
, three_digit_exp
);
649 decimal_point
= strchr(tmp
, '.');
651 *decimal_point
= *locinfo
->lconv
->decimal_point
;
653 if(inf
|| nan
|| ind
) {
654 static const char inf_str
[] = "#INF";
655 static const char ind_str
[] = "#IND";
656 static const char nan_str
[] = "#QNAN";
663 size
= sizeof(inf_str
);
666 size
= sizeof(ind_str
);
669 size
= sizeof(nan_str
);
672 for(i
=1; i
<size
; i
++) {
673 if(decimal_point
[i
]<'0' || decimal_point
[i
]>'9')
676 decimal_point
[i
] = str
[i
-1];
680 decimal_point
[i
-1]++;
685 i
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, &flags
, TRUE
);
688 r
= FUNC_NAME(pf_output_str
)(pf_puts
, puts_ctx
, tmp
, len
, locinfo
);
693 HeapFree(GetProcessHeap(), 0, tmp
);
694 r
= FUNC_NAME(pf_fill
)(pf_puts
, puts_ctx
, len
, &flags
, FALSE
);
699 if(invoke_invalid_param_handler
) {
700 MSVCRT__invalid_parameter(NULL
, NULL
, NULL
, 0, 0);
701 *MSVCRT__errno() = MSVCRT_EINVAL
;
718 enum types_clbk_flags
{
719 TYPE_CLBK_VA_LIST
= 1,
720 TYPE_CLBK_POSITIONAL
= 2,
721 TYPE_CLBK_ERROR_POS
= 4,
722 TYPE_CLBK_ERROR_TYPE
= 8
725 /* This functions stores types of arguments. It uses args[0] internally */
726 static printf_arg
arg_clbk_type(void *ctx
, int pos
, int type
, __ms_va_list
*valist
)
728 static const printf_arg ret
;
729 printf_arg
*args
= ctx
;
732 args
[0].get_int
|= TYPE_CLBK_VA_LIST
;
735 args
[0].get_int
|= TYPE_CLBK_POSITIONAL
;
737 if(pos
<1 || pos
>MSVCRT__ARGMAX
)
738 args
[0].get_int
|= TYPE_CLBK_ERROR_POS
;
739 else if(args
[pos
].get_int
&& args
[pos
].get_int
!=type
)
740 args
[0].get_int
|= TYPE_CLBK_ERROR_TYPE
;
742 args
[pos
].get_int
= type
;
748 int FUNC_NAME(create_positional_ctx
)(void *args_ctx
, const APICHAR
*format
, __ms_va_list valist
)
750 struct FUNC_NAME(_str_ctx
) puts_ctx
= {INT_MAX
, NULL
};
751 printf_arg
*args
= args_ctx
;
754 i
= FUNC_NAME(pf_printf
)(FUNC_NAME(puts_clbk_str
), &puts_ctx
, format
, NULL
,
755 MSVCRT_PRINTF_POSITIONAL_PARAMS
, arg_clbk_type
, args_ctx
, NULL
);
759 if(args
[0].get_int
==0 || args
[0].get_int
==TYPE_CLBK_VA_LIST
)
761 if(args
[0].get_int
!= TYPE_CLBK_POSITIONAL
)
764 for(i
=MSVCRT__ARGMAX
; i
>0; i
--)
768 for(j
=1; j
<=i
; j
++) {
769 switch(args
[j
].get_int
) {
771 args
[j
].get_longlong
= va_arg(valist
, LONGLONG
);
774 args
[j
].get_int
= va_arg(valist
, int);
777 args
[j
].get_double
= va_arg(valist
, double);
780 args
[j
].get_ptr
= va_arg(valist
, void*);