vprintf.c 4.91 KB
/* Portions of this work are derived from the Standard C Library, */
/* copyright (c) 1992 by P.J. Plauger, published by Prentice-Hall, */
/* and are used with permission. */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include "gload.h"

/* macros */

#define ISDIGIT(c)	((c >= '0') && (c <= '9'))
#define MAX_PAD	(sizeof(spaces) - 1)
#define PAD(s, n) \
	if (0 < (n)) { \
		int i, j = (n); \
		for (; 0 < j; j -= i) { \
			i = MAX_PAD < j ? MAX_PAD : j; \
			PUT(s, i); \
		} \
	}
#define PUT(s, n) \
	if (0 < (n)) { \
		if (fwrite(s, 1, n, stdout) == n) \
			x.nchar += (n); \
		else \
			return (x.nchar); \
	}

static char spaces[] = "                                ";
static char zeroes[] = "00000000000000000000000000000000";

static void putfld(field_t *, va_list *, char, char *);

/*VARARGS1*/
int
logvprintf(const char *fmt, va_list ap)
{
	field_t x;

	if (!(stdout->_flag & _IOWRT)) {
		/* if no write flag */
		if (stdout->_flag & _IORW) {
			/* if ok, cause read-write */
			stdout->_flag |= _IOWRT;
		} else {
			/* else error */
			setoserror(EBADF);
			return EOF;
		}
	}

	x.nchar = 0;
	for (;;) {	/* scan format string */
		const char *s = fmt;
		char c;
		const char *t;
		static const char fchar[] = {" +-#0"};
		static const unsigned int fbit[] = {
			_FSP, _FPL, _FMI, _FNO, _FZE, 0};
		char ac[32];

		/* copy any literal text */
		while (0 < (c = *s++)) {
			/* scan for '%' or '\0' */
			if (c == '%') {
				/* got a conversion specifier */
				--s;
				break;
			}
		}
		if (c == '\0') {
			PUT(fmt, s - fmt - 1);
			return (x.nchar);
		} else {
			PUT(fmt, s - fmt);
		}
		fmt = ++s;

		/* parse a conversion specifier */
		for (x.flags = 0; (t = strchr(fchar, *s)) != NULL; ++s)
			x.flags |= fbit[t - fchar];
		if (*s == '*') {	/* get width argument */
			x.width = va_arg(ap, int);
			if (x.width < 0) {	/* same as '-' flag */
				x.width = -x.width;
				x.flags |= _FMI;
			}
			++s;
		} else	/* accumulate width digits */
			for (x.width = 0; ISDIGIT(*s); ++s)
				if (x.width < _WMAX)
					x.width = x.width * 10 + *s - '0';
		if (*s != '.')
			x.prec = -1;
		else if (*++s == '*') {	/* get precision argument */
			x.prec = va_arg(ap, int);
			++s;
		} else	/* accumulate precision digits */
			for (x.prec = 0; ISDIGIT(*s); ++s)
				if (x.prec < _WMAX)
					x.prec = x.prec * 10 + *s - '0';
		x.qual = strchr("hl", *s) ? *s++ : '\0';

		/* do the conversion */
		putfld(&x, &ap, *s, ac);
		x.width -= x.n0 + x.nz0 + x.n1 + x.nz1 + x.n2 + x.nz2;
		if (!(x.flags & _FMI))
			PAD(spaces, x.width);
		PUT(ac, x.n0);
		PAD(zeroes, x.nz0);
		PUT(x.s, x.n1);
		PAD(zeroes, x.nz1);
		PUT(x.s + x.n1, x.n2);
		PAD(zeroes, x.nz2);
		if (x.flags & _FMI)
			PAD(spaces, x.width);
		fmt = s + 1;
	}
	/* shouldn't reach here, only used to eliminate the compiler warning */
	return 0;
}

static void
putfld(field_t *px, va_list *pap, char code, char *ac)
{	/* convert a field */

	px->n0 = px->nz0 = px->n1 = px->nz1 = px->n2 = px->nz2 = 0;
	switch (code) {	/* switch on conversion specifier */
	case 'c':
		ac[px->n0++] = va_arg(*pap, int);
		break;
	case 'd':
	case 'i':	/* convert a signed decimal integer */
		if (px->qual == 'l')
			px->v.l = va_arg(*pap, long);
		else
			px->v.l = va_arg(*pap, int);
		if (px->qual == 'h')
			px->v.l = (short) px->v.l;
		if (px->v.l < 0)	/* negate safely in itob */
			ac[px->n0++] = '-';
		else if (px->flags & _FPL)
			ac[px->n0++] = '+';
		else if (px->flags & _FSP)
			ac[px->n0++] = ' ';
		px->s = &ac[px->n0];
		itob(px, code);
		break;
	case 'o':
	case 'u':
	case 'x':
	case 'X':	/* convert unsigned */
		if (px->qual == 'l')
			px->v.l = va_arg(*pap, long);
		else
			px->v.l = va_arg(*pap, int);
		if (px->qual == 'h')
			px->v.l = (unsigned short) px->v.l;
		else if (px->qual == '\0')
			px->v.l = (unsigned int) px->v.l;
		if (px->flags & _FNO) {	/* indicate base with prefix */
			ac[px->n0++] = '0';
			if (code == 'x' || code == 'X')
				ac[px->n0++] = code;
		}
		px->s = &ac[px->n0];
		itob(px, code);
		break;
	case 'e':
	case 'E':
	case 'f':
	case 'g':
	case 'G':	/* convert floating */
		px->v.d = va_arg(*pap, float);
		if (DSIGN(px->v.d))
			ac[px->n0++] = '-';
		else if (px->flags & _FPL)
			ac[px->n0++] = '+';
		else if (px->flags & _FSP)
			ac[px->n0++] = ' ';
		px->s = &ac[px->n0];
		ftob(px, code);
		break;
	case 'n':	/* return output count */
		if (px->qual == 'h')
			*va_arg(*pap, short *) = px->nchar;
		else if (px->qual == 'l')
			*va_arg(*pap, long *) = px->nchar;
		else
			*va_arg(*pap, int *) = px->nchar;
		break;
	case 'p':	/* convert a pointer, hex long version */
		px->v.l = (long) va_arg(*pap, void *);
		px->s = &ac[px->n0];
		itob(px, 'x');
		break;
	case 's':	/* convert a string */
		px->s = va_arg(*pap, char *);
		px->n1 = strlen(px->s);
		if (0 <= px->prec && px->prec < px->n1)
			px->n1 = px->prec;
		break;
	case '%':	/* put a '%' */
		ac[px->n0++] = '%';
		break;
	default:	/* undefined specifier, print it out */
		ac[px->n0++] = code;
		break;
	}
}