/* from Dale Schumacher's dLibs library */

#ifndef __NO_FLOATS__
#define FLOATS 1
#endif

#include <stddef.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>

#define alloca __builtin_alloca

#ifndef TRUE
#define TRUE  1
#define FALSE 0
#endif

#define TEN_MUL(X)	((((X) << 2) + (X)) << 1)

int _printf(op, put, fmt, args)
	char *op;
	unsigned int (*put)();
	register unsigned char *fmt;
	register unsigned int *args;
	{
	register int i, cnt = 0, ljustf, lval;
	int preci, dpoint, width;
	int prefixf;	/* `#' flag */
	char pad, sign, radix;
	register char *ptmp;
        char *_ltoa(), *_ultoa();
#if FLOATS
	double fx;
#endif
	char prefix[3];
	char *tmp = (char *)alloca((size_t)128);

	prefix[0] = '\0';
	while(*fmt)
		{
		if(*fmt == '%')
			{
			ljustf = FALSE;	/* left justify flag */
			sign = '\0';	/* sign char & status */
			pad = ' ';	/* justification padding char */
			width = -1;	/* min field width */
			dpoint = FALSE;	/* found decimal point */
			preci = -1;	/* max data width */
			radix = 10;	/* number base */
			ptmp = tmp;	/* pointer to area to print */
			lval = FALSE;	/* long value flaged */
			prefixf  = FALSE; /* add prefix flag */
fmtnxt:
			i = 0;
			while (isdigit(*++fmt))
				{
				i = TEN_MUL(i) + (*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 = TRUE;
					goto fmtnxt;

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

				case '*':	/* parameter width value */
					i = *args++;
					if (dpoint)
						preci = i;
					else
						width = i;
					goto fmtnxt;

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

				case 'l':	/* long data */
					lval = TRUE;
					goto fmtnxt;
				case '#':
					prefixf = TRUE;
					goto fmtnxt;
			        case 'h':  /* here for completeness, does
					      not need to do anything 
					      as short ints are promoted
					      to ints at call time */
					goto fmtnxt;	

				case 'd':	/* Signed decimal */
				case 'i':
                                      _ltoa((long)((lval)
						?(*((long *) args))
						:(*((int  *) args))),
					      ptmp, 10);
					if(lval)
						args = ((unsigned int *)
							(((long *) args) + 1));
					else
						args = ((unsigned int *)
							(((int *) args) + 1));
					goto printit;

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

				case 'o':	/* Unsigned octal */
					if(prefixf) strcpy(prefix, "0");
					radix = 8;
					goto usproc;

				case 'p':	/* Pointer */
					lval = TRUE;
					pad = '0';
					width = 6;
					preci = 8;
					radix = 16;
					goto usproc;

				case 'x':	/* Unsigned hexadecimal */
				case 'X':
					if(prefixf) strcpy(prefix, "0X");
					radix = 16;
					/* fall thru */

				case 'u':	/* Unsigned decimal */
usproc:
				        if(prefixf && prefix[0] != '\0')
					{
					    strcpy(tmp, prefix);
					    prefix[0] = '\0';
					    while(*ptmp != '\0') ptmp++;
					}
					
                                        _ultoa((unsigned long)((lval)
						?(*((unsigned long *) args))
						: *args++ ),
					      ptmp, radix);
					ptmp = tmp;
					if(lval)
						args = ((unsigned int *)
						(((unsigned long *) args) + 1));
					if (*fmt == 'x')
						strlwr(ptmp);
					goto printit;

#if FLOATS
				case 'e':	/* float */
				case 'f':
				case 'g':
				case 'E':
				case 'G':
					fx = *((double *) args);
					args=(unsigned int *)
						 (((double *) args)+1);

					fp_print(fx, *fmt, preci, ptmp);
					preci = -1;
					goto printit;
#endif

				case 'c':	/* Character */
					ptmp[0] = *args++;
					ptmp[1] = '\0';
					goto nopad;

				case 's':	/* String */
					ptmp = *((char **) args);
					args = ((unsigned int *)
						(((char **) args) + 1));
nopad:
					sign = '\0';
					pad  = ' ';
printit:
					cnt += _prtfld(op, put, ptmp, ljustf,
						       sign, pad, width, preci);
					break;

				default:	/* unknown character */
					goto charout;
				}
			}
		else
			{
charout:
			(*put)(*fmt, op);		/* normal char out */
			++cnt;
			}
		++fmt;
		}
	return(cnt);
	}

