#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
{
const char * parse_out( const char * spec, struct status_t * status );
inline void test( size_t n, const char * expect, ... );
-int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap );
+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 )
{
- int rc;
- puts( "- Signed min / max -\n" );
test( SIZE_MAX, "%hhd", CHAR_MIN );
test( SIZE_MAX, "%hhd", CHAR_MAX );
test( SIZE_MAX, "%hhd", 0 );
test( SIZE_MAX, "%lld", LLONG_MIN );
test( SIZE_MAX, "%lld", LLONG_MAX );
test( SIZE_MAX, "%lld", 0ll );
- puts( "- Unsigned min / max -\n" );
test( SIZE_MAX, "%hhu", UCHAR_MAX );
test( SIZE_MAX, "%hhu", (unsigned char)-1 );
test( SIZE_MAX, "%hu", USHRT_MAX );
test( SIZE_MAX, "%lu", -1ul );
test( SIZE_MAX, "%llu", ULLONG_MAX );
test( SIZE_MAX, "%llu", -1ull );
- puts( "- Hex and Octal, normal and alternative, upper and lowercase -\n" );
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 );
- puts( "- Plus flag -\n" );
+ 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 );
- puts( "- Space flag -\n" );
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 );
- puts( "- Field width -\n" );
test( SIZE_MAX, "%9d", INT_MIN );
test( SIZE_MAX, "%9d", INT_MAX );
test( SIZE_MAX, "%10d", INT_MIN );
test( SIZE_MAX, "%11d", INT_MAX );
test( SIZE_MAX, "%12d", INT_MIN );
test( SIZE_MAX, "%12d", INT_MAX );
- puts( "- Field width (left bound) -\n" );
test( SIZE_MAX, "%-9d", INT_MIN );
test( SIZE_MAX, "%-9d", INT_MAX );
test( SIZE_MAX, "%-10d", INT_MIN );
test( SIZE_MAX, "%-11d", INT_MAX );
test( SIZE_MAX, "%-12d", INT_MIN );
test( SIZE_MAX, "%-12d", INT_MAX );
- puts( "- Field width, zero padding -\n");
test( SIZE_MAX, "%09d", INT_MIN );
test( SIZE_MAX, "%09d", INT_MAX );
test( SIZE_MAX, "%010d", INT_MIN );
test( SIZE_MAX, "%011d", INT_MAX );
test( SIZE_MAX, "%012d", INT_MIN );
test( SIZE_MAX, "%012d", INT_MAX );
- puts( "- Field width, zero padding (left bound) -\n" );
test( SIZE_MAX, "%-09d", INT_MIN );
test( SIZE_MAX, "%-09d", INT_MAX );
test( SIZE_MAX, "%-010d", INT_MIN );
test( SIZE_MAX, "%-011d", INT_MAX );
test( SIZE_MAX, "%-012d", INT_MIN );
test( SIZE_MAX, "%-012d", INT_MAX );
- puts( "- Limited n -\n" );
test( 8, "%9d", INT_MAX );
test( 8, "%9d", INT_MIN );
test( 9, "%9d", INT_MAX );
test( 12, "%12d", INT_MIN );
test( 13, "%12d", INT_MAX );
test( 13, "%12d", INT_MIN );
- puts( "- Precision (tbd) -\n" );
+ 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 );
{
- const char * format = "%030.20d";
- printf( "glibc '" );
- rc = printf( format, INT_MAX );
- printf( "', RC %d\n", rc );
- test( SIZE_MAX, format, INT_MAX );
+ 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 );
+ }
}
- puts( "- vanilla -" );
- printf( "No width, no precision: %#x\n", 42 );
- printf( "Width, no precision: %#6x\n", 42 );
- printf( "No width, precision: %#.6x\n", 42 );
- printf( "Big width, small precision: %#6.3x\n", 42 );
- printf( "Small width, big precision: %#3.6x\n", 42 );
- printf( "No width, no precision: %#d\n", 42 );
- printf( "Width, no precision: %#6d\n", 42 );
- printf( "No width, precision: %#.6d\n", 42 );
- printf( "Big width, small precision: %#6.3d\n", 42 );
- printf( "Small width, big precision: %#3.6d\n", 42 );
- puts( "- zero flag -" );
- printf( "No width, no precision: %#0x\n", 42 );
- printf( "Width, no precision: %#06x\n", 42 );
- printf( "No width, precision: %#0.6x\n", 42 );
- printf( "Big width, small precision: %#06.3x\n", 42 );
- printf( "Small width, big precision: %#03.6x\n", 42 );
- printf( "No width, no precision: %#0d\n", 42 );
- printf( "Width, no precision: %#06d\n", 42 );
- printf( "No width, precision: %#0.6d\n", 42 );
- printf( "Big width, small precision: %#06.3d\n", 42 );
- printf( "Small width, big precision: %#03.6d\n", 42 );
- puts( "- plus flag -" );
- printf( "No width, no precision: %#+d\n", 42 );
- printf( "Width, no precision: %#+6d\n", 42 );
- printf( "No width, precision: %#+.6d\n", 42 );
- printf( "Big width, small precision: %#+6.3d\n", 42 );
- printf( "Small width, big precision: %#+3.6d\n", 42 );
- puts( "- plus and zero flag -" );
- printf( "No width, no precision: %#+0d\n", 42 );
- printf( "Width, no precision: %#+06d\n", 42 );
- printf( "No width, precision: %#+0.6d\n", 42 );
- printf( "Big width, small precision: %#+06.3d\n", 42 );
- printf( "Small width, big precision: %#+03.6d\n", 42 );
return 0;
}
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 )
if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) )
{
/* Octal / hexadecimal prefix for "%#" conversions */
- preface[ preidx++ ] = '0'; /* TODO: For octal, standard states "extend the precision" */
+ preface[ preidx++ ] = '0';
if ( status->base == 16 )
{
preface[ preidx++ ] = ( status->flags & E_lower ) ? 'x' : 'X';
preface[ preidx++ ] = ' ';
}
}
+ {
+ 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
for ( int i = 0; i < status->width - characters; ++i )
{
DELIVER( ' ' );
- ++(status->this); /* TODO: Probably have to do something so I still know how many zeroes are required, later. */
+ ++(status->this);
}
}
}
if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) )
{
/* If field is not left aligned, and zero padding is requested, do
- so. TODO: This should include precision handling (probably).
+ so.
*/
while ( status->this < status->width )
{
++(status->this);
}
}
+ /* Do the precision padding if necessary. */
+ for ( int i = 0; i < prec_pads; ++i )
+ {
+ DELIVER( '0' );
+ }
+ }
}
/* Recursion tail - print the current digit. */
{
if ( *(++spec) == '%' )
{
DELIVER( *spec );
- return spec;
+ return ++spec;
}
/* Initializing status structure */
status->flags = 0;
}
spec = endptr;
}
- status->flags &= ! E_zero;
+ /* 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;
int rc;
va_list ap;
va_start( ap, expect );
- myrc = _PDCLIB_sprintf( buffer1, n, expect, ap );
+ myrc = _PDCLIB_vsnprintf( buffer1, n, expect, ap );
rc = vsnprintf( buffer2, n, expect, ap );
if ( ( strcmp( buffer1, buffer2 ) != 0 ) || ( myrc != rc ) )
{
free( buffer2 );
}
-int _PDCLIB_sprintf( char * buffer, size_t n, const char * format, va_list ap )
+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' )
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 )
{