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

#define MAXSTR 300
#define B32MASK 0x7fffffff	/* strip the long int sign bit */

/*
Format string options:

	"%-0w.p,l{dux}"

		- 	left justified (else right)
		0 	left zero fill
		w 	field width
		,	comma separate 1000's
		l	long
		a	base 36
*/

char _spr_sepchar = ',';	/* 1000's seperator character */

/* Write a tring to the console. */

cputs(s)
char *s;
{
	while (*s) lconout(*s++);
}
/* Replacements for the overly large Lattice ones. */

cprintf(f)
char *f;
{
char buf[MAXSTR],*p;

	_spr(buf,&f);
	cputs(buf);
}

printf(f)
char *f;
{
char buf[MAXSTR],*p;

	_spr(buf,&f);
	p= buf;
	while (*p) bdos(2,*p++);
}

sprintf(s,f)
char *s,*f;
{
	_spr(s,&f);
}

/* Formatted string processor for printf() etc. Will only work on a Lattice
long data model. Gross. Plagiarized from Leor Zolman's STDLIB2
for BDS C 1.42. Brain Damage Software still lives on ...

This is gross, as it uses funny pointers instead of a structure. Oh well. Also,
it is dependent on a char being the same size as an int on the stack. */

_spr(line,fmt)
char *line, **fmt;
{
char c, base, *sptr, *format, **s;
char ljflag, zfflag, sepflag;
int width, *args;
int sizearg,islong;
long num;
char sign;

unsigned li;			/* index into line, above */
char sb[MAXSTR];
char wb[32];			/* room for largest possible digit string */
unsigned si;			/* index to sb[] */
unsigned i,j,l;

long *longarg;
char **charstararg;

	format = (char *)*fmt++;
	args = (int *)fmt;
	li= 0;				/* total output line length */

	while (c = *format++) {
		if (c == '%') {
			si= 0;				/* output buffer index */
			ljflag= zfflag= sepflag= 0;	/* no options yet */
			if (*format == '-') {		/* left-justify */
				format++;
				ljflag++;
			}
			if (*format == '0') zfflag++;	/* test for zero-fill */

			width = (isdigit(*format)) ? _gv2(&format) : 0;

			c= *format++;			/* get format char */

			if (c == ',') {			/* en-comma-ization */
				c= *format++;		/* re-get format char */
				sepflag= 1;
			}
			longarg= (long *)args;		/* preset ptrs */
			charstararg= (char **)args;	/* to various arg types */
			islong= 0;			/* assume short arg */

			if (c == 'l') {			/* if long, */
				c= *format++;		/* skip the L, */
				sizearg= sizeof(long);	/* for bumping pointer */
				longarg= (long *)args;	/* force confusing typing */
				num= *longarg;		/* get the value, */
				islong= 1;

			} else {
				sizearg= sizeof(int);
				num= 0L + *args;
			}
			sign= '\0';			/* not negative */
			switch(toupper(c)) {
				case 'D':
					if (num < 0L) {	/* for negative numbers */
						sign= '-'; /* flag it */
						--width;
						num= 0L - num; /* and flip it */
					}		/* fall through */
				case 'U': base = 10; goto val;
				case 'X': base = 16; goto val;
				case 'O': base = 8; goto val;
				case 'A': base = 36; goto val;

/* _uspr() builds the plain digit string in wb, the Work Buffer. If using
comma formatting, then separators are added before the string is moved to
sb, the String Buffer. */

/* Since Lattice 2.15 doesn't have an 'unsigned long' type, we have
cannot have long numbers < 0; or else infinite recursion in uspr results.
Flag truncated long ints with a leading "*". */

val:					if (islong) {
						if (num & ~B32MASK) sign= '*';
						num &= B32MASK;

					} else num &= 0xffffL;

					_uspr(wb,&si,num,base);
					if ((si > 3) && sepflag) {
						l = si % 3;
						if (l == 0) l= 3;
/*
				si= raw string length
				wb= raw digit string (ie. "12345")
				sb= working string (ie. "12,345")
				l= 100's digits counter, MOD 3
				i= index into sb[]
				j= index into wb[]
*/
						for (i= j= 0; j < si;) {
							sb[i++]= wb[j++];
							if ((--l == 0) && (j < si))
								sb[i++]= _spr_sepchar;
							if (l <= 0) l= 3;
						}
						si= i;		/* see pad: */

					} else for (i= 0; i < si; i++) sb[i]= wb[i];

					width -= i;	/* compensate for actual width */

					if (islong) args= (int *) ++longarg;
					else ++args;
					goto pad;

/* A char on the stack is the same size as an int. */

				case 'C':
					sb[si++]= *(char *)args;
					++args;
					width--;
					goto pad;

				case 'S':
					sptr = *(char **)args;
					args= (int *) ++charstararg;

					while (*sptr) {
						sb[si++]= *sptr++;
						width--;
					}

/* Pad this out to the specified width, if any. Insert the leading sign
character.  */

pad:					sb[si]= '\0';		/* terminate it */
					if (! ljflag) {
						while (width-- > 0) {
							line[li++]= (zfflag ? '0' : ' ');
						}
					}
					if (sign) line[li++]= sign; /* possible sign char */
					for (i= 0; sb[i];)	/* then the text */
						line[li++]= sb[i++];

					if (ljflag) while (width-- > 0)
						line[li++]= ' ';
					break;

				 default: line[li++]= c; break;
			}

		} else line[li++]= c;	/* else not % */
	}
	line[li]= '\0';
}

/*
	Internal routine used by "_spr" to perform ascii-
	to-decimal conversion and update an associated pointer:
*/

int _gv2(sptr)
char **sptr;
{
int n;
	n = 0;
	while (isdigit(**sptr)) n = 10 * n + *(*sptr)++ - '0';
	return(n);
}

static char itoctbl[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

_uspr(buff, buffi, n, base)
char buff[];
unsigned *buffi;
long n;
unsigned base;
{
unsigned length;

	if ((n < base) && (n >= 0L)) {
		buff[*buffi]= itoctbl[n];
		if (*buffi < MAXSTR) ++(*buffi);
		buff[*buffi]= '\0';
		return(1);
	}
	length = _uspr(buff, buffi, (long)(n / base), base);
	_uspr(buff, buffi, (long)(n % base), base);
	return (length + 1);
}
