287 lines
5.3 KiB
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;
|
||
|
}
|