Loading...
stdio/FreeBSD/xprintf_float.c /dev/null Libc-1583.40.7
--- /dev/null
+++ Libc/Libc-1583.40.7/stdio/FreeBSD/xprintf_float.c
@@ -0,0 +1,446 @@
+/*-
+ * Copyright (c) 2005 Poul-Henning Kamp
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libc/stdio/xprintf_float.c,v 1.1 2005/12/16 18:56:38 phk Exp $
+ */
+
+#include <namespace.h>
+#include "xlocale_private.h"
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <assert.h>
+#include <locale.h>
+#include <limits.h>
+
+#define	dtoa		__dtoa
+#define	freedtoa	__freedtoa
+
+#include <float.h>
+#include <math.h>
+#include "gdtoa.h"
+#include "floatio.h"
+#include "printf.h"
+#include "xprintf_private.h"
+#include <un-namespace.h>
+
+/*
+ * The size of the buffer we use as scratch space for integer
+ * conversions, among other things.  Technically, we would need the
+ * most space for base 10 conversions with thousands' grouping
+ * characters between each pair of digits.  100 bytes is a
+ * conservative overestimate even for a 128-bit uintmax_t.
+ */
+#define	BUF	100
+
+#define	DEFPREC		6	/* Default FP precision */
+
+
+/* various globals ---------------------------------------------------*/
+
+
+/* padding function---------------------------------------------------*/
+
+#define	PRINTANDPAD(p, ep, len, with) do {		\
+	n2 = (ep) - (p);       				\
+	if (n2 > (len))					\
+		n2 = (len);				\
+	if (n2 > 0)					\
+		ret += __printf_puts(io, (p), n2);		\
+	ret += __printf_pad(io, (len) - (n2 > 0 ? n2 : 0), (with));	\
+} while(0)
+
+/* misc --------------------------------------------------------------*/
+
+extern const char *__fix_nogrouping(const char *str);
+
+#define	to_char(n)	((n) + '0')
+
+static int
+exponent(char *p0, int expo, int fmtch)
+{
+	char *p, *t;
+	char expbuf[MAXEXPDIG];
+
+	p = p0;
+	*p++ = fmtch;
+	if (expo < 0) {
+		expo = -expo;
+		*p++ = '-';
+	}
+	else
+		*p++ = '+';
+	t = expbuf + MAXEXPDIG;
+	if (expo > 9) {
+		do {
+			*--t = to_char(expo % 10);
+		} while ((expo /= 10) > 9);
+		*--t = to_char(expo);
+		for (; t < expbuf + MAXEXPDIG; *p++ = *t++)
+			;
+	}
+	else {
+		/*
+		 * 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(expo);
+	}
+	return (p - p0);
+}
+
+/* 'f' ---------------------------------------------------------------*/
+
+__private_extern__ int
+__printf_arginfo_float(const struct printf_info *pi, size_t n, int *argt)
+{
+	assert (n > 0);
+#ifdef VECTORS
+	if (pi->is_vec)
+		argt[0] = PA_VECTOR;
+	else {
+#endif /* VECTORS */
+	argt[0] = PA_DOUBLE;
+	if (pi->is_long_double)
+		argt[0] |= PA_FLAG_LONG_DOUBLE;
+#ifdef VECTORS
+	}
+#endif /* VECTORS */
+	return (1);
+}
+
+/*
+ * 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
+ */
+
+__private_extern__ int
+__printf_render_float(struct __printf_io *io, const struct printf_info *pi, const void *const *arg)
+{
+	int prec;		/* precision from format; <0 for N/A */
+	char *dtoaresult;	/* buffer allocated by dtoa */
+	char expchar;		/* exponent character: [eEpP\0] */
+	char *cp;
+	int expt;		/* integer value of exponent */
+	int signflag;		/* true if float is negative */
+	char *dtoaend;		/* pointer to end of converted digits */
+	char sign;		/* sign prefix (' ', '+', '-', or \0) */
+	int size;		/* size of converted field or string */
+	int ndig;		/* actual number of digits returned by dtoa */
+	int expsize;		/* character count for expstr */
+	char expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */
+	int nseps;		/* number of group separators with ' */
+	int nrepeats;		/* number of repeats of the last group */
+	const char *grouping;	/* locale specific numeric grouping rules */
+	int lead;		/* sig figs before decimal or group sep */
+	long double ld;
+	double d;
+	int realsz;		/* field size expanded by dprec, sign, etc */
+	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
+	char ox[2];		/* space for 0x; ox[1] is either x, X, or \0 */
+	int prsize;             /* max size of printed field */
+	int ret;		/* return value accumulator */
+	const char *decimal_point;	/* locale specific decimal point */
+	int decimal_point_len;	/* length of locale specific decimal point */
+	int n2;			/* XXX: for PRINTANDPAD */
+	const char *thousands_sep;	/* locale specific thousands separator */
+	int thousands_sep_len;	/* length of locale specific thousands separator */
+	char buf[BUF];		/* buffer with space for digits of uintmax_t */
+	const char *xdigs;
+	int flag;
+
+#ifdef VECTORS
+	if (pi->is_vec) return __xprintf_vector(io, pi, arg);
+#endif /* VECTORS */
+
+	prec = pi->prec;
+	ox[1] = '\0';
+	sign = pi->signchar;
+	flag = 0;
+	ret = 0;
+
+	thousands_sep = localeconv_l(pi->loc)->thousands_sep;
+	thousands_sep_len = strlen(thousands_sep);
+	grouping = NULL;
+	if (pi->group)
+		grouping = __fix_nogrouping(localeconv_l(pi->loc)->grouping);
+	decimal_point = localeconv_l(pi->loc)->decimal_point;
+	decimal_point_len = strlen(decimal_point);
+	dprec = -1;
+
+	switch(pi->spec) {
+	case 'a':
+	case 'A':
+		if (pi->spec == 'a') {
+			ox[1] = 'x';
+			xdigs = __lowercase_hex;
+			expchar = 'p';
+		} else {
+			ox[1] = 'X';
+			xdigs = __uppercase_hex;
+			expchar = 'P';
+		}
+		if (prec >= 0)
+			prec++;
+		if (pi->is_long_double) {
+			ld = *((long double *)arg[0]);
+			dtoaresult = cp =
+			    __hldtoa(ld, xdigs, prec,
+			    &expt, &signflag, &dtoaend);
+		} else {
+			d = *((double *)arg[0]);
+			dtoaresult = cp =
+			    __hdtoa(d, xdigs, prec,
+			    &expt, &signflag, &dtoaend);
+		}
+		if (prec < 0)
+			prec = dtoaend - cp;
+		if (expt == INT_MAX)
+			ox[1] = '\0';
+		goto fp_common;
+	case 'e':
+	case 'E':
+		expchar = pi->spec;
+		if (prec < 0)	/* account for digit before decpt */
+			prec = DEFPREC + 1;
+		else
+			prec++;
+		break;
+	case 'f':
+	case 'F':
+		expchar = '\0';
+		break;
+	case 'g':
+	case 'G':
+		expchar = pi->spec - ('g' - 'e');
+		if (prec == 0)
+			prec = 1;
+		break;
+	default:
+		assert(pi->spec == 'f');
+	}
+
+	if (prec < 0)
+		prec = DEFPREC;
+	if (pi->is_long_double) {
+		ld = *((long double *)arg[0]);
+		dtoaresult = cp =
+		    __ldtoa(&ld, expchar ? 2 : 3, prec,
+		    &expt, &signflag, &dtoaend);
+	} else {
+		d = *((double *)arg[0]);
+		dtoaresult = cp =
+		    dtoa(d, expchar ? 2 : 3, prec,
+		    &expt, &signflag, &dtoaend);
+		if (expt == 9999)
+			expt = INT_MAX;
+	}
+fp_common:
+	if (signflag)
+		sign = '-';
+	if (expt == INT_MAX) {	/* inf or nan */
+		if (*cp == 'N') {
+			cp = (pi->spec >= 'a') ? "nan" : "NAN";
+			sign = '\0';
+		} else
+			cp = (pi->spec >= 'a') ? "inf" : "INF";
+		size = 3;
+		flag = 1;
+		goto here;
+	}
+	ndig = dtoaend - cp;
+	if (pi->spec == 'g' || pi->spec == 'G') {
+		if (expt > -4 && expt <= prec) {
+			/* Make %[gG] smell like %[fF] */
+			expchar = '\0';
+			if (pi->alt)
+				prec -= expt;
+			else
+				prec = ndig - expt;
+			if (prec < 0)
+				prec = 0;
+		} else {
+			/*
+			 * Make %[gG] smell like %[eE], but
+			 * trim trailing zeroes if no # flag.
+			 */
+			if (!pi->alt)
+				prec = ndig;
+		}
+	}
+	if (expchar) {
+		expsize = exponent(expstr, expt - 1, expchar);
+		size = expsize + prec;
+		if (prec > 1 || pi->alt)
+			++size;
+	} else {
+		/* space for digits before decimal point */
+		if (expt > 0)
+			size = expt;
+		else	/* "0" */
+			size = 1;
+		/* space for decimal pt and following digits */
+		if (prec || pi->alt)
+			size += prec + 1;
+		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;
+	}
+
+here:
+	/*
+	 * All reasonable formats wind up here.  At this point, `cp'
+	 * points to a string which (if not flags&LADJUST) should be
+	 * padded out to `width' places.  If flags&ZEROPAD, it should
+	 * first be prefixed by any sign or other prefix; otherwise,
+	 * it should be blank padded before the prefix is emitted.
+	 * After any left-hand padding and prefixing, emit zeroes
+	 * required by a decimal [diouxX] precision, then print the
+	 * string proper, then emit zeroes required by any leftover
+	 * floating precision; finally, if LADJUST, pad with blanks.
+	 *
+	 * Compute actual size, so we know how much to pad.
+	 * size excludes decimal prec; realsz includes it.
+	 */
+	realsz = dprec > size ? dprec : size;
+	if (sign)
+		realsz++;
+	if (ox[1])
+		realsz += 2;
+
+	prsize = pi->width > realsz ? pi->width : realsz;
+
+	/* right-adjusting blank padding */
+	if (pi->pad != '0' && pi->left == 0)
+		ret += __printf_pad(io, pi->width - realsz, 0);
+
+	/* prefix */
+	if (sign)
+		ret += __printf_puts(io, &sign, 1);
+
+	if (ox[1]) {	/* ox[1] is either x, X, or \0 */
+		ox[0] = '0';
+		ret += __printf_puts(io, ox, 2);
+	}
+
+	/* right-adjusting zero padding */
+	if (pi->pad == '0' && pi->left == 0)
+		ret += __printf_pad(io, pi->width - realsz, 1);
+
+	/* leading zeroes from decimal precision */
+	ret += __printf_pad(io, dprec - size, 1);
+
+	if (flag)
+		ret += __printf_puts(io, cp, size);
+	else {
+		/* glue together f_p fragments */
+		if (!expchar) {	/* %[fF] or sufficiently short %[gG] */
+			if (expt <= 0) {
+				ret += __printf_puts(io, "0", 1);
+				if (prec || pi->alt)
+					ret += __printf_puts(io, decimal_point, decimal_point_len);
+				ret += __printf_pad(io, -expt, 1);
+				/* already handled initial 0's */
+				prec += expt;
+			} else {
+				PRINTANDPAD(cp, dtoaend, lead, 1);
+				cp += lead;
+				if (grouping) {
+					while (nseps>0 || nrepeats>0) {
+						if (nrepeats > 0)
+							nrepeats--;
+						else {
+							grouping--;
+							nseps--;
+						}
+						ret += __printf_puts(io, thousands_sep, thousands_sep_len);
+						PRINTANDPAD(cp,dtoaend,
+						    *grouping, 1);
+						cp += *grouping;
+					}
+					if (cp > dtoaend)
+						cp = dtoaend;
+				}
+				if (prec || pi->alt)
+					ret += __printf_puts(io, decimal_point, decimal_point_len);
+			}
+			PRINTANDPAD(cp, dtoaend, prec, 1);
+		} else {	/* %[eE] or sufficiently long %[gG] */
+			if (prec > 1 || pi->alt) {
+				buf[0] = *cp++;
+				memcpy(buf + 1, decimal_point, decimal_point_len);
+				ret += __printf_puts(io, buf, decimal_point_len + 1);
+				ret += __printf_puts(io, cp, ndig-1);
+				ret += __printf_pad(io, prec - ndig, 1);
+			} else	/* XeYYY */
+				ret += __printf_puts(io, cp, 1);
+			ret += __printf_puts(io, expstr, expsize);
+		}
+	}
+	/* left-adjusting padding (always blank) */
+	if (pi->left)
+		ret += __printf_pad(io, pi->width - realsz, 0);
+
+	__printf_flush(io);
+	if (dtoaresult != NULL)
+		freedtoa(dtoaresult);
+
+	return (ret);
+}