#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
{
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 */
};
-const char * parse_out( const char * spec, struct status_t * status, va_list ap );
-inline void test( char * buffer, size_t n, const char * expect, 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, ... );
/* The following only for testing. */
#include <limits.h>
int main( void )
{
- struct status_t status;
- char * buffer = malloc( 50 );
- status.s = calloc( 50, 1 );
- status.i = 0;
- status.stream = NULL;
- status.n = SIZE_MAX;
- puts( "- Signed min / max -\n" );
-// inline void test( char * buffer, size_t n, const char * expect, struct status_t * status, ... );
- test( buffer, SIZE_MAX, "%hhd", &status, CHAR_MIN );
- test( buffer, SIZE_MAX, "%hhd", &status, CHAR_MAX );
- test( buffer, SIZE_MAX, "%hhd", &status, 0 );
- test( buffer, SIZE_MAX, "%hd", &status, SHRT_MIN );
- test( buffer, SIZE_MAX, "%hd", &status, SHRT_MAX );
- test( buffer, SIZE_MAX, "%hd", &status, 0 );
- test( buffer, SIZE_MAX, "%d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%d", &status, 0 );
- test( buffer, SIZE_MAX, "%ld", &status, LONG_MIN );
- test( buffer, SIZE_MAX, "%ld", &status, LONG_MAX );
- test( buffer, SIZE_MAX, "%ld", &status, 0l );
- test( buffer, SIZE_MAX, "%lld", &status, LLONG_MIN );
- test( buffer, SIZE_MAX, "%lld", &status, LLONG_MAX );
- test( buffer, SIZE_MAX, "%lld", &status, 0ll );
- puts( "- Unsigned min / max -\n" );
- test( buffer, SIZE_MAX, "%hhu", &status, UCHAR_MAX );
- test( buffer, SIZE_MAX, "%hhu", &status, (unsigned char)-1 );
- test( buffer, SIZE_MAX, "%hu", &status, USHRT_MAX );
- test( buffer, SIZE_MAX, "%hu", &status, (unsigned short)-1 );
- test( buffer, SIZE_MAX, "%u", &status, UINT_MAX );
- test( buffer, SIZE_MAX, "%u", &status, -1u );
- test( buffer, SIZE_MAX, "%lu", &status, ULONG_MAX );
- test( buffer, SIZE_MAX, "%lu", &status, -1ul );
- test( buffer, SIZE_MAX, "%llu", &status, ULLONG_MAX );
- test( buffer, SIZE_MAX, "%llu", &status, -1ull );
- puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
- test( buffer, SIZE_MAX, "%X", &status, UINT_MAX );
- test( buffer, SIZE_MAX, "%#X", &status, -1u );
- test( buffer, SIZE_MAX, "%x", &status, UINT_MAX );
- test( buffer, SIZE_MAX, "%#x", &status, -1u );
- test( buffer, SIZE_MAX, "%o", &status, UINT_MAX );
- test( buffer, SIZE_MAX, "%#o", &status, -1u );
- puts( "- Plus flag -\n" );
- test( buffer, SIZE_MAX, "%+d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%+d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%+d", &status, 0 );
- test( buffer, SIZE_MAX, "%+u", &status, UINT_MAX );
- test( buffer, SIZE_MAX, "%+u", &status, -1u );
- puts( "- Space flag -\n" );
- test( buffer, SIZE_MAX, "% d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "% d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "% d", &status, 0 );
- test( buffer, SIZE_MAX, "% u", &status, UINT_MAX );
- test( buffer, SIZE_MAX, "% u", &status, -1u );
- puts( "- Field width -\n" );
- test( buffer, SIZE_MAX, "%9d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%9d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%10d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%10d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%11d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%11d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%12d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%12d", &status, INT_MAX );
- puts( "- Field width (left bound) -\n" );
- test( buffer, SIZE_MAX, "%-9d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%-9d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%-10d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%-10d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%-11d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%-11d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%-12d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%-12d", &status, INT_MAX );
- puts( "- Field width, zero padding -\n");
- test( buffer, SIZE_MAX, "%09d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%09d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%010d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%010d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%011d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%011d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%012d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%012d", &status, INT_MAX );
- puts( "- Field width, zero padding (left bound) -\n" );
- test( buffer, SIZE_MAX, "%-09d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%-09d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%-010d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%-010d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%-011d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%-011d", &status, INT_MAX );
- test( buffer, SIZE_MAX, "%-012d", &status, INT_MIN );
- test( buffer, SIZE_MAX, "%-012d", &status, INT_MAX );
+ 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 )
{
}
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++ ] = '+';
preface[ preidx++ ] = ' ';
}
}
+ {
+ size_t prec_pads = ( status->prec > status->this ) ? ( status->prec - status->this ) : 0;
if ( ! ( status->flags & ( E_minus | E_zero ) ) )
{
- while ( ( status->this + preidx ) < status->width )
+ /* 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' )
{
}
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 )
}
if ( status->flags & E_lower )
{
+ /* Lowercase letters. Same array used for strto...(). */
DELIVER( _PDCLIB_digits[ digit ] );
}
else
{
+ /* Uppercase letters. Array only used here, only 0-F. */
DELIVER( _PDCLIB_Xdigits[ digit ] );
}
}
}
-const char * 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 )
{
const char * orig_spec = spec;
-#if 0
if ( *(++spec) == '%' )
{
DELIVER( *spec );
- return spec;
+ return ++spec;
}
-#endif
/* 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
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;
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
{
/* 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
switch ( *spec )
{
case 'd':
+ /* FALLTHROUGH */
case 'i':
status->base = 10;
break;
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':
+ /* 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:
/* No conversion specifier. Bad conversion. */
return orig_spec;
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)va_arg( ap, unsigned long );
+ value = (uintmax_t)va_arg( status->ap, unsigned long );
break;
case E_llong:
- value = (uintmax_t)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( ap, size_t );
+ value = (uintmax_t)va_arg( status->ap, size_t );
break;
}
++(status->this);
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( ap, ptrdiff_t ), status );
+ int2base( (intmax_t)va_arg( status->ap, ptrdiff_t ), status );
break;
case E_intmax:
- int2base( va_arg( ap, intmax_t ), status );
+ int2base( va_arg( status->ap, intmax_t ), status );
break;
}
}
return ++spec;
}
-inline void test( char * buffer, size_t n, const char * expect, struct status_t * status, ... )
+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, status );
- status->n = n;
- status->i = 0;
- memset( status->s, '\0', 50 );
- parse_out( expect + 1, status, ap );
- rc = vsnprintf( buffer, n, expect, ap );
- if ( ( strcmp( status->s, buffer ) != 0 ) || ( status->i != rc ) )
+ 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", status->s, status->i, buffer, 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;
+}
+
+int _PDCLIB_snprintf( char * s, size_t n, const char * format, ... )
+{
+ va_list 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 )
{
- char * buffer = malloc( 50 );
- struct status_t status = { 0, 0, SIZE_MAX, 0, 0, /* stream->buffer */ buffer, 0, 0, stream };
+ struct status_t status = { 0, 0, SIZE_MAX, 0, 0, NULL, 0, 0, stream, ap };
while ( *format != '\0' )
{
const char * rc;