diff options
| author | Tim J. Robbins <tjr@FreeBSD.org> | 2003-04-07 06:36:49 +0000 | 
|---|---|---|
| committer | Tim J. Robbins <tjr@FreeBSD.org> | 2003-04-07 06:36:49 +0000 | 
| commit | adfd6b312db50694c5b7ef03b8c7bf70e9c5a6dd (patch) | |
| tree | e7d90196703c8690008b6d54f43a24a82242fd6b /lib/libc/stdio/vfwprintf.c | |
| parent | 3e3a4395a906ec5ec501a167a8cfde3b23dee473 (diff) | |
Notes
Diffstat (limited to 'lib/libc/stdio/vfwprintf.c')
| -rw-r--r-- | lib/libc/stdio/vfwprintf.c | 390 | 
1 files changed, 215 insertions, 175 deletions
diff --git a/lib/libc/stdio/vfwprintf.c b/lib/libc/stdio/vfwprintf.c index 658f87985e56..3feacf2e4267 100644 --- a/lib/libc/stdio/vfwprintf.c +++ b/lib/libc/stdio/vfwprintf.c @@ -39,7 +39,7 @@  #if defined(LIBC_SCCS) && !defined(lint)  static char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 6/4/93";  #endif /* LIBC_SCCS and not lint */ -__FBSDID("FreeBSD: src/lib/libc/stdio/vfprintf.c,v 1.52 2003/03/14 04:48:09 das Exp"); +__FBSDID("FreeBSD: src/lib/libc/stdio/vfprintf.c,v 1.57 2003/04/07 03:17:39 ache Exp");  #endif  __FBSDID("$FreeBSD$"); @@ -114,10 +114,10 @@ enum typeid {  };  static int	__sbprintf(FILE *, const wchar_t *, va_list); -static wchar_t	*__ujtoa(uintmax_t, wchar_t *, int, int, wchar_t *, int, char, -		    const char *); -static wchar_t	*__ultoa(u_long, wchar_t *, int, int, wchar_t *, int, char, -		    const char *); +static wchar_t	*__ujtoa(uintmax_t, wchar_t *, int, int, const wchar_t *, int, +		    char, const char *); +static wchar_t	*__ultoa(u_long, wchar_t *, int, int, const wchar_t *, int, +		    char, const char *);  static wchar_t	*__mbsconv(char *, int);  static void	__find_arguments(const wchar_t *, va_list, union arg **);  static void	__grow_type_table(int, enum typeid **, int *); @@ -169,7 +169,7 @@ __sbprintf(FILE *fp, const wchar_t *fmt, va_list ap)   * use the given digits.   */  static wchar_t * -__ultoa(u_long val, wchar_t *endp, int base, int octzero, wchar_t *xdigs, +__ultoa(u_long val, wchar_t *endp, int base, int octzero, const wchar_t *xdigs,  	int needgrp, char thousep, const char *grp)  {  	wchar_t *cp = endp; @@ -246,8 +246,8 @@ __ultoa(u_long val, wchar_t *endp, int base, int octzero, wchar_t *xdigs,  /* Identical to __ultoa, but for intmax_t. */  static wchar_t * -__ujtoa(uintmax_t val, wchar_t *endp, int base, int octzero, wchar_t *xdigs,  -	int needgrp, char thousep, const char *grp) +__ujtoa(uintmax_t val, wchar_t *endp, int base, int octzero, +	const wchar_t *xdigs, int needgrp, char thousep, const char *grp)  {  	wchar_t *cp = endp;  	intmax_t sval; @@ -330,6 +330,9 @@ __mbsconv(char *mbsarg, int prec)  	size_t insize, nchars, nconv;  	mbstate_t mbs; +	if (mbsarg == NULL) +		return (NULL); +  	/*  	 * Supplied argument is a multibyte string; convert it to wide  	 * characters first. @@ -400,16 +403,17 @@ vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, va_list ap)  }  #ifdef FLOATING_POINT + +#define	dtoa		__dtoa +#define	freedtoa	__freedtoa +  #include <float.h>  #include <math.h>  #include "floatio.h" +#include "gdtoa.h"  #define	DEFPREC		6 -extern char *__dtoa(double, int, int, int *, int *, char **); -extern void __freedtoa(char *s); - -static wchar_t *cvt(double, int, int, char *, int *, wchar_t, int *);  static int exponent(wchar_t *, int, wchar_t);  #endif /* FLOATING_POINT */ @@ -429,7 +433,6 @@ static int exponent(wchar_t *, int, wchar_t);   * Flags used during conversion.   */  #define	ALT		0x001		/* alternate form */ -#define	HEXPREFIX	0x002		/* add 0x or 0X prefix */  #define	LADJUST		0x004		/* left adjustment */  #define	LONGDBL		0x008		/* long double */  #define	LONGINT		0x010		/* long integer */ @@ -452,24 +455,46 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap)  {  	wchar_t *fmt;		/* format string */  	wchar_t ch;		/* character from fmt */ -	int n, n2;		/* handy integer (short term usage) */ +	int n, n2, n3;		/* handy integer (short term usage) */  	wchar_t *cp;		/* handy char pointer (short term usage) */  	int flags;		/* flags as above */  	int ret;		/* return value accumulator */  	int width;		/* width from format (%8d), or 0 */ -	int prec;		/* precision from format (%.3d), or -1 */ +	int prec;		/* precision from format; <0 for N/A */  	wchar_t sign;		/* sign prefix (' ', '+', '-', or \0) */  	char thousands_sep;	/* locale specific thousands separator */  	const char *grouping;	/* locale specific numeric grouping rules */  #ifdef FLOATING_POINT +	/* +	 * We can decompose the printed representation of floating +	 * point numbers into several parts, some of which may be empty: +	 * +	 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ +	 *    A       B     ---C---      D       E   F +	 * +	 * A:	'sign' holds this value if present; '\0' otherwise +	 * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal +	 * C:	cp points to the string MMMNNN.  Leading and trailing +	 *	zeros are not in the string and must be added. +	 * D:	expchar holds this character; '\0' if no exponent, e.g. %f +	 * F:	at least two digits for decimal, at least one digit for hex +	 */  	char *decimal_point;	/* locale specific decimal point */ -	char softsign;		/* temporary negative sign for floats */ -	double _double;		/* double precision arguments %[eEfgG] */ +	int signflag;		/* true if float is negative */ +	union {			/* floating point arguments %[aAeEfFgG] */ +		double dbl; +		long double ldbl; +	} fparg;  	int expt;		/* integer value of exponent */ +	char expchar;		/* exponent character: [eEpP\0] */ +	char *dtoaend;		/* pointer to end of converted digits */  	int expsize;		/* character count for expstr */ -	int ndig;		/* actual number of digits returned by cvt */ -	wchar_t expstr[7];	/* buffer for exponent string */ -	wchar_t *dtoaresult;	/* buffer allocated by dtoa */ +	int lead;		/* sig figs before decimal or group sep */ +	int ndig;		/* actual number of digits returned by dtoa */ +	wchar_t expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */ +	char *dtoaresult;	/* buffer allocated by dtoa */ +	int nseps;		/* number of group separators with ' */ +	int nrepeats;		/* number of repeats of the last group */  #endif  	u_long	ulval;		/* integer arguments %[diouxX] */  	uintmax_t ujval;	/* %j, %ll, %q, %t, %z integers */ @@ -478,7 +503,7 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap)  	int realsz;		/* field size expanded by dprec, sign, etc */  	int size;		/* size of converted field or string */  	int prsize;             /* max size of printed field */ -	wchar_t *xdigs;		/* digits for [xX] conversion */ +	const wchar_t *xdigs;	/* digits for [xX] conversion */  	wchar_t buf[BUF];	/* buffer with space for digits of uintmax_t */  	wchar_t ox[2];		/* space for 0x hex-prefix */  	union arg *argtable;	/* args, built due to positional arg */ @@ -498,13 +523,16 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap)  	static wchar_t zeroes[PADSIZE] =  	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; +	static const wchar_t xdigs_lower[16] = L"0123456789abcdef"; +	static const wchar_t xdigs_upper[16] = L"0123456789ABCDEF"; +  	/*  	 * BEWARE, these `goto error' on error, PRINT uses `n2' and  	 * PAD uses `n'.  	 */  #define	PRINT(ptr, len)	do {			\ -	for (n2 = 0; n2 < (len); n2++)		\ -		__fputwc((ptr)[n2], fp);	\ +	for (n3 = 0; n3 < (len); n3++)		\ +		__fputwc((ptr)[n3], fp);	\  } while (0)  #define	PAD(howmany, with)	do {		\  	if ((n = (howmany)) > 0) {		\ @@ -515,6 +543,14 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap)  		PRINT(with, n);			\  	}					\  } while (0) +#define	PRINTANDPAD(p, ep, len, with) do {	\ +	n2 = (ep) - (p);       			\ +	if (n2 > (len))				\ +		n2 = (len);			\ +	if (n2 > 0)				\ +		PRINT((p), n2);			\ +	PAD((len) - (n2 > 0 ? n2 : 0), (with));	\ +} while(0)  	/*  	 * Get the argument indexed by nextarg.   If the argument table is @@ -580,7 +616,6 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap)  	thousands_sep = '\0';  	grouping = NULL;  #ifdef FLOATING_POINT -	dtoaresult = NULL;  	decimal_point = localeconv()->decimal_point;  #endif  	convbuf = NULL; @@ -622,6 +657,7 @@ __vfwprintf(FILE *fp, const wchar_t *fmt0, va_list ap)  		width = 0;  		prec = -1;  		sign = '\0'; +		ox[1] = '\0';  rflag:		ch = *fmt++;  reswitch:	switch (ch) { @@ -662,16 +698,14 @@ reswitch:	switch (ch) {  			goto rflag;  		case '.':  			if ((ch = *fmt++) == '*') { -				GETASTER (n); -				prec = n < 0 ? -1 : n; +				GETASTER (prec);  				goto rflag;  			} -			n = 0; +			prec = 0;  			while (is_digit(ch)) { -				n = 10 * n + to_digit(ch); +				prec = 10 * prec + to_digit(ch);  				ch = *fmt++;  			} -			prec = n < 0 ? -1 : n;  			goto reswitch;  		case '0':  			/*- @@ -765,86 +799,128 @@ reswitch:	switch (ch) {  #ifdef HEXFLOAT  		case 'a':  		case 'A': +			if (ch == 'a') { +				ox[1] = 'x'; +				xdigs = xdigs_lower; +				expchar = 'p'; +			} else { +				ox[1] = 'X'; +				xdigs = xdigs_upper; +				expchar = 'P'; +			} +			/* +			 * XXX We don't actually have a conversion +			 * XXX routine for this yet. +			 */ +			if (flags & LONGDBL) { +				fparg.ldbl = (double)GETARG(long double); +				dtoaresult = +				    __hldtoa(fparg.ldbl, xdigs, prec, +				        &expt, &signflag, &dtoaend); +			} else { +				fparg.dbl = GETARG(double); +				dtoaresult = +				    __hdtoa(fparg.dbl, xdigs, prec, +				        &expt, &signflag, &dtoaend); +			} +			if (convbuf != NULL) +				free(convbuf); +			cp = convbuf = __mbsconv(dtoaresult, -1); +			freedtoa(dtoaresult); +			goto fp_begin;  #endif  		case 'e':  		case 'E': -			/*- -			 * Grouping apply to %i, %d, %u, %f, %F, %g, %G -			 * conversion specifiers only. For other conversions -			 * behavior is undefined. -			 *	-- POSIX -			 */ -			flags &= ~GROUPING; -			/*FALLTHROUGH*/ +			expchar = ch; +			if (prec < 0)	/* account for digit before decpt */ +				prec = DEFPREC + 1; +			else +				prec++; +			goto fp_begin;  		case 'f':  		case 'F': +			expchar = '\0';  			goto fp_begin;  		case 'g':  		case 'G': +			expchar = ch - ('g' - 'e');  			if (prec == 0)  				prec = 1; -fp_begin:		if (prec == -1) +fp_begin: +			if (prec < 0)  				prec = DEFPREC; -			if (flags & LONGDBL) -				/* XXX this loses precision. */ -				_double = (double)GETARG(long double); -			else -				_double = GETARG(double); -			/* do this before tricky precision changes */ -			if (isinf(_double)) { -				if (_double < 0) -					sign = '-'; -				if (iswupper(ch)) -					cp = L"INF"; -				else -					cp = L"inf"; -				size = 3; -				break; +			if (convbuf != NULL) +				free(convbuf); +			if (flags & LONGDBL) { +				fparg.ldbl = GETARG(long double); +				dtoaresult = +				    __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, +				    &expt, &signflag, &dtoaend); +			} else { +				fparg.dbl = GETARG(double); +				dtoaresult = +				    dtoa(fparg.dbl, expchar ? 2 : 3, prec, +				    &expt, &signflag, &dtoaend); +				if (expt == 9999) +					expt = INT_MAX;  			} -			if (isnan(_double)) { -				if (iswupper(ch)) -					cp = L"NAN"; -				else -					cp = L"nan"; +			ndig = dtoaend - dtoaresult; +			cp = convbuf = __mbsconv(dtoaresult, -1); +			freedtoa(dtoaresult); +			if (signflag) +				sign = '-'; +			if (expt == INT_MAX) {	/* inf or nan */ +				if (*cp == 'N') { +					cp = (ch >= 'a') ? L"nan" : L"NAN"; +					sign = '\0'; +				} else +					cp = (ch >= 'a') ? L"inf" : L"INF";  				size = 3;  				break;  			}  			flags |= FPT; -			if (dtoaresult != NULL) { -				free(dtoaresult); -				dtoaresult = NULL; -			} -			dtoaresult = cp = cvt(_double, prec, flags, &softsign, -				&expt, ch, &ndig);  			if (ch == 'g' || ch == 'G') { -				if (expt <= -4 || expt > prec) -					ch = (ch == 'g') ? 'e' : 'E'; -				else -					ch = 'g'; +				if (expt > -4 && expt <= prec) { +					/* Make %[gG] smell like %[fF] */ +					expchar = '\0'; +					if (flags & ALT) +						prec -= expt; +					else +						prec = ndig - expt; +					if (prec < 0) +						prec = 0; +				}  			} -			if (ch == 'e' || ch == 'E') { -				--expt; -				expsize = exponent(expstr, expt, ch); -				size = expsize + ndig; -				if (ndig > 1 || flags & ALT) +			if (expchar) { +				expsize = exponent(expstr, expt - 1, expchar); +				size = expsize + prec; +				if (prec > 1 || flags & ALT)  					++size; -			} else if (ch == 'f' || ch == 'F') { +			} else {  				if (expt > 0) {  					size = expt;  					if (prec || flags & ALT)  						size += prec + 1;  				} else	/* "0.X" */  					size = prec + 2; -			} else if (expt >= ndig) {	/* fixed g fmt */ -				size = expt; -				if (flags & ALT) -					++size; -			} else -				size = ndig + (expt > 0 ? -					1 : 2 - expt); - -			if (softsign) -				sign = '-'; +				if (grouping && expt > 0) { +					/* space for thousands' grouping */ +					nseps = nrepeats = 0; +					lead = expt; +					while (*grouping != CHAR_MAX) { +						if (lead <= *grouping) +							break; +						lead -= *grouping; +						if (*(grouping+1)) { +							nseps++; +							grouping++; +						} else +							nrepeats++; +					} +					size += nseps + nrepeats; +				} else +					lead = expt; +			}  			break;  #endif /* FLOATING_POINT */  		case 'n': @@ -890,9 +966,9 @@ fp_begin:		if (prec == -1)  			 */  			ujval = (uintmax_t)(uintptr_t)GETARG(void *);  			base = 16; -			xdigs = L"0123456789abcdef"; -			flags = flags | INTMAXT | HEXPREFIX; -			ch = 'x'; +			xdigs = xdigs_lower; +			flags = flags | INTMAXT; +			ox[1] = 'x';  			goto nosign;  		case 'S':  			flags |= LONGINT; @@ -947,10 +1023,10 @@ fp_begin:		if (prec == -1)  			base = 10;  			goto nosign;  		case 'X': -			xdigs = L"0123456789ABCDEF"; +			xdigs = xdigs_upper;  			goto hex;  		case 'x': -			xdigs = L"0123456789abcdef"; +			xdigs = xdigs_lower;  hex:  			if (flags & INTMAX_SIZE)  				ujval = UJARG(); @@ -960,7 +1036,7 @@ hex:  			/* leading 0x/X only if non-zero */  			if (flags & ALT &&  			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) -				flags |= HEXPREFIX; +				ox[1] = ch;  			flags &= ~GROUPING;  			/* unsigned conversions */ @@ -1024,7 +1100,7 @@ number:			if ((dprec = prec) >= 0)  		realsz = dprec > size ? dprec : size;  		if (sign)  			realsz++; -		else if (flags & HEXPREFIX) +		else if (ox[1])  			realsz += 2;  		prsize = width > realsz ? width : realsz; @@ -1040,9 +1116,8 @@ number:			if ((dprec = prec) >= 0)  		/* prefix */  		if (sign) {  			PRINT(&sign, 1); -		} else if (flags & HEXPREFIX) { +		} else if (ox[1]) {	/* ox[1] is either x, X, or \0 */  			ox[0] = '0'; -			ox[1] = ch;  			PRINT(ox, 2);  		} @@ -1058,40 +1133,48 @@ number:			if ((dprec = prec) >= 0)  		if ((flags & FPT) == 0) {  			PRINT(cp, size);  		} else {	/* glue together f_p fragments */ -			if (ch >= 'f') {	/* 'f' or 'g' */ -				if (_double == 0) { -					/* kludge for __dtoa irregularity */ -					PRINT(L"0", 1); -					if (expt < ndig || (flags & ALT) != 0) { -						PRINT(decimal_point, 1); -						PAD(ndig - 1, zeroes); -					} -				} else if (expt <= 0) { -					PRINT(L"0", 1); -					PRINT(decimal_point, 1); +			if (!expchar) {	/* %[fF] or sufficiently short %[gG] */ +				if (expt <= 0) { +					buf[0] = '0'; +					buf[1] = *decimal_point; +					PRINT(buf, 2);  					PAD(-expt, zeroes); -					PRINT(cp, ndig); -				} else if (expt >= ndig) { -					PRINT(cp, ndig); -					PAD(expt - ndig, zeroes); -					if (flags & ALT) -						PRINT(decimal_point, 1); +					/* already handled initial 0's */ +					prec += expt;  				} else { -					PRINT(cp, expt); -					cp += expt; -					PRINT(decimal_point, 1); -					PRINT(cp, ndig-expt); +					PRINTANDPAD(cp, convbuf + ndig, lead, zeroes); +					cp += lead; +					if (grouping) { +						while (nseps>0 || nrepeats>0) { +							if (nrepeats > 0) +								nrepeats--; +							else { +								grouping--; +								nseps--; +							} +							PRINT(&thousands_sep, +							    1); +							PRINTANDPAD(cp, +							    convbuf + ndig, +							    *grouping, zeroes); +							cp += *grouping; +						} +						if (cp > convbuf + ndig) +							cp = convbuf + ndig; +					} +					if (prec || flags & ALT) { +						buf[0] = *decimal_point; +						PRINT(buf, 1); +					}  				} -			} else {	/* 'e' or 'E' */ -				if (ndig > 1 || flags & ALT) { -					ox[0] = *cp++; -					ox[1] = *decimal_point; -					PRINT(ox, 2); -					if (_double) { -						PRINT(cp, ndig-1); -					} else	/* 0.[0..] */ -						/* __dtoa irregularity */ -						PAD(ndig - 1, zeroes); +				PRINTANDPAD(cp, convbuf + ndig, prec, zeroes); +			} else {	/* %[eE] or sufficiently long %[gG] */ +				if (prec > 1 || flags & ALT) { +					buf[0] = *cp++; +					buf[1] = *decimal_point; +					PRINT(buf, 2); +					PRINT(cp, ndig-1); +					PAD(prec - ndig, zeroes);  				} else	/* XeYYY */  					PRINT(cp, 1);  				PRINT(expstr, expsize); @@ -1109,10 +1192,6 @@ number:			if ((dprec = prec) >= 0)  	}  done:  error: -#ifdef FLOATING_POINT -	if (dtoaresult != NULL) -		free(dtoaresult); -#endif  	if (convbuf != NULL)  		free(convbuf);  	if (__sferror(fp)) @@ -1481,52 +1560,6 @@ __grow_type_table (int nextarg, enum typeid **typetable, int *tablesize)  #ifdef FLOATING_POINT -static wchar_t * -cvt(double value, int ndigits, int flags, char *sign, int *decpt, -    wchar_t ch, int *length) -{ -	int i, mode, dsgn; -	wchar_t *digits, *bp, *result, *rve; -	char *tresult, *trve; - -	if (ch == 'f') -		mode = 3;		/* ndigits after the decimal point */ -	else { -		/* -		 * To obtain ndigits after the decimal point for the 'e' -		 * and 'E' formats, round to ndigits + 1 significant -		 * figures. -		 */ -		if (ch == 'e' || ch == 'E') -			ndigits++; -		mode = 2;		/* ndigits significant digits */ -	} -	tresult = __dtoa(value, mode, ndigits, decpt, &dsgn, &trve); -	if ((result = malloc((trve - tresult + 1) * sizeof(*result))) == NULL) -		abort();		/* XXX handle better */ -	for (i = 0; i < trve - tresult + 1; i++) -		result[i] = (wchar_t)(unsigned char)tresult[i]; -	rve = result + (trve - tresult); -	__freedtoa(tresult); -	digits = result; -	*sign = dsgn != 0; -	if ((ch != 'g' && ch != 'G') || flags & ALT) { -		/* print trailing zeros */ -		bp = digits + ndigits; -		if (ch == 'f') { -			if ((*digits == '0' || *digits == '\0') && value) -				*decpt = -ndigits + 1; -			bp += *decpt; -		} -		if (value == 0)	/* kludge for __dtoa irregularity */ -			rve = bp; -		while (rve < bp) -			*rve++ = '0'; -	} -	*length = rve - digits; -	return (digits); -} -  static int  exponent(wchar_t *p0, int exp, wchar_t fmtch)  { @@ -1550,7 +1583,14 @@ exponent(wchar_t *p0, int exp, wchar_t fmtch)  		for (; t < expbuf + MAXEXPDIG; *p++ = *t++);  	}  	else { -		*p++ = '0'; +		/* +		 * Exponents for decimal floating point conversions +		 * (%[eEgG]) must be at least two characters long, +		 * whereas exponents for hexadecimal conversions can +		 * be only one character long. +		 */ +		if (fmtch == 'e' || fmtch == 'E') +			*p++ = '0';  		*p++ = to_char(exp);  	}  	return (p - p0);  | 
