/*
	name...
		printf

	purpose...
		formatted I/O

	authors...
		Original program by J. E. Hendrix, floating
		point functions added by J. R. Van Zandt.

	history...
		11 Sept 84 Calling _outc() rather than putc(),
			_outc() calls putc() or putchar().
		9 Sept 84  Calling putc() rather than putchar(),
			1st parameter can be device #.
		11 Aug 84  ftoe() handles argument of 0.
		3 Aug 84   Calling putchar() directly rather
			than through cout().
		Aug 84  Added atof()
		12 Aug 83  Added 'e' format.
		10 Aug 83  changed back to &argcnt+i-1.
			Added 'f' format.
		8 Aug 83  Changed addr of first arg to
			&argcnt + i + 1.
*/

#include printf2.h
#include iolib.h
#include float.h

#define NULL 0
#define ERR -1

int device;

/*
** printf(controlstring, arg, arg, ...) -- formatted print
**        operates as described by Kernighan & Ritchie
**        only d, x, c, s, f, e, and u specs are supported.
*/
printf(argcnt) int argcnt; {
  int i, width, prec, preclen, len, *nxtarg;
  char *ctl, *cx, c, right, str[128], *sptr, pad;
  double *pd;
  i = argc();   /* fetch arg count from A reg first */
  nxtarg = &argcnt + i - 1;
  ctl = device = *nxtarg;
  if (device==(device&31))	/* *small* value must be device # */
	{ctl=*--nxtarg;}
  else device=0;
  while(c=*ctl++) {
    if(c!='%') {_outc(c); continue;}
    if(*ctl=='%') {_outc(*ctl++); continue;}
    cx=ctl;
    if(*cx=='-') {right=0; ++cx;} else right=1;
    if(*cx=='0') {pad='0'; ++cx;} else pad=' ';
    if((i=utoi(cx, &width)) >= 0) cx=cx+i; else continue;
    if(*cx=='.') {
      if((preclen=utoi(++cx, &prec)) >= 0) cx=cx+preclen;
      else continue;
      }
    else preclen=0;
    sptr=str; c=*cx++; i=*(--nxtarg);
    if(c=='d') itod(i, str, 7);
    else if(c=='x') itox(i, str, 7);
    else if(c=='c') {str[0]=i; str[1]=NULL;}
    else if(c=='s') sptr=i;
    else if(c=='u') itou(i, str, 7);
    else
	{if(preclen==0)prec=6;
	if(c=='f')
	    {nxtarg=nxtarg-2; pd=nxtarg; ftoa(*pd,prec,str);
	    }
	else if(c=='e')
	    {nxtarg=nxtarg-2; pd=nxtarg; ftoe(*pd,prec,str);
	    }
	else continue;
	}
    ctl=cx; /* accept conversion spec */
    if(c!='s') while(*sptr==' ') ++sptr;
    len=-1; while(sptr[++len]); /* get length */
    if((c=='s')&(len>prec)&(preclen>0)) len=prec;
    if(right) while(((width--)-len)>0) _outc(pad);
    while(len) {_outc(*sptr++); --len; --width;}
    while(((width--)-len)>0) _outc(pad);
    }
  }


/*
** utoi -- convert unsigned decimal string to integer nbr
**          returns field size, else ERR on error
*/
utoi(decstr, nbr)  char *decstr;  int *nbr;  {
  int d,t; d=0;
  *nbr=0;
  while((*decstr>='0')&(*decstr<='9')) {
    t=*nbr;t=(10*t) + (*decstr++ - '0');
    if ((t>=0)&(*nbr<0)) return ERR;
    d++; *nbr=t;
    }
  return d;
  }


/*
** itod -- convert nbr to signed decimal string of width sz
**         right adjusted, blank filled; returns str
**
**        if sz > 0 terminate with null byte
**        if sz = 0 find end of string
**        if sz < 0 use last byte for data
*/
itod(nbr, str, sz)  int nbr;  char str[];  int sz;  {
  char sgn;
  if(nbr<0) {nbr = -nbr; sgn='-';}
  else sgn=' ';
  if(sz>0) str[--sz]=NULL;
  else if(sz<0) sz = -sz;
  else while(str[sz]!=NULL) ++sz;
  while(sz) {
    str[--sz]=(nbr%10+'0');
    if((nbr=nbr/10)==0) break;
    }
  if(sz) str[--sz]=sgn;
  while(sz>0) str[--sz]=' ';
  return str;
  }


/*
** itou -- convert nbr to unsigned decimal string of width sz
**         right adjusted, blank filled; returns str
**
**        if sz > 0 terminate with null byte
**        if sz = 0 find end of string
**        if sz < 0 use last byte for data
*/
itou(nbr, str, sz)  int nbr;  char str[];  int sz;  {
  int lowbit;
  if(sz>0) str[--sz]=NULL;
  else if(sz<0) sz = -sz;
  else while(str[sz]!=NULL) ++sz;
  while(sz) {
    lowbit=nbr&1;
    nbr=(nbr>>1)&32767;  /* divide by 2 */
    str[--sz]=((nbr%5)<<1)+lowbit+'0';
    if((nbr=nbr/5)==0) break;
    }
  while(sz) str[--sz]=' ';
  return str;
  }


/*
** itox -- converts nbr to hex string of length sz
**         right adjusted and blank filled, returns str
**
**        if sz > 0 terminate with null byte
**        if sz = 0 find end of string
**        if sz < 0 use last byte for data
*/
itox(nbr, str, sz)  int nbr;  char str[];  int sz;  {
  int digit, offset;
  if(sz>0) str[--sz]=NULL;
  else if(sz<0) sz = -sz;
  else while(str[sz]!=NULL) ++sz;
  while(sz) {
    digit=nbr&15; nbr=(nbr>>4)&4095;
    if(digit<10) offset=48; else offset=55;
    str[--sz]=digit+offset;
    if(nbr==0) break;
    }
  while(sz) str[--sz]=' ';
  return str;
  }
/*
	name...
		putf

	purpose...
		to convert from double precision floating
		point to ASCII string

	history...
		10 Aug 82  Adapted from 'putf': output into
			a string, no padding.
		11 Oct 82 'int' => 'floor'
		4 Oct 82  Using floating point constants,
			not scaling x down before printing
			digits before decimal point.
*/
ftoa(x,f,str)
double x;	/* the number to be converted */
int f;		/* number of digits to follow decimal point */
char *str;	/* output string */
{	double scale;	/* scale factor */
	int i,		/* copy of f, and # digits before
			decimal point */
	minus,		/* nonzero if x<0 */
	d;		/* a digit */
	if(x>=0.) minus=0;
	else	{minus=1; x=-x;}
	if(minus)*str++ = '-';
			/* round off the number */
	i=f;
	scale=2.;
	while(i--)scale=scale*10.;
	x=x+1./scale;
	/* count places before decimal & scale the number */
	i=0;
	scale=1.;
	while(x>=scale) {scale=scale*10.; i++;}
	while(i--)	/* output digits before decimal */
		{scale=floor(.5+scale*.1);
		d=ifix(x/scale);
		*str++ = d+'0';
		x=x-float(d)*scale;
		}
	if(f<=0) {*str=NULL;return;}
	*str++ = '.';
	while(f--)	/* output digits after decimal */
		{x=x*10.; d=ifix(x);
		*str++ = d+'0'; x=x-float(d);
		}
	*str=NULL;
}
/*	e format conversion			*/
ftoe(x,prec,str)
double x;	/* number to be converted */
int prec;	/* # digits after decimal place */
char *str;	/* output string */
{	double scale;	/* scale factor */
	int i,		/* counter */
	d,		/* a digit */
	expon;		/* exponent */
	scale=1.;		/* scale = 10 ** prec */
	i=prec; while(i--) scale=scale*10.;
	if(x==0.) {expon=0; scale=scale*10.;}
	else	{expon=prec;
		if(x<0.) {*str++='-'; x=-x;}
		if(x>scale)	/* need: scale<x<scale*10 */
			{scale=scale*10.;
			while(x>=scale) {x=x/10.; ++expon;}
			}
		else	{while(x<scale) {x=x*10.; --expon;}
			scale=scale*10.;
			}
		/* at this point, .1*scale <= x < scale */
		x=x+.5;			/* round */
		if(x>=scale) {x=x/10.; ++expon;}
		}
	i=0;
	while(i<=prec)
		{scale=floor(.5+scale*.1);
		/* now, scale <= x < 10*scale */
		d=ifix(x/scale);
		*str++ = d+'0';
		x=x-float(d)*scale;
		if(i++) continue;
		*str++ ='.';
		}
	*str++='e';
	if(expon<0) {*str++='-'; expon=-expon;}
	if(expon>9) *str++ ='0'+expon/10;
	*str++='0'+expon%10;
	*str=NULL;
}

	/* decimal to (double) binary conversion */

atof(s) char s[]; /* s points to a character string */
{	double sum,	/* the partial result */
	scale;		/* scale factor for the next digit */
	char *start,	/* copy if input pointer */
	*end,		/* points to end of number */
	c;		/* character from input line */
	int minus,	/* nonzero if number is negative */
	dot,		/* nonzero if *s is decimal point */
	decimal;	/* nonzero if decimal point found */
	if(*s=='-') {minus=1; s++;}
	else minus=0;
	start=s;
	decimal=0;  /* no decimal point seen yet */
	while(((*s<='9')&(*s>='0'))|(dot=(*s=='.')))
		{if(dot)decimal++;
		s++;	/* scan to end of string */
		}
	end=s;
	sum=0.;	/* initialize answer */
	if(decimal)  /* handle digits to right of decimal */
		{s--;
		while(*s!='.')
			sum=(sum+float(*(s--)-'0'))/10.;
		}
	scale=1.;	/* initialize scale factor */
	while(--s>=start)	/* handle remaining digits */
		{sum=sum+scale*float(*s-'0'); scale=scale*10.;}
	c=*end++;
	if((c=='e')|(c=='E'))	/* interpret exponent */
		{int neg,	/* nonzero if exp negative */
		expon,		/* absolute value of exp */
		k;		/* mask */
		neg=expon=0;
		if(*end=='-')	/* negative exponent */
			{neg=1; end++;}
		while(1)	/* read an integer */
			{if((c=*end++)>='0')
				{if(c<='9')
					{expon=expon*10+c-'0';
					continue;
					}
				}
			break;
			}
		if(expon>38) {puts("overflow"); expon=0;}
		k=32;	/* set one bit in mask */
		scale=1.;
		while(k)
			{scale=scale*scale;
			if(k&expon) scale=scale*10.;
			k=k>>1;
			}
		if(neg) sum=sum/scale;
		else    sum=sum*scale;
		}
	if(minus)sum=-sum;
	return sum;
}

_outc(c) char c;
{	if(device) putc(c,device);
	else putchar(c);
}
                                                                          
