ntdll: Merge new features from msvcrt's printf implementation.
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
040227e5b3
commit
b3a0a39356
|
@ -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;
|
||||
|
@ -212,7 +212,14 @@ static inline int pf_output_format_W( pf_output *out, LPCWSTR str,
|
|||
int r = 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)
|
||||
len = flags->Precision;
|
||||
|
@ -234,7 +241,14 @@ static inline int pf_output_format_A( pf_output *out, LPCSTR str,
|
|||
int r = 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)
|
||||
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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue