X-Git-Url: https://pd.if.org/git/?a=blobdiff_plain;f=functions%2F_PDCLIB%2Fprint.c;h=5dacfbf4595e8cfeda6df070fc476ec0a7405077;hb=c5c4350d2beb1ebe93f3f6d45643a64eed2a7c7a;hp=424228b29e837397cbb092aff6dd2a52d6334c33;hpb=0f88da0454edf3c1ca1a7f786c2fd9afdef0eb9c;p=pdclib diff --git a/functions/_PDCLIB/print.c b/functions/_PDCLIB/print.c index 424228b..5dacfbf 100644 --- a/functions/_PDCLIB/print.c +++ b/functions/_PDCLIB/print.c @@ -1,5 +1,3 @@ -/* $Id$ */ - /* _PDCLIB_print( const char *, struct _PDCLIB_status_t * ) This file is part of the Public Domain C Library (PDCLib). @@ -11,6 +9,9 @@ #include #include #include +#include + +#ifndef REGTEST /* Using an integer's bits as flags for both the conversion flags and length modifiers. @@ -18,23 +19,26 @@ /* FIXME: one too many flags to work on a 16-bit machine, join some (e.g. the width flags) into a combined field. */ -#define E_minus 1<<0 -#define E_plus 1<<1 -#define E_alt 1<<2 -#define E_space 1<<3 -#define E_zero 1<<4 -#define E_done 1<<5 -#define E_char 1<<6 -#define E_short 1<<7 -#define E_long 1<<8 -#define E_llong 1<<9 -#define E_intmax 1<<10 -#define E_size 1<<11 -#define E_ptrdiff 1<<12 -#define E_intptr 1<<13 -#define E_ldouble 1<<14 -#define E_lower 1<<15 -#define E_unsigned 1<<16 +#define E_minus (1<<0) +#define E_plus (1<<1) +#define E_alt (1<<2) +#define E_space (1<<3) +#define E_zero (1<<4) +#define E_done (1<<5) + +#define E_char (1<<6) +#define E_short (1<<7) +#define E_long (1<<8) +#define E_llong (1<<9) +#define E_intmax (1<<10) +#define E_size (1<<11) +#define E_ptrdiff (1<<12) +#define E_pointer (1<<13) + +#define E_ldouble (1<<14) + +#define E_lower (1<<15) +#define E_unsigned (1<<16) /* This macro delivers a given character to either a memory buffer or a stream, depending on the contents of 'status' (struct _PDCLIB_status_t). @@ -43,7 +47,7 @@ n - pointer to maximum number of characters to be delivered in this call s - the buffer into which the character shall be delivered */ -#define DELIVER( x ) \ +#define PUT( x ) \ do { \ int character = x; \ if ( status->i < status->n ) { \ @@ -58,10 +62,14 @@ do { \ static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) { + if ( status->prec < 0 ) + { + status->prec = 1; + } /* 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 ) ) + if ( ( status->flags & E_alt ) && ( status->base == 16 || status->base == 8 ) && ( value != 0 ) ) { /* Octal / hexadecimal prefix for "%#" conversions */ preface[ preidx++ ] = '0'; @@ -88,23 +96,27 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) } } { - size_t prec_pads = ( status->prec > status->current ) ? ( status->prec - status->current ) : 0; + /* At this point, status->current has the number of digits queued up. + Determine if we have a precision requirement to pad those. + */ + size_t prec_pads = ( (_PDCLIB_size_t)status->prec > status->current ) ? ( (_PDCLIB_size_t)status->prec - status->current ) : 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. + is requested. Calculate the number of characters that WILL be + printed, including 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->current > status->prec ) ? status->current : status->prec ); + size_t characters = preidx + ( ( status->current > (_PDCLIB_size_t)status->prec ) ? status->current : (_PDCLIB_size_t)status->prec ); if ( status->width > characters ) { for ( size_t i = 0; i < status->width - characters; ++i ) { - DELIVER( ' ' ); + PUT( ' ' ); ++(status->current); } } @@ -113,7 +125,13 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) preidx = 0; while ( preface[ preidx ] != '\0' ) { - DELIVER( preface[ preidx++ ] ); + PUT( preface[ preidx++ ] ); + ++(status->current); + } + /* Do the precision padding if necessary. */ + while ( prec_pads-- > 0 ) + { + PUT( '0' ); ++(status->current); } if ( ( ! ( status->flags & E_minus ) ) && ( status->flags & E_zero ) ) @@ -123,15 +141,10 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) */ while ( status->current < status->width ) { - DELIVER( '0' ); + PUT( '0' ); ++(status->current); } } - /* Do the precision padding if necessary. */ - for ( size_t i = 0; i < prec_pads; ++i ) - { - DELIVER( '0' ); - } } } @@ -144,44 +157,108 @@ static void intformat( intmax_t value, struct _PDCLIB_status_t * status ) output once the number of characters to be printed is known, which happens at the lowermost recursion level. */ +#define INT2BASE() \ +do \ +{ \ + /* Special case: zero value, zero precision -- no output (but padding) */ \ + if ( status->current == 0 && value == 0 && status->prec == 0 ) \ + { \ + intformat( value, status ); \ + } \ + else \ + { \ + /* 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->current); \ + if ( ( value / status->base ) != 0 ) \ + { \ + /* More digits to be done - recurse deeper */ \ + int2base( value / status->base, status ); \ + } \ + else \ + { \ + /* 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. \ + */ \ + intformat( value, status ); \ + } \ + /* Recursion tail - print the current digit. */ \ + { \ + int digit = value % status->base; \ + if ( digit < 0 ) \ + { \ + digit *= -1; \ + } \ + if ( status->flags & E_lower ) \ + { \ + /* Lowercase letters. Same array used for strto...(). */ \ + PUT( _PDCLIB_digits[ digit ] ); \ + } \ + else \ + { \ + /* Uppercase letters. Array only used here, only 0-F. */ \ + PUT( _PDCLIB_Xdigits[ digit ] ); \ + } \ + } \ + } \ +} while ( 0 ) + + static void int2base( intmax_t value, struct _PDCLIB_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->current); - if ( ( value / status->base ) != 0 ) + INT2BASE(); +} + + +static void stringformat( const char * s, struct _PDCLIB_status_t * status ) +{ + if ( status->flags & E_char ) { - /* More digits to be done - recurse deeper */ - int2base( value / status->base, status ); + status->prec = 1; } else { - /* 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. - */ - intformat( value, status ); + if ( status->prec < 0 ) + { + status->prec = strlen( s ); + } + else + { + for ( int i = 0; i < status->prec; ++i ) + { + if ( s[i] == 0 ) + { + status->prec = i; + break; + } + } + } } - /* Recursion tail - print the current digit. */ + if ( ! ( status->flags & E_minus ) && ( status->width > (_PDCLIB_size_t)status->prec ) ) { - int digit = value % status->base; - if ( digit < 0 ) - { - digit *= -1; + while ( status->current < ( status->width - status->prec ) ) + { + PUT( ' ' ); + ++(status->current); + } } - if ( status->flags & E_lower ) + while ( status->prec > 0 ) { - /* Lowercase letters. Same array used for strto...(). */ - DELIVER( _PDCLIB_digits[ digit ] ); + PUT( *(s++) ); + --(status->prec); + ++(status->current); } - else + if ( status->flags & E_minus ) { - /* Uppercase letters. Array only used here, only 0-F. */ - DELIVER( _PDCLIB_Xdigits[ digit ] ); - } + while ( status->width > status->current ) + { + PUT( ' ' ); + ++(status->current); + } } } @@ -192,7 +269,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status if ( *(++spec) == '%' ) { /* %% -> print single '%' */ - DELIVER( *spec ); + PUT( *spec ); return ++spec; } /* Initializing status structure */ @@ -200,7 +277,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status status->base = 0; status->current = 0; status->width = 0; - status->prec = 0; + status->prec = EOF; /* First come 0..n flags */ do @@ -275,6 +352,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status EOF (negative), there is no need for testing for negative here. */ status->prec = va_arg( status->arg, int ); + ++spec; } else { @@ -282,13 +360,13 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status status->prec = (int)strtol( spec, &endptr, 10 ); if ( spec == endptr ) { - /* Decimal point but no number - bad conversion specifier. */ - return orig_spec; + /* Decimal point but no number - equals zero */ + status->prec = 0; } spec = endptr; } /* Having a precision cancels out any zero flag. */ - status->flags ^= E_zero; + status->flags &= ~E_zero; } /* Optional length modifier @@ -380,23 +458,21 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status case 'A': break; case 'c': - /* TODO: Flags, wide chars. */ - DELIVER( va_arg( status->arg, int ) ); - return ++spec; - case 's': - /* TODO: Flags, wide chars. */ + /* TODO: wide chars. */ { - char * s = va_arg( status->arg, char * ); - while ( *s != '\0' ) - { - DELIVER( *(s++) ); - } + char c[1]; + c[0] = (char)va_arg( status->arg, int ); + status->flags |= E_char; + stringformat( c, status ); return ++spec; } + case 's': + /* TODO: wide chars. */ + stringformat( va_arg( status->arg, char * ), status ); + return ++spec; case 'p': - /* TODO: E_long -> E_intptr */ status->base = 16; - status->flags |= ( E_lower | E_unsigned | E_alt | E_long ); + status->flags |= ( E_lower | E_unsigned | E_alt | E_pointer ); break; case 'n': { @@ -417,7 +493,7 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status if ( status->flags & E_unsigned ) { uintmax_t value; - switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size ) ) + switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_size | E_pointer ) ) { case E_char: value = (uintmax_t)(unsigned char)va_arg( status->arg, int ); @@ -437,64 +513,52 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status case E_size: value = (uintmax_t)va_arg( status->arg, size_t ); break; + case E_pointer: + value = (uintmax_t)(uintptr_t)va_arg( status->arg, void * ); + break; + default: + puts( "UNSUPPORTED PRINTF FLAG COMBINATION" ); + return NULL; } - ++(status->current); - /* FIXME: The if clause means one-digit values do not get formatted */ - /* Was introduced originally to get value to "safe" levels re. uintmax_t. */ - if ( ( value / status->base ) != 0 ) - { - int2base( (intmax_t)(value / status->base), status ); - } - else - { - intformat( (intmax_t)value, 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 ] ); - } + INT2BASE(); } else { + intmax_t value; switch ( status->flags & ( E_char | E_short | E_long | E_llong | E_intmax ) ) { case E_char: - int2base( (intmax_t)(char)va_arg( status->arg, int ), status ); + value = (intmax_t)(char)va_arg( status->arg, int ); break; case E_short: - int2base( (intmax_t)(short)va_arg( status->arg, int ), status ); + value = (intmax_t)(short)va_arg( status->arg, int ); break; case 0: - int2base( (intmax_t)va_arg( status->arg, int ), status ); + value = (intmax_t)va_arg( status->arg, int ); break; case E_long: - int2base( (intmax_t)va_arg( status->arg, long ), status ); + value = (intmax_t)va_arg( status->arg, long ); break; case E_llong: - int2base( (intmax_t)va_arg( status->arg, long long ), status ); + value = (intmax_t)va_arg( status->arg, long long ); break; case E_ptrdiff: - int2base( (intmax_t)va_arg( status->arg, ptrdiff_t ), status ); + value = (intmax_t)va_arg( status->arg, ptrdiff_t ); break; case E_intmax: - int2base( va_arg( status->arg, intmax_t ), status ); + value = va_arg( status->arg, intmax_t ); break; + default: + puts( "UNSUPPORTED PRINTF FLAG COMBINATION" ); + return NULL; } + INT2BASE(); } if ( status->flags & E_minus ) { while ( status->current < status->width ) { - DELIVER( ' ' ); + PUT( ' ' ); ++(status->current); } } @@ -506,12 +570,15 @@ const char * _PDCLIB_print( const char * spec, struct _PDCLIB_status_t * status return ++spec; } +#endif + #ifdef TEST #define _PDCLIB_FILEID "_PDCLIB/print.c" -#include <_PDCLIB_test.h> +#define _PDCLIB_STRINGIO -#include -#include +#include "_PDCLIB_test.h" + +#ifndef REGTEST static int testprintf( char * buffer, const char * format, ... ) { @@ -524,7 +591,7 @@ static int testprintf( char * buffer, const char * format, ... ) status.current = 0; status.s = buffer; status.width = 0; - status.prec = 0; + status.prec = EOF; status.stream = NULL; va_start( status.arg, format ); memset( buffer, '\0', 100 ); @@ -537,12 +604,16 @@ static int testprintf( char * buffer, const char * format, ... ) return status.i; } +#endif + #define TEST_CONVERSION_ONLY int main( void ) { +#ifndef REGTEST char target[100]; -#include "printf_testcases.incl" +#include "printf_testcases.h" +#endif return TEST_RESULTS; }