/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 *	_doprnt: common code for printf, fprintf, sprintf
 *	Floating-point code is included or not, depending
 *	on whether the preprocessor variable FLOAT is 1 or 0.
 */
#define MAXARGS 50
#ifndef FLOAT
#define	FLOAT	1	/* YES! we want floating */
#endif

#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <values.h>
#include <locale.h>
#include "doprnt.h"
#include "stdiom.h"
#include <string.h>	/* strchr, strlen, strspn */

#define max(a,b)	((a) > (b) ? (a) : (b))
#define min(a,b)	((a) < (b) ? (a) : (b))

/* If this symbol is nonzero, allow '0' as a flag */
/* If this symbol is nonzero, allow '0' as a flag */
#define FZERO 1

#if FLOAT
/*
 *	libc/gen/common functions for floating-point conversion
 */
#include <floatingpoint.h>
extern void _fourdigitsquick();
#endif

#define emitchar(c)   { if (--filecnt < 0) { \
				FILE *iop = file; \
				if (((iop->_flag & (_IOLBF|_IONBF)) == 0 \
				    || -filecnt >= iop->_bufsiz)) { \
					iop->_ptr = fileptr; \
					if (iop->_flag & _IOSTRG) \
						return iop->_ptr - iop->_base; \
					else \
						(void) _xflsbuf(iop); \
					fileptr = iop->_ptr; \
					filecnt = iop->_cnt; \
					filecnt--; \
				    } \
			} \
			*fileptr++ = (unsigned)(c); \
			count++; \
                      }

static char *nullstr = "(null)";
static char *lowerhex = "0123456789abcdef";
static char *upperhex = "0123456789ABCDEF";

/* stva_list is used to subvert C's restriction that a variable with an
 * array type can not appear on the left hand side of an assignment operator.
 * By putting the array inside a structure, the functionality of assigning to
 * the whole array through a simple assignment is achieved..
*/
typedef struct stva_list {
	va_list ap;
} stva_list;

void	_mkarglst(char *, stva_list, stva_list []);
void	_getarg(char *, stva_list *, int);
static char	*_check_dol(char *, int *);

int
_doprnt(char *format, va_list in_args, FILE *file)
{
	char convertbuffer[1024] ;
	
	/* Current position in format */
	char *cp;

	/* Starting and ending points for value to be printed */
	char *bp;
	char *p;

	/* Pointer and count for I/O buffer */
	unsigned char *fileptr;
	int filecnt;

	/* Field width and precision */
	int width;
	int prec;

	/* Format code */
	char fcode;

	/* Number of padding zeroes required on the left */
	int lzero;

	/* Flags - nonzero if corresponding character appears in format */
	bool fplus;		/* + */
	bool fminus;		/* - */
	bool fblank;		/* blank */
	bool fsharp;		/* # */
#if FZERO
	bool ansi_fzero;	/* 0 for ansi-dictated formats */
	bool compat_fzero;	/* 0 for backward compatibility */
#endif
	bool Lsize;             /* Capital L for size = long double = quadruple */

	/* Pointer to sign, "0x", "0X", or empty */
	char *prefix;

	/* Scratch */
	int nblank;

#if FLOAT
	/* Exponent or empty */
	char *suffix;

	/* Buffer to create exponent */
	char expbuf[7];  /* "e+xxxx\0" */

	/* Number of padding zeroes required on the right */
	int rzero;

	/* Length of exponent suffix. */
	int suffixlength;

	/* The value being converted, if real or quadruple */
	double dval;
	quadruple qval;

	/* Output values from fconvert and econvert */
	int decpt, sign;

	/* Values are developed in this buffer */
	char buf[1034];		/* Size of convertbuffer, plus some for exponent and sign. */

	/* Current locale's decimal point */
	char decpt_char = *(localeconv()->decimal_point);

#else
	/* Values are developed in this buffer */
	char buf[MAXDIGS];
#endif


	/* The value being converted, if integer */
	unsigned long val;

	/* Work variables */
	int n;
	char c;
	char radix;
	int svswitch = 0;
	/* count of output characters */
	int count;

	/* variables for positional parameters */
	char    *sformat = format;      /* save the beginning of the format */
	int     fpos = 1;               /* 1 if first positional parameter */
	stva_list       args,   /* used to step through the argument list */
			args_width, 	/* for width */
			args_prec, 	/* for prec */
			sargs;  /* used to save the start of the argument list */
	stva_list       arglst[MAXARGS];/* array giving the approriate values
					 * for va_arg() to retrieve the
				  	 * corresponding argument:
					 * arglst[0] is the first argument
				         * arglst[1] is the second argument, etc.
				         */
											      int index = 0;			/* argument placeolder */
											     /* Initialize args and sargs to the start of the argument list.
        * Note that ANSI guarantees that the address of the first member of
        * a structure will be the same as the address of the structure. */
											       args_width = args_prec = args = sargs = *(struct stva_list *)&in_args;


/*  initialize p an bp (starting and ending points)  bugid 1141781 */

	p = bp = NULL;

	cp = format;
	if ((c = *cp++) != '\0') {
		/*
		 * We know we're going to write something; make sure
		 * we can write and set up buffers, etc..
		 */
		if (_WRTCHK(file))
			return(EOF);
	} else
		return(0);	/* no fault, no error */

	count = 0;
	fileptr = file->_ptr;
	filecnt = file->_cnt;

	/*
	 *	The main loop -- this loop goes through one iteration
	 *	for each ordinary character or format specification.
	 */
	do {
		if (c != '%') {
			/* Ordinary (non-%) character */
			emitchar(c);
		} else {
			/*
			 *	% has been spotted!
			 *
			 *	First, try the 99% cases.
			 *	then parse the format specification.
			 *
			 *	Note that this code assumes the Sun
			 *	Workstation environment (all params
			 *	passed as int == long, no interrupts
			 *	for fixed point overflow from negating
			 *	the most negative number).
			 */
		skipit:
			switch(c = *cp++) {

			case 'l':
			case 'h':
				/* Quickly ignore long & short specifiers */
				goto skipit;

			case 's':
				bp = va_arg(args.ap, char *);
				if (bp == NULL)
					bp = nullstr;
				while (c = *bp++)
					emitchar(c);
				p = bp;
				continue;

			case 'c':
				c = va_arg(args.ap, int);
			emitc:
				emitchar(c);
				continue;

			case 'i':
			case 'd':
			case 'D':
				val = va_arg(args.ap, int);
				if ((long) val < 0) {
					emitchar('-');
					val = -val;
				}
				goto udcommon;

			case 'U':
			case 'u':
				val = va_arg(args.ap, unsigned);
			udcommon:
                                {
                                char *stringp = lowerhex;
                                bp = buf+MAXDIGS;
                                stringp = lowerhex;
                                do {
                                        *--bp = stringp[val%10];
                                        val /= 10;
                                } while (val);
				}
                                goto intout;

			case 'X':
				{
				char *stringp = upperhex;
				val = va_arg(args.ap, unsigned);
				bp = buf + MAXDIGS;
				if (val == 0)
					goto zero;
				while (val) {
					*--bp = stringp[val%16];
					val /= 16;
				}
				}
				goto intout;

			case 'x':
			case 'p':
				{
				char *stringp = lowerhex;
				val = va_arg(args.ap, unsigned);
				bp = buf + MAXDIGS;
				if (val == 0)
					goto zero;
				while (val) {
					*--bp = stringp[val%16];
					val /= 16;
				}
				}
				goto intout;

			case 'O':
			case 'o':
				{
				char *stringp = lowerhex;
				val = va_arg(args.ap, unsigned);
				bp = buf + MAXDIGS;
				if (val == 0)
					goto zero;
				while (val) {
					*--bp = stringp[val%8];
					val /= 8;
				}
				}
				/* Common code to output integers */
			intout:
				p = buf + MAXDIGS;
				while (bp < p) {
					c = *bp++;
					emitchar(c);
				}
				continue;

			zero:
				c = '0';
				goto emitc;

			default:
				/*
				 * let AT&T deal with it
				 */
				cp-= 2;
			}

			Lsize = 0;      /* Not long double unless we say so. */
                        /* Scan the <flags> */
			fplus = 0;
			fminus = 0;
			fblank = 0;
			fsharp = 0;
#if FZERO
			ansi_fzero = 0;
			compat_fzero = 0;
#endif
		scan:	switch (*++cp) {
			case '+':
				fplus = 1;
				goto scan;
			case '-':
				fminus = 1;
				goto scan;
			case ' ':
				fblank = 1;
				goto scan;
			case '#':
				fsharp = 1;
				goto scan;
#if FZERO
			case '0':
				ansi_fzero = 1;
				compat_fzero = 1;
				goto scan;
#endif
			}

			/* Scan the field width */
			if (*cp == '*') {
				char *p;
				int val;

				p = _check_dol(cp+1, &val);
				if (p != (char *)NULL) {
					/*
					 * argument re-order
					 */
					if (fpos) {
						_mkarglst(sformat, sargs, arglst);
						fpos = 0;
					}
					if (val <= MAXARGS) {
						args_width = arglst[val - 1];
					} else {
						args_width = arglst[MAXARGS - 1];
						_getarg(sformat, &args_width, val);
					}
					width = va_arg(args_width.ap, int);
					if (width < 0) {
						width = -width;
						fminus = 1;
					}
					cp = p;
				}
				else {
					width = va_arg(args.ap, int);
					if (width < 0) {
						width = -width;
						fminus = 1;
					}
					cp++;
				}
			} else {
				index = width = 0;
				while (isdigit(*cp)) {
					n = tonumber(*cp++);
					index = width = width * 10 + n;
				}
			}

			/* Scan the precision */
			if (*cp == '.') {

				/* '*' instead of digits? */
				if (*++cp == '*') {
					char *p;
					int val;

					p = _check_dol(cp+1, &val);
					if (p != (char *)NULL) {
						/*
						 * argument re-order
						 */
						if (fpos) {
							_mkarglst(sformat, sargs, arglst);
							fpos = 0;
						}
						if (val <= MAXARGS) {
							args_prec = arglst[val - 1];
						} else {
							args_prec = arglst[MAXARGS - 1];
							_getarg(sformat, &args_prec, val);
						}
						prec = va_arg(args_prec.ap, int);
						cp = p;
					}
					else {
						prec = va_arg(args.ap, int);
						cp++;
					}
				} else {
					prec = 0;
					while (isdigit(*cp)) {
						n = tonumber(*cp++);
						prec = prec * 10 + n;
					}
				}
			} else
				prec = -1;

			if (*cp == '$') {
				if (fpos) {
					_mkarglst(sformat, sargs, arglst);
					fpos = 0;
				}
				if (index <= MAXARGS) {
					args = arglst[index - 1];
				} else {
					args = arglst[MAXARGS - 1];
					_getarg(sformat, &args, index);
				}
				goto scan;
			}
			/*
			 *	The character addressed by cp must be the
			 *	format letter -- there is nothing left for
			 *	it to be.
			 *
			 *	The status of the +, -, #, blank, and 0
			 *	flags are reflected in the variables
			 *	"fplus", "fminus", "fsharp", "fblank",
			 *	and "ansi_fzero"/"compat_fzero", respectively.
			 *	"width" and "prec" contain numbers
			 *	corresponding to the digit strings
			 *	before and after the decimal point,
			 *	respectively. If there was no decimal
			 *	point, "prec" is -1.
			 *
			 *	The following switch sets things up
			 *	for printing.  What ultimately gets
			 *	printed will be padding blanks, a prefix,
			 *	left padding zeroes, a value, right padding
			 *	zeroes, a suffix, and more padding
			 *	blanks.  Padding blanks will not appear
			 *	simultaneously on both the left and the
			 *	right.  Each case in this switch will
			 *	compute the value, and leave in several
			 *	variables the information necessary to
			 *	construct what is to be printed.
			 *
			 *	The prefix is a sign, a blank, "0x", "0X",
			 *	or null, and is addressed by "prefix".
			 *
			 *	The suffix is either null or an exponent,
			 *	and is addressed by "suffix".
			 *
			 *	The value to be printed starts at "bp"
			 *	and continues up to and not including "p".
			 *
			 *	"lzero" and "rzero" will contain the number
			 *	of padding zeroes required on the left
			 *	and right, respectively.  If either of
			 *	these variables is negative, it will be
			 *	treated as if it were zero.
			 *
			 *	The number of padding blanks, and whether
			 *	they go on the left or the right, will be
			 *	computed on exit from the switch.
			 */
			
			lzero = 0;
			prefix = "";
#if FLOAT
			rzero = 0;
			suffix = prefix;
#endif

#if FZERO
		  	/* if both zero-padding and left-justify flags
			 * are used, ignore zero-padding, per ansi c
			 */
		  	if (ansi_fzero & fminus) {
				ansi_fzero = 0;
				compat_fzero = 0;
			}

		  	/* if zero-padding and precision are specified, 
			 * ignore zero-padding for ansi-dictated formats,
			 * per ansi c
			 */
		  	if (ansi_fzero & (prec != -1)) ansi_fzero = 0; 
#endif
			  
		next:
			switch (fcode = *cp++) {

			/* toss the length modifier, if any */
			case 'l':
			case 'h':
				goto next;

                       	case 'L':
                      		Lsize = 1;      /* Remember long double size. */
                         	goto next;

			/*
			 *	fixed point representations
			 *
			 *	"radix" is the radix for the conversion.
			 *	Conversion is unsigned unless fcode is 'd'.
			 *	We assume a 2's complement machine and
			 *	that fixed point overflow (from negating
			 *	the largest negative int) is ignored.
			 */

			case 'i':
			case 'D':
			case 'U':
			case 'd':
			case 'u':
				radix = 10;
				goto fixed;

			case 'O':
			case 'o':
				radix = 8;
				goto fixed;

			case 'X':
			case 'x':
				radix = 16;

			fixed:
				/* Establish default precision */
				if (prec < 0)
					prec = 1;

				/* Fetch the argument to be printed */
				val = va_arg(args.ap, unsigned);

				/* If signed conversion, establish sign */
				if (fcode == 'd' || fcode == 'D' || fcode == 'i') {
					if ((long) val < 0) {
						prefix = "-";
						val = -val;
					} else if (fplus)
						prefix = "+";
					else if (fblank)
						prefix = " ";
				}
				/* Set translate table for digits */
				{
				char *stringp;
				if (fcode == 'X')
					stringp = upperhex;
				else
					stringp = lowerhex;

				/* Develop the digits of the value */
				bp = buf + MAXDIGS;
				switch(radix) {
				case 8:	/*octal*/
					while (val) {
						*--bp = stringp[val%8];
						val /= 8;
					}
					break;
				case 16:/*hex*/
					while (val) {
						*--bp = stringp[val%16];
						val /= 16;
					}
					break;
				default:
					while (val) {
						*--bp = stringp[val%10];
						val /= 10;
					}
					break;
				} /* switch */
				}

				/* Calculate padding zero requirement */
				p = buf + MAXDIGS;

				/* Handle the # flag */
				if (fsharp && bp != p) {
					switch (fcode) {
					case 'x':
						prefix = "0x";
						break;
					case 'X':
						prefix = "0X";
						break;
					}
				}
#if FZERO
				if (ansi_fzero) {
					n = width - strlen(prefix);
					if (n > prec)
						prec = n;
				}
#endif
				lzero = bp - p + prec;

				/* Handle the # flag for 'o' */
				if (fsharp && bp != p && fcode == 'o' &&
				    lzero < 1) {
					lzero = 1;
				}
				break;
#if FLOAT

#if	defined(__sparc)
#define GETQVAL /* Sun-4 macro to get a quad q from the argument list, passed as a pointer. */ \
      { qval = *(va_arg(args.ap, quadruple*)) ; }
#else
#define GETQVAL /* Sun-3 macro to get a quad q from the argument list, passed as a value. */ \
      { int iq ; unsigned long * pl = (unsigned long *) (&qval) ; for(iq=0;iq<4;iq++) pl[iq] = (unsigned long) va_arg(args.ap, unsigned long) ; }
#endif

			case 'E':
			case 'e':
				/*
				 *	E-format.  The general strategy
				 *	here is fairly easy: we take
				 *	what econvert gives us and re-format it.
				 */

				/* Establish default precision */
				if (prec < 0)
					prec = 6;

				/* Fetch the value */
                               if (Lsize == 0) { /* Double */
                                dval = va_arg(args.ap, double);
                                bp = econvert(dval, prec + 1, &decpt, &sign, convertbuffer);
                               } else { /* Long Double = quadruple */
                               GETQVAL;
                               bp = qeconvert(&qval, prec + 1, &decpt, &sign, convertbuffer);
                               }

				/* Determine the prefix */
				if (sign)
					prefix = "-";
				else if (fplus)
					prefix = "+";
				else if (fblank)
					prefix = " ";
				if (convertbuffer[0] > '9')
					{ /* handle infinity, nan */
					bp = &convertbuffer[0];
					for (p = bp+1 ; *p != 0 ; p++) ;
					goto ebreak ;
					}
				{
				char *stringp;
				/* Place the first digit in the buffer */
				stringp = &buf[0];
				*stringp++ = *bp != '\0'? *bp++: '0';

				/* Put in a decimal point if needed */
				if (prec != 0 || fsharp)
					*stringp++ = decpt_char;

				/* Create the rest of the mantissa */
				rzero = prec;
				while (rzero > 0 && *bp!= '\0') {
					--rzero;
					*stringp++ = *bp++;
				}
				p = stringp;
				}

				bp = &buf[0];

				/* Create the exponent */
				if (convertbuffer[0] != '0')
					n = decpt - 1;
				else
					n = 0 ;
				if (n < 0)
					n = -n;
				_fourdigitsquick( (short unsigned) n, &(expbuf[2]) ) ;
				expbuf[6] = 0 ;
				if (n < 100)
					/*
                                         * Normally two digit exponent field,
                                         * three or four if required.
					 */
					{ suffix = &(expbuf[4]) ; suffixlength = 4 ; }
                                else if (n < 1000)
                                        { suffix = &(expbuf[3]) ; suffixlength = 5 ; }
                                else 
                                        { suffix = &(expbuf[2]) ; suffixlength = 6 ; }
				/* Put in the exponent sign */
				*--suffix = (decpt > 0 || convertbuffer[0] == '0' )? '+': '-';

				/* Put in the e; note kludge in 'g' format */
				*--suffix = fcode;
ebreak:
#if FZERO
				if (compat_fzero &! fminus)
					/* Calculate padding zero requirement */
					lzero = width - (strlen(prefix)
					    + (p - buf) + rzero + suffixlength);
#endif
				break;

			case 'f':
				/*
				 *	F-format floating point.  This is
				 *	a good deal less simple than E-format.
				 *	The overall strategy will be to call
				 *	fconvert, reformat its result into buf,
				 *	and calculate how many trailing
				 *	zeroes will be required.  There will
				 *	never be any leading zeroes needed.
				 */

				/* Establish default precision */
				if (prec < 0)
					prec = 6;

                                if (Lsize == 0) {
                                dval = va_arg(args.ap, double);
                                bp = fconvert(dval, prec, &decpt, &sign, convertbuffer);
                                } else {
                                GETQVAL ;
                                bp = qfconvert(&qval, prec, &decpt, &sign, convertbuffer);
                                }
 
				/* Determine the prefix */
				if (sign)
					prefix = "-";
				else if (fplus)
					prefix = "+";
				else if (fblank)
					prefix = " ";
				if (convertbuffer[0] > '9')
					{ /* handle infinity, nan */
					bp = &convertbuffer[0];
					for (p = bp+1 ; *p != 0 ; p++) ;
					goto fbreak ;    
					}
				{
				char *stringp;
				/* Initialize buffer pointer */
				stringp = &buf[0];

				/* Emit the digits before the decimal point */
				n = decpt;
				if (n <= 0)
					*stringp++ = '0';
				else
					do
						if (*bp == '\0' )
							*stringp++ = '0';
						else {
							*stringp++ = *bp++;
						}
					while (--n != 0);

				/* Decide whether we need a decimal point */
				if (fsharp || prec > 0)
					*stringp++ = decpt_char;

				/* Digits(if any) after the decimal point */
				n = prec;
				rzero = prec - n;
				while (--n >= 0) {
					if (++decpt <= 0 || *bp == '\0')
						*stringp++ = '0';
					else {
						*stringp++ = *bp++;
					}
				}
#if FZERO
				if (compat_fzero &! fminus)
					/* Calculate padding zero requirement */
					lzero = width - (strlen(prefix)
					    + (stringp - buf) + rzero);
#endif
				p = stringp;
				}

				bp = &buf[0];
fbreak:
				break;

			case 'G':
			case 'g':
				/*
				 *	g-format.  We play around a bit
				 *	and then jump into e or f, as needed.
				 */
			
				/* Establish default precision */
				if (prec < 0)
					prec = 6;
				else if (prec == 0)
					prec = 1;

                                if (Lsize == 0) {
                                dval = va_arg(args.ap, double);
                                bp = gconvert(dval, prec, fsharp, convertbuffer);
                                } else {
                                GETQVAL;
                                bp = qgconvert(&qval, prec, fsharp, convertbuffer);
                                }
				bp = convertbuffer ;
				if (convertbuffer[0] == '-') {
					prefix = "-" ;
					bp++;
					}
				else if (fplus)
					prefix = "+";
				else if (fblank)
					prefix = " ";
				if (isupper(fcode))
				        { /* Put in a big E for small minds. */
				        for (p = bp ; (*p != NULL) && (*p != 'e') ; p++) ;
				        if (*p == 'e') *p = 'E' ;
				        for (; (*p != NULL) ; p++) ;
				                                /* Find end of string. */
			                }
				else
				        for (p = bp ; *p != NULL ; p++) ;
				                                /* Find end of string. */
				rzero = 0;
#if FZERO
				if (compat_fzero & !fminus)
					/* Calculate padding zero requirement */
					lzero = width - (strlen(prefix)
					    + (p - bp) + rzero);
#endif
				break ;

#endif
			case 'c':
				buf[0] = va_arg(args.ap, int);
				bp = &buf[0];
				p = bp + 1;
				break;

                        case 's':
				bp = va_arg(args.ap, char *);
				if (prec < 0)
					prec = MAXINT;
				/* avoid *(0) */
				if (bp == NULL)
					bp = nullstr;
				for (n=0; *bp++ != '\0' && n < prec; n++) 
					;
#if FZERO
				if (compat_fzero &! fminus)
				        lzero = width - n;
#endif
				p = --bp;
				bp -= n;
				break;

			case '\0':
				/* well, what's the punch line? */
				goto out;

			case 'n':
				svswitch = 1;
				break;
			default:
				p = bp = &fcode;
				p++;
				break;

			}
			/* Calculate number of padding blanks */
			nblank = width
#if FLOAT
				- (rzero < 0? 0: rzero)
				- strlen(suffix)
#endif
				- (p - bp)
				- (lzero < 0? 0: lzero)
				- strlen(prefix);

			/* Blanks on left if required */
			if (!fminus)
				while (--nblank >= 0)
					emitchar(' ');

			/* Prefix, if any */
			while (*prefix != '\0') {
				emitchar(*prefix);
				prefix++;
			}

			/* Zeroes on the left */
			while (--lzero >= 0)
				emitchar('0');
			
			/* The value itself */
			while (bp < p) {
				emitchar(*bp);
				bp++;
			}
#if FLOAT
			/* Zeroes on the right */
			while (--rzero >= 0)
				emitchar('0');

			/* The suffix */
			while (*suffix != '\0') {
				emitchar(*suffix);
				suffix++;
			}
#endif
			/* Blanks on the right if required */
			if (fminus)
				while (--nblank >= 0)
					emitchar(' ');
			/* If %n is seen, save count in argument */
			if (svswitch == 1) {
				long *svcount;
				svcount = va_arg (args.ap, long *);
				*svcount = count;
				svswitch = 0;
			}
		} /* else */
	} while ((c = *cp++) != '\0');	/* do */
out:
	file->_ptr = fileptr;
	file->_cnt = filecnt;
	if (file->_flag & (_IONBF | _IOLBF) &&
	    (file->_flag & _IONBF ||
	     memchr((char *)file->_base, '\n', fileptr - file->_base) != NULL))
		(void) _xflsbuf(file);
	return (ferror(file)? EOF: count);
}

#if	defined(__sparc)
/*
 * We use "double *" instead of "quadruple *" to skip over the pointer to
 * long double on the argument list since a pointer is a pointer after all.
 */
#define SKIPQVAL { \
	(void) va_arg(args.ap, double *); \
} 
#else	/* Sun-3 */
#define SKIPQVAL { \
	int iq; \
	for (iq = 0; iq < 4; iq++) \
		(void) va_arg(args.ap, unsigned long); \
}
#endif
/*
 * This function initializes arglst, to contain the appropriate va_list values
 * for the first MAXARGS arguments.
 */
void
_mkarglst(char *fmt, stva_list args, stva_list arglst[])
{
	static char *digits = "01234567890", *skips = "# +-.0123456789h$";

	enum types {INT = 1, LONG, CHAR_PTR, DOUBLE, LONG_DOUBLE, VOID_PTR,
		LONG_PTR, INT_PTR};
	enum types typelst[MAXARGS], curtype;
	int maxnum, n, curargno, flags;

	/*
	* Algorithm	1. set all argument types to zero.
	*		2. walk through fmt putting arg types in typelst[].
	*		3. walk through args using va_arg(args.ap, typelst[n])
	*		   and set arglst[] to the appropriate values.
	* Assumptions:	Cannot use %*$... to specify variable position.
	*/

	(void)memset((void *)typelst, 0, sizeof(typelst));
	maxnum = -1;
	curargno = 0;
	while ((fmt = strchr(fmt, '%')) != 0)
	{
		fmt++;	/* skip % */
		if (fmt[n = strspn(fmt, digits)] == '$')
		{
			curargno = atoi(fmt) - 1;	/* convert to zero base */
			fmt += n + 1;
		}
		flags = 0;
	again:;
		fmt += strspn(fmt, skips);
		switch (*fmt++)
		{
		case '%':	/*there is no argument! */
			continue;
		case 'l':
			flags |= 0x1;
			goto again;
		case 'L':
			flags |= 0x8;
			goto again;
		case '*':	/* int argument used for value */
			flags |= 0x2;
			curtype = INT;
			break;
		case 'e':
		case 'E':
		case 'f':
		case 'g':
		case 'G':
			if (flags & 0x8)
				curtype = LONG_DOUBLE;
			else
				curtype = DOUBLE;
			break;
		case 's':
			curtype = CHAR_PTR;
			break;
		case 'p':
			curtype = VOID_PTR;
			break;
		case 'n':
			if (flags & 0x1)
				curtype = LONG_PTR;
			else
				curtype = INT_PTR;
			break;
		default:
			if (flags & 0x1)
				curtype = LONG;
			else
				curtype = INT;
			break;
		}
		if (curargno >= 0 && curargno < MAXARGS)
		{
			typelst[curargno] = curtype;
			if (maxnum < curargno)
				maxnum = curargno;
		}
		curargno++;	/* default to next in list */
		if (flags & 0x2)	/* took care of *, keep going */
		{
			flags ^= 0x2;
			goto again;
		}
	}
	for (n = 0 ; n <= maxnum; n++)
	{
		arglst[n] = args;
		if (typelst[n] == 0)
			typelst[n] = INT;
		
		switch (typelst[n])
		{
		case INT:
			va_arg(args.ap, int);
			break;
		case LONG:
			va_arg(args.ap, long);
			break;
		case CHAR_PTR:
			va_arg(args.ap, char *);
			break;
		case DOUBLE:
			va_arg(args.ap, double);
			break;
		case LONG_DOUBLE:
			SKIPQVAL
			break;
		case VOID_PTR:
			va_arg(args.ap, void *);
			break;
		case LONG_PTR:
			va_arg(args.ap, long *);
			break;
		case INT_PTR:
			va_arg(args.ap, int *);
			break;
		}
	}
}

/*
 * This function is used to find the va_list value for arguments whose
 * position is greater than MAXARGS.  This function is slow, so hopefully
 * MAXARGS will be big enough so that this function need only be called in
 * unusual circumstances.
 * pargs is assumed to contain the value of arglst[MAXARGS - 1].
 */
void
_getarg(char *fmt, stva_list *pargs, int argno)
{
	static char *digits = "01234567890", *skips = "# +-.0123456789h$";
	int i, n, curargno, flags;
	char	*sfmt = fmt;
	int	found = 1;

	curargno = i = MAXARGS;
	while (found)
	{
		fmt = sfmt;
		found = 0;
		while ((i != argno) && (fmt = strchr(fmt, '%')) != 0)
		{
			fmt++;	/* skip % */
			if (fmt[n = strspn(fmt, digits)] == '$')
			{
				curargno = atoi(fmt);
				fmt += n + 1;
			}

			/* find conversion specifier for next argument */
			if (i != curargno)
			{
				curargno++;
				continue;
			} else
				found = 1;
			flags = 0;
		again:;
			fmt += strspn(fmt, skips);
			switch (*fmt++)
			{
			case '%':	/*there is no argument! */
				continue;
			case 'l':
				flags |= 0x1;
				goto again;
			case 'L':
				flags |= 0x8;
				goto again;
			case '*':	/* int argument used for value */
				flags |= 0x2;
				(void)va_arg((*pargs).ap, int);
				break;
			case 'e':
			case 'E':
			case 'f':
			case 'g':
			case 'G':
				if (flags & 0x8) {
#define	args	(*pargs)
					SKIPQVAL
#undef	args
				}
				else
					(void)va_arg((*pargs).ap, double);
				break;
			case 's':
				(void)va_arg((*pargs).ap, char *);
				break;
			case 'p':
				(void)va_arg((*pargs).ap, void *);
				break;
			case 'n':
				if (flags & 0x1)
					(void)va_arg((*pargs).ap, long *);
				else
					(void)va_arg((*pargs).ap, int *);
				break;
			default:
				if (flags & 0x1)
					(void)va_arg((*pargs).ap, long int);
				else
					(void)va_arg((*pargs).ap, int);
				break;
			}
			i++;
			curargno++;	/* default to next in list */
			if (flags & 0x2)	/* took care of *, keep going */
			{
				flags ^= 0x2;
				goto again;
			}
		}

		/* missing specifier for parameter, assume parameter is an int */
		if (!found && i != argno) {
			(void)va_arg((*pargs).ap, int);
			i++;
			curargno++;
			found = 1;
		}
	}
}


/*
 * parse a string, mini parse
 */
static char *
_check_dol(char *s, int *val)
{
	char *os;	/* save old string */
	int tmp_val = 0;
	int flag = 0;

	while (isdigit (*s)) {
		++flag;
		tmp_val = tmp_val*10 + *s - '0';
		s++;
	}
	if (flag == 0)
		return ((char *)NULL);
	if (*s == '$') {
		*val = tmp_val;
		return(++s);
	}
	return ((char *)NULL);
}