#include <ascii.h>
#include <stdio.h>
#include <ctype.h>
/*

ReadMail -- offine message reader/privacy shell for MSDOS/FidoNet
copyright Tom Jennings 1992
Box 77731, San Francisco, CA 94107, USA, 1:125/111@fidonet

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/
#define MAXSTR 300

/*
Format string options (some extentions):

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

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

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

/* Write a string to the console. */

cputs(s)
char *s;
{
	while (*s) bdos(6,*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);
}

/* Format string processor. Relies mainly on the order of stack arguments;
"standard" Lattice/Microsoft etc. The call:

	sprintf(buffer,fmt,arg1,arg2,...);

	results in the arguments on the stack in the following order:

addr+N	...
	arg2
	arg1
-->	&fmt
addr	buffer

The call to _spr() passes a pointer to 'fmt' itself, with it's args,
if any, following.

	_spr(buffer,&fmt);

This code takes the address of 'fmt' and indexes the stack. Object
sizes are coded with #define's below. I suppose I should use unions
but I don't feel like recoding it.

There is some funny business also around long vs. short ints, what with
negative numbers and a bit mask.

Code originally stolen from Leor Zolman's STDLIB2 for CP/M BDS C 1.42. 
(ca. 1980) Brain Damage Software still lives on ... */

/* ?!?!?!? sizeof(char *) is 4?!?!?!?!
#define CHARSTARSIZE sizeof(char near *) */

#define CHARSTARSIZE sizeof(int) /* yeeow */
#define CHARSIZE sizeof(int)	/* occupies 2 bytes of stack! */
#define INTSIZE sizeof(int)	/* plus unsigneds */
#define LONGSIZE sizeof(long)

_spr(line,fmt)
char *line, **fmt;
{
char c, base, *format;
char ljflag, sepflag;		/* feature flags */
char fillchar;			/* '0' for zero fill else ' ' */
char sign;			/* ASCII sign character; NULL else '-' */
int width;			/* format width else 0 */
long num;			/* numerical value */

char *args;			/* used as a BYTE POINTER */
unsigned sizearg;		/* size of current object */

char *lp;			/* ptr into line[] */
char buff[MAXSTR];		/* item work buffer */
char *sp;			/* ptr into buff[] */
char wb[32];			/* room for largest possible digit string */
char *cp;			/* ptr into wb[] */
unsigned i;

char *_uspr();

	format = (char *)(*fmt++);			/* format text */
	args= (char *)fmt;				/* ptr to first arg */
	lp= line;					/* output pointer */

	while (c = *format++) {
		if (c == '%') {
			sp= buff;			/* output buffer index */
			sign= ljflag= sepflag= 0;	/* no options yet */
			sizearg= INTSIZE;		/* most commonly used */
			num= 0L + *(unsigned *)args;	/* %d, %u, %x, etc */
			fillchar= ' ';			/* assume space fill */
			if (*format == '-') {		/* left-justify */
				format++;
				ljflag= 1;
			}
			if (*format == '0') fillchar= '0';/* test for zero-fill */

			width= _gv2(&format);		/* specified width else 0 */

next_fmt:;		c= *format++;			/* get format char */
			switch (toupper(c)) {

/* NOTE: sepflag is used to (1) flag en-comma-ization and (2) determine 
whether to en-comma after 4 (%,) or 3 (%;) digits. */

			case ',':
				sepflag= 4;		/* flag en-comma-ization */
				goto next_fmt;		/* get format */

			case ';':
				sepflag= 3;		/* flag en-comma-ization */
				goto next_fmt;		/* get format */

			case 'L':
				num= *(long *)args;	/* get the value, */
				sizearg= LONGSIZE;	/* for bumping pointer */
				goto next_fmt;		/* re-get format */

			case 'D':
				if (num < 0L) {		/* for negative numbers */
					sign= '-';	/* flag it */
					--width;
					num= -num;	/* flip it */
					if (sizearg == LONGSIZE)
						num &= 0xffff; /* make 16 bits */
				}			/* 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, separators are added before the string is moved to
buff, the String Buffer. */

val:				i= _uspr(cp= wb,num,base) - wb; /* i= digit count */
				if (sepflag && (i > sepflag)) {

/* Encommaization consists of putting a comma between every group of (sepflag)
digits. Leading digits are a special case, since there may be 1 or two. If
so, copy them and add their comma. */

					if (i %= 3) {
						while (i--) *sp++= *cp++; /* copy leading digits */
						*sp++= _spr_sepchar;
					}

/* Now simply copy groups of three digits and a comma. (There are > 3 digits; 
we checked. It's also a multiple of 3 remaining; we enforced that.) */

					while (*cp) {
						*sp++= *cp++;	/* copy digits */
						*sp++= *cp++;
						*sp++= *cp++;
						if (*cp) *sp++= _spr_sepchar;
					}
					*sp= '\0';		/* terminate string */

				} else while (*sp= *cp++) ++sp;	/* string copy */

				width -= (sp - buff);		/* actual width */
				goto pad;

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

			case 'C':
				*sp++= *args;
				sizearg= CHARSIZE;
				*sp= '\0';
				width--;
				goto pad;

			case 'S':
				cp= *(char **)args;	/* ptr to text */
				sizearg= CHARSTARSIZE;

				while (*sp++= *cp++) --width;

/* Pad it out on the left or the right, and add the sign character. */

pad:				if (! ljflag) {		/* if left justified */
					while (width-- > 0) {
						*lp++= fillchar;
					}		/* pad on the left */
				}
				if (sign) *lp++= sign;	/* possible sign char */
				sp= buff; 		/* copy item text */
				while (*lp= *sp++) ++lp;
				if (ljflag) while (width-- > 0)
					*lp++= ' ';	/* pad on the right */
				args += sizearg;	/* point to next arg */
				break;

			default: *lp++= c; break;
			}

		} else *lp++= c;		/* end if '%' */
	}
	*lp= '\0';
}

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

/* **************** 

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

USPR.ASM not working yet **************** */


static char itoctbl[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

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