forked from Mirrors/wine-wine
ntdll: Merge new features from msvcrt's printf implementation.
Signed-off-by: Alexandre Julliard <julliard@winehq.org>feature/deterministic
parent
040227e5b3
commit
b3a0a39356
|
@ -55,7 +55,7 @@ typedef struct pf_flags_t
|
||||||
{
|
{
|
||||||
char Sign, LeftAlign, Alternate, PadZero;
|
char Sign, LeftAlign, Alternate, PadZero;
|
||||||
int FieldLength, Precision;
|
int FieldLength, Precision;
|
||||||
char IntegerLength, IntegerDouble;
|
char IntegerLength, IntegerDouble, IntegerNative;
|
||||||
char WideString;
|
char WideString;
|
||||||
char Format;
|
char Format;
|
||||||
} pf_flags;
|
} pf_flags;
|
||||||
|
@ -211,8 +211,15 @@ static inline int pf_output_format_W( pf_output *out, LPCWSTR str,
|
||||||
{
|
{
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
if( len < 0 )
|
if (len < 0)
|
||||||
len = wcslen( str );
|
{
|
||||||
|
/* Do not search past the length specified by the precision. */
|
||||||
|
if (flags->Precision >= 0)
|
||||||
|
{
|
||||||
|
for (len = 0; len < flags->Precision; len++) if (!str[len]) break;
|
||||||
|
}
|
||||||
|
else len = wcslen( str );
|
||||||
|
}
|
||||||
|
|
||||||
if (flags->Precision >= 0 && flags->Precision < len)
|
if (flags->Precision >= 0 && flags->Precision < len)
|
||||||
len = flags->Precision;
|
len = flags->Precision;
|
||||||
|
@ -233,8 +240,15 @@ static inline int pf_output_format_A( pf_output *out, LPCSTR str,
|
||||||
{
|
{
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
if( len < 0 )
|
if (len < 0)
|
||||||
len = strlen( str );
|
{
|
||||||
|
/* Do not search past the length specified by the precision. */
|
||||||
|
if (flags->Precision >= 0)
|
||||||
|
{
|
||||||
|
for (len = 0; len < flags->Precision; len++) if (!str[len]) break;
|
||||||
|
}
|
||||||
|
else len = strlen( str );
|
||||||
|
}
|
||||||
|
|
||||||
if (flags->Precision >= 0 && flags->Precision < len)
|
if (flags->Precision >= 0 && flags->Precision < len)
|
||||||
len = flags->Precision;
|
len = flags->Precision;
|
||||||
|
@ -270,80 +284,20 @@ static int pf_handle_string_format( pf_output *out, const void* str, int len,
|
||||||
return pf_output_format_W( out, str, len, flags);
|
return pf_output_format_W( out, str, len, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline BOOL pf_is_integer_format( char fmt )
|
|
||||||
{
|
|
||||||
static const char float_fmts[] = "diouxX";
|
|
||||||
if (!fmt)
|
|
||||||
return FALSE;
|
|
||||||
return strchr( float_fmts, fmt ) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline BOOL pf_is_double_format( char fmt )
|
|
||||||
{
|
|
||||||
static const char float_fmts[] = "aeEfgG";
|
|
||||||
if (!fmt)
|
|
||||||
return FALSE;
|
|
||||||
return strchr( float_fmts, fmt ) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline BOOL pf_is_valid_format( char fmt )
|
|
||||||
{
|
|
||||||
static const char float_fmts[] = "acCdeEfgGinouxX";
|
|
||||||
if (!fmt)
|
|
||||||
return FALSE;
|
|
||||||
return strchr( float_fmts, fmt ) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pf_rebuild_format_string( char *p, pf_flags *flags )
|
|
||||||
{
|
|
||||||
*p++ = '%';
|
|
||||||
if( flags->Sign )
|
|
||||||
*p++ = flags->Sign;
|
|
||||||
if( flags->LeftAlign )
|
|
||||||
*p++ = flags->LeftAlign;
|
|
||||||
if( flags->Alternate )
|
|
||||||
*p++ = flags->Alternate;
|
|
||||||
if( flags->PadZero )
|
|
||||||
*p++ = flags->PadZero;
|
|
||||||
if( flags->FieldLength )
|
|
||||||
{
|
|
||||||
sprintf(p, "%d", flags->FieldLength);
|
|
||||||
p += strlen(p);
|
|
||||||
}
|
|
||||||
if( flags->Precision >= 0 )
|
|
||||||
{
|
|
||||||
sprintf(p, ".%d", flags->Precision);
|
|
||||||
p += strlen(p);
|
|
||||||
}
|
|
||||||
*p++ = flags->Format;
|
|
||||||
*p++ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pf_integer_conv: prints x to buf, including alternate formats and
|
/* pf_integer_conv: prints x to buf, including alternate formats and
|
||||||
additional precision digits, but not field characters or the sign */
|
additional precision digits, but not field characters or the sign */
|
||||||
static void pf_integer_conv( char *buf, int buf_len, pf_flags *flags,
|
static void pf_integer_conv( char *buf, pf_flags *flags, LONGLONG x )
|
||||||
LONGLONG x )
|
|
||||||
{
|
{
|
||||||
unsigned int base;
|
unsigned int base;
|
||||||
const char *digits;
|
const char *digits;
|
||||||
|
|
||||||
int i, j, k;
|
int i, j, k;
|
||||||
char number[40], *tmp = number;
|
|
||||||
|
|
||||||
if( buf_len > sizeof number )
|
|
||||||
{
|
|
||||||
if (!(tmp = RtlAllocateHeap( GetProcessHeap(), 0, buf_len )))
|
|
||||||
{
|
|
||||||
buf[0] = '\0';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
base = 10;
|
|
||||||
if( flags->Format == 'o' )
|
if( flags->Format == 'o' )
|
||||||
base = 8;
|
base = 8;
|
||||||
else if( flags->Format == 'x' || flags->Format == 'X' )
|
else if( flags->Format == 'x' || flags->Format == 'X' )
|
||||||
base = 16;
|
base = 16;
|
||||||
|
else
|
||||||
|
base = 10;
|
||||||
|
|
||||||
if( flags->Format == 'X' )
|
if( flags->Format == 'X' )
|
||||||
digits = "0123456789ABCDEFX";
|
digits = "0123456789ABCDEFX";
|
||||||
|
@ -356,82 +310,44 @@ static void pf_integer_conv( char *buf, int buf_len, pf_flags *flags,
|
||||||
flags->Sign = '-';
|
flags->Sign = '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do conversion (backwards) */
|
|
||||||
i = 0;
|
i = 0;
|
||||||
if( x == 0 && flags->Precision )
|
if( x == 0 )
|
||||||
tmp[i++] = '0';
|
{
|
||||||
|
flags->Alternate = 0;
|
||||||
|
if( flags->Precision )
|
||||||
|
buf[i++] = '0';
|
||||||
|
}
|
||||||
else
|
else
|
||||||
while( x != 0 )
|
while( x != 0 )
|
||||||
{
|
{
|
||||||
j = (ULONGLONG) x % base;
|
j = (ULONGLONG) x % base;
|
||||||
x = (ULONGLONG) x / base;
|
x = (ULONGLONG) x / base;
|
||||||
tmp[i++] = digits[j];
|
buf[i++] = digits[j];
|
||||||
}
|
}
|
||||||
k = flags->Precision - i;
|
k = flags->Precision - i;
|
||||||
while( k-- > 0 )
|
while( k-- > 0 )
|
||||||
tmp[i++] = '0';
|
buf[i++] = '0';
|
||||||
if( flags->Alternate )
|
if( flags->Alternate )
|
||||||
{
|
{
|
||||||
if( base == 16 )
|
if( base == 16 )
|
||||||
{
|
{
|
||||||
tmp[i++] = digits[16];
|
buf[i++] = digits[16];
|
||||||
tmp[i++] = '0';
|
buf[i++] = '0';
|
||||||
}
|
}
|
||||||
else if( base == 8 && tmp[i-1] != '0' )
|
else if( base == 8 && buf[i-1] != '0' )
|
||||||
tmp[i++] = '0';
|
buf[i++] = '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reverse for buf */
|
|
||||||
j = 0;
|
|
||||||
while( i-- > 0 )
|
|
||||||
buf[j++] = tmp[i];
|
|
||||||
buf[j] = '\0';
|
|
||||||
|
|
||||||
/* Adjust precision so pf_fill won't truncate the number later */
|
/* Adjust precision so pf_fill won't truncate the number later */
|
||||||
flags->Precision = strlen( buf );
|
flags->Precision = i;
|
||||||
|
|
||||||
if( tmp != number )
|
buf[i] = '\0';
|
||||||
RtlFreeHeap( GetProcessHeap(), 0, tmp );
|
j = 0;
|
||||||
|
while(--i > j) {
|
||||||
return;
|
char tmp = buf[j];
|
||||||
}
|
buf[j] = buf[i];
|
||||||
|
buf[i] = tmp;
|
||||||
/* pf_fixup_exponent: convert a string containing a 2 digit exponent
|
j++;
|
||||||
to 3 digits, accounting for padding, in place. Needed to match
|
|
||||||
the native printf's which always use 3 digits. */
|
|
||||||
static void pf_fixup_exponent( char *buf )
|
|
||||||
{
|
|
||||||
char* tmp = buf;
|
|
||||||
|
|
||||||
while (tmp[0] && NTDLL_tolower(tmp[0]) != 'e')
|
|
||||||
tmp++;
|
|
||||||
|
|
||||||
if (tmp[0] && (tmp[1] == '+' || tmp[1] == '-') &&
|
|
||||||
isdigit(tmp[2]) && isdigit(tmp[3]))
|
|
||||||
{
|
|
||||||
char final;
|
|
||||||
|
|
||||||
if (isdigit(tmp[4]))
|
|
||||||
return; /* Exponent already 3 digits */
|
|
||||||
|
|
||||||
/* We have a 2 digit exponent. Prepend '0' to make it 3 */
|
|
||||||
tmp += 2;
|
|
||||||
final = tmp[2];
|
|
||||||
tmp[2] = tmp[1];
|
|
||||||
tmp[1] = tmp[0];
|
|
||||||
tmp[0] = '0';
|
|
||||||
if (final == '\0')
|
|
||||||
{
|
|
||||||
/* We didn't expand into trailing space, so this string isn't left
|
|
||||||
* justified. Terminate the string and strip a ' ' at the start of
|
|
||||||
* the string if there is one (as there may be if the string is
|
|
||||||
* right justified).
|
|
||||||
*/
|
|
||||||
tmp[3] = '\0';
|
|
||||||
if (buf[0] == ' ')
|
|
||||||
memmove(buf, buf + 1, (tmp - buf) + 3);
|
|
||||||
}
|
|
||||||
/* Otherwise, we expanded into trailing space -> nothing to do */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,7 +462,12 @@ static int pf_vsnprintf( pf_output *out, const WCHAR *format, __ms_va_list valis
|
||||||
/* deal with integer width modifier */
|
/* deal with integer width modifier */
|
||||||
while( *p )
|
while( *p )
|
||||||
{
|
{
|
||||||
if( *p == 'h' || *p == 'l' || *p == 'L' )
|
if (*p == 'l' && *(p+1) == 'l')
|
||||||
|
{
|
||||||
|
flags.IntegerDouble++;
|
||||||
|
p += 2;
|
||||||
|
}
|
||||||
|
else if( *p == 'h' || *p == 'l' || *p == 'L' )
|
||||||
{
|
{
|
||||||
flags.IntegerLength = *p;
|
flags.IntegerLength = *p;
|
||||||
p++;
|
p++;
|
||||||
|
@ -573,6 +494,13 @@ static int pf_vsnprintf( pf_output *out, const WCHAR *format, __ms_va_list valis
|
||||||
}
|
}
|
||||||
else if( *p == 'w' )
|
else if( *p == 'w' )
|
||||||
flags.WideString = *p++;
|
flags.WideString = *p++;
|
||||||
|
else if ((*p == 'z' || *p == 't') && p[1] && strchr("diouxX", p[1]))
|
||||||
|
flags.IntegerNative = *p++;
|
||||||
|
else if (*p == 'j')
|
||||||
|
{
|
||||||
|
flags.IntegerDouble++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
else if( *p == 'F' )
|
else if( *p == 'F' )
|
||||||
p++; /* ignore */
|
p++; /* ignore */
|
||||||
else
|
else
|
||||||
|
@ -605,12 +533,13 @@ static int pf_vsnprintf( pf_output *out, const WCHAR *format, __ms_va_list valis
|
||||||
{
|
{
|
||||||
char pointer[32];
|
char pointer[32];
|
||||||
void *ptr = va_arg( valist, void * );
|
void *ptr = va_arg( valist, void * );
|
||||||
|
int prec = flags.Precision;
|
||||||
|
flags.Format = 'X';
|
||||||
|
flags.PadZero = '0';
|
||||||
|
flags.Precision = 2*sizeof(void*);
|
||||||
|
pf_integer_conv( pointer, &flags, (ULONG_PTR)ptr );
|
||||||
flags.PadZero = 0;
|
flags.PadZero = 0;
|
||||||
if( flags.Alternate )
|
flags.Precision = prec;
|
||||||
sprintf(pointer, "0X%0*lX", 2 * (int)sizeof(ptr), (ULONG_PTR)ptr);
|
|
||||||
else
|
|
||||||
sprintf(pointer, "%0*lX", 2 * (int)sizeof(ptr), (ULONG_PTR)ptr);
|
|
||||||
r = pf_output_format_A( out, pointer, -1, &flags );
|
r = pf_output_format_A( out, pointer, -1, &flags );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,63 +549,33 @@ static int pf_vsnprintf( pf_output *out, const WCHAR *format, __ms_va_list valis
|
||||||
int *x = va_arg(valist, int *);
|
int *x = va_arg(valist, int *);
|
||||||
*x = out->used;
|
*x = out->used;
|
||||||
}
|
}
|
||||||
|
else if( flags.Format && strchr("diouxX", flags.Format ))
|
||||||
/* deal with 64-bit integers */
|
|
||||||
else if( pf_is_integer_format( flags.Format ) && flags.IntegerDouble )
|
|
||||||
{
|
{
|
||||||
char number[40], *x = number;
|
char number[40], *x = number;
|
||||||
|
int max_len;
|
||||||
|
|
||||||
/* Estimate largest possible required buffer size:
|
/* 0 padding is added after '0x' if Alternate flag is in use */
|
||||||
* Chooses the larger of the field or precision
|
if((flags.Format=='x' || flags.Format=='X') && flags.PadZero && flags.Alternate
|
||||||
* Includes extra bytes: 1 byte for null, 1 byte for sign,
|
&& !flags.LeftAlign && flags.Precision<flags.FieldLength-2)
|
||||||
4 bytes for exponent, 2 bytes for alternate formats, 1 byte
|
flags.Precision = flags.FieldLength - 2;
|
||||||
for a decimal, and 1 byte for an additional float digit. */
|
|
||||||
int x_len = ((flags.FieldLength > flags.Precision) ?
|
|
||||||
flags.FieldLength : flags.Precision) + 10;
|
|
||||||
|
|
||||||
if( x_len >= sizeof number)
|
max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
|
||||||
if (!(x = RtlAllocateHeap( GetProcessHeap(), 0, x_len )))
|
if(max_len > ARRAY_SIZE(number))
|
||||||
return -1;
|
if (!(x = RtlAllocateHeap( GetProcessHeap(), 0, max_len ))) return -1;
|
||||||
|
|
||||||
pf_integer_conv( x, x_len, &flags, va_arg(valist, LONGLONG) );
|
if(flags.IntegerDouble || (flags.IntegerNative && sizeof(void*) == 8))
|
||||||
|
pf_integer_conv( x, &flags, va_arg(valist, LONGLONG) );
|
||||||
|
else if(flags.Format=='d' || flags.Format=='i')
|
||||||
|
pf_integer_conv( x, &flags, flags.IntegerLength!='h' ?
|
||||||
|
va_arg(valist, int) : (short)va_arg(valist, int) );
|
||||||
|
else
|
||||||
|
pf_integer_conv( x, &flags, flags.IntegerLength!='h' ?
|
||||||
|
(unsigned int)va_arg(valist, int) : (unsigned short)va_arg(valist, int) );
|
||||||
|
|
||||||
r = pf_output_format_A( out, x, -1, &flags );
|
r = pf_output_format_A( out, x, -1, &flags );
|
||||||
if( x != number )
|
if( x != number )
|
||||||
RtlFreeHeap( GetProcessHeap(), 0, x );
|
RtlFreeHeap( GetProcessHeap(), 0, x );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* deal with integers and floats using libc's printf */
|
|
||||||
else if( pf_is_valid_format( flags.Format ) )
|
|
||||||
{
|
|
||||||
char fmt[20], number[40], *x = number;
|
|
||||||
|
|
||||||
/* Estimate largest possible required buffer size:
|
|
||||||
* Chooses the larger of the field or precision
|
|
||||||
* Includes extra bytes: 1 byte for null, 1 byte for sign,
|
|
||||||
4 bytes for exponent, 2 bytes for alternate formats, 1 byte
|
|
||||||
for a decimal, and 1 byte for an additional float digit. */
|
|
||||||
int x_len = ((flags.FieldLength > flags.Precision) ?
|
|
||||||
flags.FieldLength : flags.Precision) + 10;
|
|
||||||
|
|
||||||
if( x_len >= sizeof number)
|
|
||||||
if (!(x = RtlAllocateHeap( GetProcessHeap(), 0, x_len )))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
pf_rebuild_format_string( fmt, &flags );
|
|
||||||
|
|
||||||
if( pf_is_double_format( flags.Format ) )
|
|
||||||
{
|
|
||||||
sprintf( x, fmt, va_arg(valist, double) );
|
|
||||||
if (NTDLL_tolower(flags.Format) == 'e' || NTDLL_tolower(flags.Format) == 'g')
|
|
||||||
pf_fixup_exponent( x );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
sprintf( x, fmt, va_arg(valist, int) );
|
|
||||||
|
|
||||||
r = pf_output_stringA( out, x, -1 );
|
|
||||||
if( x != number )
|
|
||||||
RtlFreeHeap( GetProcessHeap(), 0, x );
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue