123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- /* 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 <COPYRIGHT HOLDER> 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 <stdio.h>
- #include <stdarg.h>
- #include <string.h>
- #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':
- 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;
- }
|