*/
#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)vfprintf.c 5.4 (Berkeley) %G%";
+static char sccsid[] = "@(#)vfprintf.c 5.10 (Berkeley) %G%";
#endif /* LIBC_SCCS and not lint */
-#include <sys/param.h>
+#include <sys/types.h>
#include <varargs.h>
#include <stdio.h>
#include <ctype.h>
-#define GETARG(r) \
- r = argsize&LONGINT ? va_arg(argp, long) : \
- argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
+#define MAXBUF 120
+#define DEFPREC 6
-#define MAXBUF 1024 /* should hold any number */
-#define MAXEXP 10 /* should hold any exponent */
-
-#define DEFPREC 6 /* default precision */
-
-#define PUTC(ch, fd) {++cnt; putc(ch, fd);}
+#define PUTC(ch) {++cnt; putc(ch, fp);}
#define LONGINT 0x01
#define LONGDBL 0x02
#define SHORTINT 0x04
+#define GETARG(r) \
+ r = argsize&LONGINT ? va_arg(argp, long) : \
+ argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
+
+static int alternate;
+static char printsign;
x_doprnt(fmt, argp, fp)
register char *fmt;
va_list argp;
register FILE *fp;
{
+ register int base, cnt;
+ register char *bp, *t;
register u_long reg_ulong;
register long reg_long;
- register int base;
- register char *digs, *bp, *t, padc;
double _double;
- char argsize, printsign, buf[MAXBUF], *ecvt(), *fcvt();
- int alternate, cnt, decpt, n, ladjust, width, prec, sign, size;
+ char argsize, padc, *_cvt(), *digs, buf[MAXBUF];
+ int n, ladjust, width, prec, size;
+ digs = "0123456789abcdef";
for (cnt = 0; *fmt; ++fmt) {
if (*fmt != '%') {
- PUTC(*fmt, fp);
+ PUTC(*fmt);
continue;
}
case '#':
alternate = 1;
goto flags;
- case '%': /* "%#%" prints as "%" */
- PUTC('%', fp);
- continue;
case '*':
/*
* ``A negative field width argument is taken as a
case 'l':
argsize |= LONGINT;
goto flags;
- }
+ case 'c': {
+ char ch;
- digs = "0123456789abcdef";
-
- switch (*fmt) {
- case 'c':
- PUTC(va_arg(argp, int), fp);
- break;
- case 'E':
- case 'e':
- if (prec == -1)
- prec = DEFPREC;
- _double = va_arg(argp, double);
- t = ecvt(_double, prec + 1, &decpt, &sign);
- bp = buf;
- *bp++ = *t ? *t++ : '0';
- if (alternate || prec > 0)
- *bp++ = '.';
- while (prec--)
- *bp++ = *t ? *t++ : '0';
- *bp++ = *fmt;
- *bp++ = (decpt > 0 || !_double) ? '+' : '-';
- /* we know exponents <= 99 */
- --decpt;
- *bp++ = (int)decpt / 10 + '0';
- *bp++ = (int)decpt % 10 + '0';
- goto pbuf;
- case 'f':
- if (prec == -1)
- prec = DEFPREC;
- _double = va_arg(argp, double);
- t = fcvt(_double, prec + 1, &decpt, &sign);
- bp = buf;
- if (decpt >= 0)
- for (;;) {
- *bp++ = *t ? *t++ : '0';
- if (!--decpt)
- break;
- }
- if (alternate || prec > 0)
- *bp++ = '.';
- while (decpt++) {
- *bp++ = *t ? *t++ : '0';
- --prec;
- }
- while (prec--)
- *bp++ = *t ? *t++ : '0';
-pbuf: size = bp - buf;
- if (sign || printsign)
- PUTC(sign ? '-' : printsign, fp);
- if (size < width && !ladjust)
- do {
- PUTC(padc, fp);
- } while (--width > size);
- for (t = buf; t < bp; ++t)
- PUTC(*t, fp);
- for (; width > size; --width)
- PUTC(padc, fp);
+ ch = va_arg(argp, int);
+ PUTC(ch);
break;
+ }
case 'd':
case 'i':
GETARG(reg_long);
reg_ulong = reg_long;
}
if (printsign)
- PUTC(printsign, fp);
+ PUTC(printsign);
base = 10;
goto num1;
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'g':
+ case 'G':
+ _double = va_arg(argp, double);
+ bp = _cvt(_double, prec, buf, buf + sizeof(buf), *fmt);
+pbuf: size = bp - buf;
+ if (size < width && !ladjust)
+ do {
+ PUTC(padc);
+ } while (--width > size);
+ for (t = buf; t < bp; ++t)
+ PUTC(*t);
+ for (; width > size; --width)
+ PUTC(padc);
+ break;
case 'n':
*(va_arg(argp, int *)) = cnt;
break;
size = &buf[sizeof(buf) - 1] - bp;
if (size < --width && !ladjust)
do {
- PUTC(padc, fp);
+ PUTC(padc);
} while (--width > size);
- PUTC('0', fp);
+ PUTC('0');
goto num2;
- break;
case 'p':
case 's':
if (!(bp = va_arg(argp, char *)))
n++, bp++);
bp = savep;
while (n++ < width)
- PUTC(' ', fp);
+ PUTC(' ');
}
for (n = 0; *bp; ++bp) {
if (++n > prec && prec >= 0)
break;
- PUTC(*bp, fp);
+ PUTC(*bp);
}
if (n < width && ladjust)
do {
- PUTC(' ', fp);
+ PUTC(' ');
} while (++n < width);
break;
case 'u':
case 'x':
GETARG(reg_ulong);
if (alternate && reg_ulong) {
- PUTC('0', fp);
- PUTC(*fmt, fp);
+ PUTC('0');
+ PUTC(*fmt);
}
base = 16;
num1: bp = buf + sizeof(buf) - 1;
for (; size < prec; *bp-- = '0', ++size);
if (size < width && !ladjust)
do {
- PUTC(padc, fp);
+ PUTC(padc);
} while (--width > size);
num2: while (++bp != &buf[MAXBUF])
- PUTC(*bp, fp);
+ PUTC(*bp);
for (; width > size; --width)
- PUTC(padc, fp);
+ PUTC(padc);
+ digs = "0123456789abcdef";
break;
case '\0': /* "%?" prints ?, unless ? is NULL */
return(ferror(fp) ? -1 : cnt);
default:
- PUTC(*fmt, fp);
+ PUTC(*fmt);
}
}
return(ferror(fp) ? -1 : cnt);
}
+
+#define EFORMAT 0x01
+#define FFORMAT 0x02
+#define GFORMAT 0x04
+
+static char *
+_cvt(number, prec, startp, endp, fmtch)
+ double number;
+ register int prec;
+ char *startp, *endp, fmtch;
+{
+ register char *p;
+ register int expcnt, format;
+ double fract, integer, tmp, modf();
+ int decpt;
+ char *savep;
+
+ if (prec == -1) /* set default precision */
+ prec = DEFPREC;
+
+ p = endp - 1;
+ if (number < 0) { /* set sign */
+ *startp++ = '-';
+ number = -number;
+ }
+ else if (printsign)
+ *startp++ = '+';
+
+ switch(fmtch) {
+ case 'e':
+ case 'E':
+ format = EFORMAT;
+ break;
+ case 'f':
+ format = FFORMAT;
+ break;
+ case 'g':
+ case 'G':
+ format = GFORMAT;
+ fmtch -= 2;
+ }
+
+ /*
+ * if the alternate flag is set, or, at least one digit of precision
+ * was requested, add a decimal point, unless it's the g/G format
+ * in which case we require two digits of precision, since it counts
+ * precision differently.
+ */
+ decpt = alternate || prec > 1 || !(format&GFORMAT) && prec;
+
+ expcnt = 0;
+ fract = modf(number, &integer);
+ if (integer) {
+ register char *p2;
+
+ /* get integer part of number; count decimal places */
+ for (; integer; ++expcnt) {
+ tmp = modf(integer / 10, &integer);
+ *p-- = (int)((tmp + .03) * 10) + '0';
+ }
+
+ /* copy, in reverse order, to start of buffer */
+ p2 = startp;
+ *p2++ = *++p;
+
+ /*
+ * if the format is g/G, and the resulting exponent will be
+ * greater than the precision, use e/E format. If e/E format,
+ * put in a decimal point as needed, and decrement precision
+ * count for each digit after the decimal point.
+ */
+ if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
+ if (format&GFORMAT) {
+ format |= EFORMAT;
+
+ /* first digit is precision for g/G format */
+ if (prec)
+ --prec;
+ }
+ if (decpt)
+ *p2++ = '.';
+ for (; ++p < endp && prec; --prec, *p2++ = *p);
+
+ /* precision ran out; round number */
+ if (p < endp) {
+ if (*p > '4') {
+ for (savep = p2--;; *p2-- = '0') {
+ if (*p2 == '.')
+ --p2;
+ if (++*p2 <= '9')
+ break;
+ }
+ p2 = savep;
+ }
+ fract = 0;
+ }
+ }
+ /*
+ * g/G in f format; if run out of precision, replace digits
+ * with zeroes, note, have to round first, otherwise lose
+ * rounding point.
+ */
+ else if (format&GFORMAT) {
+ for (; ++p < endp && prec; --prec, *p2++ = *p);
+ /* precision ran out; round and then add zeroes */
+ if (p < endp) {
+ if (*p > '4') {
+ for (savep = p2--; ++*p2 > '9';
+ *p2-- = '0');
+ p2 = savep;
+ }
+ do {
+ *p2++ = '0';
+ } while (++p < endp);
+ fract = 0;
+ }
+ if (decpt)
+ *p2++ = '.';
+ }
+ /* f format */
+ else {
+ for (; ++p < endp; *p2++ = *p);
+ if (decpt)
+ *p2++ = '.';
+ }
+ p = p2;
+ }
+ /*
+ * it's unclear from the ANSI X3J11 spec if the g/G format should
+ * just result in an empty string, because it's supposed to remove
+ * trailing zeroes. That seems counter-intuitive, so here it does
+ * what f and e/E do; if no fraction, the number was zero, and if
+ * no precision can't show anything after the decimal point.
+ */
+ else if (!fract || !prec) {
+ *startp++ = '0';
+ if (decpt)
+ *startp++ = '.';
+ *startp++ = '\0';
+ return(startp);
+ }
+ /*
+ * if the format is g/G, and the resulting exponent will be less than
+ * -4 use e/E format. If e/E format, compute exponent value.
+ */
+ else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
+ format |= EFORMAT;
+ if (fract)
+ for (p = startp; fract;) {
+ fract = modf(fract * 10, &tmp);
+ if (!tmp) {
+ --expcnt;
+ continue;
+ }
+ *p++ = (int)tmp + '0';
+ break;
+ }
+ else
+ *p++ = '0';
+
+ /* g/G format, decrement precision for first digit */
+ if (format&GFORMAT && prec)
+ --prec;
+
+ /* add decimal after first non-zero digit */
+ if (decpt)
+ *p++ = '.';
+ }
+ /*
+ * f format or g/G printed as f format; don't worry about decimal
+ * point, if g/G format doesn't need it, will get stripped later.
+ */
+ else {
+ p = startp;
+ *p++ = '0';
+ *p++ = '.';
+ }
+
+ /* finish out requested precision from fractional value */
+ while (prec--)
+ if (fract) {
+ fract = modf(fract * 10, &tmp);
+ *p++ = (int)tmp + '0';
+ }
+ else
+ *p++ = '0';
+
+ /*
+ * if any fractional value left, "round" it back up to the beginning
+ * of the number, fixing the exponent as necessary, and avoiding the
+ * decimal point.
+ */
+ if (fract) {
+ (void)modf(fract * 10, &tmp);
+ if (tmp > 4) {
+ for (savep = p--;; *p-- = '0') {
+ if (*p == '.')
+ --p;
+ if (p == startp) {
+ *p = '1';
+ ++expcnt;
+ break;
+ }
+ if (++*p <= '9')
+ break;
+ }
+ p = savep;
+ }
+ }
+
+ /*
+ * if a g/G format and not alternate flag, lose trailing zeroes,
+ * if e/E or g/G format, and last char is decimal point, lose it.
+ */
+ if (!alternate) {
+ if (format&GFORMAT)
+ for (; p[-1] == '0'; --p);
+ if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
+ --p;
+ }
+
+ /* if an e/E format, add exponent */
+ if (format&EFORMAT) {
+ *p++ = fmtch;
+ if (--expcnt < 0) {
+ expcnt = -expcnt;
+ *p++ = '-';
+ }
+ else
+ *p++ = '+';
+ *p++ = expcnt / 10 + '0';
+ *p++ = expcnt % 10 + '0';
+ }
+ return(p);
+}