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

#ifdef _i386_
#define USE_FLOAT
#endif

#define NUM_SIZE 32

static int getnum(char *string, int num,int radix,int lc,int unsign)
{
	int i,sz=0;
	unsigned unum;
	for (i=0; i < NUM_SIZE-1; i++)
		string[i] = '0';
	string[NUM_SIZE-1] = 0;
	if (num < 0 && !unsign)
		unum = - num;
	else
		unum = num;
	i = NUM_SIZE-2;
	while (unum) {
		string[i] = (char)((unum % radix)+ '0');
		if (string[i] > '9') {
			string[i] += 7;
			if (lc)
				string[i] += 32;
		}
		i--;
		unum /= radix;
		sz++;
	}
	if (sz == 0)
		sz++;
	return sz;
}
static char *onetostr(char *obuf,char **buffer, const char *format, void *arg,int *count)
{

	int c,sz,fxs;
	int *p;
	int looping = 1;
	int issigned = 0, ljustify = 0, spaced = 0, prefixed = 0;
	int	leadzero = 0;
	int width = 0;
	int prec = -1;
	int mode = 0;
	int lc,i;
	char echar = 'e';
	int isf=1, isg=0;
	long double fmant;
	int fsign,fexp;
	char	fbuf[64],*fbp,*xxx;

	char locbuf[NUM_SIZE],*ofm = format;
	while (looping) 
		switch (*format) {
			case '+':	issigned = 1;
					format++;
					break;
			case '-':	ljustify = 1;
					format++;
					break;
			case ' ': spaced = 1;
					format++;
					break;
			case '#': prefixed = 1;
					format++;
					break;
			case '0':	leadzero = 1;
					format++;
					break;
			default:
					looping = 0;
		}
	if (isdigit(*format)) {
		width = 0;
		while (isdigit(*format)) {
			width *= 10;
			width += *format++ - '0';
		}
	}
	else if (*format == '*') {
		width = *((int *)arg)++;
		(*count)++;
		format++;
	}
	if (*format == '.') {
		format++;
		prec = 0;
		while (isdigit(*format)) {
			prec *= 10;
			prec += *format++ - '0';
		}
	}
	if (*format == 'h' || *format == 'l')
		mode = *format++;
	else if (*format == 'F' || *format == 'N') /* back support real mode formats*/
		format++;
	switch (*format++) {
		case '%':
			*(*buffer)++ = '%';
			*(*buffer) = 0;
			break;
		case 'c':
			(*count)++;
			c = *(int *)arg;
			*(*buffer)++ = (char)c;
			*(*buffer) = 0;
			break;
		case 'd':
		case 'i':
			(*count)++;
			c = *(int *)arg;
			if (mode == 'h') {
				c &= 0xffff;
				if (c & 0x8000)
					c |= 0xffff0000;
			}
			sz = getnum(locbuf,c,10,0,0);
			if (issigned) {
				if (c < 0) 
					*(*buffer)++ = '-';
				else
					*(*buffer)++ = '+';
				if (width)
					width--;
			}
			else if (spaced) {
				if (c < 0) 
					*(*buffer)++ = '-';
				else
					*(*buffer)++ = ' ';
				if (width)
					width--;
			}
			else {
				if (c < 0) {
					*(*buffer)++ = '-';
					if (width)
						width--;
				}
			}
			goto numfin;
				
		case 'G':
			echar='E';
			isf = 0;
		case 'g':
			isg = 1;
			goto dofloat;
		case 'E':
			echar='E';
		case 'e':
			isf = 0;
			goto dofloat;
		case 'f':
dofloat:
			/* long double not supported properly */
			if (mode == 'l') {
				(*count)+=3;
				fmant = *(long double *)arg;
			}
			else {
				(*count)+=2;
				fmant = *(double *)arg;
			}
#ifndef USE_FLOAT
			strcpy(*buffer,"(FP not linked)");
			*buffer += strlen(*buffer);
#else
			if (fxs = _fextract(&fmant,&fexp,&fsign) < 0) {
				strcpy(fbuf,"(NaN)");
			}
			else {
				fbp = fbuf;
				/* sign */
				if (issigned || fsign < 0)
					if (fsign < 0)
						*fbp++='-';
					else
						*fbp++='+';

				/* handle infinities */
				if (fxs) {
					strcpy(fbp,"(Inf)");
				}
				else {
					/* adjust for negative exponents, I was too lazy to do this in ASM */
					if (fexp < 0) {
						_fnd(&fmant);
						fexp--;
					}

					if (prec == -1)
						prec = 6;

					/* scratch f format if won't fit in field */

					if (fexp > 0)
						if ((width && (prec +1+fexp > width)) || fexp > 10)
							isf = 0;
					else
						if (-fexp > prec)
							isf = 0;

					/* for g do all digits */
					if (isg) prec = 18;

					xxx = fbp;		/* pointer to the roundup digit */
					*fbp++ = '0';						/* in case of round from 9.99999999.... */

					/* mantissa */
					if (isf) {
						if (fexp < 0) {
							*fbp++ = '0';
							*fbp++ = '.';
							if (fexp < -prec)
								fexp = -prec-1;
							for (i=fexp; i < -1; i++)
								*fbp++ = '0';
							for (i=-fexp-1; i < prec; i++)
								*fbp++ = _fnd(&fmant) + '0';
						}
						else {
							*fbp++ = _fnd(&fmant) + '0';
							while (fexp > 0) {
								*fbp++ = _fnd(&fmant) + '0';
								fexp--;
							}
							*fbp++ = '.';
							for (i=0; i < prec; i++)
								*fbp++ = _fnd(&fmant) + '0';
						}
					}
					else {
						*fbp++ = _fnd(&fmant) + '0';
						*fbp++ = '.';
						for (i=0; i < prec; i++)
							*fbp++ = _fnd(&fmant) + '0';
					}
		
					/* Rounding */
					i = _fnd(&fmant);
					if (i >4) {
						char *fbp2=fbp-1;
						while (fbp2 >= fbuf) {
							if (*fbp2 < '0') {
								fbp2--;
								continue;
							}
							(*fbp2)++;
							if (*fbp2 != 0x3a)
								break;
							(*fbp2--)='0';
						}
					}

					/* zerowipe */
					while (isg && *(fbp-1) == '0')
						fbp--;

					/* check for round out of first digit */
					*fbp = 0;
					if (*xxx == '0') {
						strcpy(xxx,xxx+1);	/* nope, blit it out */
						fbp--;
					}
					else {
						if (!isf) {
							*(xxx+1) = '.';	/* yes, readjust decimal */
							*(xxx+2) = '0';
							if (!isg)
								fbp--;	/* fix precision */
							fexp++;	/* fix exponent */
						}
						
					}
					/* exponent */
					if ((isg || prefixed) && *(fbp-1) == '.')
						fbp--;
					if (!isf) {
						if (*(fbp-1) == '.')
							fbp--;
						if (*(fbp-1) == '1' && fbp == fbuf+1)
							fbp--;
						*fbp++ = echar;
						if (issigned || fexp < 0)
							if (fexp < 0) {
								fexp=-fexp;
								*fbp++='-';
							}
							else
								*fbp++='+';
						if (fexp > 99)
							*fbp++= fexp/100 + '0';
						if (fexp > 9)
							*fbp++ = fexp/10 % 10 + '0';
						else
							*fbp++ = '0';
						*fbp++ = fexp %10 + '0';
					}
					*fbp++ = 0;
				}
			}
			i = strlen(fbuf);
			if (ljustify) {
				strcpy(*buffer,fbuf);
				for (; i < width; i++)
					(*buffer)[i] = ' ';
				(*buffer)[i]=0;
			}
			else {
				fbp = *buffer;
				for (;i< width; i++)
					*fbp++= ' ';
				strcpy(fbp,fbuf);
			}
			*buffer+=strlen(*buffer);	
#endif
			break;
		case 'n':
			(*count)++;
			p = *(int **)arg;
			*p = (int)((*buffer) - obuf);
			break;
		case 'o':
			(*count)++;
			c = *(int *)arg;
			if (mode == 'h') {
				c &= 0xffff;
				if (c & 0x8000)
					c |= 0xffff0000;
			}
			sz = getnum(locbuf,c,8,0,1);
			if (prefixed && c)
				sz--;
			if (issigned) {
				*(*buffer)++ = '+';
				if (width)
					width--;
			}
			else if (spaced) {
				*(*buffer)++ = ' ';
				if (width)
					width--;
			}
			goto numfin;
		case 's':
			(*count)++;
			sz = strlen(*(char **)arg);
			if (width && (sz < width))
				if (!ljustify) {
					for (i=0; i < width-sz; i++)
						*(*buffer)++ = ' ';
					strcpy(*buffer,*(char **)arg);
			 		*buffer += strlen(*buffer);
				}
				else {
					strcpy(*buffer,*(char **)arg);
			 		*buffer += strlen(*buffer);
					for (i=0; i < width-sz; i++)
						*(*buffer)++ = ' ';
				}
			else {
				strcpy(*buffer,*(char **)arg);
				*buffer += strlen(*buffer);
			}
			break;
		case 'u':
			(*count)++;
			c = (int )arg;
			if (mode == 'h') {
				c &= 0xffff;
				if (c & 0x8000)
					c |= 0xffff0000;
			}
			sz = getnum(locbuf,*(int *)arg,10,0,1);

			if (issigned) {
				*(*buffer)++ = '+';
				if (width)
					width--;
			}
			else if (spaced) {
				*(*buffer)++ = ' ';
				if (width)
					width--;
			}
			goto numfin;
		case 'x':
		case 'p':
					lc = 1;
		case 'X':
			(*count)++;
			if (*(format-1) == 'X')
				lc = 0;
			c = *(int *)arg;
			if (mode == 'h') {
				c &= 0xffff;
				if (c & 0x8000)
					c |= 0xffff0000;
			}
			sz = getnum(locbuf,c,16,lc,1);
			if (issigned) {
				*(*buffer)++ = '+';
				if (width)
					width--;
			}
			else if (spaced) {
				*(*buffer)++ = ' ';
				if (width)
					width--;
			}
			if (prefixed) {
				*(*buffer)++ = '0';
				*(*buffer)++ = 'x';
			}
numfin:
			if (width) {
				if (width <= sz) {
					goto nowidth;
				}
				if (!ljustify || leadzero) {
					if (leadzero) {
						for (i=0; i < width-sz; i++)
							*(*buffer)++ = '0';
					}
					else {
						for (i=0; i < width-sz; i++)
							*(*buffer)++ = ' ';
					}
					strcpy(*buffer,&locbuf[NUM_SIZE-sz-1]);
					(*buffer) += strlen(*buffer);
				}
				else {
					strcpy(*buffer,&locbuf[NUM_SIZE-sz-1]);
					(*buffer) += strlen(*buffer);
					for (i=0; i < width-sz; i++)
						*(*buffer)++ = ' ';
					*(*buffer) = 0;
				}
			}
			else {
nowidth:
				strcpy(*buffer,&locbuf[NUM_SIZE-sz-1]);
				(*buffer) += strlen(*buffer);
				*(*buffer) = 0;
			}
			break;
		default:
		 	*(*buffer)++ = '%';
			format = ofm;
			break;
	}
	return(format);
}
int vsprintf(char *buffer, const char *format,
                                    void *arglist)
{
	int i = 0;
	char *obuf = buffer;
	while (*format) {
		while (*format != '%' && *format != 0)
			*buffer++ = *format++;
		if (*format && *(format+1))
			format = onetostr(obuf,&buffer,format+1,&((char **)arglist+i),&i);
	}
	*buffer++ = 0;
	return(strlen(obuf));
}
int sprintf(char *buffer, const char *format, ...)
{
	return vsprintf(buffer,format,(((char *)&format) + sizeof(char *)));
}