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
// [email protected]
// ----------------------------------------------------------------------
// 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