/* Small, noncompliant, not-full-featured printf implementation * * * Copyright (c) 2010, Ingo Korb * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Ingo Korb nor the * names of the contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * FIXME: Selection of output function should be more flexible */ #include #include #include #include "config.h" #include "uart.h" #define outfunc(x) uart_putc(x) #define FLAG_ZEROPAD 1 #define FLAG_LEFTADJ 2 #define FLAG_BLANK 4 #define FLAG_FORCESIGN 8 #define FLAG_WIDTH 16 #define FLAG_LONG 32 #define FLAG_UNSIGNED 64 #define FLAG_NEGATIVE 128 /* Digits used for conversion */ static const char hexdigits[] = "0123456789abcdef"; /* Temporary buffer used for numbers - just large enough for 32 bit in octal */ static char buffer[12]; /* Output string length */ static unsigned int outlength; /* Output pointer */ static char *outptr; static int maxlen; /* printf */ static void outchar( char x ) { if ( maxlen ) { maxlen--; outfunc( x ); outlength++; } } /* sprintf */ static void outstr( char x ) { if ( maxlen ) { maxlen--; *outptr++ = x; outlength++; } } static int internal_nprintf( void ( *output_function )( char c ), const char *fmt, va_list ap ) { unsigned int width; unsigned int flags; unsigned int base = 0; char *ptr = NULL; outlength = 0; while ( *fmt ) { while ( 1 ) { if ( *fmt == 0 ) { goto end; } if ( *fmt == '%' ) { fmt++; if ( *fmt != '%' ) { break; } } output_function( *fmt++ ); } flags = 0; width = 0; /* read all flags */ do { if ( flags < FLAG_WIDTH ) { switch ( *fmt ) { case '0': flags |= FLAG_ZEROPAD; continue; case '-': flags |= FLAG_LEFTADJ; continue; case ' ': flags |= FLAG_BLANK; continue; case '+': flags |= FLAG_FORCESIGN; continue; } } if ( flags < FLAG_LONG ) { if ( *fmt >= '0' && *fmt <= '9' ) { unsigned char tmp = *fmt - '0'; width = 10 * width + tmp; flags |= FLAG_WIDTH; continue; } if ( *fmt == 'h' ) { continue; } if ( *fmt == 'l' ) { flags |= FLAG_LONG; continue; } } break; } while ( *fmt++ ); /* Strings */ if ( *fmt == 'c' || *fmt == 's' ) { switch ( *fmt ) { case 'c': buffer[0] = va_arg( ap, int ); ptr = buffer; break; case 's': ptr = va_arg( ap, char * ); break; } goto output; } /* Numbers */ switch ( *fmt ) { case 'u': flags |= FLAG_UNSIGNED; case 'd': base = 10; break; case 'o': base = 8; flags |= FLAG_UNSIGNED; break; case 'p': // pointer output_function( '0' ); output_function( 'x' ); width -= 2; case 'x': case 'X': base = 16; flags |= FLAG_UNSIGNED; break; } unsigned int num; if ( !( flags & FLAG_UNSIGNED ) ) { int tmp = va_arg( ap, int ); if ( tmp < 0 ) { num = -tmp; flags |= FLAG_NEGATIVE; } else { num = tmp; } } else { num = va_arg( ap, unsigned int ); } /* Convert number into buffer */ ptr = buffer + sizeof( buffer ); *--ptr = 0; do { *--ptr = hexdigits[num % base]; num /= base; } while ( num != 0 ); /* Sign */ if ( flags & FLAG_NEGATIVE ) { output_function( '-' ); width--; } else if ( flags & FLAG_FORCESIGN ) { output_function( '+' ); width--; } else if ( flags & FLAG_BLANK ) { output_function( ' ' ); width--; } output: /* left padding */ if ( ( flags & FLAG_WIDTH ) && !( flags & FLAG_LEFTADJ ) ) { while ( strlen( ptr ) < width ) { if ( flags & FLAG_ZEROPAD ) { output_function( '0' ); } else { output_function( ' ' ); } width--; } } /* data */ while ( *ptr ) { output_function( *ptr++ ); if ( width ) { width--; } } /* right padding */ if ( flags & FLAG_WIDTH ) { while ( width ) { output_function( ' ' ); width--; } } fmt++; } end: return outlength; } int printf( const char *format, ... ) { va_list ap; int res; maxlen = -1; va_start( ap, format ); res = internal_nprintf( outchar, format, ap ); va_end( ap ); return res; } int snprintf( char *str, size_t size, const char *format, ... ) { va_list ap; int res; maxlen = size; outptr = str; va_start( ap, format ); res = internal_nprintf( outstr, format, ap ); va_end( ap ); if ( res < size ) { str[res] = 0; } return res; } /* Required for gcc compatibility */ int puts( const char *str ) { uart_puts( str ); uart_putc( '\n' ); return 0; } #undef putchar int putchar( int c ) { uart_putc( c ); return 0; }