ntdll: Merge new features from msvcrt's printf implementation.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
feature/deterministic
Alexandre Julliard 2020-05-13 09:36:53 +02:00
parent 040227e5b3
commit b3a0a39356
1 changed files with 78 additions and 179 deletions

View File

@ -55,7 +55,7 @@ typedef struct pf_flags_t
{
char Sign, LeftAlign, Alternate, PadZero;
int FieldLength, Precision;
char IntegerLength, IntegerDouble;
char IntegerLength, IntegerDouble, IntegerNative;
char WideString;
char Format;
} pf_flags;
@ -211,8 +211,15 @@ static inline int pf_output_format_W( pf_output *out, LPCWSTR str,
{
int r = 0;
if( len < 0 )
len = wcslen( str );
if (len < 0)
{
/* 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)
len = flags->Precision;
@ -233,8 +240,15 @@ static inline int pf_output_format_A( pf_output *out, LPCSTR str,
{
int r = 0;
if( len < 0 )
len = strlen( str );
if (len < 0)
{
/* 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)
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);
}
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
additional precision digits, but not field characters or the sign */
static void pf_integer_conv( char *buf, int buf_len, pf_flags *flags,
LONGLONG x )
static void pf_integer_conv( char *buf, pf_flags *flags, LONGLONG x )
{
unsigned int base;
const char *digits;
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' )
base = 8;
else if( flags->Format == 'x' || flags->Format == 'X' )
base = 16;
else
base = 10;
if( flags->Format == 'X' )
digits = "0123456789ABCDEFX";
@ -356,82 +310,44 @@ static void pf_integer_conv( char *buf, int buf_len, pf_flags *flags,
flags->Sign = '-';
}
/* Do conversion (backwards) */
i = 0;
if( x == 0 && flags->Precision )
tmp[i++] = '0';
if( x == 0 )
{
flags->Alternate = 0;
if( flags->Precision )
buf[i++] = '0';
}
else
while( x != 0 )
{
j = (ULONGLONG) x % base;
x = (ULONGLONG) x / base;
tmp[i++] = digits[j];
buf[i++] = digits[j];
}
k = flags->Precision - i;
while( k-- > 0 )
tmp[i++] = '0';
buf[i++] = '0';
if( flags->Alternate )
{
if( base == 16 )
{
tmp[i++] = digits[16];
tmp[i++] = '0';
buf[i++] = digits[16];
buf[i++] = '0';
}
else if( base == 8 && tmp[i-1] != '0' )
tmp[i++] = '0';
else if( base == 8 && buf[i-1] != '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 */
flags->Precision = strlen( buf );
flags->Precision = i;
if( tmp != number )
RtlFreeHeap( GetProcessHeap(), 0, tmp );
return;
}
/* pf_fixup_exponent: convert a string containing a 2 digit exponent
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 */
buf[i] = '\0';
j = 0;
while(--i > j) {
char tmp = buf[j];
buf[j] = buf[i];
buf[i] = tmp;
j++;
}
}
@ -546,7 +462,12 @@ static int pf_vsnprintf( pf_output *out, const WCHAR *format, __ms_va_list valis
/* deal with integer width modifier */
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;
p++;
@ -573,6 +494,13 @@ static int pf_vsnprintf( pf_output *out, const WCHAR *format, __ms_va_list valis
}
else if( *p == 'w' )
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' )
p++; /* ignore */
else
@ -605,12 +533,13 @@ static int pf_vsnprintf( pf_output *out, const WCHAR *format, __ms_va_list valis
{
char pointer[32];
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;
if( flags.Alternate )
sprintf(pointer, "0X%0*lX", 2 * (int)sizeof(ptr), (ULONG_PTR)ptr);
else
sprintf(pointer, "%0*lX", 2 * (int)sizeof(ptr), (ULONG_PTR)ptr);
flags.Precision = prec;
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 *);
*x = out->used;
}
/* deal with 64-bit integers */
else if( pf_is_integer_format( flags.Format ) && flags.IntegerDouble )
else if( flags.Format && strchr("diouxX", flags.Format ))
{
char number[40], *x = number;
int max_len;
/* 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;
/* 0 padding is added after '0x' if Alternate flag is in use */
if((flags.Format=='x' || flags.Format=='X') && flags.PadZero && flags.Alternate
&& !flags.LeftAlign && flags.Precision<flags.FieldLength-2)
flags.Precision = flags.FieldLength - 2;
if( x_len >= sizeof number)
if (!(x = RtlAllocateHeap( GetProcessHeap(), 0, x_len )))
return -1;
max_len = (flags.FieldLength>flags.Precision ? flags.FieldLength : flags.Precision) + 10;
if(max_len > ARRAY_SIZE(number))
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 );
if( x != number )
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
continue;