View Single Post
  #8   Spotlight this post!  
Unread 10-03-2004, 01:50
WizardOfAz's Avatar
WizardOfAz WizardOfAz is offline
Lead Mentor
AKA: Bill Bennett
FRC #1011 (CRUSH)
Team Role: Engineer
 
Join Date: Mar 2003
Rookie Year: 2002
Location: Tucson, AZ
Posts: 101
WizardOfAz will become famous soon enough
Send a message via AIM to WizardOfAz
Re: printf bug - beware!

Quote:
Originally Posted by gwross
Bill and Mark, if you could share your code, it would be appreciated.
OK, happy to do so.

The changes are in three files.

In ifi_utilities.h I added
#define printfBufrLength 80

In ifi_utilities.c I changed the line
char ifi_printfBufr[80];
to
char ifi_printfBufr[printfBufrLength];

Neither of these were really necessary, I could have just coded "80" instead of "printBufrLength" in the changes in printf_lib.c.

This is what is added to printf_lib.c:
the function strlenrom(const rom char * string) counts the length of the string.
strlenrom() is called in several places to compare the length of the source and target.

Some will suggest that I could have used strncpypgm2ram which would limit the amount of the copy with less work. I did it as I did so as to get a clear error on the screen when the function is used improperly, rather than just omit data from the print. I suppose either way is arguable.

Here's the new printf_lib.c code:
Code:
/*******************************************************************************
* FILE NAME: printf_lib.c
* 
* 2004/03/06 (bill bennett) added length check on copy to ifi_printfBufr and to tmpBufr
*
* DESCRIPTION:
*  This file contains generic routines that work like the standard C I/O library.
*  These unsupported routines have been modified to work with the Microchip 
*  C compiler.  The printf routine is limited as follows:
*
*  1.  Only the %s, %lx, %d, %x, %u, %X directives are parsed.  An additional %b 
*      directive is parsed for those who like to view data in binary (base 2).
*
*      Examples:   
*
*            rom const char *StrPtr = "Hello world!";
*            int x = 15;
*            int y = 0x50;
*            long z = 0xdeadface;
*
*            printf("%s\n",StrPtr);                         will display 'Hello world!'
*                                  
*            printf("X = %d, Y = %x, Z = %lx\n",x,y,z);     will display 'X = 15, Y = 0x50, Z = deadface'
*                                                      
*            printf("X = %b (base 2)\n",x);                 will display 'X = 1111 (base 2)'
*                   
*            printf("X = %16b\n",x);                        will display 'X = 0000000000001111'
*                           
*            printf("X = %04x\n",x);                        will display 'X = 000f'
*                           
*  2.  All bytes (8 bits) or bit references (rc_dig_in01) must be type-cast to a  
*      16 bit word.  The %c directive is unsupported.
*
*      Examples:   
*
*            unsigned char byte1 = 0xfa;
*            char byte2 = 25;
*
*            printf("1st byte = %x, 2nd byte = %d\n",(int)byte1,(int)byte2);
*                                                           will display '1st byte = fa, 2nd byte = 25
*            if (rc_dig_in01)
*              printf("On %d\n",(int)rc_dig_in01);          will display 'On 1'
*            else
*              printf("Off %d\n",(int)rc_dig_in01);         will display 'Off 0'
*
*  3.  The %s directive only supports a string of 40 bytes and the format string
*      may not exceed 80 bytes.  The sprintf routine is not included.  Keep in 
*      mind that the stack size has a 256 byte limit.
*
* USAGE:
*  These library routines  may be modified to suit the needs of the user.
*******************************************************************************/

#include "ifi_default.h"
#include "printf_lib.h"
#include "ifi_utilities.h"
#include "string.h"

/* the following should be enough for a 32 bit word */
#define PRINT_BUF_LEN 20

extern char ifi_printfBufr[]; /* declared in ifi_utilities.c */
rom char *nullStr = "(null)";
static const rom char * bufrTooLongMsg = "**** printf format string too long, max is 80 ****\n";
static const rom char * s_formatTooLongMsg = "**** %s arg too long, limit 40 ****"; // make sure this error msg len < 40!

static char print_buf[PRINT_BUF_LEN];
static char scr[2];
static int  k;

unsigned short strlenrom(const rom char * string) 
{
	unsigned short len;
	for(len=0; string[len]!=0; len++) ;
	return len;
}

/*******************************************************************************
* SUBROUTINE NAME: Write_Byte_To_Uart
* PURPOSE:       Writes a byte to the UART.
*     Argument       Type           IO   Description
*     --------       -----------    --   -----------
*         data       int            I    data to transmit to the UART
* RETURNS:       void
*******************************************************************************/

static void Write_Byte_To_Uart(int data)
{
  TXREG = data;  /* a carriage return */
  Wait4TXEmpty();
}

#define PAD_RIGHT 1
#define PAD_ZERO  2

/*******************************************************************************
* FUNCTION NAME: prints
* PURPOSE:       Pads the output stream.
* RETURNS:       void
*******************************************************************************/

static int prints(char *string, int width, int pad)
{
	register int pc = 0, padchar = ' ';

	if (width > 0) {
		register int len = 0;
		register char *ptr;
		for (ptr = string; *ptr; ++ptr) ++len;
		if (len >= width) width = 0;
		else width -= len;
		if (pad & PAD_ZERO) padchar = '0';
	}
	if (!(pad & PAD_RIGHT)) {
		for ( ; width > 0; --width) {
			Write_Byte_To_Uart (padchar);
			++pc;
		}
	}
	for ( ; *string ; ++string) {
		Write_Byte_To_Uart (*string);
		++pc;
	}
	for ( ; width > 0; --width) {
		Write_Byte_To_Uart (padchar);
		++pc;
	}

	return pc;
}

/*******************************************************************************
* FUNCTION NAME: printi
* PURPOSE:       Converts the output stream to the proper width and base.
* RETURNS:       void
*******************************************************************************/

static int printi(int i, int b, int sg, int width, int pad, int letbase)
{
	register char *s;
	register int t, neg = 0, pc = 0;
	register unsigned int u = i;

   print_buf[0] = 0xBE;
   print_buf[PRINT_BUF_LEN] = 0xEF;
	if (i == 0) {
		print_buf[0] = '0';
		print_buf[1] = '\0';
		return prints (print_buf, width, pad);
	}

	if (sg && b == 10 && i < 0) {
		neg = 1;
		u = -i;
	}

	s = print_buf + PRINT_BUF_LEN-1;
	*s = '\0';

	while (u) {
		t = u % b;
		if( t >= 10 )
			t += letbase - '0' - 10;
		*--s = t + '0';
		u /= b;
	}

	if (neg) {
		if( width && (pad & PAD_ZERO) ) {
			Write_Byte_To_Uart ('-');
			++pc;
			--width;
		}
		else {
			*--s = '-';
		}
	}

	return pc + prints (s, width, pad);
}

/*******************************************************************************
* FUNCTION NAME: print
* PURPOSE:       Parses the output stream.
* RETURNS:       void
*******************************************************************************/

static int print(char *format, int *varg)
{
  char tmpBufr[40];
	register int width, pad;
	register int pc = 0;

	for (; *format != 0; ++format) {
		if (*format == '%') {
			++format;
			width = pad = 0;
			if (*format == '\0') break;
			if (*format == '%') goto out;
			if (*format == '-') {
				++format;
				pad = PAD_RIGHT;
			}
			while (*format == '0') {
				++format;
				pad |= PAD_ZERO;
			}
			for ( ; *format >= '0' && *format <= '9'; ++format) {
				width *= 10;
				width += *format - '0';
			}
			if( *format == 's' ) 
      {
		if(strlenrom((rom char *) *varg)<40)
	        strcpypgm2ram(tmpBufr,(rom char *) *varg);
		else
			strcpypgm2ram(tmpBufr, (rom char *) s_formatTooLongMsg);
		varg--;
        if (tmpBufr[0] == 0)
          strcpypgm2ram(tmpBufr,nullStr);
				pc += prints (tmpBufr, width, pad);
				continue;
			}
			if( *format == 'd' ) {
				pc += printi (*varg--, 10, 1, width, pad, 'a');
				continue;
			}
			if( *format == 'x' ) {
				pc += printi (*varg--, 16, 0, width, pad, 'a');
				continue;
			}
			if( *format == 'X' ) {
				pc += printi (*varg--, 16, 0, width, pad, 'A');
				continue;
			}
			if( *format == 'u' ) {
				pc += printi (*varg--, 10, 0, width, pad, 'a');
				continue;
			}
			if( *format == 'l' ) {    /* assumes lx */
				pc += printi (*varg--, 16, 0, width, pad, 'a');
				pc += printi (*varg--, 16, 0, width, pad, 'a');
				format++;               /* skip over x*/
				continue;
			}
			if( *format == 'b' ) {
				pc += printi (*varg--, 2, 0, width, 2, 'a');
				continue;
			}
		}
		else {
		out:
      if (*format == '\n') *format = '\r';  /* replace line feed with cr */
			Write_Byte_To_Uart (*format);
			++pc;
		}
	}
	return pc;
}

/*******************************************************************************
* FUNCTION NAME: printf
* PURPOSE:       Formats an output stream.
* RETURNS:       void
*******************************************************************************/
int printf(rom const char *format, ...)
{
    register int *varg = (int *)(&format);
    /* 
       Since constant strings are kept in program (flash) memory, the strcpypgm2ram
       routine copies the string from flash to ram. 
    */
	if(strlenrom(format)<printfBufrLength) 
	    strcpypgm2ram(ifi_printfBufr,(rom char *) format);
	else 	
		// format string too long, print error message instead
		strcpypgm2ram(ifi_printfBufr, (rom char *)bufrTooLongMsg);

    varg--; /* adjust stack for Microchip C Compiler */
    return print(ifi_printfBufr, varg);
}

/******************************************************************************
* 
* The following routines are examples how to use the printf library routines
* to create your own output modules:
*  
*******************************************************************************/

/*******************************************************************************
* FUNCTION NAME: printid
* PURPOSE:       Prints a 16bit word (base 10) w/o a carriage return.
* RETURNS:       void
*******************************************************************************/

void printid(int data,int crtOn)
{
  printi (data, 10, 1, 4, 2, 'a');
  if (crtOn)
    Write_Byte_To_Uart('\r');
}

/*******************************************************************************
* FUNCTION NAME: printd
* PURPOSE:       Prints an 8bit word (base 10) w/o a carriage return.
* RETURNS:       void
*******************************************************************************/

void printd(unsigned char data,int crtOn)
{
  printi ((int) data, 10, 1, 3, 2, 'a');
  if (crtOn)
    Write_Byte_To_Uart('\r');
}

/*******************************************************************************
* FUNCTION NAME: printib
* PURPOSE:       Prints a 16bit binary word (base 2) w/o a carriage return.
* RETURNS:       void
*******************************************************************************/

void printib(unsigned int data,int crtOn)
{
  printi (data, 2, 0, 16, 2, 'a');
  if (crtOn)
    Write_Byte_To_Uart('\r');
}

/*******************************************************************************
* FUNCTION NAME: printb
* PURPOSE:       Prints an 8bit binary word (base 2) w/o a carriage return.
* RETURNS:       void
*******************************************************************************/

void printb(unsigned char data,int crtOn)
{
  printi ((int) data, 2, 0, 8, 2, 'a');
  if (crtOn)
    Write_Byte_To_Uart('\r');
}

/*******************************************************************************
* FUNCTION NAME: printix
* PURPOSE:       Prints a 16bit hex word (base 16) w/o a carriage return.
* RETURNS:       void
*******************************************************************************/

void printix(int data,int crtOn)
{
  printi (data, 16, 0, 2, 0, 'a');
  if (crtOn)
    Write_Byte_To_Uart('\r');
}

/*******************************************************************************
* FUNCTION NAME: printx
* PURPOSE:       Prints an 8bit hex word (base 16) w/o a carriage return.
* RETURNS:       void
*******************************************************************************/

void printx(unsigned char data,int crtOn)
{
  printi ((int) data, 16, 0, 2, 2, 'a');
  if (crtOn)
    Write_Byte_To_Uart('\r');
}

/*******************************************************************************
* FUNCTION NAME: debug_print
* PURPOSE:       Prints a header and a 16bit hex word (base 16) with a carriage 
*                return.
* RETURNS:       void
*******************************************************************************/

void debug_print(char *bufr,int data)
{
  if(strlenrom(bufr)<printfBufrLength) 
     strcpypgm2ram(ifi_printfBufr,(rom char *) bufr);
  else 	
    // format string too long, print error message instead
	strcpypgm2ram(ifi_printfBufr, (rom char *)bufrTooLongMsg);
  for (k=0;k<strlen(ifi_printfBufr);k++)
    Write_Byte_To_Uart(ifi_printfBufr[k]);
  printix(data,1);
}

/*******************************************************************************
* FUNCTION NAME: debug_printb
* PURPOSE:       Prints a header and an 8bit binary word (base 2) with a carriage 
*                return.
* RETURNS:       void
*******************************************************************************/

void debug_printb(char *bufr,unsigned int data)
{
  if(strlenrom(bufr)<printfBufrLength) 
     strcpypgm2ram(ifi_printfBufr,(rom char *) bufr);
  else 	
    // format string too long, print error message instead
	strcpypgm2ram(ifi_printfBufr, (rom char *)bufrTooLongMsg);
  for (k=0;k<strlen(ifi_printfBufr);k++)
    Write_Byte_To_Uart(ifi_printfBufr[k]);
  printib(data,1);
}

/*******************************************************************************
* FUNCTION NAME: debug_println
* PURPOSE:       Prints a header (assumming a carriage return is supplied).
* RETURNS:       void
*******************************************************************************/

void debug_println(char *bufr)
{
  if(strlenrom(bufr)<printfBufrLength) 
     strcpypgm2ram(ifi_printfBufr,(rom char *) bufr);
  else 	
    // format string too long, print error message instead
	strcpypgm2ram(ifi_printfBufr, (rom char *)bufrTooLongMsg);
  for (k=0;k<strlen(ifi_printfBufr);k++)
    Write_Byte_To_Uart(ifi_printfBufr[k]);
}


/******************************************************************************/
/******************************************************************************/
/******************************************************************************/