-#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.
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 */
FILE * stream;/* for to-stream output */
};
+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, ... );
+int _PDCLIB_sprintf( char * buffer, const char * format, va_list ap );
+
+/* The following only for testing. */
+#include <limits.h>
+#include <string.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" );
+ 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 );
+ return 0;
+}
+
/* 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
preface[ preidx++ ] = ' ';
}
}
- if ( ! ( ( status->flags & E_minus ) || ( status->flags & E_zero ) ) )
+ if ( ! ( status->flags & ( E_minus | E_zero ) ) )
{
while ( ( status->this + preidx ) < status->width )
{
}
else
{
- DELIVER( toupper( _PDCLIB_digits[ digit ] ) );
+ DELIVER( _PDCLIB_Xdigits[ digit ] );
}
}
}
-static void padwrap( intmax_t value, struct status_t * status )
+const char * parse_out( const char * spec, struct status_t * status, va_list ap )
{
- 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 )
- {
- DELIVER( ' ' );
- ++(status->this);
- }
- }
- 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 )
- {
- 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( toupper( _PDCLIB_digits[ digit ] ) );
- }
- if ( status->flags & E_minus )
+ const char * orig_spec = spec;
+ if ( *(++spec) == '%' )
{
- while ( status->this < status->width )
- {
- DELIVER( ' ' );
- ++(status->this);
- }
+ DELIVER( *spec );
+ return spec;
}
- 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 )
-{
- /* TODO: "%%" handled correctly? */
-
/* Initializing status structure */
status->flags = 0;
status->base = 0;
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;
}
}
}
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? */
break;
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( ap, int );
+ break;
+ case E_short:
+ value = (uintmax_t)(unsigned short)va_arg( ap, int );
+ break;
+ case 0:
+ value = (uintmax_t)va_arg( ap, unsigned int );
+ break;
+ case E_long:
+ value = (uintmax_t)va_arg( ap, unsigned long );
+ break;
+ case E_llong:
+ value = (uintmax_t)va_arg( ap, unsigned long long );
+ break;
+ case E_size:
+ value = (uintmax_t)va_arg( 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( ap, int ), status );
+ break;
+ case E_short:
+ int2base( (intmax_t)(short)va_arg( ap, int ), status );
+ break;
+ case 0:
+ int2base( (intmax_t)va_arg( ap, int ), status );
+ break;
+ case E_long:
+ int2base( (intmax_t)va_arg( ap, long ), status );
+ break;
+ case E_llong:
+ int2base( (intmax_t)va_arg( ap, long long ), status );
+ break;
+ case E_ptrdiff:
+ int2base( (intmax_t)va_arg( ap, ptrdiff_t ), status );
+ break;
+ case E_intmax:
+ int2base( va_arg( 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( char * buffer, size_t n, const char * expect, struct status_t * status, ... )
{
- va_list ap;
- va_start( ap, status );
- parse_out( spec, status, ap );
- va_end( ap );
+ int myrc;
+ int rc; // y
+ va_list ap; // y
+ va_start( ap, status ); // y
+ memset( status->s, '\0', 50 ); // n
+ myrc = _PDCLIB_sprintf( status->s, expect, ap );
+ rc = vsnprintf( buffer, n, expect, ap ); // n
+ if ( ( strcmp( status->s, buffer ) != 0 ) || ( myrc != rc ) )
+ {
+ printf( "Output '%s', RC %d\nExpect '%s', RC %d\n\n", buffer, myrc, buffer, rc );
+ }
}
-#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 ); \
+int _PDCLIB_sprintf( char * buffer, const char * format, va_list ap )
+{
+ struct status_t status = { 0, 0, SIZE_MAX, 0, 0, buffer, 0, 0, NULL };
+ while ( *format != '\0' )
+ {
+ const char * rc;
+ if ( ( *format != '%' ) || ( ( rc = parse_out( format, &status, ap ) ) == format ) )
+ {
+ /* No conversion specifier, print verbatim */
+ buffer[ status.i++ ] = *format;
+ }
+ else
+ {
+ /* Continue parsing after conversion specifier */
+ format = rc;
+ }
}
+ return status.i;
+}
-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 );
- 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" );
- return 0;
+ struct status_t status = { 0, 0, SIZE_MAX, 0, 0, /* stream->buffer */ buffer, 0, 0, stream };
+ 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