I have noticed that the libraries for IAR and GCC for ?printf seem to be an all or nothing approach, ie you get floating point or you dont, and in practice a usable printf family just doesnt fit. I thought I would share a different approach which I used for a recent project, the key is that I dont even attempt to try to adhere to the standard for ?printf but just the options that are useful, in particular I dont bother with left justification for example, anyway heres the source code - it supports most types, I hope others find it useful imho the code looks a hell of a lot neater than most printf's ive seen which seem littered with goto's etc, OK I know there is a reason for it BUT... anyway let me know what you guys think Peter
// ====================================================================== // File: Format.c // Purpose: Provide printf functionality // ---------------------------------------------------------------------- // Author: Peter Kenyon // Anarksoft Limited // anarks...@ukonline.co.uk // ---------------------------------------------------------------------- // Copyright (c) 2003 Anarksoft Limited. // ======================================================================
// ====================================================================== // See the file "license.terms" for information on usage and // redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. // ====================================================================== // ====================================================================== // 10 Mar 2003 : Inital Revision // // ====================================================================== // supports %% %c %[w]d %[w]l(d) %[w]u %[w]s %[w]X... // also %w.pf // but with restrictions ie field width < 10 // we could support left justification but it seems pointless // this code should be reentrant assuming put is of course //------------------------------------------------------------- // clients will need this #include "Project.h" #include "Format.h" //------------------------------------------------------------- // so we dont need the project header //typedef char bool; //#define true 1 //#define false 0 // only uses 500 bytes but sucks in a few library routines... // module configuration... #define FORMAT_U true #define FORMAT_I true #define FORMAT_L true #define FORMAT_X true #define FORMAT_S true #define FORMAT_F true #define FORMAT_C false #define FORMAT_P false // an example... #define FORMAT_SPRINTF false #define NO_NEGATIVE_ZERO true // allocated on stack // nb padding is palindromic so not inserted into buffer // these sizes are for 16 bit numbers - NOT ok for win32 ?? // can we use DBL_DIG in math.h ?? #define BUFFER_SIZE_HEX (2*sizeof(int)) // 4 or 8 #define BUFFER_SIZE_INT (sizeof(int)+3) // 5 or 7?? #define BUFFER_SIZE_LONG (sizeof(long)+3) // 12 or 24 //------------------------------------------------------------- // in theory we could #define if we want to use these stub routines // #define lcd_print_i( i, w ) fmt_u( lcd_put, i, w ) //------------------------------------------------------------- #if (FORMAT_U) static void fmt_u( PUT_FN put, unsigned u, int width ) { char buffer[ BUFFER_SIZE_INT ]; char* p = buffer; // clock out the digits do { *p++ = '0' + (u % 10); u /= 10; width --; } while ( u != 0 ); // spaces palindromic while ( width -- > 0 ) put( ' ' ); // call the output routine while ( p > buffer ) put( *--p ); } #endif #if (FORMAT_I) static void fmt_i( PUT_FN put, int i, int width ) { char buffer[ BUFFER_SIZE_INT ]; char* p = buffer; bool negative = false; // handle -ve numbers if ( i < 0 ) { i = -i; negative = true; } // clock out the digits do { *p++ = '0' + (i % 10); i /= 10; width --; } while ( i != 0 ); if ( negative ) { *p++ = '-'; -- width; } // spaces palindromic while ( width -- > 0 ) put( ' ' ); // call the output routine while ( p > buffer ) put( *--p ); } #endif //------------------------------------------------------------- #if (FORMAT_L) static void fmt_l( PUT_FN put, long l, int width ) { char buffer[ BUFFER_SIZE_LONG ]; char* p = buffer; bool negative = false; // handle -ve numbers if ( l < 0 ) { l = -l; negative = true; } // clock out the digits do { *p++ = '0' + (int)(l % 10); l /= 10; width --; } while ( l != 0 ); if ( negative ) { *p++ = '-'; -- width; } // spaces palindromic while ( width -- > 0 ) put( ' ' ); // call the output routine while ( p > buffer ) put( *--p ); } #endif //------------------------------------------------------------- #if (FORMAT_X) static void fmt_x( PUT_FN put, unsigned x, int width ) { char buffer[ BUFFER_SIZE_HEX ]; char* p = buffer; // clock out the digits do { *p++ = "0123456789ABCDEF" [(x & 0xf)]; x >>= 4; width --; } while ( x != 0 ); // 0's palindromic while ( width -- > 0 ) put( '0' ); // call the output routine while ( p > buffer ) put( *--p ); } #endif //.............................................................. /***** #if (FORMAT_S) #include <string.h> // for strlen static void fmt_s( PUT_FN put, const char* s, int width ) { int pad = width - (int)strlen( s ); while ( pad-- > 0 ) put( ' ' ); while ( *s ) put( *s++ ); } #endif *****/ // slightly revised that %8s means max of 8 chars // this means we can have string that arent 0 terminated // ie in flash somewhere !! #if (FORMAT_S) static void fmt_s( PUT_FN put, const char* s, int width ) { // normal case - just stick them out if ( width == 0 ) { while ( *s ) put( *s++ ); } else { int count = 0; // count up to width chars while ( count < width && s[ count ] ) ++ count; // output any pad while( width-- > count ) put( ' ' ); // output count chars while ( count-- ) put( *s++ ); } } #endif #if (FORMAT_C) /* static void fmt_c( PUT_FN put, char ch, int width ) { // turn this into a repeat function // so we could do say %8c while ( --width > 0 ) put( ch ) ; //put( ' ' ); put( ch ); } */ #endif //.............................................................. // now its not so easy // really depends on what we want to do // often we want auto ranging output and not fixed dp #if (FORMAT_F) static void fmt_f( PUT_FN put, double f, int width, int dp ) { char buffer[ BUFFER_SIZE_LONG ]; char* p = buffer; bool negative = false; long l; int n; // handle -ve if ( f < 0.0 ) { f = -f; negative = true; } // multiply up by dp for ( n = 0; n < dp; n++ ) { f *= 10; } // convert to long l = (long)(f + 0.5); #if (NO_NEGATIVE_ZERO) // prevent -0.0 and such like !! if ( l == 0 && negative ) negative = false; #endif // now clock it out do { //*p++ = '0' + (((int)l) % 10); *p++ = '0' + (int)(l % 10); l /= 10; width --; if ( --dp == 0 ) { *p++ = '.'; width --; } } // make sure we print at least 1 '0' while ( l != 0 || dp >= 0 ); if ( negative ) { *p++ = '-'; -- width; } // spaces palindromic while ( width -- > 0 ) put( ' ' ); // call the output routine while ( p > buffer ) put( *--p ); } #endif //.............................................................. //export void format( PUT_FN put, const char* fmt, va_list args ) { char ch; while ( (ch = *fmt++) ) { if ( ch != '%' ) { put( ch ); } else { int width = 0; #if (FORMAT_F) int dp = 0; #endif ch = *fmt++; // look for otional width // change to while for > 10 ?? //if ( ch >= '0' && ch <= '9' ) while ( ch >= '0' && ch <= '9' ) { //width = (ch - '0'); width = width * 10 + (ch - '0'); ch = *fmt++; } #if (FORMAT_F) // support for %w.p // nb assumes p is only 1 digit if ( ch == '.' ) { ch = *fmt++; // assume theres a digit after the dot dp = ch - '0'; ch = *fmt++; } #endif // now check for specifier if ( ch == '%' ) { put( ch ); } #if (FORMAT_X) else if ( ch == 'x' ) // || 'X' { fmt_x( put, va_arg( args, unsigned ), width ); } #endif #if (FORMAT_U) else if ( ch == 'u' ) { fmt_u( put, va_arg( args, unsigned ), width ); } #endif #if (FORMAT_I) else if ( ch == 'd' ) // || 'i' { fmt_i( put, va_arg( args, int ), width ); } #endif #if (FORMAT_S) else if ( ch == 's' ) { fmt_s( put, va_arg( args, const char* ), width ); } #endif #if (FORMAT_C) else if ( ch == 'c' ) { //fmt_c( put, va_arg( args, char ), width ); put( va_arg( args, char ) ); } #endif #if (FORMAT_L) else if ( ch == 'l' ) { fmt_l( put, va_arg( args, long ), width ); // optional d or u if we make a mistake ?? if ( *fmt == 'd' ) ++fmt; } #endif #if (FORMAT_F) else if ( ch == 'f' ) { fmt_f( put, va_arg( args, double ), width, dp ); } #endif #if (FORMAT_P) else if ( ch == 'p' ) { fmt_x( put, va_arg( args, unsigned ), 4 ); //width ); } #endif else { put( ch ); } } } } //--------------------------------------------------------- #if (FORMAT_SPRINTF) static char* str_pointer; static void str_put( byte ch ) { *str_pointer++ = ch; } //export int sprintf( char* s, const char* fmt, ... ) { va_list args; va_start( args, fmt ); // assign buffer pointer str_pointer = s; format( str_put, fmt, args ); // not implemented !! return 0; } #endif //--------------------------------------------------------- #if 0 //export int test_format( void ) { //con_printf( "hello world %x %2x %8x\r\n", 0x0012, 0x0034, 0x1234 ); con_printf( "example 1: [%%] [%x] [%2x] [%4x]\r\n", 0x0012, 0x1234, 0x00 ); con_printf( "example 2: [%u] [%4u] [%12u] \r\n", 123, 12, 12345 ); con_printf( "example 3: [%d] [%4d] [%12d] \r\n", 123, -12, -12345 ); con_printf( "example 4: [%s] [%4s] [%6s] \r\n", "abc", "abc", "abcdefg" ); con_printf( "example 5: [%c] [%4c] [%6c] \r\n", 'a', 'a', 'a' ); con_printf( "example 6: [%ld] [%l] [%6ld] \r\n", 123L, 123L, 123456789L ); con_printf( "example 7: [%f] [%.8f] [%8.3f] \r\n", 1.23, 1.23, 1.23 ); printf( "\r\n" ); return 0; } #endif