X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=draft.c;h=570f305353b9352e07b51be7265c5ccb4e981ef7;hb=41d961304327a91980f408494bb99c5f60278291;hp=12a510cfed8216fa9004d49a24ef2e8ff7bf6003;hpb=c65ff2fc516667ee270ba7efd6eb3bfd1b379eb1;p=pdclib diff --git a/draft.c b/draft.c index 12a510c..570f305 100644 --- a/draft.c +++ b/draft.c @@ -1,16 +1,13 @@ -#include -#include #include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +/* These can be removed once integrated into PDCLIB make procedure */ #undef TEST #include +#include /* Using an integer's bits as flags for both the conversion flags and length modifiers. @@ -28,14 +25,15 @@ #define E_intmax 1<<10 #define E_size 1<<11 #define E_ptrdiff 1<<12 -#define E_double 1<<13 -#define E_lower 1<<14 -#define E_unsigned 1<<15 +#define E_intptr 1<<13 +#define E_double 1<<14 +#define E_lower 1<<15 +#define E_unsigned 1<<16 struct status_t { int base; /* base to which the value shall be converted */ - int_fast16_t flags; /* flags and length modifiers */ + int_fast32_t flags; /* flags and length modifiers */ size_t n; /* maximum number of characters to be written */ size_t i; /* number of characters already written */ size_t this; /* number of output chars in the current conversion */ @@ -43,140 +41,195 @@ struct status_t size_t width; /* width of current field */ size_t prec; /* precision of current field */ FILE * stream;/* for to-stream output */ + va_list ap; /* the argument stack passed to the printf function */ }; -void parse_out( const char * spec, struct status_t * status, va_list ap ); -void parse_out_wrapper( const char * spec, struct status_t * status, ... ); +const char * parse_out( const char * spec, struct status_t * status ); +inline void test( size_t n, const char * expect, ... ); +int _PDCLIB_vsnprintf( char * buffer, size_t n, const char * format, va_list ap ); +int _PDCLIB_snprintf( char * s, size_t n, const char * format, ... ); -#define TESTCASE( _n, _value, _expect ) \ - status.n = _n; \ - status.i = 0; \ - memset( status.s, '\0', 50 ); \ - spec = _expect; \ - ++spec; \ - parse_out_wrapper( spec, &status, _value ); \ - rc = snprintf( buffer, _n, _expect, _value ); \ - if ( ( strcmp( status.s, buffer ) != 0 ) || ( status.i != rc ) ) \ -{ \ - printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", status.s, status.i, buffer, rc ); \ -} +/* The following only for testing. */ +#include +#include int main( void ) { - struct status_t status; - int rc; - char * buffer = malloc( 50 ); - const char * spec; - status.s = calloc( 50, 1 ); - status.i = 0; - status.stream = NULL; - status.n = SIZE_MAX; - puts( "- Signed min / max -\n" ); - TESTCASE( SIZE_MAX, CHAR_MIN, "%hhd" ); - TESTCASE( SIZE_MAX, CHAR_MAX, "%hhd" ); - TESTCASE( SIZE_MAX, 0, "%hhd" ); - TESTCASE( SIZE_MAX, SHRT_MIN, "%hd" ); - TESTCASE( SIZE_MAX, SHRT_MAX, "%hd" ); - TESTCASE( SIZE_MAX, 0, "%hd" ); - TESTCASE( SIZE_MAX, INT_MIN, "%d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%d" ); - TESTCASE( SIZE_MAX, 0, "%d" ); - TESTCASE( SIZE_MAX, LONG_MIN, "%ld" ); - TESTCASE( SIZE_MAX, LONG_MAX, "%ld" ); - TESTCASE( SIZE_MAX, 0l, "%ld" ); - TESTCASE( SIZE_MAX, LLONG_MIN, "%lld" ); - TESTCASE( SIZE_MAX, LLONG_MAX, "%lld" ); - TESTCASE( SIZE_MAX, 0ll, "%lld" ); - puts( "- Unsigned min / max -\n" ); - TESTCASE( SIZE_MAX, UCHAR_MAX, "%hhu" ); - TESTCASE( SIZE_MAX, (unsigned char)-1, "%hhu" ); - TESTCASE( SIZE_MAX, USHRT_MAX, "%hu" ); - TESTCASE( SIZE_MAX, (unsigned short)-1, "%hu" ); - TESTCASE( SIZE_MAX, UINT_MAX, "%u" ); - TESTCASE( SIZE_MAX, -1u, "%u" ); - TESTCASE( SIZE_MAX, ULONG_MAX, "%lu" ); - TESTCASE( SIZE_MAX, -1ul, "%lu" ); - TESTCASE( SIZE_MAX, ULLONG_MAX, "%llu" ); - TESTCASE( SIZE_MAX, -1ull, "%llu" ); - puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" ); - TESTCASE( SIZE_MAX, UINT_MAX, "%X" ); - TESTCASE( SIZE_MAX, -1u, "%#X" ); - TESTCASE( SIZE_MAX, UINT_MAX, "%x" ); - TESTCASE( SIZE_MAX, -1u, "%#x" ); - TESTCASE( SIZE_MAX, UINT_MAX, "%o" ); - TESTCASE( SIZE_MAX, -1u, "%#o" ); - puts( "- Plus flag -\n" ); - TESTCASE( SIZE_MAX, INT_MIN, "%+d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%+d" ); - TESTCASE( SIZE_MAX, 0, "%+d" ); - TESTCASE( SIZE_MAX, UINT_MAX, "%+u" ); - TESTCASE( SIZE_MAX, -1u, "%+u" ); - puts( "- Space flag -\n" ); - TESTCASE( SIZE_MAX, INT_MIN, "% d" ); - TESTCASE( SIZE_MAX, INT_MAX, "% d" ); - TESTCASE( SIZE_MAX, 0, "% d" ); - TESTCASE( SIZE_MAX, UINT_MAX, "% u" ); - TESTCASE( SIZE_MAX, -1u, "% u" ); - puts( "- Field width -\n" ); - TESTCASE( SIZE_MAX, INT_MIN, "%9d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%9d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%10d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%10d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%11d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%11d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%12d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%12d" ); - puts( "- Field width (left bound) -\n" ); - TESTCASE( SIZE_MAX, INT_MIN, "%-9d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%-9d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%-10d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%-10d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%-11d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%-11d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%-12d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%-12d" ); - puts( "- Field width, zero padding -\n"); - TESTCASE( SIZE_MAX, INT_MIN, "%09d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%09d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%010d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%010d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%011d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%011d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%012d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%012d" ); - puts( "- Field width, zero padding (left bound) -\n" ); - TESTCASE( SIZE_MAX, INT_MIN, "%-09d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%-09d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%-010d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%-010d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%-011d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%-011d" ); - TESTCASE( SIZE_MAX, INT_MIN, "%-012d" ); - TESTCASE( SIZE_MAX, INT_MAX, "%-012d" ); + test( SIZE_MAX, "%hhd", CHAR_MIN ); + test( SIZE_MAX, "%hhd", CHAR_MAX ); + test( SIZE_MAX, "%hhd", 0 ); + test( SIZE_MAX, "%hd", SHRT_MIN ); + test( SIZE_MAX, "%hd", SHRT_MAX ); + test( SIZE_MAX, "%hd", 0 ); + test( SIZE_MAX, "%d", INT_MIN ); + test( SIZE_MAX, "%d", INT_MAX ); + test( SIZE_MAX, "%d", 0 ); + test( SIZE_MAX, "%ld", LONG_MIN ); + test( SIZE_MAX, "%ld", LONG_MAX ); + test( SIZE_MAX, "%ld", 0l ); + test( SIZE_MAX, "%lld", LLONG_MIN ); + test( SIZE_MAX, "%lld", LLONG_MAX ); + test( SIZE_MAX, "%lld", 0ll ); + test( SIZE_MAX, "%hhu", UCHAR_MAX ); + test( SIZE_MAX, "%hhu", (unsigned char)-1 ); + test( SIZE_MAX, "%hu", USHRT_MAX ); + test( SIZE_MAX, "%hu", (unsigned short)-1 ); + test( SIZE_MAX, "%u", UINT_MAX ); + test( SIZE_MAX, "%u", -1u ); + test( SIZE_MAX, "%lu", ULONG_MAX ); + test( SIZE_MAX, "%lu", -1ul ); + test( SIZE_MAX, "%llu", ULLONG_MAX ); + test( SIZE_MAX, "%llu", -1ull ); + test( SIZE_MAX, "%X", UINT_MAX ); + test( SIZE_MAX, "%#X", -1u ); + test( SIZE_MAX, "%x", UINT_MAX ); + test( SIZE_MAX, "%#x", -1u ); + test( SIZE_MAX, "%o", UINT_MAX ); + test( SIZE_MAX, "%#o", -1u ); + test( SIZE_MAX, "%.0#o", 0 ); + test( SIZE_MAX, "%+d", INT_MIN ); + test( SIZE_MAX, "%+d", INT_MAX ); + test( SIZE_MAX, "%+d", 0 ); + test( SIZE_MAX, "%+u", UINT_MAX ); + test( SIZE_MAX, "%+u", -1u ); + test( SIZE_MAX, "% d", INT_MIN ); + test( SIZE_MAX, "% d", INT_MAX ); + test( SIZE_MAX, "% d", 0 ); + test( SIZE_MAX, "% u", UINT_MAX ); + test( SIZE_MAX, "% u", -1u ); + test( SIZE_MAX, "%9d", INT_MIN ); + test( SIZE_MAX, "%9d", INT_MAX ); + test( SIZE_MAX, "%10d", INT_MIN ); + test( SIZE_MAX, "%10d", INT_MAX ); + test( SIZE_MAX, "%11d", INT_MIN ); + test( SIZE_MAX, "%11d", INT_MAX ); + test( SIZE_MAX, "%12d", INT_MIN ); + test( SIZE_MAX, "%12d", INT_MAX ); + test( SIZE_MAX, "%-9d", INT_MIN ); + test( SIZE_MAX, "%-9d", INT_MAX ); + test( SIZE_MAX, "%-10d", INT_MIN ); + test( SIZE_MAX, "%-10d", INT_MAX ); + test( SIZE_MAX, "%-11d", INT_MIN ); + test( SIZE_MAX, "%-11d", INT_MAX ); + test( SIZE_MAX, "%-12d", INT_MIN ); + test( SIZE_MAX, "%-12d", INT_MAX ); + test( SIZE_MAX, "%09d", INT_MIN ); + test( SIZE_MAX, "%09d", INT_MAX ); + test( SIZE_MAX, "%010d", INT_MIN ); + test( SIZE_MAX, "%010d", INT_MAX ); + test( SIZE_MAX, "%011d", INT_MIN ); + test( SIZE_MAX, "%011d", INT_MAX ); + test( SIZE_MAX, "%012d", INT_MIN ); + test( SIZE_MAX, "%012d", INT_MAX ); + test( SIZE_MAX, "%-09d", INT_MIN ); + test( SIZE_MAX, "%-09d", INT_MAX ); + test( SIZE_MAX, "%-010d", INT_MIN ); + test( SIZE_MAX, "%-010d", INT_MAX ); + test( SIZE_MAX, "%-011d", INT_MIN ); + test( SIZE_MAX, "%-011d", INT_MAX ); + test( SIZE_MAX, "%-012d", INT_MIN ); + test( SIZE_MAX, "%-012d", INT_MAX ); + test( 8, "%9d", INT_MAX ); + test( 8, "%9d", INT_MIN ); + test( 9, "%9d", INT_MAX ); + test( 9, "%9d", INT_MIN ); + test( 10, "%9d", INT_MAX ); + test( 10, "%9d", INT_MIN ); + test( 9, "%10d", INT_MAX ); + test( 9, "%10d", INT_MIN ); + test( 10, "%10d", INT_MAX ); + test( 10, "%10d", INT_MIN ); + test( 11, "%10d", INT_MAX ); + test( 11, "%10d", INT_MIN ); + test( 10, "%11d", INT_MAX ); + test( 10, "%11d", INT_MIN ); + test( 11, "%11d", INT_MAX ); + test( 11, "%11d", INT_MIN ); + test( 12, "%11d", INT_MAX ); + test( 12, "%11d", INT_MIN ); + test( 11, "%12d", INT_MAX ); + test( 11, "%12d", INT_MIN ); + test( 12, "%12d", INT_MAX ); + test( 12, "%12d", INT_MIN ); + test( 13, "%12d", INT_MAX ); + test( 13, "%12d", INT_MIN ); + test( SIZE_MAX, "%030.20d", INT_MAX ); + test( SIZE_MAX, "%.6x", UINT_MAX ); + test( SIZE_MAX, "%#6.3x", UINT_MAX ); + test( SIZE_MAX, "%#3.6x", UINT_MAX ); + test( SIZE_MAX, "%.6d", INT_MIN ); + test( SIZE_MAX, "%6.3d", INT_MIN ); + test( SIZE_MAX, "%3.6d", INT_MIN ); + test( SIZE_MAX, "%#0.6x", UINT_MAX ); + test( SIZE_MAX, "%#06.3x", UINT_MAX ); + test( SIZE_MAX, "%#03.6x", UINT_MAX ); + test( SIZE_MAX, "%#0.6d", INT_MAX ); + test( SIZE_MAX, "%#06.3d", INT_MAX ); + test( SIZE_MAX, "%#03.6d", INT_MAX ); + test( SIZE_MAX, "%#+.6d", INT_MAX ); + test( SIZE_MAX, "%#+6.3d", INT_MAX ); + test( SIZE_MAX, "%#+3.6d", INT_MAX ); + test( SIZE_MAX, "%+0.6d", INT_MAX ); + test( SIZE_MAX, "%+06.3d", INT_MAX ); + test( SIZE_MAX, "%+03.6d", INT_MAX ); + test( SIZE_MAX, "- %d", INT_MAX ); + test( SIZE_MAX, "- %d %% %d", INT_MAX, INT_MIN ); + test( SIZE_MAX, "%c", 'x' ); + test( SIZE_MAX, "%s", "abcdef" ); + test( SIZE_MAX, "%p", 0xdeadbeef ); + { + char buffer[50]; + int val1, val2, val3, val4; + snprintf( buffer, SIZE_MAX, "123456%n789%n", &val1, &val2 ); + _PDCLIB_snprintf( buffer, SIZE_MAX, "123456%n789%n", &val3, &val4 ); + if ( ( val1 != val3 ) || ( val2 != val4 ) ) + { + printf( "Output %d/%d\nExpect %d/%d\n\n", val1, val2, val3, val4 ); + } + } return 0; } -/* x - the character to be delivered +/* This macro delivers a given character to either a memory buffer or a stream, + depending on the contents of 'status' (struct status_t). + x - the character to be delivered i - pointer to number of characters already delivered in this call n - pointer to maximum number of characters to be delivered in this call s - the buffer into which the character shall be delivered - TODO: Overruns. */ #define DELIVER( x ) do { if ( status->i < status->n ) { if ( status->stream != NULL ) putc( x, status->stream ); else status->s[status->i] = x; } ++(status->i); } while ( 0 ) +/* This function recursively converts a given integer value to a given base + into a character string. Persistent information - like the number of digits + parsed so far - is recorded in a struct status_t, which allows to avoid + overwriting snprintf() limits, and enables the function to do the necessary + padding / prefixing of the character string eventually printed. +*/ static void int2base( intmax_t value, struct status_t * status ) { + /* Registering the character being printed at the end of the function here + already so it will be taken into account when the deepestmost recursion + does the prefix / padding stuff. + */ ++(status->this); if ( ( value / status->base ) != 0 ) { + /* More digits to be done - recurse deeper */ int2base( value / status->base, status ); } else { - char preface[3] = "\0\0"; + /* We reached the last digit, the deepest point of our recursion, and + only now know how long the number to be printed actually is. Now we + have to do the sign, prefix, width, and precision padding stuff + before printing the numbers while we resurface from the recursion. + */ + /* At worst, we need two prefix characters (hex prefix). */ + char preface[3] = "\0"; size_t preidx = 0; if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) ) { + /* Octal / hexadecimal prefix for "%#" conversions */ preface[ preidx++ ] = '0'; if ( status->base == 16 ) { @@ -185,10 +238,12 @@ static void int2base( intmax_t value, struct status_t * status ) } if ( value < 0 ) { + /* Negative sign for negative values - at all times. */ preface[ preidx++ ] = '-'; } else if ( ! ( status->flags & E_unsigned ) ) { + /* plus sign / extra space are only for unsigned conversions */ if ( status->flags & E_plus ) { preface[ preidx++ ] = '+'; @@ -198,14 +253,29 @@ static void int2base( intmax_t value, struct status_t * status ) preface[ preidx++ ] = ' '; } } - if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) ) { - while ( ( status->this + preidx ) < status->width ) + size_t prec_pads = ( status->prec > status->this ) ? ( status->prec - status->this ) : 0; + if ( ! ( status->flags & ( E_minus | E_zero ) ) ) + { + /* Space padding is only done if no zero padding or left alignment + is requested. Leave space for any prefixes determined above. + */ + /* The number of characters to be printed, plus prefixes if any. */ + /* This line contained probably the most stupid, time-wasting bug + I've ever perpetrated. Greetings to Samface, DevL, and all + sceners at Breakpoint 2006. + */ + size_t characters = preidx + ( ( status->this > status->prec ) ? status->this : status->prec ); + if ( status->width > characters ) { - DELIVER( ' ' ); - ++(status->this); + for ( int i = 0; i < status->width - characters; ++i ) + { + DELIVER( ' ' ); + ++(status->this); + } } } + /* Now we did the padding, do the prefixes (if any). */ preidx = 0; while ( preface[ preidx ] != '\0' ) { @@ -214,13 +284,23 @@ static void int2base( intmax_t value, struct status_t * status ) } if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) ) { + /* If field is not left aligned, and zero padding is requested, do + so. + */ while ( status->this < status->width ) { DELIVER( '0' ); ++(status->this); } } + /* Do the precision padding if necessary. */ + for ( int i = 0; i < prec_pads; ++i ) + { + DELIVER( '0' ); + } + } } + /* Recursion tail - print the current digit. */ { int digit = value % status->base; if ( digit < 0 ) @@ -229,25 +309,34 @@ static void int2base( intmax_t value, struct status_t * status ) } if ( status->flags & E_lower ) { + /* Lowercase letters. Same array used for strto...(). */ DELIVER( _PDCLIB_digits[ digit ] ); } else { - DELIVER( toupper( _PDCLIB_digits[ digit ] ) ); + /* Uppercase letters. Array only used here, only 0-F. */ + DELIVER( _PDCLIB_Xdigits[ digit ] ); } } } -void parse_out( const char * spec, struct status_t * status, va_list ap ) +/* This function is to be called with spec pointing to the leading '%' of a + printf() conversion specifier, with ap being +*/ +const char * parse_out( const char * spec, struct status_t * status ) { - /* TODO: "%%" handled correctly? */ - + const char * orig_spec = spec; + if ( *(++spec) == '%' ) + { + DELIVER( *spec ); + return ++spec; + } /* Initializing status structure */ status->flags = 0; - status->base = 0; - status->this = 0; + status->base = 0; + status->this = 0; status->width = 0; - status->prec = 0; + status->prec = 0; /* First come 0..n flags */ do @@ -284,7 +373,7 @@ void parse_out( const char * spec, struct status_t * status, va_list ap ) if ( *spec == '*' ) { /* Retrieve width value from argument stack */ - if ( ( status->width = va_arg( ap, int ) ) < 0 ) + if ( ( status->width = va_arg( status->ap, int ) ) < 0 ) { /* Negative value is '-' flag plus absolute value */ status->flags |= E_minus; @@ -311,7 +400,7 @@ void parse_out( const char * spec, struct status_t * status, va_list ap ) is as if no precision is given - as precision is initalized to EOF (negative), there is no need for testing for negative here. */ - status->prec = va_arg( ap, int ); + status->prec = va_arg( status->ap, int ); } else { @@ -319,9 +408,13 @@ void parse_out( const char * spec, struct status_t * status, va_list ap ) status->prec = (int)strtol( spec, &endptr, 10 ); if ( spec == endptr ) { - /* TODO: Decimal point but no number - bad conversion specifier. */ + /* Decimal point but no number - bad conversion specifier. */ + return orig_spec; } + spec = endptr; } + /* Having a precision cancels out any zero flag. */ + status->flags ^= E_zero; } /* Optional length modifier @@ -374,6 +467,7 @@ void parse_out( const char * spec, struct status_t * status, va_list ap ) switch ( *spec ) { case 'd': + /* FALLTHROUGH */ case 'i': status->base = 10; break; @@ -404,47 +498,62 @@ void parse_out( const char * spec, struct status_t * status, va_list ap ) case 'A': break; case 'c': - break; + /* TODO: Flags, wide chars. */ + DELIVER( va_arg( status->ap, int ) ); + return ++spec; case 's': - break; + /* TODO: Flags, wide chars. */ + { + char * s = va_arg( status->ap, char * ); + while ( *s != '\0' ) + { + DELIVER( *(s++) ); + } + return ++spec; + } case 'p': - /* uint2base( 16, (intptr_t)value, true ) */ - case 'n': - case '%': - // conversion specifier - /* TODO: May this be accompaigned by flags, width, precision, length modifier at all? */ + /* TODO: E_long -> E_intptr */ + status->base = 16; + status->flags |= ( E_lower | E_unsigned | E_alt | E_long ); break; + case 'n': + { + int * val = va_arg( status->ap, int * ); + *val = status->i; + return ++spec; + } default: - /* TODO: No conversion specifier. Bad conversion. */ - return; - } - switch ( status->flags ) - { - /* TODO */ + /* No conversion specifier. Bad conversion. */ + return orig_spec; } + + /* Do the actual output based on our findings */ if ( status->base != 0 ) { /* Integer conversions */ - /* TODO: Eliminate the padwrap as far as possible. */ + /* TODO: Check for invalid flag combinations. */ if ( status->flags & E_unsigned ) { uintmax_t value; - switch ( status->flags & ( E_char | E_short | E_long | E_llong ) ) + switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) ) { case E_char: - value = (uintmax_t)(unsigned char)va_arg( ap, int ); + value = (uintmax_t)(unsigned char)va_arg( status->ap, int ); break; case E_short: - value = (uintmax_t)(unsigned short)va_arg( ap, int ); + value = (uintmax_t)(unsigned short)va_arg( status->ap, int ); break; case 0: - value = (uintmax_t)va_arg( ap, unsigned int ); + value = (uintmax_t)va_arg( status->ap, unsigned int ); break; case E_long: - value = (uintmax_t)(unsigned long)va_arg( ap, unsigned long ); + value = (uintmax_t)va_arg( status->ap, unsigned long ); break; case E_llong: - value = (uintmax_t)(unsigned long long)va_arg( ap, unsigned long long ); + value = (uintmax_t)va_arg( status->ap, unsigned long long ); + break; + case E_size: + value = (uintmax_t)va_arg( status->ap, size_t ); break; } ++(status->this); @@ -463,27 +572,33 @@ void parse_out( const char * spec, struct status_t * status, va_list ap ) } else { - DELIVER( toupper( _PDCLIB_digits[ digit ] ) ); + DELIVER( _PDCLIB_Xdigits[ digit ] ); } } else { - switch ( status->flags & ( E_char | E_short | E_long | E_llong ) ) + switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) ) { case E_char: - int2base( (intmax_t)(char)va_arg( ap, int ), status ); + int2base( (intmax_t)(char)va_arg( status->ap, int ), status ); break; case E_short: - int2base( (intmax_t)(short)va_arg( ap, int ), status ); + int2base( (intmax_t)(short)va_arg( status->ap, int ), status ); break; case 0: - int2base( (intmax_t)va_arg( ap, int ), status ); + int2base( (intmax_t)va_arg( status->ap, int ), status ); break; case E_long: - int2base( (intmax_t)va_arg( ap, long ), status ); + int2base( (intmax_t)va_arg( status->ap, long ), status ); break; case E_llong: - int2base( (intmax_t)va_arg( ap, long long ), status ); + int2base( (intmax_t)va_arg( status->ap, long long ), status ); + break; + case E_ptrdiff: + int2base( (intmax_t)va_arg( status->ap, ptrdiff_t ), status ); + break; + case E_intmax: + int2base( va_arg( status->ap, intmax_t ), status ); break; } } @@ -500,12 +615,73 @@ void parse_out( const char * spec, struct status_t * status, va_list ap ) status->s[status->n - 1] = '\0'; } } + return ++spec; +} + +inline void test( size_t n, const char * expect, ... ) +{ + char * buffer1 = malloc( 50 ); + char * buffer2 = malloc( 50 ); + int myrc; + int rc; + va_list ap; + va_start( ap, expect ); + myrc = _PDCLIB_vsnprintf( buffer1, n, expect, ap ); + rc = vsnprintf( buffer2, n, expect, ap ); + if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) ) + { + printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer1, myrc, buffer2, rc ); + } + free( buffer1 ); + free( buffer2 ); +} + +int _PDCLIB_vsnprintf( char * buffer, size_t n, const char * format, va_list ap ) +{ + struct status_t status = { 0, 0, n, 0, 0, buffer, 0, 0, NULL, ap }; + while ( *format != '\0' ) + { + const char * rc; + if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status ) ) == format ) ) + { + /* No conversion specifier, print verbatim */ + buffer[ status.i++ ] = *(format++); + } + else + { + /* Continue parsing after conversion specifier */ + format = rc; + } + } + buffer[ status.i ] = '\0'; + return status.i; } -void parse_out_wrapper( const char * spec, struct status_t * status, ... ) +int _PDCLIB_snprintf( char * s, size_t n, const char * format, ... ) { va_list ap; - va_start( ap, status ); - parse_out( spec, status, ap ); - va_end( ap ); + va_start( ap, format ); + return _PDCLIB_vsnprintf( s, n, format, ap ); +} + +#if 0 +int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap ) +{ + struct status_t status = { 0, 0, SIZE_MAX, 0, 0, NULL, 0, 0, stream, ap }; + while ( *format != '\0' ) + { + const char * rc; + if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) ) + { + /* No conversion specifier, print verbatim */ + putc( *(format++), stream ); + } + else + { + /* Continue parsing after conversion specifier */ + format = rc; + } + } + return status.i; } +#endif