-#include <stdio.h>
-#include <stdlib.h>
#include <stdarg.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <ctype.h>
-#include <assert.h>
-#include <string.h>
-#include <limits.h>
#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+/* These can be removed once integrated into PDCLIB make procedure */
#undef TEST
#include </home/solar/src/pdclib/functions/_PDCLIB/digits.c>
+#include </home/solar/src/pdclib/functions/_PDCLIB/Xdigits.c>
/* Using an integer's bits as flags for both the conversion flags and length
modifiers.
#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 */
char * s; /* target buffer */
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 */
};
-/* x - the character to be delivered
+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>
+#include <string.h>
+
+int main( void )
+{
+ 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;
+}
+
+/* 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 ) status->s[status->i] = x; ++(status->i); } while ( 0 )
+#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++ ] = ' ';
}
}
- 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' )
{
}
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);
}
}
- }
- {
- int digit = value % status->base;
- if ( digit < 0 )
- {
- digit *= -1;
- }
- if ( status->flags & E_lower )
- {
- DELIVER( _PDCLIB_digits[ digit ] );
- }
- else
- {
- DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
- }
- }
-}
-
-static void padwrap( intmax_t value, struct status_t * status )
-{
- if ( status->flags & E_char )
- {
- value = (char)value;
- }
- else if ( status->flags & E_short )
- {
- value = (short)value;
- }
- else if ( status->flags & E_long )
- {
- value = (long)value;
- }
- else if ( status->flags & E_llong )
- {
- value = (long long)value;
- }
- else if ( status->flags & E_ptrdiff )
- {
- value = (ptrdiff_t)value;
- }
- else if ( ! ( status->flags & E_intmax ) )
- {
- value = (int)value;
- }
- int2base( value, status );
- if ( status->flags & E_minus )
- {
- while ( status->this < status->width )
+ /* Do the precision padding if necessary. */
+ for ( int i = 0; i < prec_pads; ++i )
{
- DELIVER( ' ' );
- ++(status->this);
+ DELIVER( '0' );
+ }
}
}
- if ( status->i >= status->n )
- {
- status->s[status->n - 1] = '\0';
- }
-}
-
-static void upadwrap( uintmax_t value, struct status_t * status )
-{
- if ( status->flags & E_char )
- {
- value = (unsigned char)value;
- }
- else if ( status->flags & E_short )
- {
- value = (unsigned short)value;
- }
- else if ( status->flags & E_long )
- {
- value = (unsigned long)value;
- }
- else if ( status->flags & E_llong )
- {
- value = (unsigned long long)value;
- }
- else if ( status->flags & E_size )
- {
- value = (size_t)value;
- }
- else
- {
- value = (unsigned int)value;
- }
- status->flags |= E_unsigned;
- ++(status->this);
- if ( ( value / status->base ) != 0 )
+ /* Recursion tail - print the current digit. */
{
- int2base( (intmax_t)(value / status->base), status );
- }
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
{
- DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
- }
- if ( status->flags & E_minus )
- {
- while ( status->this < status->width )
- {
- DELIVER( ' ' );
- ++(status->this);
- }
+ /* Uppercase letters. Array only used here, only 0-F. */
+ DELIVER( _PDCLIB_Xdigits[ digit ] );
}
- if ( status->i >= status->n )
- {
- status->s[status->n - 1] = '\0';
}
}
-void parse_out( const char * spec, struct status_t * status, va_list ap );
-
-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
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
{
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
switch ( *spec )
{
case 'd':
+ /* FALLTHROUGH */
case 'i':
status->base = 10;
break;
case 'o':
status->base = 8;
+ status->flags |= E_unsigned;
break;
case 'u':
status->base = 10;
status->flags |= ( E_lower | E_unsigned );
break;
case 'X':
- status->flags = E_unsigned;
status->base = 16;
+ status->flags |= E_unsigned;
break;
case 'f':
case 'F':
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 */
- switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_unsigned ) )
+ /* TODO: Check for invalid flag combinations. */
+ if ( status->flags & E_unsigned )
{
- case E_char:
- padwrap( (intmax_t)(char)va_arg( ap, int ), status );
- break;
- case E_char | E_unsigned:
- upadwrap( (uintmax_t)(unsigned char)va_arg( ap, int ), status );
- break;
- case E_short:
- padwrap( (intmax_t)(short)va_arg( ap, int ), status );
- break;
- case E_short | E_unsigned:
- upadwrap( (uintmax_t)(unsigned short)va_arg( ap, int ), status );
- break;
- case 0:
- padwrap( (intmax_t)va_arg( ap, int ), status );
- break;
- case E_unsigned:
- upadwrap( (uintmax_t)va_arg( ap, unsigned int ), status );
- break;
- case E_long:
- padwrap( (intmax_t)va_arg( ap, long ), status );
- break;
- case E_long | E_unsigned:
- upadwrap( (uintmax_t)va_arg( ap, unsigned long ), status );
- break;
- case E_llong:
- padwrap( (intmax_t)va_arg( ap, long long ), status );
- break;
- case E_llong | E_unsigned:
- upadwrap( (uintmax_t)va_arg( ap, unsigned long long ), status );
- break;
+ uintmax_t value;
+ switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) )
+ {
+ case E_char:
+ value = (uintmax_t)(unsigned char)va_arg( status->ap, int );
+ break;
+ case E_short:
+ value = (uintmax_t)(unsigned short)va_arg( status->ap, int );
+ break;
+ case 0:
+ value = (uintmax_t)va_arg( status->ap, unsigned int );
+ break;
+ case E_long:
+ value = (uintmax_t)va_arg( status->ap, unsigned long );
+ break;
+ case E_llong:
+ 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);
+ if ( ( value / status->base ) != 0 )
+ {
+ int2base( (intmax_t)(value / status->base), status );
+ }
+ int digit = value % status->base;
+ if ( digit < 0 )
+ {
+ digit *= -1;
+ }
+ if ( status->flags & E_lower )
+ {
+ DELIVER( _PDCLIB_digits[ digit ] );
+ }
+ else
+ {
+ DELIVER( _PDCLIB_Xdigits[ digit ] );
+ }
+ }
+ else
+ {
+ switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) )
+ {
+ case E_char:
+ int2base( (intmax_t)(char)va_arg( status->ap, int ), status );
+ break;
+ case E_short:
+ int2base( (intmax_t)(short)va_arg( status->ap, int ), status );
+ break;
+ case 0:
+ int2base( (intmax_t)va_arg( status->ap, int ), status );
+ break;
+ case E_long:
+ int2base( (intmax_t)va_arg( status->ap, long ), status );
+ break;
+ case E_llong:
+ 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;
+ }
+ }
+ if ( status->flags & E_minus )
+ {
+ while ( status->this < status->width )
+ {
+ DELIVER( ' ' );
+ ++(status->this);
+ }
+ }
+ if ( status->i >= status->n )
+ {
+ status->s[status->n - 1] = '\0';
}
}
+ return ++spec;
}
-void parse_out_wrapper( const char * spec, struct status_t * status, ... );
-
-void parse_out_wrapper( const char * spec, 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 );
- parse_out( spec, status, ap );
- va_end( 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 );
}
-#define TESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
- status.flags = _flags; \
- status.n = _n; \
- status.i = 0; \
- status.width = _width; \
- status.prec = _prec; \
- status.base = _base; \
- status.this = 0; \
- memset( status.s, '\0', 50 ); \
- padwrap( (intmax_t)_value, &status ); \
- 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 ); \
+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;
+}
-#define UTESTCASE( _flags, _n, _width, _prec, _value, _base, _expect ) \
- status.flags = _flags; \
- status.n = _n; \
- status.i = 0; \
- status.width = _width; \
- status.prec = _prec; \
- status.base = _base; \
- status.this = 0; \
- memset( status.s, '\0', 50 ); \
- upadwrap( (uintmax_t)_value, &status ); \
- 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 ); \
- }
+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 );
+}
-int main( void )
+#if 0
+int _PDCLIB_fprintf( FILE * stream, const char * format, va_list ap )
{
- struct status_t status;
- int rc;
- char * buffer = malloc( 50 );
- status.s = calloc( 50, 1 );
- status.i = 0;
- status.n = SIZE_MAX;
- puts( "- parse_out() -\n" );
- parse_out_wrapper( "d", &status, 1234 );
- puts( status.s );
- puts( "- Signed min / max -\n" );
- TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MIN, 10, "%hhd" );
- TESTCASE( E_char, SIZE_MAX, 0, 0, CHAR_MAX, 10, "%hhd" );
- TESTCASE( E_char, SIZE_MAX, 0, 0, 0, 10, "%hhd" );
- TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MIN, 10, "%hd" );
- TESTCASE( E_short, SIZE_MAX, 0, 0, SHRT_MAX, 10, "%hd" );
- TESTCASE( E_short, SIZE_MAX, 0, 0, 0, 10, "%hd" );
- TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MIN, 10, "%d" );
- TESTCASE( E_done, SIZE_MAX, 0, 0, INT_MAX, 10, "%d" );
- TESTCASE( E_done, SIZE_MAX, 0, 0, 0, 10, "%d" );
- TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MIN, 10, "%ld" );
- TESTCASE( E_long, SIZE_MAX, 0, 0, LONG_MAX, 10, "%ld" );
- TESTCASE( E_long, SIZE_MAX, 0, 0, 0l, 10, "%ld" );
- TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MIN, 10, "%lld" );
- TESTCASE( E_llong, SIZE_MAX, 0, 0, LLONG_MAX, 10, "%lld" );
- TESTCASE( E_llong, SIZE_MAX, 0, 0, 0ll, 10, "%lld" );
- puts( "- Unsigned min / max -\n" );
- UTESTCASE( E_char, SIZE_MAX, 0, 0, UCHAR_MAX, 10, "%hhu" );
- UTESTCASE( E_char, SIZE_MAX, 0, 0, (unsigned char)-1, 10, "%hhu" );
- UTESTCASE( E_short, SIZE_MAX, 0, 0, USHRT_MAX, 10, "%hu" );
- UTESTCASE( E_short, SIZE_MAX, 0, 0, (unsigned short)-1, 10, "%hu" );
- UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 10, "%u" );
- UTESTCASE( E_done, SIZE_MAX, 0, 0, -1u, 10, "%u" );
- UTESTCASE( E_long, SIZE_MAX, 0, 0, ULONG_MAX, 10, "%lu" );
- UTESTCASE( E_long, SIZE_MAX, 0, 0, -1ul, 10, "%lu" );
- UTESTCASE( E_llong, SIZE_MAX, 0, 0, ULLONG_MAX, 10, "%llu" );
- UTESTCASE( E_llong, SIZE_MAX, 0, 0, -1ull, 10, "%llu" );
- puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
- UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 16, "%X" );
- UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 16, "%#X" );
- UTESTCASE( E_done | E_lower, SIZE_MAX, 0, 0, UINT_MAX, 16, "%x" );
- UTESTCASE( E_alt | E_lower, SIZE_MAX, 0, 0, -1u, 16, "%#x" );
- UTESTCASE( E_done, SIZE_MAX, 0, 0, UINT_MAX, 8, "%o" );
- UTESTCASE( E_alt, SIZE_MAX, 0, 0, -1u, 8, "%#o" );
- puts( "- Plus flag -\n" );
- TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MIN, 10, "%+d" );
- TESTCASE( E_plus, SIZE_MAX, 0, 0, INT_MAX, 10, "%+d" );
- TESTCASE( E_plus, SIZE_MAX, 0, 0, 0, 10, "%+d" );
- UTESTCASE( E_plus, SIZE_MAX, 0, 0, UINT_MAX, 10, "%+u" );
- UTESTCASE( E_plus, SIZE_MAX, 0, 0, -1u, 10, "%+u" );
- puts( "- Space flag -\n" );
- TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MIN, 10, "% d" );
- TESTCASE( E_space, SIZE_MAX, 0, 0, INT_MAX, 10, "% d" );
- TESTCASE( E_space, SIZE_MAX, 0, 0, 0, 10, "% d" );
- UTESTCASE( E_space, SIZE_MAX, 0, 0, UINT_MAX, 10, "% u" );
- UTESTCASE( E_space, SIZE_MAX, 0, 0, -1u, 10, "% u" );
- puts( "- Field width -\n" );
- TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MIN, 10, "%9d" );
- TESTCASE( E_done, SIZE_MAX, 9, 0, INT_MAX, 10, "%9d" );
- TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MIN, 10, "%10d" );
- TESTCASE( E_done, SIZE_MAX, 10, 0, INT_MAX, 10, "%10d" );
- TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MIN, 10, "%11d" );
- TESTCASE( E_done, SIZE_MAX, 11, 0, INT_MAX, 10, "%11d" );
- TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MIN, 10, "%12d" );
- TESTCASE( E_done, SIZE_MAX, 12, 0, INT_MAX, 10, "%12d" );
- puts( "- Field width (left bound) -\n" );
- TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MIN, 10, "%-9d" );
- TESTCASE( E_minus, SIZE_MAX, 9, 0, INT_MAX, 10, "%-9d" );
- TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MIN, 10, "%-10d" );
- TESTCASE( E_minus, SIZE_MAX, 10, 0, INT_MAX, 10, "%-10d" );
- TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MIN, 10, "%-11d" );
- TESTCASE( E_minus, SIZE_MAX, 11, 0, INT_MAX, 10, "%-11d" );
- TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MIN, 10, "%-12d" );
- TESTCASE( E_minus, SIZE_MAX, 12, 0, INT_MAX, 10, "%-12d" );
- puts( "- Field width, zero padding -\n");
- TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%09d" );
- TESTCASE( E_done | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%09d" );
- TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%010d" );
- TESTCASE( E_done | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%010d" );
- TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%011d" );
- TESTCASE( E_done | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%011d" );
- TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%012d" );
- TESTCASE( E_done | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%012d" );
- puts( "- Field width, zero padding (left bound) -\n" );
- TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MIN, 10, "%-09d" );
- TESTCASE( E_minus | E_zero, SIZE_MAX, 9, 0, INT_MAX, 10, "%-09d" );
- TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MIN, 10, "%-010d" );
- TESTCASE( E_minus | E_zero, SIZE_MAX, 10, 0, INT_MAX, 10, "%-010d" );
- TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MIN, 10, "%-011d" );
- TESTCASE( E_minus | E_zero, SIZE_MAX, 11, 0, INT_MAX, 10, "%-011d" );
- TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MIN, 10, "%-012d" );
- TESTCASE( E_minus | E_zero, SIZE_MAX, 12, 0, INT_MAX, 10, "%-012d" );
- return 0;
+ 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