usp10/tests: A spelling fix in an ok() message.
[wine.git] / dlls / ntdll / printf.c
blob018f774dc0067728ab7cc5c7d91c30191042271f
1 /*
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <assert.h>
26 #include <ctype.h>
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
32 #include "windef.h"
33 #include "winternl.h"
34 #include "ntdll_misc.h"
35 #include "wine/unicode.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
40 static const SIZE_T size_max = ~(SIZE_T)0 >> 1;
42 /* FIXME: convert sizes to SIZE_T etc. */
44 typedef struct pf_output_t
46 int used;
47 int len;
48 BOOL unicode;
49 union {
50 LPWSTR W;
51 LPSTR A;
52 } buf;
53 } pf_output;
55 typedef struct pf_flags_t
57 char Sign, LeftAlign, Alternate, PadZero;
58 int FieldLength, Precision;
59 char IntegerLength, IntegerDouble;
60 char WideString;
61 char Format;
62 } pf_flags;
65 * writes a string of characters to the output
66 * returns -1 if the string doesn't fit in the output buffer
67 * return the length of the string if all characters were written
69 static inline int pf_output_stringW( pf_output *out, LPCWSTR str, int len )
71 int space = out->len - out->used;
73 if( len < 0 )
74 len = strlenW( str );
75 if( out->unicode )
77 LPWSTR p = out->buf.W + out->used;
79 if (!out->buf.W)
81 out->used += len;
82 return len;
84 if( space >= len )
86 memcpy( p, str, len*sizeof(WCHAR) );
87 out->used += len;
88 return len;
90 if( space > 0 )
92 memcpy( p, str, space*sizeof(WCHAR) );
93 out->used += space;
96 else
98 LPSTR p = out->buf.A + out->used;
99 ULONG n;
101 RtlUnicodeToMultiByteSize( &n, str, len * sizeof(WCHAR) );
103 if (!out->buf.A)
105 out->used += n;
106 return len;
108 if( space >= n )
110 RtlUnicodeToMultiByteN( p, n, NULL, str, len * sizeof(WCHAR) );
111 out->used += n;
112 return len;
114 if (space > 0)
116 RtlUnicodeToMultiByteN( p, space, NULL, str, len * sizeof(WCHAR) );
117 out->used += space;
120 return -1;
123 static inline int pf_output_stringA( pf_output *out, LPCSTR str, int len )
125 int space = out->len - out->used;
127 if( len < 0 )
128 len = strlen( str );
129 if( !out->unicode )
131 LPSTR p = out->buf.A + out->used;
133 if (!out->buf.A)
135 out->used += len;
136 return len;
138 if( space >= len )
140 memcpy( p, str, len );
141 out->used += len;
142 return len;
144 if( space > 0 )
146 memcpy( p, str, space );
147 out->used += space;
150 else
152 LPWSTR p = out->buf.W + out->used;
153 ULONG n;
155 RtlMultiByteToUnicodeSize( &n, str, len );
157 if (!out->buf.W)
159 out->used += n / sizeof(WCHAR);
160 return len;
162 if (space >= n / sizeof(WCHAR))
164 RtlMultiByteToUnicodeN( p, n, NULL, str, len );
165 out->used += n / sizeof(WCHAR);
166 return len;
168 if (space > 0)
170 RtlMultiByteToUnicodeN( p, space * sizeof(WCHAR), NULL, str, len );
171 out->used += space;
174 return -1;
177 /* pf_fill: takes care of signs, alignment, zero and field padding */
178 static inline int pf_fill( pf_output *out, int len, pf_flags *flags, char left )
180 int i, r = 0;
182 if( flags->Sign && !( flags->Format == 'd' || flags->Format == 'i' ) )
183 flags->Sign = 0;
185 if( left && flags->Sign )
187 flags->FieldLength--;
188 if( flags->PadZero )
189 r = pf_output_stringA( out, &flags->Sign, 1 );
192 if( ( !left && flags->LeftAlign ) ||
193 ( left && !flags->LeftAlign ))
195 for( i=0; (i<(flags->FieldLength-len)) && (r>=0); i++ )
197 if( left && flags->PadZero )
198 r = pf_output_stringA( out, "0", 1 );
199 else
200 r = pf_output_stringA( out, " ", 1 );
204 if (left && flags->Sign && !flags->PadZero && r >= 0)
205 r = pf_output_stringA( out, &flags->Sign, 1 );
207 return r;
210 static inline int pf_output_format_W( pf_output *out, LPCWSTR str,
211 int len, pf_flags *flags )
213 int r = 0;
215 if( len < 0 )
216 len = strlenW( str );
218 if (flags->Precision >= 0 && flags->Precision < len)
219 len = flags->Precision;
221 r = pf_fill( out, len, flags, 1 );
223 if( r>=0 )
224 r = pf_output_stringW( out, str, len );
226 if( r>=0 )
227 r = pf_fill( out, len, flags, 0 );
229 return r;
232 static inline int pf_output_format_A( pf_output *out, LPCSTR str,
233 int len, pf_flags *flags )
235 int r = 0;
237 if( len < 0 )
238 len = strlen( str );
240 if (flags->Precision >= 0 && flags->Precision < len)
241 len = flags->Precision;
243 r = pf_fill( out, len, flags, 1 );
245 if( r>=0 )
246 r = pf_output_stringA( out, str, len );
248 if( r>=0 )
249 r = pf_fill( out, len, flags, 0 );
251 return r;
254 static int pf_handle_string_format( pf_output *out, const void* str, int len,
255 pf_flags *flags, BOOL capital_letter)
257 if(str == NULL) /* catch NULL pointer */
258 return pf_output_format_A( out, "(null)", -1, flags);
260 /* prefixes take priority over %c,%s vs. %C,%S, so we handle them first */
261 if(flags->WideString || flags->IntegerLength == 'l')
262 return pf_output_format_W( out, str, len, flags);
263 if(flags->IntegerLength == 'h')
264 return pf_output_format_A( out, str, len, flags);
266 /* %s,%c -> chars in ansi functions & wchars in unicode
267 * %S,%C -> wchars in ansi functions & chars in unicode */
268 if( capital_letter == out->unicode) /* either both TRUE or both FALSE */
269 return pf_output_format_A( out, str, len, flags);
270 else
271 return pf_output_format_W( out, str, len, flags);
274 static inline BOOL pf_is_integer_format( char fmt )
276 static const char float_fmts[] = "diouxX";
277 if (!fmt)
278 return FALSE;
279 return strchr( float_fmts, fmt ) != 0;
282 static inline BOOL pf_is_double_format( char fmt )
284 static const char float_fmts[] = "aeEfgG";
285 if (!fmt)
286 return FALSE;
287 return strchr( float_fmts, fmt ) != 0;
290 static inline BOOL pf_is_valid_format( char fmt )
292 static const char float_fmts[] = "acCdeEfgGinouxX";
293 if (!fmt)
294 return FALSE;
295 return strchr( float_fmts, fmt ) != 0;
298 static void pf_rebuild_format_string( char *p, pf_flags *flags )
300 *p++ = '%';
301 if( flags->Sign )
302 *p++ = flags->Sign;
303 if( flags->LeftAlign )
304 *p++ = flags->LeftAlign;
305 if( flags->Alternate )
306 *p++ = flags->Alternate;
307 if( flags->PadZero )
308 *p++ = flags->PadZero;
309 if( flags->FieldLength )
311 sprintf(p, "%d", flags->FieldLength);
312 p += strlen(p);
314 if( flags->Precision >= 0 )
316 sprintf(p, ".%d", flags->Precision);
317 p += strlen(p);
319 *p++ = flags->Format;
320 *p++ = 0;
323 /* pf_integer_conv: prints x to buf, including alternate formats and
324 additional precision digits, but not field characters or the sign */
325 static void pf_integer_conv( char *buf, int buf_len, pf_flags *flags,
326 LONGLONG x )
328 unsigned int base;
329 const char *digits;
331 int i, j, k;
332 char number[40], *tmp = number;
334 if( buf_len > sizeof number )
336 if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, buf_len )))
338 buf[0] = '\0';
339 return;
343 base = 10;
344 if( flags->Format == 'o' )
345 base = 8;
346 else if( flags->Format == 'x' || flags->Format == 'X' )
347 base = 16;
349 if( flags->Format == 'X' )
350 digits = "0123456789ABCDEFX";
351 else
352 digits = "0123456789abcdefx";
354 if( x < 0 && ( flags->Format == 'd' || flags->Format == 'i' ) )
356 x = -x;
357 flags->Sign = '-';
360 /* Do conversion (backwards) */
361 i = 0;
362 if( x == 0 && flags->Precision )
363 tmp[i++] = '0';
364 else
365 while( x != 0 )
367 j = (ULONGLONG) x % base;
368 x = (ULONGLONG) x / base;
369 tmp[i++] = digits[j];
371 k = flags->Precision - i;
372 while( k-- > 0 )
373 tmp[i++] = '0';
374 if( flags->Alternate )
376 if( base == 16 )
378 tmp[i++] = digits[16];
379 tmp[i++] = '0';
381 else if( base == 8 && tmp[i-1] != '0' )
382 tmp[i++] = '0';
385 /* Reverse for buf */
386 j = 0;
387 while( i-- > 0 )
388 buf[j++] = tmp[i];
389 buf[j] = '\0';
391 /* Adjust precision so pf_fill won't truncate the number later */
392 flags->Precision = strlen( buf );
394 if( tmp != number )
395 RtlFreeHeap( GetProcessHeap(), 0, tmp );
397 return;
400 /* pf_fixup_exponent: convert a string containing a 2 digit exponent
401 to 3 digits, accounting for padding, in place. Needed to match
402 the native printf's which always use 3 digits. */
403 static void pf_fixup_exponent( char *buf )
405 char* tmp = buf;
407 while (tmp[0] && NTDLL_tolower(tmp[0]) != 'e')
408 tmp++;
410 if (tmp[0] && (tmp[1] == '+' || tmp[1] == '-') &&
411 isdigit(tmp[2]) && isdigit(tmp[3]))
413 char final;
415 if (isdigit(tmp[4]))
416 return; /* Exponent already 3 digits */
418 /* We have a 2 digit exponent. Prepend '0' to make it 3 */
419 tmp += 2;
420 final = tmp[2];
421 tmp[2] = tmp[1];
422 tmp[1] = tmp[0];
423 tmp[0] = '0';
424 if (final == '\0')
426 /* We didn't expand into trailing space, so this string isn't left
427 * justified. Terminate the string and strip a ' ' at the start of
428 * the string if there is one (as there may be if the string is
429 * right justified).
431 tmp[3] = '\0';
432 if (buf[0] == ' ')
433 memmove(buf, buf + 1, (tmp - buf) + 3);
435 /* Otherwise, we expanded into trailing space -> nothing to do */
439 static inline BOOL isDigit(WCHAR c)
441 return c >= '0' && c <= '9';
444 /*********************************************************************
445 * pf_vsnprintf (INTERNAL)
447 * implements both A and W vsnprintf functions
449 static int pf_vsnprintf( pf_output *out, const WCHAR *format, __ms_va_list valist )
451 int r;
452 LPCWSTR q, p = format;
453 pf_flags flags;
455 TRACE("format is %s\n",debugstr_w(format));
456 while (*p)
458 q = strchrW( p, '%' );
460 /* there are no % characters left: output the rest of the string */
461 if( !q )
463 r = pf_output_stringW(out, p, -1);
464 if( r<0 )
465 return r;
466 p += r;
467 continue;
470 /* there are characters before the %: output them */
471 if( q != p )
473 r = pf_output_stringW(out, p, q - p);
474 if( r<0 )
475 return r;
476 p = q;
479 /* we must be at a % now, skip over it */
480 assert( *p == '%' );
481 p++;
483 /* output a single % character */
484 if( *p == '%' )
486 r = pf_output_stringW(out, p++, 1);
487 if( r<0 )
488 return r;
489 continue;
492 /* parse the flags */
493 memset( &flags, 0, sizeof flags );
494 while (*p)
496 if( *p == '+' || *p == ' ' )
498 if ( flags.Sign != '+' )
499 flags.Sign = *p;
501 else if( *p == '-' )
502 flags.LeftAlign = *p;
503 else if( *p == '0' )
504 flags.PadZero = *p;
505 else if( *p == '#' )
506 flags.Alternate = *p;
507 else
508 break;
509 p++;
512 /* deal with the field width specifier */
513 flags.FieldLength = 0;
514 if( *p == '*' )
516 flags.FieldLength = va_arg( valist, int );
517 if (flags.FieldLength < 0)
519 flags.LeftAlign = '-';
520 flags.FieldLength = -flags.FieldLength;
522 p++;
524 else while( isDigit(*p) )
526 flags.FieldLength *= 10;
527 flags.FieldLength += *p++ - '0';
530 /* deal with precision */
531 flags.Precision = -1;
532 if( *p == '.' )
534 flags.Precision = 0;
535 p++;
536 if( *p == '*' )
538 flags.Precision = va_arg( valist, int );
539 p++;
541 else while( isDigit(*p) )
543 flags.Precision *= 10;
544 flags.Precision += *p++ - '0';
548 /* deal with integer width modifier */
549 while( *p )
551 if( *p == 'h' || *p == 'l' || *p == 'L' )
553 flags.IntegerLength = *p;
554 p++;
556 else if( *p == 'I' )
558 if( *(p+1) == '6' && *(p+2) == '4' )
560 flags.IntegerDouble++;
561 p += 3;
563 else if( *(p+1) == '3' && *(p+2) == '2' )
564 p += 3;
565 else if( isDigit(*(p+1)) || *(p+1) == 0 )
566 break;
567 else
568 p++;
570 else if( *p == 'w' )
571 flags.WideString = *p++;
572 else if( *p == 'F' )
573 p++; /* ignore */
574 else
575 break;
578 flags.Format = *p;
579 r = 0;
581 if (flags.Format == '$')
583 FIXME("Positional parameters are not supported (%s)\n", wine_dbgstr_w(format));
584 return -1;
586 /* output a string */
587 if( flags.Format == 's' || flags.Format == 'S' )
588 r = pf_handle_string_format( out, va_arg(valist, const void*), -1,
589 &flags, (flags.Format == 'S') );
591 /* output a single character */
592 else if( flags.Format == 'c' || flags.Format == 'C' )
594 INT ch = va_arg( valist, int );
596 r = pf_handle_string_format( out, &ch, 1, &flags, (flags.Format == 'C') );
599 /* output a pointer */
600 else if( flags.Format == 'p' )
602 char pointer[32];
603 void *ptr = va_arg( valist, void * );
605 flags.PadZero = 0;
606 if( flags.Alternate )
607 sprintf(pointer, "0X%0*lX", 2 * (int)sizeof(ptr), (ULONG_PTR)ptr);
608 else
609 sprintf(pointer, "%0*lX", 2 * (int)sizeof(ptr), (ULONG_PTR)ptr);
610 r = pf_output_format_A( out, pointer, -1, &flags );
613 /* deal with %n */
614 else if( flags.Format == 'n' )
616 int *x = va_arg(valist, int *);
617 *x = out->used;
620 /* deal with 64-bit integers */
621 else if( pf_is_integer_format( flags.Format ) && flags.IntegerDouble )
623 char number[40], *x = number;
625 /* Estimate largest possible required buffer size:
626 * Chooses the larger of the field or precision
627 * Includes extra bytes: 1 byte for null, 1 byte for sign,
628 4 bytes for exponent, 2 bytes for alternate formats, 1 byte
629 for a decimal, and 1 byte for an additional float digit. */
630 int x_len = ((flags.FieldLength > flags.Precision) ?
631 flags.FieldLength : flags.Precision) + 10;
633 if( x_len >= sizeof number)
634 if (!(x = RtlAllocateHeap( GetProcessHeap(), 0, x_len )))
635 return -1;
637 pf_integer_conv( x, x_len, &flags, va_arg(valist, LONGLONG) );
639 r = pf_output_format_A( out, x, -1, &flags );
640 if( x != number )
641 RtlFreeHeap( GetProcessHeap(), 0, x );
644 /* deal with integers and floats using libc's printf */
645 else if( pf_is_valid_format( flags.Format ) )
647 char fmt[20], number[40], *x = number;
649 /* Estimate largest possible required buffer size:
650 * Chooses the larger of the field or precision
651 * Includes extra bytes: 1 byte for null, 1 byte for sign,
652 4 bytes for exponent, 2 bytes for alternate formats, 1 byte
653 for a decimal, and 1 byte for an additional float digit. */
654 int x_len = ((flags.FieldLength > flags.Precision) ?
655 flags.FieldLength : flags.Precision) + 10;
657 if( x_len >= sizeof number)
658 if (!(x = RtlAllocateHeap( GetProcessHeap(), 0, x_len )))
659 return -1;
661 pf_rebuild_format_string( fmt, &flags );
663 if( pf_is_double_format( flags.Format ) )
665 sprintf( x, fmt, va_arg(valist, double) );
666 if (NTDLL_tolower(flags.Format) == 'e' || NTDLL_tolower(flags.Format) == 'g')
667 pf_fixup_exponent( x );
669 else
670 sprintf( x, fmt, va_arg(valist, int) );
672 r = pf_output_stringA( out, x, -1 );
673 if( x != number )
674 RtlFreeHeap( GetProcessHeap(), 0, x );
676 else
677 continue;
679 if( r<0 )
680 return r;
681 p++;
684 /* check we reached the end, and null terminate the string */
685 assert( *p == 0 );
686 return out->used;
690 /*********************************************************************
691 * _vsnprintf (NTDLL.@)
693 int CDECL NTDLL__vsnprintf( char *str, SIZE_T len, const char *format, __ms_va_list args )
695 DWORD sz;
696 LPWSTR formatW = NULL;
697 pf_output out;
698 int r;
700 out.unicode = FALSE;
701 out.buf.A = str;
702 out.used = 0;
703 out.len = len;
705 if (format)
707 RtlMultiByteToUnicodeSize( &sz, format, strlen(format) + 1 );
708 if (!(formatW = RtlAllocateHeap( GetProcessHeap(), 0, sz ))) return -1;
709 RtlMultiByteToUnicodeN( formatW, sz, NULL, format, strlen(format) + 1 );
711 r = pf_vsnprintf( &out, formatW, args );
712 RtlFreeHeap( GetProcessHeap(), 0, formatW );
713 if (out.used < len) str[out.used] = 0;
714 return r;
718 /***********************************************************************
719 * _vsnwprintf (NTDLL.@)
721 int CDECL NTDLL__vsnwprintf( WCHAR *str, SIZE_T len, const WCHAR *format, __ms_va_list args )
723 pf_output out;
724 int r;
726 out.unicode = TRUE;
727 out.buf.W = str;
728 out.used = 0;
729 out.len = len;
731 r = pf_vsnprintf( &out, format, args );
732 if (out.used < len) str[out.used] = 0;
733 return r;
737 /*********************************************************************
738 * _snprintf (NTDLL.@)
740 int WINAPIV NTDLL__snprintf( char *str, SIZE_T len, const char *format, ... )
742 int ret;
743 __ms_va_list valist;
745 __ms_va_start( valist, format );
746 ret = NTDLL__vsnprintf( str, len, format, valist );
747 __ms_va_end( valist );
748 return ret;
752 /***********************************************************************
753 * _snwprintf (NTDLL.@)
755 int WINAPIV NTDLL__snwprintf( WCHAR *str, SIZE_T len, const WCHAR *format, ... )
757 int ret;
758 __ms_va_list valist;
760 __ms_va_start(valist, format);
761 ret = NTDLL__vsnwprintf( str, len, format, valist );
762 __ms_va_end(valist);
763 return ret;
767 /*********************************************************************
768 * _vsnprintf_s (NTDLL.@)
770 int CDECL _vsnprintf_s( char *str, SIZE_T size, SIZE_T len, const char *format, __ms_va_list args )
772 DWORD sz;
773 LPWSTR formatW = NULL;
774 pf_output out;
775 int r;
777 out.unicode = FALSE;
778 out.buf.A = str;
779 out.used = 0;
780 out.len = min( size, len );
782 if (format)
784 RtlMultiByteToUnicodeSize( &sz, format, strlen(format) + 1 );
785 if (!(formatW = RtlAllocateHeap( GetProcessHeap(), 0, sz ))) return -1;
786 RtlMultiByteToUnicodeN( formatW, sz, NULL, format, strlen(format) + 1 );
788 r = pf_vsnprintf( &out, formatW, args );
789 RtlFreeHeap( GetProcessHeap(), 0, formatW );
790 if (out.used < size) str[out.used] = 0;
791 else str[0] = 0;
792 if (r == size) r = -1;
793 return r;
797 /*********************************************************************
798 * _snprintf_s (NTDLL.@)
800 int WINAPIV _snprintf_s( char *str, SIZE_T size, SIZE_T len, const char *format, ... )
802 int ret;
803 __ms_va_list valist;
805 __ms_va_start( valist, format );
806 ret = _vsnprintf_s( str, size, len, format, valist );
807 __ms_va_end( valist );
808 return ret;
812 /*********************************************************************
813 * vsprintf (NTDLL.@)
815 int CDECL NTDLL_vsprintf( char *str, const char *format, __ms_va_list args )
817 return NTDLL__vsnprintf( str, size_max, format, args );
821 /*********************************************************************
822 * sprintf (NTDLL.@)
824 int WINAPIV NTDLL_sprintf( char *str, const char *format, ... )
826 int ret;
827 __ms_va_list valist;
829 __ms_va_start( valist, format );
830 ret = NTDLL__vsnprintf( str, size_max, format, valist );
831 __ms_va_end( valist );
832 return ret;
836 /***********************************************************************
837 * swprintf (NTDLL.@)
839 int WINAPIV NTDLL_swprintf( WCHAR *str, const WCHAR *format, ... )
841 int ret;
842 __ms_va_list valist;
844 __ms_va_start(valist, format);
845 ret = NTDLL__vsnwprintf( str, size_max, format, valist );
846 __ms_va_end(valist);
847 return ret;