/*
 * This file based on printf.c from 'Dlibs' on the atari ST  (RdeBath)
 *
 * 
 *    Dale Schumacher                         399 Beacon Ave.
 *    (alias: Dalnefre')                      St. Paul, MN  55104
 *    dal@syntel.UUCP                         United States of America
 *  "It's not reality that's important, but how you perceive things."
 */

/* Altered to use stdarg, made the core function vfprintf.
 * Hooked into the stdio package using 'inside information'
 * Altered sizeof() assumptions, now assumes all integers except chars
 * will be either
 *  sizeof(xxx) == sizeof(long) or sizeof(xxx) == sizeof(short)
 *
 * -RDB
 */

#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

#ifdef __STDC__
#include <stdarg.h>
#define va_strt      va_start
#else
#include <varargs.h>
#define va_strt(p,i) va_start(p)
#endif

#include "stdio.h"



extern int vfnprintf(FILE * op, size_t max_size,
					 register __const char *fmt, register va_list ap);




#ifdef L_printf
int printf(const char *fmt, ...)
{
	va_list ptr;
	int rv;

	va_strt(ptr, fmt);
	rv = vfnprintf(stdout, -1, fmt, ptr);
	va_end(ptr);
	return rv;
}
#endif

#ifdef L_sprintf
int sprintf(char *sp, const char *fmt, ...)
{
	static FILE string[1] = {
		{0, 0, (char *) (unsigned) -1, 0, (char *) (unsigned) -1, -1,
		 _IOFBF | __MODE_WRITE}
	};

	va_list ptr;
	int rv;

	va_strt(ptr, fmt);
	string->bufpos = sp;
	rv = vfnprintf(string, -1, fmt, ptr);
	va_end(ptr);
	*(string->bufpos) = 0;
	return rv;
}
#endif


#ifdef L_snprintf
int snprintf(char *sp, size_t size, const char *fmt, ...)
{
	static FILE string[1] = {
		{0, 0, (char *) (unsigned) -1, 0, (char *) (unsigned) -1, -1,
		 _IOFBF | __MODE_WRITE}
	};

	va_list ptr;
	int rv;

	va_strt(ptr, fmt);
	string->bufpos = sp;
	rv = vfnprintf(string, size, fmt, ptr);
	va_end(ptr);
	*(string->bufpos) = 0;
	return rv;
}
#endif

#ifdef L_fprintf
int fprintf(FILE * fp, const char *fmt, ...)
{
	va_list ptr;
	int rv;

	va_strt(ptr, fmt);
	rv = vfnprintf(fp, -1, fmt, ptr);
	va_end(ptr);
	return rv;
}
#endif

#ifdef L_vprintf
int vprintf(const char *fmt, va_list ap)
{
	return vfprintf(stdout, fmt, ap);
}
#endif

#ifdef L_vsprintf
int vsprintf(char *sp, __const char *fmt, va_list ap)
{
	static FILE string[1] = {
		{0, 0, (char *) (unsigned) -1, 0, (char *) (unsigned) -1, -1,
		 _IOFBF | __MODE_WRITE}
	};

	int rv;

	string->bufpos = sp;
	rv = vfnprintf(string, -1, fmt, ap);
	*(string->bufpos) = 0;
	return rv;
}
#endif

#ifdef L_vsprintf
int vsnprintf(char *sp, size_t size, __const char *fmt, va_list ap)
{
	static FILE string[1] = {
		{0, 0, (char *) (unsigned) -1, 0, (char *) (unsigned) -1, -1,
		 _IOFBF | __MODE_WRITE}
	};

	int rv;

	string->bufpos = sp;
	rv = vfnprintf(string, size, fmt, ap);
	*(string->bufpos) = 0;
	return rv;
}
#endif

#ifdef L_vfprintf

#if FLOATS
int _vfprintf_fp_ref = 1;
#else
int _vfprintf_fp_ref = 0;
#endif

static int
printfield(op, buf, ljustf, sign, pad, width, preci, buffer_mode, max_size,
		   current_size)
register FILE *op;
register unsigned char *buf;
int ljustf;
register char sign;
char pad;
register int width;
int preci;
int buffer_mode;
size_t max_size;
size_t current_size;

/*
 * Output the given field in the manner specified by the arguments. Return
 * the number of characters output.
 */
{
	register int cnt = 0, len;
	register unsigned char ch;

	len = strlen(buf);

	if (*buf == '-')
		sign = *buf++;
	else if (sign)
		len++;

	if ((preci != -1) && (len > preci))	/* limit max data width */
		len = preci;

	if (width < len)			/* flexible field width or width overflow */
		width = len;

	/*
	 * at this point: width = total field width len   = actual data width
	 * (including possible sign character)
	 */
	cnt = width;
	width -= len;

	while (width || len) {
		if (!ljustf && width) {	/* left padding */
			if (len && sign && (pad == '0'))
				goto showsign;
			ch = pad;
			--width;
		} else if (len) {
			if (sign) {
			  showsign:ch = sign;
								/* sign */
				sign = '\0';
			} else
				ch = *buf++;	/* main field */
			--len;
		} else {
			ch = pad;			/* right padding */
			--width;
		}
		current_size++;
		if (max_size > 0 && current_size < max_size)
			putc(ch, op);
		if (ch == '\n' && buffer_mode == _IOLBF)
			fflush(op);
	}

	return (cnt);
}



int vfnprintf(FILE * op, size_t max_size, register __const char *fmt,
			  register va_list ap)
{
	register int i, cnt = 0, ljustf, lval;
	int preci, dpoint, width;
	char pad, sign, radix, hash;
	register char *ptmp;
	char tmp[64], *ltostr(), *ultostr();
	int buffer_mode;

	/* This speeds things up a bit for unbuffered */
	buffer_mode = (op->mode & __MODE_BUF);
	op->mode &= (~__MODE_BUF);

	while (*fmt) {
		if (*fmt == '%') {
			if (buffer_mode == _IONBF)
				fflush(op);
			ljustf = 0;			/* left justify flag */
			sign = '\0';		/* sign char & status */
			pad = ' ';			/* justification padding char */
			width = -1;			/* min field width */
			dpoint = 0;			/* found decimal point */
			preci = -1;			/* max data width */
			radix = 10;			/* number base */
			ptmp = tmp;			/* pointer to area to print */
			hash = 0;
			lval = (sizeof(int) == sizeof(long));	/* long value flaged */

		  fmtnxt:
			i = 0;
			for (;;) {
				++fmt;
				if (*fmt < '0' || *fmt > '9')
					break;
				i = (i * 10) + (*fmt - '0');
				if (dpoint)
					preci = i;
				else if (!i && (pad == ' ')) {
					pad = '0';
					goto fmtnxt;
				} else
					width = i;
			}

			switch (*fmt) {
			case '\0':			/* early EOS */
				--fmt;
				goto charout;

			case '-':			/* left justification */
				ljustf = 1;
				goto fmtnxt;

			case ' ':
			case '+':			/* leading sign flag */
				sign = *fmt;
				goto fmtnxt;

			case '*':			/* parameter width value */
				i = va_arg(ap, int);

				if (dpoint)
					preci = i;
				else
					width = i;
				goto fmtnxt;

			case '.':			/* secondary width field */
				dpoint = 1;
				goto fmtnxt;

			case 'l':			/* long data */
				lval = 1;
				goto fmtnxt;

			case 'h':			/* short data */
				lval = 0;
				goto fmtnxt;

			case 'd':			/* Signed decimal */
			case 'i':
				ptmp = ltostr((long) ((lval)
									  ? va_arg(ap, long)
									  : va_arg(ap, short)), 10, 0);

				goto printit;

			case 'b':			/* Unsigned binary */
				radix = 2;
				goto usproc;

			case 'o':			/* Unsigned octal */
				radix = 8;
				goto usproc;

			case 'p':			/* Pointer */
				lval = (sizeof(char *) == sizeof(long));

				pad = '0';
				width = 6;
				preci = 8;
				/* fall thru */

			case 'x':			/* Unsigned hexadecimal */
			case 'X':
				radix = 16;
				/* fall thru */

			case 'u':			/* Unsigned decimal */
			  usproc:
				ptmp = ultostr((unsigned long) ((lval)
												? va_arg(ap, unsigned long)
												: va_arg(ap,
														 unsigned short)),
							   radix, (*fmt == 'X') ? 1 : 0);

				if (hash && radix == 8) {
					width = strlen(ptmp) + 1;
					pad = '0';
				}
				goto printit;

			case '#':
				hash = 1;
				goto fmtnxt;

			case 'c':			/* Character */
				ptmp[0] = va_arg(ap, int);

				ptmp[1] = '\0';
				goto nopad;

			case 's':			/* String */
				ptmp = va_arg(ap, char *);

			  nopad:
				sign = '\0';
				pad = ' ';
			  printit:
				cnt += printfield(op, ptmp, ljustf, sign, pad, width,
								  preci, buffer_mode, max_size, cnt);
				break;

#if FLOATS
			case 'e':			/* float */
			case 'f':
			case 'g':
			case 'E':
			case 'G':
				fprintf(stderr, "LIBM:PRINTF");
				gcvt(va_arg(ap, double), preci, ptmp);

				preci = -1;
				goto printit;
#else
			case 'e':			/* float */
			case 'f':
			case 'g':
			case 'E':
			case 'G':
				fprintf(stderr, "LIBC:PRINTF");
				exit(-1);
#endif

			default:			/* unknown character */
				goto charout;
			}
		} else {
		  charout:
			if (max_size > 0 && ++cnt < max_size)
				putc(*fmt, op);	/* normal char out */
			if (*fmt == '\n' && buffer_mode == _IOLBF)
				fflush(op);
		}
		++fmt;
	}
	op->mode |= buffer_mode;
	if (buffer_mode == _IONBF)
		fflush(op);
	if (buffer_mode == _IOLBF)
		op->bufwrite = op->bufstart;
	return (cnt);
}

int vfprintf(FILE * op, register __const char *fmt, register va_list ap)
{
	return (vfnprintf(op, -1, fmt, ap));
}

#endif