/*
 * Copyright (c) 2001-2006 The Trustees of Indiana University.  
 *                         All rights reserved.
 * Copyright (c) 1998-2001 University of Notre Dame. 
 *                         All rights reserved.
 * Copyright (c) 1994-1998 The Ohio State University.  
 *                         All rights reserved.
 * 
 * This file is part of the LAM/MPI software package.  For license
 * information, see the LICENSE file in the top level directory of the
 * LAM/MPI source distribution.
 * 
 * $HEADER$
 * 
 *	Function:	- tstdio output formatting rountines
 */
 
#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#include <string.h>			/* for size_t strlen() */
#include <stddef.h>			/* for size_t */
#include <ctype.h>			/* for isdigit */

#include <portable.h>			/* for isdigit */
#include <tstdio.h>			/* for int tputc() */

/*
 * useful defines
 */
#define MAXLENGTH	15
#define RESET_FLAGS	width		= 0; \
			precision	= 0; \
			dotf		= 0; \
			percentf	= 0; \
			dashf		= 0; \
			plusf		= 0; \
			spacef		= 0; \
			zerof		= 0; \
			hashf		= 0; \
			wstarf		= 0; \
			pstarf		= 0; \
			dispmod		= '\0' ;
/*
 * global variables
 */
char			converted_arg[MAXLENGTH];

/*
 * static functions
 */
static int		output();
static int		getnum();
static int		charformat();
static void		round_up();
static void		norm();
static int		fegformat();
static int		nextdigit();
static int		trailzscan();
static int
intconv(unsigned long		arg,
        char			type,
        int			*minusfp);
static int
intformat(unsigned long		ularg,
          int			width,
          int			precision,
          int			zerof,
          int			plusf,
          int			spacef,
          int			dashf,
          int			hashf,
          char			type,
          TFILE			*fp,
          char			**storepp);

/*
 *	_fmt()
 *
 *	Function:	- performs stdio output formatting
 *			- outputs to either a file or a buffer
 *			- differs from ANSI printf() in two respects:
 *				- hash flag has no effect on types f,e,E,g,G
 *				- pointers are printed in a min 8-character
 *				  field with filling zeros
 *			- called by printf, fprintf, sprintf, vprintf,
 *			  vfprintf and vsprintf
 *	Accepts:	- file pointer to destination file
 *			- a character pointer to destination buffer
 *			- the format string
 *			- va_list of items to print
 *	Returns:	- number of printed characters, or EOF if error
 */
int
_fmt(fp, storep, format, arglist)

TFILE			*fp;
char			*storep;
CONST char		*format;
va_list			arglist;

{
	char		c;
	char		dispmod;	/* h,l,L */
	CONST char	*index;		/* to format string */
	char		*stringp;	/* to string argument */
	double		darg;
	int		count 	= 0;	/* number of printed characters */
	int		dashf;		/* left-justification */
	int		dotf;		/* explicit precision expression */
	int		errorf	= 0;
	int		hashf;		/* hex and octal prefixes */
/*
 *	hashf HAS NO EFFECT ON f,e,E,g,G TYPES.
 */
	int		iarg;
	int		percentf;	/* inside format descriptor */	
	int		plusf;		/* sign display */
	int		precision;
	int		pstarf;		/* precision star flag */
	int		returnval;	/* return value of format functions */
	int		spacef;		/* -/<space> sign display */
	int		width;
	int		wstarf;		/* width star flag */
	int		zerof;		/* zero-fill */
	long		larg;
	short		sarg;
	unsigned	uarg;
	unsigned long	ularg;
	unsigned short	usarg;
	void		*parg;

	if ((fp == 0) && (storep == 0)) {
		return(EOF);
	}

	index = format;
	RESET_FLAGS;
/*
 *	Main loop through format string.
 */
	while ((c = *index) != '\0') {
		if (!percentf) {
			if (c != '%') {
				if (output(c, fp, &storep) == EOF) {
					errorf = 1;
					break;
				}
				else {
					++ count;
				}
			}
			else {
				percentf = 1;	/* start of format descrip */
			}
			++ index;
		}
		else {
/*
 * Handle flags
 */
			while ((c == '-') || (c == '+') || (c == ' ')
					|| (c == '0') || (c == '#')) {
				switch (c = *index)
				{
				case '-':
					dashf = 1;
					break;
				case '+':
					plusf = 1;
					break;
				case ' ':
					spacef = 1;
					break;
				case '0':
					zerof = 1;
					break;
				case '#':
					hashf = 1;
					break;
				default:
					-- index;
					break;
				}
				++ index;
			}
/*
 *	Read fields (width.precision) and size display-mode (h,l,L).
 */
			width = getnum(&index, &wstarf);
			if (wstarf) {
				width = va_arg(arglist, int);
			}
			c = *index;
			if (c == '.') {
				dotf = 1;
				++ index;
				precision = getnum(&index, &pstarf);
				if (pstarf) {
					precision = va_arg(arglist, int);
				}
				c =* index;
			}
			if ((c == 'h') || (c == 'l') || (c == 'L')) {
				dispmod = c;
				++ index;
				c = *index;
			}
			switch (c)
			{
			case '%':
				if (output(c, fp, &storep) == EOF) {
					errorf = 1;
					break;
				}
				else {
					++ count;
				}
				break;
			case 'i':
			case 'd':
				if (dispmod == 'h') {
					sarg = (short int) va_arg(arglist, int);
					ularg = sarg;
				}
				else if (dispmod == 'l') {
					larg = (long int) va_arg(arglist, long int);
					ularg = larg;
				}
				else {
					iarg = va_arg(arglist, int);
					ularg = iarg;
				}
				returnval = intformat(ularg, width, precision,
						      zerof, plusf, spacef,
						      dashf, hashf, c, fp,
						      &storep);
				if (returnval == EOF) {
					errorf = 1;
					break;
				}
				count += returnval;
				break;
			case 'u':
			case 'o':
			case 'x':
			case 'X':
				if (dispmod == 'h') {
					usarg = (unsigned short) va_arg(arglist,unsigned int);
					ularg = usarg;
				}
				else if (dispmod == 'l') {
					ularg = (unsigned long) va_arg(arglist, unsigned long);
				}
				else {
					uarg = (unsigned) va_arg(arglist, unsigned);
					ularg = uarg;
				}
				returnval = intformat(ularg, width, precision,
						      zerof, plusf, spacef,
						      dashf, hashf, c, fp,
						      &storep);
				if (returnval == EOF) {
					errorf = 1;
					break;
				}
				count += returnval;
				break;
			case 'f':
			case 'e':
			case 'E':
			case 'g':
			case 'G':
				darg = (double) va_arg(arglist, double);
				returnval = fegformat(darg, width, precision,
						      dotf, zerof, plusf,
						      spacef, dashf, c, fp,
						      &storep);
				if (returnval == EOF) {
					errorf = 1;
					break;
				}
				count += returnval;
				break;
			case 'c':
				converted_arg[ 0 ] = (char) va_arg(arglist, int);
				converted_arg[ 1 ] = '\0';
				returnval = charformat(converted_arg, width,
						       precision, dashf, dotf,
						       c, fp, &storep);
				if (returnval == EOF) {
					errorf = 1;
					break;
				}
				count += returnval;
				break;
			case 's':
				stringp = va_arg(arglist, char *);
				returnval = charformat(stringp, width,
						       precision, dashf, dotf,
						       c, fp, &storep);
				if (returnval == EOF) {
					errorf = 1;
					break;
				}
				count += returnval;
				break;
			case 'p':
				parg = va_arg(arglist, void *);
				returnval = intformat((unsigned long) parg,
						      width, 8, 0, 0, 0, dashf,
						      hashf, 'x', fp, 
						      &storep);
				if (returnval == EOF) {
					errorf = 1;
					break;
				}
				count += returnval;
				break;
			case 'n':
				*va_arg(arglist, int *) = count;
				break;
			default:
				converted_arg[ 0 ] = c;
				converted_arg[ 1 ] = '\0';
				returnval = charformat(converted_arg, width,
						       precision, dashf, dotf,
						       'c', fp, &storep);
				if (returnval == EOF) {
					errorf = 1;
					break;
				}
				count += returnval;
				break;
			}
			if (errorf) {
				break;
			}
			++ index;
			RESET_FLAGS;
		}
	}
	if (errorf) {
		count = EOF;
	}

/*
 * Don't forget to null-terminate.
 */
	if ((fp == 0) && !(storep == 0)) {
		*storep = 0;
	}

	return(count);
}

/*
 *	output
 *
 *	Functions:	- send one char to the output, either string or file
 *	Accepts:	- character to be printed
 *			- file pointer for destination
 *			- char * to the destination
 *	Returns:	- the printed character, or EOF in case of an error
 */
static int
output(c, fp, storepp)

char			c;
TFILE			*fp;
char			**storepp;
	
{
	int		returnval;

/* 
 * If the file pointer is null, the character is stored 
 * in the momery pointed to by (*storepp).
 * If not, the character is stored in the file pointed to
 * by fp.
 */
	if (fp == 0) {
		**storepp = c;
		++ (*storepp);
		returnval = c;
	}
	else {
		returnval = tputc(c, fp);
	}
	return(returnval);
}


/*
 *	getnum
 *
 *	Function:	- read and evaluate numeric item from a given
 *			  position in a string
 *	Accepts:	- pointer to flag
 *			- pointer to pointer to string
 *	Returns:	- integer value of read string
 */
static int
getnum(stringpp, flagp)

CONST char			**stringpp;
int			*flagp;		/* width and precision star flags */

{
	char		c;
	int		val	= 0;	/* int value of read numeric string */

/*
 * If given position is an asterisk, * sets flag to 1. Non-digit
 * characters other than *, * cause zero as a return. At return,
 * the index points to next character to be read.
 */
	c = *(*stringpp);
	if (c == '*') { 
		*flagp = 1;
		++ (*stringpp);
	}
	else if (c == '-') {	/* negative fields ignored */
		val = 0;
	}
	else if (isdigit((int) c)) {
		while(isdigit((int) c)) {
			val = val * 10 + ((int) c - (int) '0');
			++ (*stringpp);
			c = *(*stringpp);
		}
	}
	return(val);
}


/*
 *	intconv
 *
 *	Function:	- convert absolute value of integer to a string
 *			- handle types i,d,u,o,x,X
 *			- set flag for	negative integers
 *	Accepts:	- int to convert
 *			- type
 *			- pointer to flag
 *	Returns:	- position in extern character array where the first
 *			  character is stored (position < MAXLENGTH)
 */
static int
intconv(unsigned long		arg,
        char			type,
        int			*minusfp)
{
	int		base = 10;			/* for conversions */
	int 		index = MAXLENGTH - 1;		/* first character */

	*minusfp = 0;
	switch(type)
	{
	case 'i':
	case 'd':
	case 'u':
		base = 10;
		break;
	case 'o':
		base = 8;
		break;
	case 'x':
	case 'X':
		base = 16;
		break;
	default:
		break;
	}
	if ((type == 'd') || (type == 'i')) {	/* signed integers */
		if ((long int) arg < 0) {
			arg =- arg;
			*minusfp = 1;
		}
	}
	if (arg == 0) {
		converted_arg[ index ] = '0';
	}
	else {
		while (arg != 0) {
							/* hex a-f, A-F */
			if ((arg % base) > 9) {
				converted_arg[ index-- ] =
					((type == 'x') ? 'a' : 'A') +
						((int) (arg % base)) - 10;
			}
							/* decimal digits */
			else {
				converted_arg[ index-- ] = '0' +
					((int) (arg % base));
			}
			arg /= base;
		}
		++ index;
	}
	return(index);
}


/*
 *	intformat
 *
 *	Function:	- print integer stored in external character array
 *	Accepts:	- the int to convert
 *			- width and precision fields
 *			- many various flags
 *			- type
 *			- fp to write to
 *			- string to write to
 *	Returns:	- number of "printed" characters
 */
static int
intformat(unsigned long		ularg,
          int			width,
          int			precision,
          int			zerof,
          int			plusf,
          int			spacef,
          int			dashf,
          int			hashf,
          char			type,
          TFILE			*fp,
          char			**storepp)
{
	int		avail;			/* available space in field */
	int		chrcount	= 0;	/* printed characters */
	int		errorf		= 0;
	int		field;			/* max(width,precision) */
	int		hexchr		= 0;	/* 2 for 0x hex prefix */
	int		i;			/* indice */
	int		length;			/* of converted integer */
	int		minusf;			/* sign flag */
	int		point;			/* index ret'd by intconv() */
	int		signchrs	= 0;	/* 1 for +,-,<space> */
	
	point = intconv(ularg, type, &minusf);
	length = MAXLENGTH - point;
	precision = ((precision < length) ? length : precision);
	if (type == 'o') {
		if (hashf) {				/* oct 0 prefix */
			if (precision < length + 1) {
				++ precision;
			}
		}
	}
	field = ((width > precision) ? width : precision);
	avail = field - precision;
	
	if (zerof) {
		dashf = 0;
	}
	if ((spacef) || (minusf) || (plusf)) {
		if ((type == 'i') || (type == 'd')) {
			signchrs = 1;
		}
	}
	if ((type == 'x') || (type == 'X')) {
		if (hashf) {
			hexchr = 2;
		}
	}
	if ((!zerof) && (!dashf)) {		/* right-justified */
		for(i=0; i<avail-signchrs-hexchr; ++i) {
			if (output(' ', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}

	switch (type)
	{
	case 'x':
	case 'X':
		if (hashf) {
			if (output('0', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
			if (output(type, fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		break;
	case 'i':
	case 'd':
		if (minusf) {
			if (output('-', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		else if (plusf) {
			if (output('+', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		else if (spacef) {
			if (output(' ', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		break;

	default:
		break;

	} /* end of switch */

	if (errorf) {
		return(EOF);
	}

	if (zerof) {					/* zero-fill due to */
		for(i=0; i<avail-signchrs; ++i) {	/* ...zero flag */
			if (output('0', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}
	for(i=0; i<precision-length; ++i) {		/* ...precision */
		if (output('0', fp, storepp) == EOF) {
			errorf = 1;
			break;
		}
		++ chrcount;
	}
	for(i=point; i<MAXLENGTH; ++i) {		/* argument */
		if (output(converted_arg[ i ], fp, storepp) == EOF) {
			errorf = 1;
			break;
		}
		++ chrcount;
	}
	if (errorf) {
		return(EOF);
	}
	if (dashf) {					/* left-justified */
		for(i=0; i<avail-signchrs-hexchr; ++i) {
			if (output(' ', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}
	return(chrcount);
}


/*
 *	charformat
 *
 *	Function:	- print character or string from external char array
 *	Accepts:	- the string to print
 *			- width and precisino fields
 *			- various flags
 *			- file to write to
 *			- string to write to
 *	Returns:	- number of "printed" characters
 */ 
static int
charformat(stringp, width, precision, dashf, dotf, type, fp, storepp)

char			*stringp;
int			width;
int			precision;
int			dashf;
int			dotf;
char			type;
TFILE			*fp;
char			**storepp;

{
	int		chrcount = 0;		/* printed characters */
	int		errorf   = 0;
	int		i;			/* index */

	size_t length = strlen(stringp);
	if (dotf) {
		if (precision == 0) {
			if (type == 'c') {
				precision = 1;
			}
		}
	}
	if ((precision > length) || (!dotf)) {
		precision = length;
	}
	if (dashf) {				/* left-justified */
		for(i=0; i<precision; ++i) {
			if (output(*(stringp + i), fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}
	for(i=0; i<width-precision; ++i) {
		if (output(' ', fp, storepp) == EOF) {
			errorf = 1;
			break;
		}
		++ chrcount;
	}
	if (errorf) {
		return(EOF);
	}
	if (!dashf) {
		for(i=0; i<precision; ++i) {
			if (output(*(stringp + i), fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}
	return(chrcount);
}


/*
 *	round_up
 *
 *	Function:	- round up argument to given precision
 *	Accepts:	- ptr to argument to round
 *			- precision
 */
static void
round_up(dargp, precision)
	double	*dargp;
	int	precision;
{
	double power = (double) 1;	/* 10 to the (-precision) */
	int i;

	for(i=0; i<precision; ++i) {
		power /= 10;
	}
	*dargp += (.5 * power);
}


/*
 *	norm
 *
 *	Function:	- write argument in exponential notation
 * 	Accepts:	- argument
 *			- ptr to mantissa ((mantissa >= 0) && (mantissa < 1))
 *			- ptr to exponent
 */
static void
norm(darg, mantissap, exponentp)

double			darg;
double			*mantissap;
int			*exponentp;

{
	int		sign;		/* 1 for pos., -1 for neg. */

	sign = (darg < 0) ? -1 : 1;
	if (darg < 0) {
		darg = -darg;
	}

	*exponentp = 0;

	while (((darg < .1) || (darg >= 1.0)) && (darg != 0)) {

		if (darg < .1) {
			darg *= 10;
			(*exponentp)--;
		}
		else if (darg >= 1) {
			darg /= 10;
			(*exponentp)++;
		}
	}
	*mantissap = darg * sign;
}


/*
 *	fegformat
 *
 *	Function:	- print argument in floating point notation
 *			- IGNORES L format for long doubles
 *	Accepts:	- argument to format
 *			- width and precision fields
 *			- various and sundry format flags
 *			- file to print into
 *			- string to print into
 *	Returns:	- number of "printed" characters
 */
static int
fegformat(darg, width, precision, dotf, zerof, plusf,
	  spacef, dashf, type, fp, storepp)

double			darg;
int			width;
int			precision;
int			dotf;
int			zerof;
int			plusf;
int			spacef;
int			dashf;
char			type;
TFILE			*fp;
char			**storepp;

{
	double		mantissa;
	int		avail;			/* available space in field */
	int		chrcount	= 0;	/* printed characters */
	int		decpointf	= 1;
	int		errorf		= 0;
	int		exponent;
	int		i;			/* indice */
	int		msdigit		= 0;	/* 1st ~0 digit in mantissa */
	int		minusf		= 0;
	int		nulargf		= 0;
	int		signchrs	= 0;	/* 1 for +,-,<space> */
	int		tempexp;		/* temporary exponent */
	
	precision = ((dotf) ? precision : 6);
	decpointf = (((dotf) && (precision == 0)) ? 0 : 1);
	if (darg < 0) {
		darg =- darg;
		minusf = 1;
	}
	norm(darg, &mantissa, &exponent);
/*
 *	Round place according to type.
 */

	tempexp = precision;
	if (type == 'f') {
		tempexp += exponent;
	}
	else if ((type == 'e') || (type == 'E')) {
		++ tempexp;
	}
	else {
		if (precision == 0) {
			++ tempexp;
		}
	}
	if (mantissa == 0) {
		nulargf = 1;
	}
	round_up(&mantissa, tempexp);

/*
 * JRV: HEY! rounding might have taken this out of
 *	the normal form we just put it in.  If so, fix it now.
 */
	if (mantissa >= 1.0) {
		mantissa /= 10;
		exponent++;
	}

/*
 *	g,G formats call either f or e,E, depending on size of argument and
 *	precision. In f format, argument are printed until first non-zero
 *	significant digit.
 */
	if ((type == 'g') || (type == 'G')) {
		if (exponent < 2) {
			msdigit = 1 - exponent;
		} 
		if ((exponent < -3) || (exponent > precision)) {
			type -= ('G' - 'E');
			precision -= ((precision > 0) ? 1 : 0);
		}
		else {
			type = 'f';
			precision -= exponent;
			if (precision < msdigit) {
				precision = msdigit;
				decpointf = 1;
			} 
		}
		tempexp = precision;
		if ((type == 'e') || (type == 'E')) {
			++ tempexp;
		}
		else {
			tempexp += exponent;
		}
		precision -= trailzscan(mantissa, tempexp);
		if (precision <= 0) {
			precision = 0;
			decpointf = 0;
		}
	}

/*
 * Set width and available space for printing mantissa.
 */	
	if ((type == 'e') || (type == 'E')) {
		width -= (3 + ((exponent > 99) ? 3 : 2));
	}
	if ((spacef) || (minusf) || (plusf)) {
		signchrs = 1;
	}
	avail = width - precision - decpointf - signchrs;
	if (type == 'f') {
		avail -= ((exponent <= 0) ? 1 : exponent);
	}
	if (dashf) {
		zerof = 0;
	}
						/* right-justified */
	if ((!zerof) && (!dashf)) {
		for(i=0; i<avail; ++i) {
			if (output(' ', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}
/*
 * Handle sign characters.
 */
	if (minusf) {
		if (output('-', fp, storepp) == EOF) {
			return(EOF);
		}
		++ chrcount;
	}
	else if (plusf) {
		if (output('+', fp, storepp) == EOF) {
			return(EOF);
		}
		++ chrcount;
	}
	else if (spacef) {
		if (output(' ', fp, storepp) == EOF) {
			return(EOF);
		}
		++ chrcount;
	}
/*
 * Handle zero-fill.
 */
	if (zerof) {
		for(i=0; i<avail; ++i) {
			if (output('0', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}

	tempexp = exponent;
	if ((type == 'e') || (type == 'E')) {
		if (!nulargf) {
			if (mantissa) {
				tempexp = 1;
				-- exponent;
			}
		}
	}
/*
 *	Float argument is printed here.
 */
	if (tempexp <= 0) {			/* integer part */
		if (output('0', fp, storepp) == EOF) {
			return (EOF);
		}
		++ chrcount;
	}
	else {
		for(i=0; i < tempexp; ++i) {
			if (output('0' + nextdigit(&mantissa),
				   fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}
	if (decpointf) {			/* decimal part */
		if (output('.', fp, storepp) == EOF) {
			return(EOF);
		}
		++ chrcount;
		if (tempexp < 0) {
			i = 0;
			while ((i < precision) && (i < -exponent)) {
				if (output('0', fp, storepp) == EOF) {
					errorf = 1;
					break;
				}
				++ chrcount;
				++ i;
			}
			if (errorf) {
				return(EOF);
			}
			precision += exponent;
		}
		for(i=0; i<precision; ++i) {
			if (output('0' + nextdigit(&mantissa), fp,
				   storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}

/*
 *	Exponential notation with: e(E)+(-)XX(X).
 */
	if ((type == 'e') || (type == 'E')) {
		width -= i;
		if (output(type, fp, storepp) == EOF) {
			return(EOF);
		}
		++ chrcount;
		precision = ((exponent > 99) ? 3 : 2);
		i = intformat((unsigned long) exponent, 0,
			      precision, 0, 1, 0, 0, 0, 'd', fp, storepp);
		if (i == EOF) {
			return(EOF);
		}
		chrcount += i;
	}
						/* left-justified */
	if (dashf) {
		for(i=0; i<avail; ++i) {
			if (output(' ', fp, storepp) == EOF) {
				errorf = 1;
				break;
			}
			++ chrcount;
		}
		if (errorf) {
			return(EOF);
		}
	}
	return(chrcount);
}


/*
 *	nextdigit
 *
 *	Function:	- extract from mantissa the next digit
 *	Accepts:	- pointer to mantissa.
 *	Returns:	- integer value of extracted digit
 */
static int
nextdigit(mantissap)

double			*mantissap;

{
	int		digit;

	*mantissap *= 10;
	digit = (int) (*mantissap);
	*mantissap -= (double) digit;
	return(digit);
}


/*
 *	trailzscan
 *
 *	Function:	- scan mantissa to precision place for consecutive
 *			  zeros
 *	Accepts:	- mantissa
 *			- precision
 *	Returns:	- number of consecutive zeros, the last being at the
 *			  precision place.
 */
static int
trailzscan(mantissa, precision)

double			mantissa;
int			precision;

{
	int	counter	= 0;
	int	countf	= 0;
/*
 *	countf: flag that starts the counter when zero encountered after a
 *	non-zero digit.
 */
	int	digit;
	int	i;				/* indice */

	for(i=0; i<precision; ++i) {
		digit = nextdigit(&mantissa);
		if (digit == 0) {
			if (!countf) {	/* first zero, counter set */
				countf = 1;
			}
			++ counter;
		}
		else {
			counter = 0;		/* counter and flag reset */
			countf = 0;
		}
	}
	return(counter);
}


syntax highlighted by Code2HTML, v. 0.9.1