
/*
 *  PFMT.C
 *
 *  (c)Copyright 1990, Matthew Dillon, All Rights Reserved
 *
 *  func format same as fwrite: func(buf, 1, n, desc)
 */

#include <stdarg.h>
#include <stdio.h>

#define F_SPAC	    1
#define F_PLUS	    2
#define F_MINUS     4
#define F_HASH	    8
#define F_ZERO	    16

#define F_SHORT     32
#define F_LONG	    64
#define F_DLONG     128

int
_pfmt(ctl, va, func, desc)
char *ctl;
va_list va;
int (*func)();
void *desc;
{
    char *base = ctl;
    char c;
    int error = 0;
    short flags;
    short i1, i2;

    for (;;) {
	while (*ctl && *ctl != '%')
	    ++ctl;
	if (ctl != base)
	    error = (*func)(base, 1, ctl - base, desc);
	if (*ctl++ == 0)
	    return(0);
	c = *ctl;
	if (c == '%') {
	    base = ctl++;
	    continue;
	}

	/*
	 *  %[flags][val[.val]]c
	 */

	flags = 0;
	for (;; c = *++ctl) {
	    if (c == '-') {
		flags |= F_MINUS;
		continue;
	    }
	    if (c == '+') {
		flags |= F_PLUS;
		continue;
	    }
	    if (c == ' ') {
		flags |= F_SPAC;
		continue;
	    }
	    if (c == '#') {
		flags |= F_HASH;
		continue;
	    }
	    if (c == '0') {
		flags |= F_ZERO;
		continue;
	    }
	    break;
	}
	if (c == '*') {
	    c = *++ctl;
	    i1 = *va_arg(va, int);
	} else if (c >= '0' && c <= '9') {
	    i1 = 0;
	    while (c >= '0' && c <= '9') {
		i1 = i1 * 10 + (c - '0');
		c = *++ctl;
	    }
	} else {
	    i1 = -1;
	}
	i2 = -1;
	if (c == '.') {
	    c = *++ctl;
	    if (c == '*') {
		c = *++ctl;
		i2 = *va_arg(va, int);
	    } else {
		i2 = 0;
		while (c >= '0' && c <= '9') {
		    i2 = i2 * 10 + (c - '0');
		    c = *++ctl;
		}
	    }
	}
	if (i1 > 4096 || i2 > 4096)    /*  bad for business! */
	    continue;
	for (;;) {
	    if (c == 'h') {
		c = *++ctl;
		flags |= F_SHORT;   /*	only if sizeof(int) != 4 */
		continue;
	    }
	    if (c == 'l') {
		c = *++ctl;
		flags |= F_LONG;
		continue;
	    }
	    if (c == 'L') {
		c = *++ctl;
		flags |= F_DLONG;
		continue;
	    }
	    break;
	}
	error = _pfmtone(c, &va, func, desc, flags, i1, i2);
	if (error != -2)
	    base = ++ctl;
    }
    return(error);
}

_pfmtone(c, pva, func, desc, flags, i1, i2)
char c;
va_list *pva;
int (*func)();
void *desc;
short flags;
int i1, i2;
{
    va_list va = *pva;
    int len = 0;
    int error = 0;
    char buf[32];
    char *ptr = buf;

    switch(c) {
    case 'c':
	c = *va_arg(va, int);
	ptr = &c;
	len = 1;
	break;
    case 'd':
    case 'u':
	{
	    short neg = 0;
	    unsigned long v = *va_arg(va, unsigned long);

	    ptr = buf + sizeof(buf);
	    if (c != 'u' && (long)v < 0) {
		v = -v;
		neg = 1;
	    }
	    do {
		*--ptr = v % 10 + '0';
		v = v / 10;
	    } while (v);
	    if (neg)
		*--ptr = '-';
	    len = buf + sizeof(buf) - ptr;
	}
	break;
    case 'e':
    case 'E':
    case 'f':
    case 'g':
    case 'G':
    case 'i':
    case 'n':
    case 'o':
    case 'p':
	break;
    case 's':
	ptr = *va_arg(va, char *);
	len = strlen(ptr);
	break;
    case 'x':
    case 'X':
	{
	    unsigned long v = *va_arg(va, unsigned long);
	    static char IToHC[17] = { "0123456789ABCDEF" };

	    ptr = buf + sizeof(buf);
	    while (v) {
		*--ptr = IToHC[v & 0x0F];
		v = v >> 4;
	    }
	    len = buf + sizeof(buf) - ptr;
	}
	break;
    default:
	return(-2);
    }

    if (i2 < 0)
	i2 = len;
    if (i2 < len)
	len = i2;
    if (i1 < len)
	i1 = len;

    if (i1 > len && !(flags & F_MINUS)) {     /*  pre-pad   */
	int n = i1 - len;
	int r;
	char tmp[64];

	r = sizeof(tmp);
	if (n < r)
	    r = n;

	if (flags & F_ZERO) {
	    setmem(tmp, r, '0');
	} else {
	    setmem(tmp, r, ' ');
	}
	while (n) {
	    error |= (*func)(tmp, r, 1, desc);
	    n -= r;
	    if (n < sizeof(tmp))
		r = n;
	}
    }
    if (len > 0)
	error |= (*func)(ptr, len, 1, desc);

    if (i1 > len && (flags & F_MINUS)) {     /*  pos-pad   */
	int n = i1 - len;
	int r;
	char tmp[64];

	r = sizeof(tmp);
	if (n < r)
	    r = n;

	setmem(tmp, r, ' ');

	while (n) {
	    error |= (*func)(tmp, r, 1, desc);
	    n -= r;
	    if (n < sizeof(tmp))
		r = n;
	}
    }

    *pva = va;
    return(error);
}

