2 * ntdll printf functions
4 * Copyright 1999, 2009 Alexandre Julliard
5 * Copyright 2000 Jon Griffiths
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "wine/port.h"
34 #include "ntdll_misc.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(ntdll
);
39 static const SIZE_T size_max
= ~(SIZE_T
)0 >> 1;
41 /* FIXME: convert sizes to SIZE_T etc. */
43 typedef struct pf_output_t
54 typedef struct pf_flags_t
56 char Sign
, LeftAlign
, Alternate
, PadZero
;
57 int FieldLength
, Precision
;
58 char IntegerLength
, IntegerDouble
;
64 * writes a string of characters to the output
65 * returns -1 if the string doesn't fit in the output buffer
66 * return the length of the string if all characters were written
68 static inline int pf_output_stringW( pf_output
*out
, LPCWSTR str
, int len
)
70 SIZE_T space
= out
->len
- out
->used
;
76 LPWSTR p
= out
->buf
.W
+ out
->used
;
85 memcpy( p
, str
, len
*sizeof(WCHAR
) );
91 memcpy( p
, str
, space
*sizeof(WCHAR
) );
97 LPSTR p
= out
->buf
.A
+ out
->used
;
100 RtlUnicodeToMultiByteSize( &n
, str
, len
* sizeof(WCHAR
) );
109 RtlUnicodeToMultiByteN( p
, n
, NULL
, str
, len
* sizeof(WCHAR
) );
115 RtlUnicodeToMultiByteN( p
, space
, NULL
, str
, len
* sizeof(WCHAR
) );
122 static inline int pf_output_stringA( pf_output
*out
, LPCSTR str
, int len
)
124 SIZE_T space
= out
->len
- out
->used
;
130 LPSTR p
= out
->buf
.A
+ out
->used
;
139 memcpy( p
, str
, len
);
145 memcpy( p
, str
, space
);
151 LPWSTR p
= out
->buf
.W
+ out
->used
;
154 RtlMultiByteToUnicodeSize( &n
, str
, len
);
158 out
->used
+= n
/ sizeof(WCHAR
);
161 if (space
>= n
/ sizeof(WCHAR
))
163 RtlMultiByteToUnicodeN( p
, n
, NULL
, str
, len
);
164 out
->used
+= n
/ sizeof(WCHAR
);
169 RtlMultiByteToUnicodeN( p
, space
* sizeof(WCHAR
), NULL
, str
, len
);
176 /* pf_fill: takes care of signs, alignment, zero and field padding */
177 static inline int pf_fill( pf_output
*out
, int len
, pf_flags
*flags
, char left
)
181 if( flags
->Sign
&& !( flags
->Format
== 'd' || flags
->Format
== 'i' ) )
184 if( left
&& flags
->Sign
)
186 flags
->FieldLength
--;
188 r
= pf_output_stringA( out
, &flags
->Sign
, 1 );
191 if( ( !left
&& flags
->LeftAlign
) ||
192 ( left
&& !flags
->LeftAlign
))
194 for( i
=0; (i
<(flags
->FieldLength
-len
)) && (r
>=0); i
++ )
196 if( left
&& flags
->PadZero
)
197 r
= pf_output_stringA( out
, "0", 1 );
199 r
= pf_output_stringA( out
, " ", 1 );
203 if (left
&& flags
->Sign
&& !flags
->PadZero
&& r
>= 0)
204 r
= pf_output_stringA( out
, &flags
->Sign
, 1 );
209 static inline int pf_output_format_W( pf_output
*out
, LPCWSTR str
,
210 int len
, pf_flags
*flags
)
217 if (flags
->Precision
>= 0 && flags
->Precision
< len
)
218 len
= flags
->Precision
;
220 r
= pf_fill( out
, len
, flags
, 1 );
223 r
= pf_output_stringW( out
, str
, len
);
226 r
= pf_fill( out
, len
, flags
, 0 );
231 static inline int pf_output_format_A( pf_output
*out
, LPCSTR str
,
232 int len
, pf_flags
*flags
)
239 if (flags
->Precision
>= 0 && flags
->Precision
< len
)
240 len
= flags
->Precision
;
242 r
= pf_fill( out
, len
, flags
, 1 );
245 r
= pf_output_stringA( out
, str
, len
);
248 r
= pf_fill( out
, len
, flags
, 0 );
253 static int pf_handle_string_format( pf_output
*out
, const void* str
, int len
,
254 pf_flags
*flags
, BOOL capital_letter
)
256 if(str
== NULL
) /* catch NULL pointer */
257 return pf_output_format_A( out
, "(null)", -1, flags
);
259 /* prefixes take priority over %c,%s vs. %C,%S, so we handle them first */
260 if(flags
->WideString
|| flags
->IntegerLength
== 'l')
261 return pf_output_format_W( out
, str
, len
, flags
);
262 if(flags
->IntegerLength
== 'h')
263 return pf_output_format_A( out
, str
, len
, flags
);
265 /* %s,%c -> chars in ansi functions & wchars in unicode
266 * %S,%C -> wchars in ansi functions & chars in unicode */
267 if( capital_letter
== out
->unicode
) /* either both TRUE or both FALSE */
268 return pf_output_format_A( out
, str
, len
, flags
);
270 return pf_output_format_W( out
, str
, len
, flags
);
273 static inline BOOL
pf_is_integer_format( char fmt
)
275 static const char float_fmts
[] = "diouxX";
278 return strchr( float_fmts
, fmt
) != 0;
281 static inline BOOL
pf_is_double_format( char fmt
)
283 static const char float_fmts
[] = "aeEfgG";
286 return strchr( float_fmts
, fmt
) != 0;
289 static inline BOOL
pf_is_valid_format( char fmt
)
291 static const char float_fmts
[] = "acCdeEfgGinouxX";
294 return strchr( float_fmts
, fmt
) != 0;
297 static void pf_rebuild_format_string( char *p
, pf_flags
*flags
)
302 if( flags
->LeftAlign
)
303 *p
++ = flags
->LeftAlign
;
304 if( flags
->Alternate
)
305 *p
++ = flags
->Alternate
;
307 *p
++ = flags
->PadZero
;
308 if( flags
->FieldLength
)
310 sprintf(p
, "%d", flags
->FieldLength
);
313 if( flags
->Precision
>= 0 )
315 sprintf(p
, ".%d", flags
->Precision
);
318 *p
++ = flags
->Format
;
322 /* pf_integer_conv: prints x to buf, including alternate formats and
323 additional precision digits, but not field characters or the sign */
324 static void pf_integer_conv( char *buf
, int buf_len
, pf_flags
*flags
,
331 char number
[40], *tmp
= number
;
333 if( buf_len
> sizeof number
)
335 if (!(tmp
= RtlAllocateHeap( GetProcessHeap(), 0, buf_len
)))
343 if( flags
->Format
== 'o' )
345 else if( flags
->Format
== 'x' || flags
->Format
== 'X' )
348 if( flags
->Format
== 'X' )
349 digits
= "0123456789ABCDEFX";
351 digits
= "0123456789abcdefx";
353 if( x
< 0 && ( flags
->Format
== 'd' || flags
->Format
== 'i' ) )
359 /* Do conversion (backwards) */
361 if( x
== 0 && flags
->Precision
)
366 j
= (ULONGLONG
) x
% base
;
367 x
= (ULONGLONG
) x
/ base
;
368 tmp
[i
++] = digits
[j
];
370 k
= flags
->Precision
- i
;
373 if( flags
->Alternate
)
377 tmp
[i
++] = digits
[16];
380 else if( base
== 8 && tmp
[i
-1] != '0' )
384 /* Reverse for buf */
390 /* Adjust precision so pf_fill won't truncate the number later */
391 flags
->Precision
= strlen( buf
);
394 RtlFreeHeap( GetProcessHeap(), 0, tmp
);
399 /* pf_fixup_exponent: convert a string containing a 2 digit exponent
400 to 3 digits, accounting for padding, in place. Needed to match
401 the native printf's which always use 3 digits. */
402 static void pf_fixup_exponent( char *buf
)
406 while (tmp
[0] && NTDLL_tolower(tmp
[0]) != 'e')
409 if (tmp
[0] && (tmp
[1] == '+' || tmp
[1] == '-') &&
410 isdigit(tmp
[2]) && isdigit(tmp
[3]))
415 return; /* Exponent already 3 digits */
417 /* We have a 2 digit exponent. Prepend '0' to make it 3 */
425 /* We didn't expand into trailing space, so this string isn't left
426 * justified. Terminate the string and strip a ' ' at the start of
427 * the string if there is one (as there may be if the string is
432 memmove(buf
, buf
+ 1, (tmp
- buf
) + 3);
434 /* Otherwise, we expanded into trailing space -> nothing to do */
438 static inline BOOL
isDigit(WCHAR c
)
440 return c
>= '0' && c
<= '9';
443 /*********************************************************************
444 * pf_vsnprintf (INTERNAL)
446 * implements both A and W vsnprintf functions
448 static int pf_vsnprintf( pf_output
*out
, const WCHAR
*format
, __ms_va_list valist
)
451 LPCWSTR q
, p
= format
;
456 q
= wcschr( p
, '%' );
458 /* there are no % characters left: output the rest of the string */
461 r
= pf_output_stringW(out
, p
, -1);
468 /* there are characters before the %: output them */
471 r
= pf_output_stringW(out
, p
, q
- p
);
477 /* we must be at a % now, skip over it */
481 /* output a single % character */
484 r
= pf_output_stringW(out
, p
++, 1);
490 /* parse the flags */
491 memset( &flags
, 0, sizeof flags
);
494 if( *p
== '+' || *p
== ' ' )
496 if ( flags
.Sign
!= '+' )
500 flags
.LeftAlign
= *p
;
504 flags
.Alternate
= *p
;
510 /* deal with the field width specifier */
511 flags
.FieldLength
= 0;
514 flags
.FieldLength
= va_arg( valist
, int );
515 if (flags
.FieldLength
< 0)
517 flags
.LeftAlign
= '-';
518 flags
.FieldLength
= -flags
.FieldLength
;
522 else while( isDigit(*p
) )
524 flags
.FieldLength
*= 10;
525 flags
.FieldLength
+= *p
++ - '0';
528 /* deal with precision */
529 flags
.Precision
= -1;
536 flags
.Precision
= va_arg( valist
, int );
539 else while( isDigit(*p
) )
541 flags
.Precision
*= 10;
542 flags
.Precision
+= *p
++ - '0';
546 /* deal with integer width modifier */
549 if( *p
== 'h' || *p
== 'l' || *p
== 'L' )
551 flags
.IntegerLength
= *p
;
556 if( *(p
+1) == '6' && *(p
+2) == '4' )
558 flags
.IntegerDouble
++;
561 else if( *(p
+1) == '3' && *(p
+2) == '2' )
563 else if( p
[1] && strchr( "diouxX", p
[1] ) )
565 if( sizeof(void *) == 8 )
566 flags
.IntegerDouble
= *p
;
569 else if( isDigit(*(p
+1)) || *(p
+1) == 0 )
575 flags
.WideString
= *p
++;
585 if (flags
.Format
== '$')
587 FIXME("Positional parameters are not supported (%s)\n", wine_dbgstr_w(format
));
590 /* output a string */
591 if( flags
.Format
== 's' || flags
.Format
== 'S' )
592 r
= pf_handle_string_format( out
, va_arg(valist
, const void*), -1,
593 &flags
, (flags
.Format
== 'S') );
595 /* output a single character */
596 else if( flags
.Format
== 'c' || flags
.Format
== 'C' )
598 INT ch
= va_arg( valist
, int );
600 r
= pf_handle_string_format( out
, &ch
, 1, &flags
, (flags
.Format
== 'C') );
603 /* output a pointer */
604 else if( flags
.Format
== 'p' )
607 void *ptr
= va_arg( valist
, void * );
610 if( flags
.Alternate
)
611 sprintf(pointer
, "0X%0*lX", 2 * (int)sizeof(ptr
), (ULONG_PTR
)ptr
);
613 sprintf(pointer
, "%0*lX", 2 * (int)sizeof(ptr
), (ULONG_PTR
)ptr
);
614 r
= pf_output_format_A( out
, pointer
, -1, &flags
);
618 else if( flags
.Format
== 'n' )
620 int *x
= va_arg(valist
, int *);
624 /* deal with 64-bit integers */
625 else if( pf_is_integer_format( flags
.Format
) && flags
.IntegerDouble
)
627 char number
[40], *x
= number
;
629 /* Estimate largest possible required buffer size:
630 * Chooses the larger of the field or precision
631 * Includes extra bytes: 1 byte for null, 1 byte for sign,
632 4 bytes for exponent, 2 bytes for alternate formats, 1 byte
633 for a decimal, and 1 byte for an additional float digit. */
634 int x_len
= ((flags
.FieldLength
> flags
.Precision
) ?
635 flags
.FieldLength
: flags
.Precision
) + 10;
637 if( x_len
>= sizeof number
)
638 if (!(x
= RtlAllocateHeap( GetProcessHeap(), 0, x_len
)))
641 pf_integer_conv( x
, x_len
, &flags
, va_arg(valist
, LONGLONG
) );
643 r
= pf_output_format_A( out
, x
, -1, &flags
);
645 RtlFreeHeap( GetProcessHeap(), 0, x
);
648 /* deal with integers and floats using libc's printf */
649 else if( pf_is_valid_format( flags
.Format
) )
651 char fmt
[20], number
[40], *x
= number
;
653 /* Estimate largest possible required buffer size:
654 * Chooses the larger of the field or precision
655 * Includes extra bytes: 1 byte for null, 1 byte for sign,
656 4 bytes for exponent, 2 bytes for alternate formats, 1 byte
657 for a decimal, and 1 byte for an additional float digit. */
658 int x_len
= ((flags
.FieldLength
> flags
.Precision
) ?
659 flags
.FieldLength
: flags
.Precision
) + 10;
661 if( x_len
>= sizeof number
)
662 if (!(x
= RtlAllocateHeap( GetProcessHeap(), 0, x_len
)))
665 pf_rebuild_format_string( fmt
, &flags
);
667 if( pf_is_double_format( flags
.Format
) )
669 sprintf( x
, fmt
, va_arg(valist
, double) );
670 if (NTDLL_tolower(flags
.Format
) == 'e' || NTDLL_tolower(flags
.Format
) == 'g')
671 pf_fixup_exponent( x
);
674 sprintf( x
, fmt
, va_arg(valist
, int) );
676 r
= pf_output_stringA( out
, x
, -1 );
678 RtlFreeHeap( GetProcessHeap(), 0, x
);
688 /* check we reached the end, and null terminate the string */
694 /*********************************************************************
695 * _vsnprintf (NTDLL.@)
697 int CDECL
NTDLL__vsnprintf( char *str
, SIZE_T len
, const char *format
, __ms_va_list args
)
700 LPWSTR formatW
= NULL
;
711 RtlMultiByteToUnicodeSize( &sz
, format
, strlen(format
) + 1 );
712 if (!(formatW
= RtlAllocateHeap( GetProcessHeap(), 0, sz
))) return -1;
713 RtlMultiByteToUnicodeN( formatW
, sz
, NULL
, format
, strlen(format
) + 1 );
715 r
= pf_vsnprintf( &out
, formatW
, args
);
716 RtlFreeHeap( GetProcessHeap(), 0, formatW
);
717 if (out
.used
< len
) str
[out
.used
] = 0;
722 /***********************************************************************
723 * _vsnwprintf (NTDLL.@)
725 int CDECL
NTDLL__vsnwprintf( WCHAR
*str
, SIZE_T len
, const WCHAR
*format
, __ms_va_list args
)
735 r
= pf_vsnprintf( &out
, format
, args
);
736 if (out
.used
< len
) str
[out
.used
] = 0;
741 /*********************************************************************
742 * _snprintf (NTDLL.@)
744 int WINAPIV
NTDLL__snprintf( char *str
, SIZE_T len
, const char *format
, ... )
749 __ms_va_start( valist
, format
);
750 ret
= NTDLL__vsnprintf( str
, len
, format
, valist
);
751 __ms_va_end( valist
);
756 /***********************************************************************
757 * _snwprintf (NTDLL.@)
759 int WINAPIV
NTDLL__snwprintf( WCHAR
*str
, SIZE_T len
, const WCHAR
*format
, ... )
764 __ms_va_start(valist
, format
);
765 ret
= NTDLL__vsnwprintf( str
, len
, format
, valist
);
771 /*********************************************************************
772 * _vsnprintf_s (NTDLL.@)
774 int CDECL
_vsnprintf_s( char *str
, SIZE_T size
, SIZE_T len
, const char *format
, __ms_va_list args
)
777 LPWSTR formatW
= NULL
;
784 out
.len
= min( size
, len
);
788 RtlMultiByteToUnicodeSize( &sz
, format
, strlen(format
) + 1 );
789 if (!(formatW
= RtlAllocateHeap( GetProcessHeap(), 0, sz
))) return -1;
790 RtlMultiByteToUnicodeN( formatW
, sz
, NULL
, format
, strlen(format
) + 1 );
792 r
= pf_vsnprintf( &out
, formatW
, args
);
793 RtlFreeHeap( GetProcessHeap(), 0, formatW
);
794 if (out
.used
< size
) str
[out
.used
] = 0;
796 if (r
== size
) r
= -1;
801 /*********************************************************************
802 * _snprintf_s (NTDLL.@)
804 int WINAPIV
_snprintf_s( char *str
, SIZE_T size
, SIZE_T len
, const char *format
, ... )
809 __ms_va_start( valist
, format
);
810 ret
= _vsnprintf_s( str
, size
, len
, format
, valist
);
811 __ms_va_end( valist
);
816 /*********************************************************************
819 int CDECL
NTDLL_vsprintf( char *str
, const char *format
, __ms_va_list args
)
821 return NTDLL__vsnprintf( str
, size_max
, format
, args
);
825 /*********************************************************************
828 int WINAPIV
NTDLL_sprintf( char *str
, const char *format
, ... )
833 __ms_va_start( valist
, format
);
834 ret
= NTDLL__vsnprintf( str
, size_max
, format
, valist
);
835 __ms_va_end( valist
);
840 /***********************************************************************
843 int WINAPIV
NTDLL_swprintf( WCHAR
*str
, const WCHAR
*format
, ... )
848 __ms_va_start(valist
, format
);
849 ret
= NTDLL__vsnwprintf( str
, size_max
, format
, valist
);