Fix bugs in vprintf_callback(3) and improve %c and %s support.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-01-07 18:13:57 +01:00
parent 021256ad8e
commit 6473d72621
1 changed files with 71 additions and 20 deletions

View File

@ -51,6 +51,14 @@ static size_t noop_callback(void*, const char*, size_t amount)
return amount; return amount;
} }
static
size_t callback_character(size_t (*callback)(void*, const char*, size_t),
void* user,
char c)
{
return callback(user, &c, 1);
}
extern "C" extern "C"
size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t), size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
void* user, void* user,
@ -74,6 +82,7 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
if ( callback(user, format, amount) != amount ) if ( callback(user, format, amount) != amount )
return SIZE_MAX; return SIZE_MAX;
format += amount; format += amount;
written += amount;
continue; continue;
} }
@ -102,17 +111,21 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
(void) group_thousands; (void) group_thousands;
(void) alternate_output_digits; (void) alternate_output_digits;
do switch ( *format++ ) while ( true )
{ {
case '#': alternate = true; continue; switch ( *format++ )
case '0': zero_pad = true; continue; {
case '-': field_width_is_negative = true; continue; case '#': alternate = true; continue;
case ' ': prepend_blank_if_positive = true; continue; case '0': zero_pad = true; continue;
case '+': prepend_plus_if_positive = true; continue; case '-': field_width_is_negative = true; continue;
case '\'': group_thousands = true; continue; case ' ': prepend_blank_if_positive = true; continue;
case 'I': alternate_output_digits = true; continue; case '+': prepend_plus_if_positive = true; continue;
default: format--; break; case '\'': group_thousands = true; continue;
} while ( false ); case 'I': alternate_output_digits = true; continue;
default: format--; break;
}
break;
}
int field_width = 0; int field_width = 0;
if ( *format == '*' && (format++, true) ) if ( *format == '*' && (format++, true) )
@ -205,6 +218,8 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
value = (uintmax_t) va_arg(parameters, void*); value = (uintmax_t) va_arg(parameters, void*);
conversion = 'x'; conversion = 'x';
alternate = !alternate; alternate = !alternate;
prepend_blank_if_positive = false;
prepend_plus_if_positive = false;
} }
else if ( conversion == 'i' || conversion == 'd' ) else if ( conversion == 'i' || conversion == 'd' )
{ {
@ -250,6 +265,8 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
value = (uintmax_t) va_arg(parameters, ptrdiff_t); value = (uintmax_t) va_arg(parameters, ptrdiff_t);
else else
goto incomprehensible_conversion; goto incomprehensible_conversion;
prepend_blank_if_positive = false;
prepend_plus_if_positive = false;
} }
const char* digits = conversion == 'X' ? "0123456789ABCDEF" : const char* digits = conversion == 'X' ? "0123456789ABCDEF" :
@ -258,25 +275,32 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
conversion == 'o' ? 8 : 10; conversion == 'o' ? 8 : 10;
char prefix[3]; char prefix[3];
size_t prefix_length = 0; size_t prefix_length = 0;
size_t prefix_digits_length = 0;
if ( negative_value ) if ( negative_value )
prefix[prefix_length++] = '-'; prefix[prefix_length++] = '-';
else if ( prepend_plus_if_positive ) else if ( prepend_plus_if_positive )
prefix[prefix_length++] = '+'; prefix[prefix_length++] = '+';
else if ( prepend_blank_if_positive ) else if ( prepend_blank_if_positive )
prefix[prefix_length++] = ' '; prefix[prefix_length++] = ' ';
if ( alternate && (conversion == 'x' || conversion == 'X') ) if ( alternate && (conversion == 'x' || conversion == 'X') && value != 0 )
prefix[prefix_length++] = '0', prefix[prefix_digits_length++, prefix_length++] = '0',
prefix[prefix_length++] = conversion; prefix[prefix_digits_length++, prefix_length++] = conversion;
if ( alternate && conversion == 'o' && value != 0 ) if ( alternate && conversion == 'o' && value != 0 )
prefix[prefix_length++] = '0'; prefix[prefix_digits_length++, prefix_length++] = '0';
char output[sizeof(uintmax_t) * 3]; char output[sizeof(uintmax_t) * 3];
size_t output_length = convert_integer(output, value, base, digits); size_t output_length = convert_integer(output, value, base, digits);
if ( !precision && output_length == 1 && output[0] == '0' )
{
output_length = 0;
output[0] = '\0';
}
size_t output_length_with_precision = size_t output_length_with_precision =
precision != SIZE_MAX && output_length < precision ? precision != SIZE_MAX && output_length < precision ?
precision : precision :
output_length; output_length;
size_t digits_length = prefix_digits_length + output_length;
size_t normal_length = prefix_length + output_length; size_t normal_length = prefix_length + output_length;
size_t length_with_precision = prefix_length + output_length_with_precision; size_t length_with_precision = prefix_length + output_length_with_precision;
@ -285,10 +309,9 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
bool use_left_pad = !use_zero_pad && 0 <= field_width; bool use_left_pad = !use_zero_pad && 0 <= field_width;
bool use_right_pad = !use_zero_pad && field_width < 0; bool use_right_pad = !use_zero_pad && field_width < 0;
char c;
if ( use_left_pad ) if ( use_left_pad )
for ( size_t i = length_with_precision; i < abs_field_width; i++ ) for ( size_t i = length_with_precision; i < abs_field_width; i++ )
if ( callback(user, &(c = ' '), 1) != 1 ) if ( callback_character(callback, user, ' ') != 1 )
return SIZE_MAX; return SIZE_MAX;
else else
written++; written++;
@ -297,13 +320,13 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
written += prefix_length; written += prefix_length;
if ( use_zero_pad ) if ( use_zero_pad )
for ( size_t i = normal_length; i < abs_field_width; i++ ) for ( size_t i = normal_length; i < abs_field_width; i++ )
if ( callback(user, &(c = '0'), 1) != 1 ) if ( callback_character(callback, user, '0') != 1 )
return SIZE_MAX; return SIZE_MAX;
else else
written++; written++;
if ( use_precision ) if ( use_precision )
for ( size_t i = normal_length; i < precision; i++ ) for ( size_t i = digits_length; i < precision; i++ )
if ( callback(user, &(c = '0'), 1) != 1 ) if ( callback_character(callback, user, '0') != 1 )
return SIZE_MAX; return SIZE_MAX;
else else
written++; written++;
@ -312,7 +335,7 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
written += output_length; written += output_length;
if ( use_right_pad ) if ( use_right_pad )
for ( size_t i = length_with_precision; i < abs_field_width; i++ ) for ( size_t i = length_with_precision; i < abs_field_width; i++ )
if ( callback(user, &(c = ' '), 1) != 1 ) if ( callback_character(callback, user, ' ') != 1 )
return SIZE_MAX; return SIZE_MAX;
else else
written++; written++;
@ -352,9 +375,23 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
else else
goto incomprehensible_conversion; goto incomprehensible_conversion;
if ( !field_width_is_negative && 1 < abs_field_width )
for ( size_t i = 1; i < abs_field_width; i++ )
if ( callback_character(callback, user, ' ') != 1 )
return SIZE_MAX;
else
written++;
if ( callback(user, &c, 1) != 1 ) if ( callback(user, &c, 1) != 1 )
return SIZE_MAX; return SIZE_MAX;
written++; written++;
if ( field_width_is_negative && 1 < abs_field_width )
for ( size_t i = 1; i < abs_field_width; i++ )
if ( callback_character(callback, user, ' ') != 1 )
return SIZE_MAX;
else
written++;
} }
else if ( *format == 's' && (format++, true) ) else if ( *format == 's' && (format++, true) )
{ {
@ -374,10 +411,24 @@ size_t vprintf_callback(size_t (*callback)(void*, const char*, size_t),
for ( size_t i = 0; i < precision && string[i]; i++ ) for ( size_t i = 0; i < precision && string[i]; i++ )
string_length++; string_length++;
if ( !field_width_is_negative && string_length < abs_field_width )
for ( size_t i = string_length; i < abs_field_width; i++ )
if ( callback_character(callback, user, ' ') != 1 )
return SIZE_MAX;
else
written++;
if ( callback(user, string, string_length) != string_length ) if ( callback(user, string, string_length) != string_length )
return SIZE_MAX; return SIZE_MAX;
written += string_length; written += string_length;
if ( field_width_is_negative && string_length < abs_field_width )
for ( size_t i = string_length; i < abs_field_width; i++ )
if ( callback_character(callback, user, ' ') != 1 )
return SIZE_MAX;
else
written++;
} }
else if ( *format == 'n' && (format++, true) ) else if ( *format == 'n' && (format++, true) )
{ {