783 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			783 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*! \file rprintf.c \brief printf routine and associated routines. */
 | |
| //*****************************************************************************
 | |
| //
 | |
| // File Name	: 'rprintf.c'
 | |
| // Title		: printf routine and associated routines
 | |
| // Author		: Pascal Stang - Copyright (C) 2000-2002
 | |
| // Created		: 2000.12.26
 | |
| // Revised		: 2003.5.1
 | |
| // Version		: 1.0
 | |
| // Target MCU	: Atmel AVR series and other targets
 | |
| // Editor Tabs	: 4
 | |
| //
 | |
| // NOTE: This code is currently below version 1.0, and therefore is considered
 | |
| // to be lacking in some functionality or documentation, or may not be fully
 | |
| // tested.  Nonetheless, you can expect most functions to work.
 | |
| //
 | |
| // This code is distributed under the GNU Public License
 | |
| //		which can be found at http://www.gnu.org/licenses/gpl.txt
 | |
| //
 | |
| //*****************************************************************************
 | |
| 
 | |
| #include <avr/pgmspace.h>
 | |
| //#include <string-avr.h>
 | |
| //#include <stdlib.h>
 | |
| #include <stdarg.h>
 | |
| #include "global.h"
 | |
| #include "rprintf.h"
 | |
| 
 | |
| #ifndef TRUE
 | |
| 	#define TRUE	-1
 | |
| 	#define FALSE	0
 | |
| #endif
 | |
| 
 | |
| #define INF     32766	// maximum field size to print
 | |
| #define READMEMBYTE(a,char_ptr)	((a)?(pgm_read_byte(char_ptr)):(*char_ptr))
 | |
| 
 | |
| #ifdef RPRINTF_COMPLEX
 | |
| 	static unsigned char buf[128];
 | |
| #endif
 | |
| 
 | |
| // use this to store hex conversion in RAM
 | |
| //static char HexChars[] = "0123456789ABCDEF";
 | |
| // use this to store hex conversion in program memory
 | |
| //static prog_char HexChars[] = "0123456789ABCDEF";
 | |
| static const char __attribute__ ((progmem)) HexChars[] = "0123456789ABCDEF";
 | |
| 
 | |
| #define hexchar(x)	pgm_read_byte( HexChars+((x)&0x0f) )
 | |
| //#define hexchar(x)	((((x)&0x0F)>9)?((x)+'A'-10):((x)+'0'))
 | |
| 
 | |
| // function pointer to single character output routine
 | |
| static void (*rputchar)(unsigned char c);
 | |
| 
 | |
| // *** rprintf initialization ***
 | |
| // you must call this function once and supply the character output
 | |
| // routine before using other functions in this library
 | |
| void rprintfInit(void (*putchar_func)(unsigned char c))
 | |
| {
 | |
| 	rputchar = putchar_func;
 | |
| }
 | |
| 
 | |
| // *** rprintfChar ***
 | |
| // send a character/byte to the current output device
 | |
| void rprintfChar(unsigned char c)
 | |
| {
 | |
| 	// do LF -> CR/LF translation
 | |
| 	if(c == '\n')
 | |
| 		rputchar('\r');
 | |
| 	// send character
 | |
| 	rputchar(c);
 | |
| }
 | |
| 
 | |
| // *** rprintfStr ***
 | |
| // prints a null-terminated string stored in RAM
 | |
| void rprintfStr(char str[])
 | |
| {
 | |
| 	// send a string stored in RAM
 | |
| 	// check to make sure we have a good pointer
 | |
| 	if (!str) return;
 | |
| 
 | |
| 	// print the string until a null-terminator
 | |
| 	while (*str)
 | |
| 		rprintfChar(*str++);
 | |
| }
 | |
| 
 | |
| // *** rprintfStrLen ***
 | |
| // prints a section of a string stored in RAM
 | |
| // begins printing at position indicated by <start>
 | |
| // prints number of characters indicated by <len>
 | |
| void rprintfStrLen(char str[], unsigned int start, unsigned int len)
 | |
| {
 | |
| 	register int i=0;
 | |
| 
 | |
| 	// check to make sure we have a good pointer
 | |
| 	if (!str) return;
 | |
| 	// spin through characters up to requested start
 | |
| 	// keep going as long as there's no null
 | |
| 	while((i++<start) && (*str++));
 | |
| //	for(i=0; i<start; i++)
 | |
| //	{
 | |
| //		// keep steping through string as long as there's no null
 | |
| //		if(*str) str++;
 | |
| //	}
 | |
| 
 | |
| 	// then print exactly len characters
 | |
| 	for(i=0; i<len; i++)
 | |
| 	{
 | |
| 		// print data out of the string as long as we haven't reached a null yet
 | |
| 		// at the null, start printing spaces
 | |
| 		if(*str)
 | |
| 			rprintfChar(*str++);
 | |
| 		else
 | |
| 			rprintfChar(' ');
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| // *** rprintfProgStr ***
 | |
| // prints a null-terminated string stored in program ROM
 | |
| void rprintfProgStr(const char *str)
 | |
| {
 | |
| 	// print a string stored in program memory
 | |
| 	register char c;
 | |
| 
 | |
| 	// check to make sure we have a good pointer
 | |
| 	if (!str) return;
 | |
| 	
 | |
| 	// print the string until the null-terminator
 | |
| 	while((c = pgm_read_byte(str++)))
 | |
| 		rprintfChar(c);
 | |
| }
 | |
| 
 | |
| // *** rprintfCRLF ***
 | |
| // prints carriage return and line feed
 | |
| void rprintfCRLF(void)
 | |
| {
 | |
| 	// print CR/LF
 | |
| 	//rprintfChar('\r');
 | |
| 	// LF -> CR/LF translation built-in to rprintfChar()
 | |
| 	rprintfChar('\n');
 | |
| }
 | |
| 
 | |
| // *** rprintfu04 ***
 | |
| // prints an unsigned 4-bit number in hex (1 digit)
 | |
| void rprintfu04(unsigned char data)
 | |
| {
 | |
| 	// print 4-bit hex value
 | |
| //	char Character = data&0x0f;
 | |
| //	if (Character>9)
 | |
| //		Character+='A'-10;
 | |
| //	else
 | |
| //		Character+='0';
 | |
| 	rprintfChar(hexchar(data));
 | |
| }
 | |
| 
 | |
| // *** rprintfu08 ***
 | |
| // prints an unsigned 8-bit number in hex (2 digits)
 | |
| void rprintfu08(unsigned char data)
 | |
| {
 | |
| 	// print 8-bit hex value
 | |
| 	rprintfu04(data>>4);
 | |
| 	rprintfu04(data);
 | |
| }
 | |
| 
 | |
| // *** rprintfu16 ***
 | |
| // prints an unsigned 16-bit number in hex (4 digits)
 | |
| void rprintfu16(unsigned short data)
 | |
| {
 | |
| 	// print 16-bit hex value
 | |
| 	rprintfu08(data>>8);
 | |
| 	rprintfu08(data);
 | |
| }
 | |
| 
 | |
| // *** rprintfu32 ***
 | |
| // prints an unsigned 32-bit number in hex (8 digits)
 | |
| void rprintfu32(unsigned long data)
 | |
| {
 | |
| 	// print 32-bit hex value
 | |
| 	rprintfu16(data>>16);
 | |
| 	rprintfu16(data);
 | |
| }
 | |
| 
 | |
| // *** rprintfNum ***
 | |
| // special printf for numbers only
 | |
| // see formatting information below
 | |
| //	Print the number "n" in the given "base"
 | |
| //	using exactly "numDigits"
 | |
| //	print +/- if signed flag "isSigned" is TRUE
 | |
| //	use the character specified in "padchar" to pad extra characters
 | |
| //
 | |
| //	Examples:
 | |
| //	uartPrintfNum(10, 6,  TRUE, ' ',   1234);  -->  " +1234"
 | |
| //	uartPrintfNum(10, 6, FALSE, '0',   1234);  -->  "001234"
 | |
| //	uartPrintfNum(16, 6, FALSE, '.', 0x5AA5);  -->  "..5AA5"
 | |
| void rprintfNum(char base, char numDigits, char isSigned, char padchar, long n)
 | |
| {
 | |
| 	// define a global HexChars or use line below
 | |
| 	//static char HexChars[16] = "0123456789ABCDEF";
 | |
| 	char *p, buf[32];
 | |
| 	unsigned long x;
 | |
| 	unsigned char count;
 | |
| 
 | |
| 	// prepare negative number
 | |
| 	if( isSigned && (n < 0) )
 | |
| 	{
 | |
| 		x = -n;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 	 	x = n;
 | |
| 	}
 | |
| 
 | |
| 	// setup little string buffer
 | |
| 	count = (numDigits-1)-(isSigned?1:0);
 | |
|   	p = buf + sizeof (buf);
 | |
|   	*--p = '\0';
 | |
| 	
 | |
| 	// force calculation of first digit
 | |
| 	// (to prevent zero from not printing at all!!!)
 | |
| 	*--p = hexchar(x%base); x /= base;
 | |
| 	// calculate remaining digits
 | |
| 	while(count--)
 | |
| 	{
 | |
| 		if(x != 0)
 | |
| 		{
 | |
| 			// calculate next digit
 | |
| 			*--p = hexchar(x%base); x /= base;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			// no more digits left, pad out to desired length
 | |
| 			*--p = padchar;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// apply signed notation if requested
 | |
| 	if( isSigned )
 | |
| 	{
 | |
| 		if(n < 0)
 | |
| 		{
 | |
|    			*--p = '-';
 | |
| 		}
 | |
| 		else if(n > 0)
 | |
| 		{
 | |
| 	   		*--p = '+';
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 	   		*--p = ' ';
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// print the string right-justified
 | |
| 	count = numDigits;
 | |
| 	while(count--)
 | |
| 	{
 | |
| 		rprintfChar(*p++);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #ifdef RPRINTF_FLOAT
 | |
| // *** rprintfFloat ***
 | |
| // floating-point print
 | |
| void rprintfFloat(char numDigits, double x)
 | |
| {
 | |
| 	unsigned char firstplace = FALSE;
 | |
| 	unsigned char negative;
 | |
| 	unsigned char i, digit;
 | |
| 	double place = 1.0;
 | |
| 	
 | |
| 	// save sign
 | |
| 	negative = (x<0);
 | |
| 	// convert to absolute value
 | |
| 	x = (x>0)?(x):(-x);
 | |
| 	
 | |
| 	// find starting digit place
 | |
| 	for(i=0; i<15; i++)
 | |
| 	{
 | |
| 		if((x/place) < 10.0)
 | |
| 			break;
 | |
| 		else
 | |
| 			place *= 10.0;
 | |
| 	}
 | |
| 	// print polarity character
 | |
| 	if(negative)
 | |
| 		rprintfChar('-');
 | |
| 	else
 | |
| 		rprintfChar('+');
 | |
| 
 | |
| 	// print digits
 | |
| 	for(i=0; i<numDigits; i++)
 | |
| 	{
 | |
| 		digit = (x/place);
 | |
| 
 | |
| 		if(digit | firstplace | (place == 1.0))
 | |
| 		{
 | |
| 			firstplace = TRUE;
 | |
| 			rprintfChar(digit+0x30);
 | |
| 		}
 | |
| 		else
 | |
| 			rprintfChar(' ');
 | |
| 		
 | |
| 		if(place == 1.0)
 | |
| 		{
 | |
| 			rprintfChar('.');
 | |
| 		}
 | |
| 		
 | |
| 		x -= (digit*place);
 | |
| 		place /= 10.0;
 | |
| 	}
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef RPRINTF_SIMPLE
 | |
| // *** rprintf1RamRom ***
 | |
| // called by rprintf() - does a simple printf (supports %d, %x, %c)
 | |
| // Supports:
 | |
| // %d - decimal
 | |
| // %x - hex
 | |
| // %c - character
 | |
| int rprintf1RamRom(unsigned char stringInRom, const char *format, ...)
 | |
| {
 | |
| 	// simple printf routine
 | |
| 	// define a global HexChars or use line below
 | |
| 	//static char HexChars[16] = "0123456789ABCDEF";
 | |
| 	char format_flag;
 | |
| 	unsigned int u_val, div_val, base;
 | |
| 	va_list ap;
 | |
| 
 | |
| 	va_start(ap, format);
 | |
| 	for (;;)
 | |
| 	{
 | |
| 		while ((format_flag = READMEMBYTE(stringInRom,format++) ) != '%')
 | |
| 		{	// Until '%' or '\0'
 | |
| 			if (!format_flag)
 | |
| 			{
 | |
| 				va_end(ap);
 | |
| 				return(0);
 | |
| 			}
 | |
| 			rprintfChar(format_flag);
 | |
| 		}
 | |
| 
 | |
| 		switch (format_flag = READMEMBYTE(stringInRom,format++) )
 | |
| 		{
 | |
| 			case 'c': format_flag = va_arg(ap,int);
 | |
| 			default:  rprintfChar(format_flag); continue;
 | |
| 			case 'd': base = 10; div_val = 10000; goto CONVERSION_LOOP;
 | |
| //			case 'x': base = 16; div_val = 0x10;
 | |
| 			case 'x': base = 16; div_val = 0x1000;
 | |
| 
 | |
| 			CONVERSION_LOOP:
 | |
| 			u_val = va_arg(ap,int);
 | |
| 			if (format_flag == 'd')
 | |
| 			{
 | |
| 				if (((int)u_val) < 0)
 | |
| 				{
 | |
| 					u_val = - u_val;
 | |
| 					rprintfChar('-');
 | |
| 				}
 | |
| 				while (div_val > 1 && div_val > u_val) div_val /= 10;
 | |
| 			}
 | |
| 			do
 | |
| 			{
 | |
| 				//rprintfChar(pgm_read_byte(HexChars+(u_val/div_val)));
 | |
| 				rprintfu04(u_val/div_val);
 | |
| 				u_val %= div_val;
 | |
| 				div_val /= base;
 | |
| 			} while (div_val);
 | |
| 		}
 | |
| 	}
 | |
| 	va_end(ap);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #ifdef RPRINTF_COMPLEX
 | |
| // *** rprintf2RamRom ***
 | |
| // called by rprintf() - does a more powerful printf (supports %d, %u, %o, %x, %c, %s)
 | |
| // Supports:
 | |
| // %d - decimal
 | |
| // %u - unsigned decimal
 | |
| // %o - octal
 | |
| // %x - hex
 | |
| // %c - character
 | |
| // %s - strings
 | |
| // and the width,precision,padding modifiers
 | |
| // **this printf does not support floating point numbers
 | |
| int rprintf2RamRom(unsigned char stringInRom, const char *sfmt, ...)
 | |
| {
 | |
| 	register unsigned char *f, *bp;
 | |
| 	register long l;
 | |
| 	register unsigned long u;
 | |
| 	register int i;
 | |
| 	register int fmt;
 | |
| 	register unsigned char pad = ' ';
 | |
| 	int flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
 | |
| 	int sign = 0;
 | |
| 
 | |
| 	va_list ap;
 | |
| 	va_start(ap, sfmt);
 | |
| 
 | |
| 	f = (unsigned char *) sfmt;
 | |
| 
 | |
| 	for (; READMEMBYTE(stringInRom,f); f++)
 | |
| 	{
 | |
| 		if (READMEMBYTE(stringInRom,f) != '%')
 | |
| 		{	// not a format character
 | |
| 			// then just output the char
 | |
| 			rprintfChar(READMEMBYTE(stringInRom,f));
 | |
| 		}
 | |
| 		else 
 | |
| 		{
 | |
| 			f++;						// if we have a "%" then skip it
 | |
| 			if (READMEMBYTE(stringInRom,f) == '-')
 | |
| 			{
 | |
| 				flush_left = 1;	// minus: flush left
 | |
| 				f++;
 | |
| 			}
 | |
|             if (READMEMBYTE(stringInRom,f) == '0'
 | |
| 				 || READMEMBYTE(stringInRom,f) == '.')
 | |
| 				{
 | |
| 					// padding with 0 rather than blank
 | |
| 					pad = '0';
 | |
| 					f++;
 | |
|             }
 | |
|             if (READMEMBYTE(stringInRom,f) == '*')
 | |
| 				{	// field width
 | |
| 					f_width = va_arg(ap, int);
 | |
| 					f++;
 | |
|             }
 | |
|             else if (Isdigit(READMEMBYTE(stringInRom,f)))
 | |
| 				{
 | |
| 					f_width = atoiRamRom(stringInRom, (char *) f);
 | |
| 					while (Isdigit(READMEMBYTE(stringInRom,f)))
 | |
| 						f++;        // skip the digits
 | |
|             }
 | |
|             if (READMEMBYTE(stringInRom,f) == '.')
 | |
| 				{	// precision
 | |
| 					f++;
 | |
| 					if (READMEMBYTE(stringInRom,f) == '*')
 | |
| 					{
 | |
| 						prec = va_arg(ap, int);
 | |
| 						f++;
 | |
| 					}
 | |
| 					else if (Isdigit(READMEMBYTE(stringInRom,f)))
 | |
| 					{
 | |
| 						prec = atoiRamRom(stringInRom, (char *) f);
 | |
| 						while (Isdigit(READMEMBYTE(stringInRom,f)))
 | |
| 							f++;    // skip the digits
 | |
| 					}
 | |
| 				}
 | |
|             if (READMEMBYTE(stringInRom,f) == '#')
 | |
| 				{	// alternate form
 | |
| 					hash = 1;
 | |
| 					f++;
 | |
|             }
 | |
|             if (READMEMBYTE(stringInRom,f) == 'l')
 | |
| 				{	// long format
 | |
| 					do_long = 1;
 | |
| 					f++;
 | |
|             }
 | |
| 
 | |
| 				fmt = READMEMBYTE(stringInRom,f);
 | |
| 				bp = buf;
 | |
| 				switch (fmt) {		// do the formatting
 | |
| 				case 'd':			// 'd' signed decimal
 | |
| 					if (do_long)
 | |
| 						l = va_arg(ap, long);
 | |
| 					else
 | |
| 						l = (long) (va_arg(ap, int));
 | |
| 					if (l < 0)
 | |
| 					{
 | |
| 						sign = 1;
 | |
| 						l = -l;
 | |
| 					}
 | |
| 					do	{
 | |
| 						*bp++ = l % 10 + '0';
 | |
| 					} while ((l /= 10) > 0);
 | |
| 					if (sign)
 | |
| 						*bp++ = '-';
 | |
| 					f_width = f_width - (bp - buf);
 | |
| 					if (!flush_left)
 | |
| 						while (f_width-- > 0)
 | |
| 							rprintfChar(pad);
 | |
| 					for (bp--; bp >= buf; bp--)
 | |
| 						rprintfChar(*bp);
 | |
| 					if (flush_left)
 | |
| 						while (f_width-- > 0)
 | |
| 							rprintfChar(' ');
 | |
| 					break;
 | |
|             case 'o':			// 'o' octal number
 | |
|             case 'x':			// 'x' hex number
 | |
|             case 'u':			// 'u' unsigned decimal
 | |
| 					if (do_long)
 | |
| 						u = va_arg(ap, unsigned long);
 | |
| 					else
 | |
| 						u = (unsigned long) (va_arg(ap, unsigned));
 | |
| 					if (fmt == 'u')
 | |
| 					{	// unsigned decimal
 | |
| 						do {
 | |
| 							*bp++ = u % 10 + '0';
 | |
| 						} while ((u /= 10) > 0);
 | |
| 					}
 | |
| 					else if (fmt == 'o')
 | |
| 					{  // octal
 | |
| 						do {
 | |
| 							*bp++ = u % 8 + '0';
 | |
| 						} while ((u /= 8) > 0);
 | |
| 						if (hash)
 | |
| 							*bp++ = '0';
 | |
| 					}
 | |
| 					else if (fmt == 'x')
 | |
| 					{	// hex
 | |
| 						do {
 | |
| 							i = u % 16;
 | |
| 							if (i < 10)
 | |
| 								*bp++ = i + '0';
 | |
| 							else
 | |
| 								*bp++ = i - 10 + 'a';
 | |
| 						} while ((u /= 16) > 0);
 | |
| 						if (hash)
 | |
| 						{
 | |
| 							*bp++ = 'x';
 | |
| 							*bp++ = '0';
 | |
| 						}
 | |
| 					}
 | |
| 					i = f_width - (bp - buf);
 | |
| 					if (!flush_left)
 | |
| 						while (i-- > 0)
 | |
| 							rprintfChar(pad);
 | |
| 					for (bp--; bp >= buf; bp--)
 | |
| 						rprintfChar((int) (*bp));
 | |
| 					if (flush_left)
 | |
| 						while (i-- > 0)
 | |
| 							rprintfChar(' ');
 | |
| 					break;
 | |
|             case 'c':			// 'c' character
 | |
| 					i = va_arg(ap, int);
 | |
| 					rprintfChar((int) (i));
 | |
| 					break;
 | |
|             case 's':			// 's' string
 | |
| 					bp = va_arg(ap, unsigned char *);
 | |
| 					if (!bp)
 | |
| 						bp = (unsigned char *) "(nil)";
 | |
| 					f_width = f_width - strlen((char *) bp);
 | |
| 					if (!flush_left)
 | |
| 						while (f_width-- > 0)
 | |
| 							rprintfChar(pad);
 | |
| 					for (i = 0; *bp && i < prec; i++)
 | |
| 					{
 | |
| 						rprintfChar(*bp);
 | |
| 						bp++;
 | |
| 					}
 | |
| 					if (flush_left)
 | |
| 						while (f_width-- > 0)
 | |
| 							rprintfChar(' ');
 | |
| 					break;
 | |
|             case '%':			// '%' character
 | |
| 					rprintfChar('%');
 | |
| 					break;
 | |
| 			}
 | |
| 			flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
 | |
| 			sign = 0;
 | |
| 			pad = ' ';
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	va_end(ap);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| unsigned char Isdigit(char c)
 | |
| {
 | |
| 	if((c >= 0x30) && (c <= 0x39))
 | |
| 		return TRUE;
 | |
| 	else
 | |
| 		return FALSE;
 | |
| }
 | |
| 
 | |
| int atoiRamRom(unsigned char stringInRom, char *str)
 | |
| {
 | |
| 	int num = 0;;
 | |
| 
 | |
| 	while(Isdigit(READMEMBYTE(stringInRom,str)))
 | |
| 	{
 | |
| 		num *= 10;
 | |
| 		num += ((READMEMBYTE(stringInRom,str++)) - 0x30);
 | |
| 	}
 | |
| 	return num;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| 
 | |
| //******************************************************************************
 | |
| // code below this line is commented out and can be ignored
 | |
| //******************************************************************************
 | |
| /*
 | |
| char* sprintf(const char *sfmt, ...)
 | |
| {
 | |
| 	register unsigned char *f, *bp, *str;
 | |
| 	register long l;
 | |
| 	register unsigned long u;
 | |
| 	register int i;
 | |
| 	register int fmt;
 | |
| 	register unsigned char pad = ' ';
 | |
| 	int     flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
 | |
| 	int     sign = 0;
 | |
| 
 | |
| 	va_list ap;
 | |
| 	va_start(ap, sfmt);
 | |
| 
 | |
| 	str = bufstring;
 | |
| 	f = (unsigned char *) sfmt;
 | |
| 
 | |
| 	for (; *f; f++)
 | |
| 	{
 | |
| 		if (*f != '%')
 | |
| 		{								// not a format character
 | |
| 			*str++ = (*f);			// then just output the char
 | |
| 		}
 | |
| 		else 
 | |
| 		{
 | |
| 			f++;						// if we have a "%" then skip it
 | |
| 			if (*f == '-')
 | |
| 			{
 | |
| 				flush_left = 1;	// minus: flush left
 | |
| 				f++;
 | |
| 			}
 | |
|             if (*f == '0' || *f == '.')
 | |
| 				{
 | |
| 					// padding with 0 rather than blank
 | |
| 					pad = '0';
 | |
| 					f++;
 | |
|             }
 | |
|             if (*f == '*')
 | |
| 				{	// field width
 | |
| 					f_width = va_arg(ap, int);
 | |
| 					f++;
 | |
|             }
 | |
|             else if (Isdigit(*f))
 | |
| 				{
 | |
| 					f_width = atoi((char *) f);
 | |
| 					while (Isdigit(*f))
 | |
| 						f++;        // skip the digits
 | |
|             }
 | |
|             if (*f == '.')
 | |
| 				{	// precision
 | |
| 					f++;
 | |
| 					if (*f == '*')
 | |
| 					{
 | |
| 						prec = va_arg(ap, int);
 | |
| 						f++;
 | |
| 					}
 | |
| 					else if (Isdigit(*f))
 | |
| 					{
 | |
| 						prec = atoi((char *) f);
 | |
| 						while (Isdigit(*f))
 | |
| 							f++;    // skip the digits
 | |
| 					}
 | |
| 				}
 | |
|             if (*f == '#')
 | |
| 				{	// alternate form
 | |
| 					hash = 1;
 | |
| 					f++;
 | |
|             }
 | |
|             if (*f == 'l')
 | |
| 				{	// long format
 | |
| 					do_long = 1;
 | |
| 					f++;
 | |
|             }
 | |
| 
 | |
| 				fmt = *f;
 | |
| 				bp = buf;
 | |
| 				switch (fmt) {		// do the formatting
 | |
| 				case 'd':			// 'd' signed decimal
 | |
| 					if (do_long)
 | |
| 						l = va_arg(ap, long);
 | |
| 					else
 | |
| 						l = (long) (va_arg(ap, int));
 | |
| 					if (l < 0)
 | |
| 					{
 | |
| 						sign = 1;
 | |
| 						l = -l;
 | |
| 					}
 | |
| 					do	{
 | |
| 						*bp++ = l % 10 + '0';
 | |
| 					} while ((l /= 10) > 0);
 | |
| 					if (sign)
 | |
| 						*bp++ = '-';
 | |
| 					f_width = f_width - (bp - buf);
 | |
| 					if (!flush_left)
 | |
| 						while (f_width-- > 0)
 | |
| 							*str++ = (pad);
 | |
| 					for (bp--; bp >= buf; bp--)
 | |
| 						*str++ = (*bp);
 | |
| 					if (flush_left)
 | |
| 						while (f_width-- > 0)
 | |
| 							*str++ = (' ');
 | |
| 					break;
 | |
|             case 'o':			// 'o' octal number
 | |
|             case 'x':			// 'x' hex number
 | |
|             case 'u':			// 'u' unsigned decimal
 | |
| 					if (do_long)
 | |
| 						u = va_arg(ap, unsigned long);
 | |
| 					else
 | |
| 						u = (unsigned long) (va_arg(ap, unsigned));
 | |
| 					if (fmt == 'u')
 | |
| 					{	// unsigned decimal
 | |
| 						do {
 | |
| 							*bp++ = u % 10 + '0';
 | |
| 						} while ((u /= 10) > 0);
 | |
| 					}
 | |
| 					else if (fmt == 'o')
 | |
| 					{  // octal
 | |
| 						do {
 | |
| 							*bp++ = u % 8 + '0';
 | |
| 						} while ((u /= 8) > 0);
 | |
| 						if (hash)
 | |
| 							*bp++ = '0';
 | |
| 					}
 | |
| 					else if (fmt == 'x')
 | |
| 					{	// hex
 | |
| 						do {
 | |
| 							i = u % 16;
 | |
| 							if (i < 10)
 | |
| 								*bp++ = i + '0';
 | |
| 							else
 | |
| 								*bp++ = i - 10 + 'a';
 | |
| 						} while ((u /= 16) > 0);
 | |
| 						if (hash)
 | |
| 						{
 | |
| 							*bp++ = 'x';
 | |
| 							*bp++ = '0';
 | |
| 						}
 | |
| 					}
 | |
| 					i = f_width - (bp - buf);
 | |
| 					if (!flush_left)
 | |
| 						while (i-- > 0)
 | |
| 							*str++ = (pad);
 | |
| 					for (bp--; bp >= buf; bp--)
 | |
| 						*str++ = ((int) (*bp));
 | |
| 					if (flush_left)
 | |
| 						while (i-- > 0)
 | |
| 							*str++ = (' ');
 | |
| 					break;
 | |
|             case 'c':			// 'c' character
 | |
| 					i = va_arg(ap, int);
 | |
| 					*str++ = ((int) (i));
 | |
| 					break;
 | |
|             case 's':			// 's' string
 | |
| 					bp = va_arg(ap, unsigned char *);
 | |
| 					if (!bp)
 | |
| 						bp = (unsigned char *) "(nil)";
 | |
| 					f_width = f_width - strlen((char *) bp);
 | |
| 					if (!flush_left)
 | |
| 						while (f_width-- > 0)
 | |
| 							*str++ = (pad);
 | |
| 					for (i = 0; *bp && i < prec; i++)
 | |
| 					{
 | |
| 						*str++ = (*bp);
 | |
| 						bp++;
 | |
| 					}
 | |
| 					if (flush_left)
 | |
| 						while (f_width-- > 0)
 | |
| 							*str++ = (' ');
 | |
| 					break;
 | |
|             case '%':			// '%' character
 | |
| 					*str++ = ('%');
 | |
| 					break;
 | |
| 			}
 | |
| 			flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
 | |
| 			sign = 0;
 | |
| 			pad = ' ';
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	va_end(ap);
 | |
| 	// terminate string with null
 | |
| 	*str++ = '\0';
 | |
| 	return bufstring;
 | |
| }
 | |
| 
 | |
| */
 |