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

Reply via email to