Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - kcc-5/lib/stdio/printf.c
There are 9 other files named printf.c in the archive. Click here to see a list.
/* PRINTF - formatted output conversion.
 *	Copyright (C) 1985 by Ken Harrenstien, SRI International.
 *
 * This version has been written to conform to the description of
 * printf in the CARM (Harbison & Steele) book.
 * !!! NOTE: Additional facilities are provided in the form of allowing
 * the user to "bind" new conversion operation chars to arbitrary functions.
 */

/* Known bugs & limitations:
 *	sprintf depends on internal knowledge of STDIO, and thus is currently
 *	specific to KCC.  STDIO should provide a string-open function.
 *
 *	MAXSIGDIG must be set to at least the largest possible number of digits
 *	that can be output for any integer type, in order to avoid buffer
 *	overflow.  Making this dynamic is possible but would be extremely
 *	inefficient.  Floating point output will print "*" for any fields
 *	that would otherwise exceed MAXSIGDIG.  This constant (or something
 *	that it can be derived from) should be provided by c-env.h.
 *	Eventually the existence of the ANSI-draft <float.h> will provide
 *	useful constants that can be used here.
 *
 *	Floating-point output can be incorrect in the last digit (or two)
 *	if the user requests too great a precision, because the roundoff method
 *	fails when the hardware precision is exceeded.
 *	It would be possible to determine the max precision at runtime and
 *	then limit things to that, by modifying both __prnd and __pff.
 *	Again, <float.h> will solve these problems.
 */
#ifdef COMMENT
Todo:
	Could improve efficiency of floating-point output stuff.
		e.g. __prexp is slow for large exponents.
	Conversion types could share more code at cost of some efficiency.

Routine provided to bind, unbind %-chars with routines.
(Also allow replacement of entire formatting scanner?)

int (*prf_bind)();		/* Returns previous binding value */

To bind:	prf_bind('i',prt_id);
To unbind:	prf_bind('i',0);
To replace scanner: prf_bind(0,prt_scan);
To restore scanner: prf_bind(0,0);

Conversion routine:
	prt_id(wp);			/* Pointer to workspace */

#endif /*COMMENT*/
#ifndef MAXSIGDIG
#define MAXSIGDIG 100
#endif

/* NOTE: Before installing source in library dir, change the <>s to ""s */
#include "c-env.h"
#include "stdio.h"			/* this is part of standard I/O lib */
#include "ctype.h"
#include "varargs.h"
#include "printf.h"			/* To get workspace & routine def */

/* Imported routines */
extern char *calloc(), *realloc();
extern double modf(), floor();

/* Local routines */
static void _prfill(), _prfs();
static void _prfc();
static void _prfo(), _prfx(), _prfd(), _prfu();
static char *__pd(),  *__pu(), *__po(),   *__px(),  *__pxu();
static char *__pld(), *__plu(), *__plo(), *__plx(), *__plxu();

static void _prff(), _prfe(), _prfg(), __pflt();
static char *__pfi(), *__pff(), *__pexp();
static double __prnd(), __fexp();
/* PRINTF - formatted output to stdout stream
*/
int
printf(format, va_alist)
char *format;
va_dcl		/* No semicolon */
{	va_list vargp;

	va_start(vargp);	/* Set up pointer to args */
	return _doprnt(format, &vargp, stdout);
}

/* FPRINTF - formatted output to general FILE* stream
*/
int
fprintf(stream, format, va_alist)
FILE *stream;
char *format;
va_dcl					/* No semicolon */
{
    va_list vargp;

    va_start(vargp);			/* Set up pointer to args */
    return _doprnt(format, &vargp, stream);
}

/* SPRINTF - formatted output to string
**	(done by hacking up a fake stream to point into memory)
*/
int
sprintf(s, format, va_alist)
char *s, *format;
va_dcl					/* No semicolon */
{
	FILE *f;
	int result;
	va_list vargp;

	va_start(vargp);		/* Set up pointer to args */

	f = sopen(s, "w", 32767);	/* open the string for output */
	result = _doprnt(format, &vargp, f);	/* print to fake stream */
	putc('\0', f);			/* append a null to terminate */
	fclose(f);
	return result;			/* return pointer to start of string */
}
typedef void (*faddr)();	/* FADDR - function addr type */

/* For holding char bindings */
struct pf_bent {
	int pfe_type;	/* 0 - last entry */
	faddr pfe_rtn;
};

static struct pf_bent *_pfearray;
static int _pfecnt;

faddr
prf_bind(ch, rtn)
register int ch;
faddr rtn;
{	register struct pf_bent *ep;
	faddr ret;

	/* First see if char already exists */
	if(ep = _pfearray)
	  for(; ep->pfe_type; ++ep)
		if(ch == ep->pfe_type)
		  {	ret = ep->pfe_rtn;	/* Save old rtn val */
			if(rtn) ep->pfe_rtn = rtn;
			else ep->pfe_type = 0, ep->pfe_rtn = 0;
			return(ret);
		  }

	/* Not already in table, so add new entry. */
	if (ep)
	    ep = (struct pf_bent *) realloc((char *) _pfearray,
					++_pfecnt * sizeof(struct pf_bent));
	else
	    ep = (struct pf_bent *) calloc((_pfecnt=2),sizeof(struct pf_bent));
	_pfearray = ep;
	ep += _pfecnt-2;	/* Point to next-to-last entry */
	ep->pfe_type = ch;
	ep->pfe_rtn = rtn;
	(++ep)->pfe_type = 0;	/* Ensure last entry has zero type */
	return(0);
}
int
_doprnt(format, vargp, stream)
register char *format;
va_list *vargp;
FILE *stream;
{
	register int c, i;
	register struct pf_bent *ep;
	struct pf_work pfw;

	if (!_writeable(stream) || ferror(stream))
		return EOF;
	pfw.pfw_argp = *vargp;	/* Set up pointer to args */
	pfw.pfw_fp = stream;
	pfw.pfw_cnt = 0;

 loop:	while(c = *format++)
	  if(c != '%') putc(c,stream), pfw.pfw_cnt++;
	  else
	  {	/* Start handling % stuff */
		c = *format;

		/* First check for all optional flag chars */
		pfw.pfw_flag = 0;	/* Clear all flags */
		for(;;)
		  {	switch(c)
			  {
			case '-': pfw.pfw_flag |= PFF_NEG; break;
			case '+': pfw.pfw_flag |= PFF_POS; break;
			case '0': pfw.pfw_flag |= PFF_ZER; break;
			case ' ': pfw.pfw_flag |= PFF_SPA; break;
			case '#': pfw.pfw_flag |= PFF_ALT; break;
			default: goto flgdon;
			  }
			c = *++format;
		  }
	flgdon:

		if(pfw.pfw_flag&PFF_ZER)	/* Zero fill? */
			pfw.pfw_fill = '0';
		else pfw.pfw_fill = ' ';

		/* Get (optional) field width */
		if(c == '*')
		  {	if((pfw.pfw_fwid = va_arg(pfw.pfw_argp, int)) < 0)
			  {	pfw.pfw_flag |= PFF_NEG;
			  }
			c = *++format;
		  }
		else
		  {	pfw.pfw_fwid = 0;		/* initialize width */
			while (isdigit(c))
			  {	pfw.pfw_fwid = pfw.pfw_fwid * 10 + (c - '0');
				c = *++format;
			  }
		  }
		if(pfw.pfw_flag&PFF_NEG)
			pfw.pfw_fwid = -pfw.pfw_fwid;

		/* Get (optional) precision */
		if(c == '.')
		  {
			if((c = *++format) == '*')
			  {	pfw.pfw_prec = va_arg(pfw.pfw_argp, int);
				c = *++format;
			  }
			else	/* prec given, initialize and parse */
			  {	pfw.pfw_prec = 0;
				while(isdigit(c))
				  {	pfw.pfw_prec = pfw.pfw_prec * 10
						 + c - '0';
					c = *++format;
				  }
			  }
		  }
		else pfw.pfw_prec = -1;		/* no precision given */


		/* Now for compatibility with 4.2BSD, check for
		 * '#' alternate-form flag which can come after precision.
		 */
		if(c == '#')
		  {	pfw.pfw_flag |= PFF_ALT;
			c = *++format;
		  }

		/* Now check for (optional) long modifier */
		if(c == 'l')
		  {	pfw.pfw_flag |= PFF_LONG;
			c = *++format;
		  }
		
		/* Now handle the (required) conversion operation */
		/* First scan dynamic %-char array to see if there
		 * is any match.
		 */
		pfw.pfw_char = c;	/* Store conversion op char */
		if(ep = _pfearray)
		  for(; ep->pfe_type; ++ep)
			if(ep->pfe_type == c)
			  {	(*(ep->pfe_rtn))(&pfw);	/* Found it, invoke! */
				goto cnvdon;		/* Then exit loop */
			  }

		/* No match in the array, check standard built-in chars */
		switch(c)
		  {
			case '%':
				_prfill(&pfw, (char *)0, "%", 0);
				break;

			case 'c': _prfc(&pfw); break;
			case 's': _prfs(&pfw); break;
			case 'o': _prfo(&pfw); break;
			case 'X':
			case 'x': _prfx(&pfw); break;
			case 'u': _prfu(&pfw); break;
			case 'd': _prfd(&pfw); break;

			case 'f': _prff(&pfw); break;
			case 'E':
			case 'e': _prfe(&pfw); break;
			case 'G':
			case 'g': _prfg(&pfw); break;

			/* Unknown conversion char, print error msg */
			default:
				fputs("(printf: '", pfw.pfw_fp);
				putc(c, pfw.pfw_fp);
				fputs("'?)", pfw.pfw_fp);
				pfw.pfw_cnt += 14;
				break;
		  }

	cnvdon:	++format;
	  }	/* end of %-handling condition */

	va_end(pfw.pfw_argp);	/* Wrap up (dunno why, just do it) */

	/* Return EOF if any error, else # chars output */
	return ferror(pfw.pfw_fp) ? EOF : pfw.pfw_cnt;
}
/* _PRFILL - General-purpose routine to handle field padding.
 */
static void
_prfill(wp, pref, cval, precf)
register struct pf_work *wp;
char *pref;	/* Prefix string (may be null ptr) */
char *cval;	/* Converted value string (must exist) */
int precf;	/* Additional precision fill (always 0) */
{	register int i;
	register int plen, clen;	/* Length of prefix and cval strings */
	int fwid, totlen;

	plen = pref==0 ? 0 : strlen(pref);
	clen = strlen(cval);
	wp->pfw_cnt += (totlen = precf + plen + clen);	/* Find # chars */

	if((fwid = wp->pfw_fwid) <= 0)
	  {	/* Left justified or no field */
		if(plen) fputs(pref, wp->pfw_fp);
		if((i = precf) > 0) {
			do putc('0', wp->pfw_fp);
			while(--i);
		}
		fputs(cval, wp->pfw_fp);
		if(fwid >= 0) return;	/* If no field width, done! */

		i = (-fwid) - totlen;
		if(i > 0) {
			wp->pfw_cnt += i;
			do putc(' ', wp->pfw_fp);
			while(--i);
		}
	  }
	else	/* Right justification */
	  {	
		if ((i = fwid - totlen) > 0)
			wp->pfw_cnt += i;	/* This much left padding */

		/* Do either 0-padding or SP-padding */
		if(i > 0 && (wp->pfw_flag&PFF_ZER)==0) {
			do putc(' ', wp->pfw_fp);
			while(--i);
		}
		if(plen) fputs(pref, wp->pfw_fp);
		if(i > 0 && (wp->pfw_flag&PFF_ZER)!=0) {
			do putc('0', wp->pfw_fp);
			while(--i);
		}

		if((i = precf) > 0) {
			do putc('0', wp->pfw_fp);
			while(--i);
		}
		fputs(cval, wp->pfw_fp);
	  }
}
/* _PRFS - Type 's' formatted output
 *	Ignores l, # options.
 *	Positive precision truncates length to that value.
 */

static void
_prfs(wp)
struct pf_work *wp;
{
	register char *cp;
	register int len, i;
	int trunc;

	cp = va_arg(wp->pfw_argp, char *);	/* Get pointer to string */
	if(cp == (char *)0)
		cp = "(null)";			/* Be kind to zero ptrs */
	len = strlen(cp);		/* Find length of string */

	/* Special fast check for usual case of %s */
	if(wp->pfw_fwid == 0 && wp->pfw_prec <= 0)
	  {	fputs(cp, wp->pfw_fp);		/* Left adj, no prec */
		wp->pfw_cnt += len;
		return;			
	  }

	/* Check length against precision to see if truncation needed */
	trunc = 0;
	if(wp->pfw_prec > 0 && wp->pfw_prec < len)
	  {	len = wp->pfw_prec;
		trunc++;	/* Sigh, remember to truncate string */
	  }

	/* See whether to prepad */
	if((i = wp->pfw_fwid - len) > 0)
	  {	wp->pfw_cnt += i;
		do putc(wp->pfw_fill, wp->pfw_fp);
		while(--i);
	  }

	/* Now output string */
	wp->pfw_cnt += len;
	if(!trunc) fputs(cp, wp->pfw_fp);
	else if((i = len) > 0)
	  {	do putc(*cp++, wp->pfw_fp);
		while(--i);
	  }

	/* Now postpad if necessary */
	if(wp->pfw_fwid < 0 && (i = -(wp->pfw_fwid) - len) > 0)
	  {	wp->pfw_cnt += i;
		do putc(' ', wp->pfw_fp);
		while(--i);
	  }
}
/* _PRFC - Character output
 *	Only uses field width, "-", and '0' flags.
 */
static void
_prfc(wp)
register struct pf_work *wp;
{	char tmpbuf[2];

	tmpbuf[0] = va_arg(wp->pfw_argp, int);
	tmpbuf[1] = '\0';
	_prfill(wp, (char *)0, tmpbuf, 0);
}
/* Simple Number conversions */

/* _PRFO - Octal number output
 *	
 *	# flag prefixes a '0'.
 *	+ and SP flags ignored.
 */
static void
_prfo(wp)
register struct pf_work *wp;
{
	register int i;
	char tmpbuf[MAXSIGDIG];		/* For bare-bones octal string */

	if(wp->pfw_flag&PFF_LONG)
		__plo(tmpbuf, va_arg(wp->pfw_argp, unsigned long));
	else    __po (tmpbuf, va_arg(wp->pfw_argp, unsigned int));

	if(wp->pfw_prec == 0 && tmpbuf[0] == '0')	/* Special case */
		tmpbuf[0] = '\0';		/* can produce null string */
	if((i = wp->pfw_prec - strlen(tmpbuf)) < 0)
		i = 0;

	_prfill(wp,	/* workspace ptr */
		((wp->pfw_flag&PFF_ALT) ? "0" : (char *)0),	/* prefix */
		tmpbuf,	/* Converted val */
		i);	/* additional precision digits */
}

/* _PRFX - Hexadecimal number output
 *	
 */
static void
_prfx(wp)
register struct pf_work *wp;
{
	register int i;
	char tmpbuf[MAXSIGDIG];

	if(wp->pfw_flag&PFF_LONG)
	  {	if(wp->pfw_char == 'X')
			__plxu(tmpbuf, va_arg(wp->pfw_argp, unsigned long));
		else    __plx (tmpbuf, va_arg(wp->pfw_argp, unsigned long));
	  }
	else
	  {	if(wp->pfw_char == 'X')
			__pxu(tmpbuf, va_arg(wp->pfw_argp, unsigned int));
		else    __px (tmpbuf, va_arg(wp->pfw_argp, unsigned int));
	  }

	if(wp->pfw_prec == 0 && tmpbuf[0] == '0')	/* Special case */
		tmpbuf[0] = '\0';		/* can produce null string */
	if((i = wp->pfw_prec - strlen(tmpbuf)) < 0)
		i = 0;

	_prfill(wp,
		((wp->pfw_flag&PFF_ALT) ?
			((wp->pfw_char == 'X') ? "0X" : "0x")
			: (char *) 0),
		tmpbuf, i);
}

/* _PRFD - Decimal number output
 *	
 */
static void
_prfd(wp)
register struct pf_work *wp;
{
	char tmpbuf[MAXSIGDIG];
	int arg, i;
	long larg;
	char *pref;

	pref = 0;
	if(wp->pfw_flag&PFF_LONG)
	  {	if((larg = va_arg(wp->pfw_argp, long)) < 0)
		  {	pref = "-";
			larg = -larg;
		  }
		__pld(tmpbuf, larg);
	  }
	else
	  {	if((arg = va_arg(wp->pfw_argp, int)) < 0)
		  {	pref = "-";
			arg = -arg;
		  }
		__pd(tmpbuf, arg);
	  }
	if(!pref)
	  {	if     (wp->pfw_flag&PFF_POS) pref = "+";
		else if(wp->pfw_flag&PFF_SPA) pref = " ";
	  }

	if(wp->pfw_prec == 0 && tmpbuf[0] == '0')	/* Special case */
		tmpbuf[0] = '\0';		/* can produce null string */
	if((i = wp->pfw_prec - strlen(tmpbuf)) < 0)
		i = 0;

	_prfill(wp, pref, tmpbuf, i);
}

/* _PRFU - Unsigned decimal number output
 *	
 */
static void
_prfu(wp)
register struct pf_work *wp;
{
	char tmpbuf[MAXSIGDIG];
	int i;

	if(wp->pfw_flag&PFF_LONG)
		__plu(tmpbuf, va_arg(wp->pfw_argp, unsigned long));
	else    __pu (tmpbuf, va_arg(wp->pfw_argp, unsigned int));

	if(wp->pfw_prec == 0 && tmpbuf[0] == '0')	/* Special case */
		tmpbuf[0] = '\0';		/* can produce null string */
	if((i = wp->pfw_prec - strlen(tmpbuf)) < 0)
		i = 0;

	_prfill(wp, (char *)0, tmpbuf, i);
}
/* Actual number conversion subroutines. */

static
#if CPU_PDP10
	int		/* Use more efficient addressing on PDP10s */
#else
	char
#endif
    __pntab[16] = {
	'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' },
    __pxtab[16] = {
	'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };

static char *
__pd (s, n)		/* Decimal */
char *s;
int n;
{	int q;

	q = n / 10;
	n %= 10;
	if (q) s = __pd(s, q);	/* high order digits */
	*s++ = __pntab[n];	/* bottom digit */
	*s = '\0';		/* null-terminate */
	return(s);		/* return string for recursive call */
}

static char *
__pld(s, n)		/* Long Decimal */
char *s;
long n;
{	long q;

	q = n / 10;
	n %= 10;
	if (q) s = __pd(s, q);	/* high order digits */
	*s++ = __pntab[n];	/* bottom digit */
	*s = '\0';		/* null-terminate */
	return(s);		/* return string for recursive call */
}

static char *
__pu (s, n)		/* Unsigned Decimal */
char *s;
unsigned int n;
{	unsigned int q;

	q = n / 10;
	n %= 10;
	if (q) s = __pd(s, q);		/* high order digits */
	*s++ = __pntab[n];		/* bottom digit */
	*s = '\0';			/* null-terminate */
	return(s);			/* return string for recursive call */
}

static char *
__plu(s, n)		/* Long Unsigned Decimal */
char *s;
unsigned long n;
{	unsigned long q;

	q = n / 10;
	n %= 10;
	if (q) s = __pd(s, q);		/* high order digits */
	*s++ = __pntab[n];		/* bottom digit */
	*s = '\0';			/* null-terminate */
	return(s);			/* return string for recursive call */
}

static char *
__po(s, n)		/* Octal */
char *s;
unsigned int n;
{
	if (n &~ 07) s = __po(s, n >> 3);	/* do high order digits */
	*s++ = __pntab[n & 07];		/* bottom digit */
	*s = '\0';			/* null-terminate */
	return(s);			/* pass back up for recursive call */
}

static char *
__plo(s, n)		/* Long Octal */
char *s;
unsigned long n;
{
	if (n &~ 07) s = __po(s, n >> 3);	/* do high order digits */
	*s++ = __pntab[n & 07];		/* bottom digit */
	*s = '\0';			/* null-terminate */
	return(s);			/* pass back up for recursive call */
}

static char *
__px(s, n)		/* Hexadecimal */
char *s;
unsigned int n;
{
	if (n & ~(0xF)) s = __px(s, n >> 4);	/* do high order digits */
	*s++ = __pntab[n & 0xF];	/* Do bottom digit */
	*s = '\0';			/* null-terminate */
	return(s);			/* pass back up for recursive call */
}

static char *
__plx(s, n)		/* Long Hexadecimal */
char *s;
unsigned long n;
{
	if (n & ~(0xF)) s = __px(s, n >> 4);	/* do high order digits */
	*s++ = __pntab[n & 0xF];	/* Do bottom digit */
	*s = '\0';			/* null-terminate */
	return(s);			/* pass back up for recursive call */
}

static char *
__pxu(s, n)		/* Hexadecimal Uppercase */
char *s;
unsigned int n;
{
	if (n & ~(0xF)) s = __pxu(s, n >> 4);	/* do high order digits */
	*s++ = __pxtab[n & 0xF];	/* Do bottom digit */
	*s = '\0';			/* null-terminate */
	return(s);			/* pass back up for recursive call */
}

static char *
__plxu(s, n)		/* Long Hexadecimal Uppercase */
char *s;
unsigned long n;
{
	if (n & ~(0xF)) s = __pxu(s, n >> 4);	/* do high order digits */
	*s++ = __pxtab[n & 0xF];	/* Do bottom digit */
	*s = '\0';			/* null-terminate */
	return(s);			/* pass back up for recursive call */
}
/* Floating-point number conversions */

/* _PRFF - 'f' format output
 */
static void
_prff(wp)
struct pf_work *wp;
{	__pflt(wp, 0);		/* Invoke general routine with right flag */
}

/* _PRFE - 'e' format output
 */
static void
_prfe(wp)
struct pf_work *wp;
{	__pflt(wp, 1);		/* Invoke general routine with right flag */
}

/* _PRFG - 'g' format output
 */
static void
_prfg(wp)
struct pf_work *wp;
{	__pflt(wp, -1);		/* Invoke general routine with right flag */
}
/* __PFLT - auxiliary to do 'f', 'e', or 'g' format output
 *	eflg:	-1 for G format
 *		 0 for F format
 *		+1 for E format
 */
static void
__pflt(wp, eflg)
struct pf_work *wp;
int eflg;
{	/* Allow MAXSIGDIG for each of integer, fraction, and exponent */
	/* plus decimal point (.) and exponent (E+) and terminator */
	char tmpbuf[MAXSIGDIG*3+4];
	register char *cp;
	char *pref;
	int exp, junk, gflg;
	double arg, frarg;

	if (wp->pfw_prec < 0)
		wp->pfw_prec = 6; /* digits after point */

	arg = va_arg(wp->pfw_argp, double);
	if(arg < 0)
	  {	pref = "-";
		arg = -arg;
	  }
	else
	  {	if     (wp->pfw_flag&PFF_POS) pref = "+";
		else if(wp->pfw_flag&PFF_SPA) pref = " ";
		else pref = 0;
	  }

	if(eflg > 0)		/* If E fmt */
	  {	gflg = 0;	/* Not G fmt */

		/* Split number into fraction and exponent */
		if((frarg = __fexp(arg, &exp)) != 0.0)
		  {	/* Adj to have 1 digit ahead of pt, and round off. */
			exp--;
		  }
	  }
	else if(eflg < 0)	/* If G fmt */
	  {	gflg = 1;
		if(wp->pfw_prec > 0)		/* G format bumps prec */
			--(wp->pfw_prec);	/*  down for internal use */

		/* Split number into fraction and exponent */
		frarg = __fexp(arg, &exp);

		/* Now check exponent value to determine which format to use.
		 * Note value of 0.0 will always win even though exponent
		 * is incorrectly munged to -1, because F format will be
		 * selected instead of E.
		 */
		--exp;		/* Get actual exponent we'd use */
		eflg = (exp < -3 || wp->pfw_prec < exp); /* TRUE if E fmt */

		if(!eflg)
			wp->pfw_prec -= exp;	/* Adjust precision for F */
	  }
	else			/* F format */
	  {	gflg = 0;
	  }

	/* Round off to desired precision */
	arg = __prnd((eflg ? frarg*10.0	/* E uses bumped fraction */
			 : arg),	/* F uses original arg */
			wp->pfw_prec);

	/* Now deposit string to represent the number.
	 * Both __pfi and __pff limit their output to MAXSIGDIG digits.
	 */
	cp = __pfi(arg, tmpbuf);			/* Deposit integer */
	if(wp->pfw_prec != 0 || (wp->pfw_flag&PFF_ALT) || gflg)
		cp = __pff(arg, cp, wp->pfw_prec);	/* Deposit fraction */

	/* Now, if PFF_ALT is NOT set for G fmt, must strip trailing zeros */
	if(gflg && (wp->pfw_flag&PFF_ALT)==0)
	  {	while(*--cp == '0') ;
		if(*cp != '.') ++cp;	/* Unless stopped at '.', keep last */
	  }

	if(eflg)
	  {	/* Now finish off with exponent */
		if(isupper(wp->pfw_char)) *cp++ = 'E';
		else *cp++ = 'e';
		__pexp(exp, cp);
	  }
	else *cp = '\0';

	_prfill(wp, pref, tmpbuf, 0);
}
/* Floating-point auxiliary routines */

/* __PRND - Round number to desired precision.
 *	prec - # of digits to right of decimal point.
 *	Method is to add 0.5 * 10^(-prec)
 *	e.g. if prec is 2, want round to .nn so we add .005 to number.
 */
static double
__prnd(d, prec)
double d;
int prec;
{	double rndoff;

#ifdef COMMENT
/* This is not completely thought out; needs coordination with __pff */
	static int maxprec = 0;
	static double minrnd;
	while(prec > maxprec)
	  {
		if(maxprec)		/* See if already initialized */
			return(d+minrnd);	/* Yes, fast return */

		/* No, find what our maximum precision is */
		rndoff = 0.5;
		do {	minrnd = rndoff;
			rndoff /= 10.0;
			++maxprec;
		  } while((1.0 + rndoff) > 1.0);
		--maxprec;
	  }
#endif /*COMMENT*/
	rndoff = 0.5;
	while(--prec >= 0)
		rndoff /= 10.0;
	return(d+rndoff);
}

/* __PFI - print integer part of a double.
 *	Cannot use a single modf call because the integer part may
 *	be larger than an int.  We also want to avoid depending on
 *	any guess about how many digits an int can hold, so we end
 *	up calling modf on each digit.
 *	If # digits is greater than limit, "*" will be output.
 */
static char *
__pfi(d, s)
double d;
char *s;
{	double i;
	int exp;

	d = floor(d) + 0.5;	/* Ensure we'll round the integer correctly */
	d = __fexp(d, &exp);	/* Find # digits to left (exponent) */
	if(exp > MAXSIGDIG)
		*s++ = '*';
	else if(exp > 0)
		while(--exp >= 0)
		  {	d = modf(d * 10.0, &i);
			*s++ = __pntab[(int)i];
		  }
	else *s++ = '0';
	*s = '\0';
	return(s);
}

/* __PFF - Print fractional part of a double.
 *	If precision is zero or negative, only a decimal point is printed.
 */
static char *
__pff(d, cp, prec)
double d;
register char *cp;
int prec;
{	double junk;

	*cp++ = '.';
	d = modf(d, &junk);		/* Get fractional part */

	if(prec > MAXSIGDIG)
		*cp++ = '*';
	else while(prec-- > 0)
	  {	d = modf(d*10.0, &junk);
		*cp++ = __pntab[(int)junk];
	  }
	*cp = '\0';
	return(cp);
}

/* __PEXP - print exponent
 */
static char *
__pexp(exp, cp)
int exp;
char *cp;
{	
	if(exp < 0)
	  {	exp = -exp;
		*cp++ = '-';
	  }
	else *cp++ = '+';
	if(exp >= 100) __pd(exp, cp);
	else
	  {	*cp++ = __pntab[exp/10];
		*cp++ = __pntab[exp%10];
		*cp = '\0';
	  }
	return(cp);
}

/* __FEXP - like "frexp" but exponent is base 10 and returns
 *	1.0 > result >= .1
 *	Arg must be positive.
 */
static double
__fexp(d, ip)
double d;
int *ip;
{	register int i;

	i = 0;
	if(d >= 1.0)
	  {	/* Divide until within range */
		for(; d >= 1.0; ++i)
			d /= 10.0;
	  }
	else if (d != 0.0)	/* Multiply until within range */
	  {	for(; d < 0.1; --i)
			d *= 10.0;
	  }
	*ip = i;
	return(d);
}