/* 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; }