292 lines
6.2 KiB
C
292 lines
6.2 KiB
C
/* 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':
|
|
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;
|
|
}
|