sam7fc/src/stdio/_putf.c

287 lines
5.3 KiB
C

#include <stddef.h> /* size_t */
#include <stdarg.h> /* va_list */
#include <stdio.h>
#include <string.h>
#include "_putf.h"
#define BUF 16
#define PADSIZE 16
static const char blanks[PADSIZE] = {
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
};
static const char zeroes[PADSIZE] = {
'0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0'
};
static void _putpad(int _putb(void *, const char *, size_t), void *base, const char *padch, int count)
{
while (count > PADSIZE) {
_putb(base, padch, PADSIZE);
count -= PADSIZE;
}
if (count > 0)
_putb(base, padch, count);
}
/*
* Flags used during conversion.
*/
#define ALT 0x01 /* alternate form */
#define LADJUST 0x04 /* left adjustment */
#define LONGINT 0x08 /* long integer */
#define ZEROPAD 0x10 /* zero (as opposed to blank) pad */
int _putf(int _putb(void *, const char *, size_t), void *base, const char *fmt, va_list ap)
{
int rc = 0; /* return value accumulator */
for (;;) {
unsigned char ch; /* character from fmt */
int size; /* size of converted field or string */
char *cp = (char *)fmt;
while ((ch = *fmt) && (ch != '%'))
fmt++;
int n = fmt - cp;
if (n) {
_putb(base, cp, n);
rc += n;
}
if (ch == 0)
break;
fmt++;
char flags = 0; /* flags as above */
char sign = 0; /* sign prefix (' ', '+', '-', or \0) */
int width = 0; /* width from format (%8d), or 0 */
int prec = -1; /* precision from format (%.3d), or -1 */
int dprec = 0; /* a copy of prec if [diouxX], 0 otherwise */
for (;;) {
ch = *fmt++;
if (ch == ' ') {
if (!sign)
sign = ' ';
} else if (ch == '+') {
sign = '+';
} else if (ch == '-') {
flags |= LADJUST;
} else if (ch == '#') {
flags |= ALT;
} else if (ch == '0') {
flags |= ZEROPAD;
} else if (ch == 'l') {
flags |= LONGINT;
} else if (ch == 'z') {
if (sizeof(size_t) > sizeof(int))
flags |= LONGINT;
} else if (ch == '*') {
width = va_arg(ap, int);
if (width < 0) {
flags |= LADJUST;
width = -width;
}
} else if (ch == '.') {
if (*fmt == '*') {
fmt++;
prec = va_arg(ap, int);
} else {
prec = 0;
while (*fmt >= '0' && *fmt <= '9')
prec = 10 * prec + (*fmt++ - '0');
}
if (prec < 0)
prec = -1;
} else if (ch >= '1' && ch <= '9') {
width = ch - '0';
while (*fmt >= '0' && *fmt <= '9')
width = 10 * width + (*fmt++ - '0');
} else {
break;
}
}
unsigned long ulval; /* integer arguments %[diouxX] */
char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
switch (ch) {
case 'c':
*(cp = buf) = va_arg(ap, int);
size = 1;
sign = 0;
break;
case 'P':
case 's':
cp = va_arg(ap, char *);
if (cp == 0)
cp = "(null)";
if (prec >= 0) {
char *p = memchr(cp, 0, (size_t) prec);
if (p) {
size = p - cp;
if (size > prec)
size = prec;
} else {
size = prec;
}
} else {
size = strlen(cp);
sign = 0;
}
break;
case 'u':
sign = 0;
case 'd':
case 'i':
if (flags & LONGINT) {
ulval = va_arg(ap, unsigned long);
} else if (ch == 'u') {
ulval = va_arg(ap, unsigned int);
} else {
ulval = va_arg(ap, int);
}
if (ch != 'u' && (long) ulval < 0) {
ulval = (unsigned long) (-((long) ulval));
sign = '-';
}
if ((dprec = prec) >= 0)
flags &= ~ZEROPAD;
cp = buf + BUF;
if (ulval || prec) {
if (ulval < 10) {
*--cp = (char) ulval + '0';
} else {
do {
*--cp = (char) (ulval % 10) + '0';
ulval /= 10;
} while (ulval);
}
}
size = buf + BUF - cp;
break;
case 'o':
ulval = (flags & LONGINT) ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int);
sign = 0;
if ((dprec = prec) >= 0)
flags &= ~ZEROPAD;
cp = buf + BUF;
if (ulval || prec) {
do {
*--cp = (char) (ulval & 7) + '0';
ulval >>= 3;
} while (ulval);
if ((flags & ALT) != 0 && *cp != '0')
*--cp = '0';
}
size = buf + BUF - cp;
break;
case 'p':
case 'X':
case 'x':
if (ch == 'p') {
ulval = (unsigned long) va_arg(ap, void *);
flags |= ALT;
ch = 'x';
} else {
ulval = (flags & LONGINT) ? va_arg(ap, unsigned long) : va_arg(ap, unsigned int);
}
sign = 0;
if ((dprec = prec) >= 0)
flags &= ~ZEROPAD;
char *xdigs = (ch == 'X') ? "0123456789ABCDEF" : "0123456789abcdef";
cp = buf + BUF;
do {
*--cp = xdigs[ulval & 0x0f];
ulval >>= 4;
} while (ulval);
if (flags & ALT) {
*--cp = ch;
*--cp = '0';
}
size = buf + BUF - cp;
break;
case 'g':
case 'G':
case 'e':
case 'E':
case 'f':
(void) va_arg(ap, long);
default:
if (ch == 0)
return rc;
cp = buf;
*cp = ch;
size = 1;
sign = '\0';
break;
}
int realsz = dprec > size ? dprec : size;
if (sign)
realsz++;
if ((flags & (LADJUST | ZEROPAD)) == 0)
_putpad(_putb, base, blanks, width - realsz);
if (sign)
_putb(base, &sign, 1);
if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD)
_putpad(_putb, base, zeroes, width - realsz);
_putpad(_putb, base, zeroes, dprec - size);
if (size)
_putb(base, cp, size);
if (flags & LADJUST)
_putpad(_putb, base, blanks, width - realsz);
rc += (width >= realsz) ? width : realsz;
}
return rc;
}