home *** CD-ROM | disk | FTP | other *** search
- /*-
- * Copyright (c) 1990 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. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. 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.
- */
-
- #if defined(LIBC_SCCS) && !defined(lint)
- static char sccsid[] = "@(#)vfprintf.c 5.47 (Berkeley) 3/22/91";
- #endif /* LIBC_SCCS and not lint */
-
- /*
- * Actual printf innards.
- *
- * This code is large and complicated...
- */
-
- #include <sys/types.h>
- #include <math.h>
- #include <stdio.h>
- #include <string.h>
- #if __STDC__
- #include <stdarg.h>
- #else
- #include <varargs.h>
- #endif
- #include "local.h"
- #include "fvwrite.h"
-
- /*
- * Define FLOATING_POINT to get floating point.
- * Define CSH to get a csh-specific version (grr).
- */
- #ifndef CSH
- #define FLOATING_POINT
- #endif
-
- /* end of configuration stuff */
-
-
- #ifdef CSH
- /*
- * C shell hacks. Ick, gag.
- */
- #undef BUFSIZ
- #include "sh.h"
-
- #if __STDC__
- int
- printf(const char *fmt, ...) {
- FILE f;
- va_list ap;
- int ret;
-
- va_start(ap, fmt);
- f._flags = __SWR;
- f._write = NULL;
- ret = vfprintf(&f, fmt, ap);
- va_end(ap);
- return ret;
- }
- #else
- int
- printf(fmt, args)
- char *fmt;
- {
- FILE f;
-
- f._flags = __SWR;
- f._write = NULL;
- return (vfprintf(&f, fmt, &args));
- }
- #endif
-
- int
- __sprint(fp, uio)
- FILE *fp;
- register struct __suio *uio;
- {
- register char *p;
- register int n, ch, iovcnt;
- register struct __siov *iov;
-
- /* must allow sprintf to work, might as well allow others too */
- if (fp->_write || fp->_flags & __SSTR) {
- if (uio->uio_resid == 0) {
- uio->uio_iovcnt = 0;
- return (0);
- }
- n = __sfvwrite(fp, uio);
- uio->uio_resid = 0;
- uio->uio_iovcnt = 0;
- return (n);
- }
- iov = uio->uio_iov;
- for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) {
- for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) {
- #ifdef CSHPUTCHAR
- ch = *p++;
- CSHPUTCHAR; /* this horrid macro uses `ch' */
- #else
- #undef putchar
- putchar(*p++);
- #endif
- }
- }
- uio->uio_resid = 0;
- uio->uio_iovcnt = 0;
- return (0);
- }
-
- #else /* CSH */
-
- /*
- * Flush out all the vectors defined by the given uio,
- * then reset it so that it can be reused.
- */
- static int
- __sprint(fp, uio)
- FILE *fp;
- register struct __suio *uio;
- {
- register int err;
-
- if (uio->uio_resid == 0) {
- uio->uio_iovcnt = 0;
- return (0);
- }
- err = __sfvwrite(fp, uio);
- uio->uio_resid = 0;
- uio->uio_iovcnt = 0;
- return (err);
- }
-
- /*
- * Helper function for `fprintf to unbuffered unix file': creates a
- * temporary buffer. We only work on write-only files; this avoids
- * worries about ungetc buffers and so forth.
- */
- static int
- __sbprintf(fp, fmt, ap)
- register FILE *fp;
- const char *fmt;
- va_list ap;
- {
- int ret;
- FILE fake;
- unsigned char buf[BUFSIZ];
-
- /* copy the important variables */
- fake._flags = fp->_flags & ~__SNBF;
- fake._file = fp->_file;
- fake._cookie = fp->_cookie;
- fake._write = fp->_write;
-
- /* set up the buffer */
- fake._bf._base = fake._p = buf;
- fake._bf._size = fake._w = sizeof(buf);
- fake._lbfsize = 0; /* not actually used, but Just In Case */
-
- /* do the work, then copy any error status */
- ret = vfprintf(&fake, fmt, ap);
- if (ret >= 0 && fflush(&fake))
- ret = EOF;
- if (fake._flags & __SERR)
- fp->_flags |= __SERR;
- return (ret);
- }
-
- #endif /* CSH */
-
-
- #ifdef FLOATING_POINT
- #include "floatio.h"
-
- #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */
- #define DEFPREC 6
-
- static int cvt();
-
- #else /* no FLOATING_POINT */
-
- #define BUF 40
-
- #endif /* FLOATING_POINT */
-
-
- /*
- * Macros for converting digits to letters and vice versa
- */
- #define to_digit(c) ((c) - '0')
- #define is_digit(c) ((unsigned)to_digit(c) <= 9)
- #define to_char(n) ((n) + '0')
-
- /*
- * Flags used during conversion.
- */
- #define LONGINT 0x01 /* long integer */
- #define LONGDBL 0x02 /* long double; unimplemented */
- #define SHORTINT 0x04 /* short integer */
- #define ALT 0x08 /* alternate form */
- #define LADJUST 0x10 /* left adjustment */
- #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */
- #define HEXPREFIX 0x40 /* add 0x or 0X prefix */
-
- int
- vfprintf(fp, fmt0, ap)
- FILE *fp;
- const char *fmt0;
- #if tahoe
- register /* technically illegal, since we do not know what type va_list is */
- #endif
- va_list ap;
- {
- register char *fmt; /* format string */
- register int ch; /* character from fmt */
- register int n; /* handy integer (short term usage) */
- register char *cp; /* handy char pointer (short term usage) */
- register struct __siov *iovp;/* for PRINT macro */
- register 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 */
- char sign; /* sign prefix (' ', '+', '-', or \0) */
- #ifdef FLOATING_POINT
- char softsign; /* temporary negative sign for floats */
- double _double; /* double precision arguments %[eEfgG] */
- int fpprec; /* `extra' floating precision in [eEfgG] */
- #endif
- u_long _ulong; /* integer arguments %[diouxX] */
- enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
- int dprec; /* a copy of prec if [diouxX], 0 otherwise */
- int fieldsz; /* field size expanded by sign, etc */
- int realsz; /* field size expanded by dprec */
- int size; /* size of converted field or string */
- char *xdigs; /* digits for [xX] conversion */
- #define NIOV 8
- struct __suio uio; /* output information: summary */
- struct __siov iov[NIOV];/* ... and individual io vectors */
- char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
- char ox[2]; /* space for 0x hex-prefix */
-
- /*
- * Choose PADSIZE to trade efficiency vs size. If larger
- * printf fields occur frequently, increase PADSIZE (and make
- * the initialisers below longer).
- */
- #define PADSIZE 16 /* pad chunk size */
- static char blanks[PADSIZE] =
- {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
- static char zeroes[PADSIZE] =
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
-
- /*
- * BEWARE, these `goto error' on error, and PAD uses `n'.
- */
- #define PRINT(ptr, len) { \
- iovp->iov_base = (ptr); \
- iovp->iov_len = (len); \
- uio.uio_resid += (len); \
- iovp++; \
- if (++uio.uio_iovcnt >= NIOV) { \
- if (__sprint(fp, &uio)) \
- goto error; \
- iovp = iov; \
- } \
- }
- #define PAD(howmany, with) { \
- if ((n = (howmany)) > 0) { \
- while (n > PADSIZE) { \
- PRINT(with, PADSIZE); \
- n -= PADSIZE; \
- } \
- PRINT(with, n); \
- } \
- }
- #define FLUSH() { \
- if (uio.uio_resid && __sprint(fp, &uio)) \
- goto error; \
- uio.uio_iovcnt = 0; \
- iovp = iov; \
- }
-
- /*
- * To extend shorts properly, we need both signed and unsigned
- * argument extraction methods.
- */
- #define SARG() \
- (flags&LONGINT ? va_arg(ap, long) : \
- flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
- (long)va_arg(ap, int))
- #define UARG() \
- (flags&LONGINT ? va_arg(ap, u_long) : \
- flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
- (u_long)va_arg(ap, u_int))
-
- #ifndef CSH
- /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
- if (cantwrite(fp))
- return (EOF);
-
- /* optimise fprintf(stderr) (and other unbuffered Unix files) */
- if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
- fp->_file >= 0)
- return (__sbprintf(fp, fmt0, ap));
- #endif /* CSH */
-
- fmt = (char *)fmt0;
- uio.uio_iov = iovp = iov;
- uio.uio_resid = 0;
- uio.uio_iovcnt = 0;
- ret = 0;
-
- /*
- * Scan the format for conversions (`%' character).
- */
- for (;;) {
- for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
- /* void */;
- if ((n = fmt - cp) != 0) {
- PRINT(cp, n);
- ret += n;
- }
- if (ch == '\0')
- goto done;
- fmt++; /* skip over '%' */
-
- flags = 0;
- dprec = 0;
- #ifdef FLOATING_POINT
- fpprec = 0;
- #endif
- width = 0;
- prec = -1;
- sign = '\0';
-
- rflag: ch = *fmt++;
- reswitch: switch (ch) {
- case ' ':
- /*
- * ``If the space and + flags both appear, the space
- * flag will be ignored.''
- * -- ANSI X3J11
- */
- if (!sign)
- sign = ' ';
- goto rflag;
- case '#':
- flags |= ALT;
- goto rflag;
- case '*':
- /*
- * ``A negative field width argument is taken as a
- * - flag followed by a positive field width.''
- * -- ANSI X3J11
- * They don't exclude field widths read from args.
- */
- if ((width = va_arg(ap, int)) >= 0)
- goto rflag;
- width = -width;
- /* FALLTHROUGH */
- case '-':
- flags |= LADJUST;
- goto rflag;
- case '+':
- sign = '+';
- goto rflag;
- case '.':
- if ((ch = *fmt++) == '*') {
- n = va_arg(ap, int);
- prec = n < 0 ? -1 : n;
- goto rflag;
- }
- n = 0;
- while (is_digit(ch)) {
- n = 10 * n + to_digit(ch);
- ch = *fmt++;
- }
- prec = n < 0 ? -1 : n;
- goto reswitch;
- case '0':
- /*
- * ``Note that 0 is taken as a flag, not as the
- * beginning of a field width.''
- * -- ANSI X3J11
- */
- flags |= ZEROPAD;
- goto rflag;
- case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- n = 0;
- do {
- n = 10 * n + to_digit(ch);
- ch = *fmt++;
- } while (is_digit(ch));
- width = n;
- goto reswitch;
- #ifdef FLOATING_POINT
- case 'L':
- flags |= LONGDBL;
- goto rflag;
- #endif
- case 'h':
- flags |= SHORTINT;
- goto rflag;
- case 'l':
- flags |= LONGINT;
- goto rflag;
- case 'c':
- *(cp = buf) = va_arg(ap, int);
- size = 1;
- sign = '\0';
- break;
- case 'D':
- flags |= LONGINT;
- /*FALLTHROUGH*/
- case 'd':
- case 'i':
- _ulong = SARG();
- if ((long)_ulong < 0) {
- _ulong = -_ulong;
- sign = '-';
- }
- base = DEC;
- goto number;
- #ifdef FLOATING_POINT
- case 'e':
- case 'E':
- case 'f':
- case 'g':
- case 'G':
- _double = va_arg(ap, double);
- /* do this before tricky precision changes */
- if (isinf(_double)) {
- if (_double < 0)
- sign = '-';
- cp = "Inf";
- size = 3;
- break;
- }
- if (isnan(_double)) {
- cp = "NaN";
- size = 3;
- break;
- }
- /*
- * don't do unrealistic precision; just pad it with
- * zeroes later, so buffer size stays rational.
- */
- if (prec > MAXFRACT) {
- if (ch != 'g' && ch != 'G' || (flags&ALT))
- fpprec = prec - MAXFRACT;
- prec = MAXFRACT;
- } else if (prec == -1)
- prec = DEFPREC;
- /*
- * cvt may have to round up before the "start" of
- * its buffer, i.e. ``intf("%.2f", (double)9.999);'';
- * if the first character is still NUL, it did.
- * softsign avoids negative 0 if _double < 0 but
- * no significant digits will be shown.
- */
- cp = buf;
- *cp = '\0';
- size = cvt(_double, prec, flags, &softsign, ch,
- cp, buf + sizeof(buf));
- if (softsign)
- sign = '-';
- if (*cp == '\0')
- cp++;
- break;
- #endif /* FLOATING_POINT */
- case 'n':
- if (flags & LONGINT)
- *va_arg(ap, long *) = ret;
- else if (flags & SHORTINT)
- *va_arg(ap, short *) = ret;
- else
- *va_arg(ap, int *) = ret;
- continue; /* no output */
- case 'O':
- flags |= LONGINT;
- /*FALLTHROUGH*/
- case 'o':
- _ulong = UARG();
- base = OCT;
- goto nosign;
- case 'p':
- /*
- * ``The argument shall be a pointer to void. The
- * value of the pointer is converted to a sequence
- * of printable characters, in an implementation-
- * defined manner.''
- * -- ANSI X3J11
- */
- /* NOSTRICT */
- _ulong = (u_long)va_arg(ap, void *);
- base = HEX;
- xdigs = "0123456789abcdef";
- flags |= HEXPREFIX;
- ch = 'x';
- goto nosign;
- case 's':
- if ((cp = va_arg(ap, char *)) == NULL)
- cp = "(null)";
- if (prec >= 0) {
- /*
- * can't use strlen; can only look for the
- * NUL in the first `prec' characters, and
- * strlen() will go further.
- */
- char *p = memchr(cp, 0, prec);
-
- if (p != NULL) {
- size = p - cp;
- if (size > prec)
- size = prec;
- } else
- size = prec;
- } else
- size = strlen(cp);
- sign = '\0';
- break;
- case 'U':
- flags |= LONGINT;
- /*FALLTHROUGH*/
- case 'u':
- _ulong = UARG();
- base = DEC;
- goto nosign;
- case 'X':
- xdigs = "0123456789ABCDEF";
- goto hex;
- case 'x':
- xdigs = "0123456789abcdef";
- hex: _ulong = UARG();
- base = HEX;
- /* leading 0x/X only if non-zero */
- if (flags & ALT && _ulong != 0)
- flags |= HEXPREFIX;
-
- /* unsigned conversions */
- nosign: sign = '\0';
- /*
- * ``... diouXx conversions ... if a precision is
- * specified, the 0 flag will be ignored.''
- * -- ANSI X3J11
- */
- number: if ((dprec = prec) >= 0)
- flags &= ~ZEROPAD;
-
- /*
- * ``The result of converting a zero value with an
- * explicit precision of zero is no characters.''
- * -- ANSI X3J11
- */
- cp = buf + BUF;
- if (_ulong != 0 || prec != 0) {
- /*
- * unsigned mod is hard, and unsigned mod
- * by a constant is easier than that by
- * a variable; hence this switch.
- */
- switch (base) {
- case OCT:
- do {
- *--cp = to_char(_ulong & 7);
- _ulong >>= 3;
- } while (_ulong);
- /* handle octal leading 0 */
- if (flags & ALT && *cp != '0')
- *--cp = '0';
- break;
-
- case DEC:
- /* many numbers are 1 digit */
- while (_ulong >= 10) {
- *--cp = to_char(_ulong % 10);
- _ulong /= 10;
- }
- *--cp = to_char(_ulong);
- break;
-
- case HEX:
- do {
- *--cp = xdigs[_ulong & 15];
- _ulong >>= 4;
- } while (_ulong);
- break;
-
- default:
- cp = "bug in vfprintf: bad base";
- size = strlen(cp);
- goto skipsize;
- }
- }
- size = buf + BUF - cp;
- skipsize:
- break;
- default: /* "%?" prints ?, unless ? is NUL */
- if (ch == '\0')
- goto done;
- /* pretend it was %c with argument ch */
- cp = buf;
- *cp = ch;
- size = 1;
- sign = '\0';
- break;
- }
-
- /*
- * 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.
- * fieldsz excludes decimal prec; realsz includes it
- */
- #ifdef FLOATING_POINT
- fieldsz = size + fpprec;
- #else
- fieldsz = size;
- #endif
- if (sign)
- fieldsz++;
- else if (flags & HEXPREFIX)
- fieldsz += 2;
- realsz = dprec > fieldsz ? dprec : fieldsz;
-
- /* right-adjusting blank padding */
- if ((flags & (LADJUST|ZEROPAD)) == 0)
- PAD(width - realsz, blanks);
-
- /* prefix */
- if (sign) {
- PRINT(&sign, 1);
- } else if (flags & HEXPREFIX) {
- ox[0] = '0';
- ox[1] = ch;
- PRINT(ox, 2);
- }
-
- /* right-adjusting zero padding */
- if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
- PAD(width - realsz, zeroes);
-
- /* leading zeroes from decimal precision */
- PAD(dprec - fieldsz, zeroes);
-
- /* the string or number proper */
- PRINT(cp, size);
-
- #ifdef FLOATING_POINT
- /* trailing f.p. zeroes */
- PAD(fpprec, zeroes);
- #endif
-
- /* left-adjusting padding (always blank) */
- if (flags & LADJUST)
- PAD(width - realsz, blanks);
-
- /* finally, adjust ret */
- ret += width > realsz ? width : realsz;
-
- FLUSH(); /* copy out the I/O vectors */
- }
- done:
- FLUSH();
- error:
- return (__sferror(fp) ? EOF : ret);
- /* NOTREACHED */
- }
-
- #ifdef FLOATING_POINT
- #include <math.h>
-
- static char *exponent();
- static char *round();
-
- static int
- cvt(number, prec, flags, signp, fmtch, startp, endp)
- double number;
- register int prec;
- int flags;
- char *signp;
- int fmtch;
- char *startp, *endp;
- {
- register char *p, *t;
- register double fract;
- int dotrim, expcnt, gformat;
- double integer, tmp;
-
- dotrim = expcnt = gformat = 0;
- if (number < 0) {
- number = -number;
- *signp = '-';
- } else
- *signp = 0;
-
- fract = modf(number, &integer);
-
- /* get an extra slot for rounding. */
- t = ++startp;
-
- /*
- * get integer portion of number; put into the end of the buffer; the
- * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
- */
- for (p = endp - 1; integer; ++expcnt) {
- tmp = modf(integer / 10, &integer);
- *p-- = to_char((int)((tmp + .01) * 10));
- }
- switch (fmtch) {
- case 'f':
- /* reverse integer into beginning of buffer */
- if (expcnt)
- for (; ++p < endp; *t++ = *p);
- else
- *t++ = '0';
- /*
- * if precision required or alternate flag set, add in a
- * decimal point.
- */
- if (prec || flags&ALT)
- *t++ = '.';
- /* if requires more precision and some fraction left */
- if (fract) {
- if (prec)
- do {
- fract = modf(fract * 10, &tmp);
- *t++ = to_char((int)tmp);
- } while (--prec && fract);
- if (fract)
- startp = round(fract, (int *)NULL, startp,
- t - 1, (char)0, signp);
- }
- for (; prec--; *t++ = '0');
- break;
- case 'e':
- case 'E':
- eformat: if (expcnt) {
- *t++ = *++p;
- if (prec || flags&ALT)
- *t++ = '.';
- /* if requires more precision and some integer left */
- for (; prec && ++p < endp; --prec)
- *t++ = *p;
- /*
- * if done precision and more of the integer component,
- * round using it; adjust fract so we don't re-round
- * later.
- */
- if (!prec && ++p < endp) {
- fract = 0;
- startp = round((double)0, &expcnt, startp,
- t - 1, *p, signp);
- }
- /* adjust expcnt for digit in front of decimal */
- --expcnt;
- }
- /* until first fractional digit, decrement exponent */
- else if (fract) {
- /* adjust expcnt for digit in front of decimal */
- for (expcnt = -1;; --expcnt) {
- fract = modf(fract * 10, &tmp);
- if (tmp)
- break;
- }
- *t++ = to_char((int)tmp);
- if (prec || flags&ALT)
- *t++ = '.';
- }
- else {
- *t++ = '0';
- if (prec || flags&ALT)
- *t++ = '.';
- }
- /* if requires more precision and some fraction left */
- if (fract) {
- if (prec)
- do {
- fract = modf(fract * 10, &tmp);
- *t++ = to_char((int)tmp);
- } while (--prec && fract);
- if (fract)
- startp = round(fract, &expcnt, startp,
- t - 1, (char)0, signp);
- }
- /* if requires more precision */
- for (; prec--; *t++ = '0');
-
- /* unless alternate flag, trim any g/G format trailing 0's */
- if (gformat && !(flags&ALT)) {
- while (t > startp && *--t == '0');
- if (*t == '.')
- --t;
- ++t;
- }
- t = exponent(t, expcnt, fmtch);
- break;
- case 'g':
- case 'G':
- /* a precision of 0 is treated as a precision of 1. */
- if (!prec)
- ++prec;
- /*
- * ``The style used depends on the value converted; style e
- * will be used only if the exponent resulting from the
- * conversion is less than -4 or greater than the precision.''
- * -- ANSI X3J11
- */
- if (expcnt > prec || !expcnt && fract && fract < .0001) {
- /*
- * g/G format counts "significant digits, not digits of
- * precision; for the e/E format, this just causes an
- * off-by-one problem, i.e. g/G considers the digit
- * before the decimal point significant and e/E doesn't
- * count it as precision.
- */
- --prec;
- fmtch -= 2; /* G->E, g->e */
- gformat = 1;
- goto eformat;
- }
- /*
- * reverse integer into beginning of buffer,
- * note, decrement precision
- */
- if (expcnt)
- for (; ++p < endp; *t++ = *p, --prec);
- else
- *t++ = '0';
- /*
- * if precision required or alternate flag set, add in a
- * decimal point. If no digits yet, add in leading 0.
- */
- if (prec || flags&ALT) {
- dotrim = 1;
- *t++ = '.';
- }
- else
- dotrim = 0;
- /* if requires more precision and some fraction left */
- if (fract) {
- if (prec) {
- do {
- fract = modf(fract * 10, &tmp);
- *t++ = to_char((int)tmp);
- } while(!tmp);
- while (--prec && fract) {
- fract = modf(fract * 10, &tmp);
- *t++ = to_char((int)tmp);
- }
- }
- if (fract)
- startp = round(fract, (int *)NULL, startp,
- t - 1, (char)0, signp);
- }
- /* alternate format, adds 0's for precision, else trim 0's */
- if (flags&ALT)
- for (; prec--; *t++ = '0');
- else if (dotrim) {
- while (t > startp && *--t == '0');
- if (*t != '.')
- ++t;
- }
- }
- return (t - startp);
- }
-
- static char *
- round(fract, exp, start, end, ch, signp)
- double fract;
- int *exp;
- register char *start, *end;
- char ch, *signp;
- {
- double tmp;
-
- if (fract)
- (void)modf(fract * 10, &tmp);
- else
- tmp = to_digit(ch);
- if (tmp > 4)
- for (;; --end) {
- if (*end == '.')
- --end;
- if (++*end <= '9')
- break;
- *end = '0';
- if (end == start) {
- if (exp) { /* e/E; increment exponent */
- *end = '1';
- ++*exp;
- }
- else { /* f; add extra digit */
- *--end = '1';
- --start;
- }
- break;
- }
- }
- /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
- else if (*signp == '-')
- for (;; --end) {
- if (*end == '.')
- --end;
- if (*end != '0')
- break;
- if (end == start)
- *signp = 0;
- }
- return (start);
- }
-
- static char *
- exponent(p, exp, fmtch)
- register char *p;
- register int exp;
- int fmtch;
- {
- register char *t;
- char expbuf[MAXEXP];
-
- *p++ = fmtch;
- if (exp < 0) {
- exp = -exp;
- *p++ = '-';
- }
- else
- *p++ = '+';
- t = expbuf + MAXEXP;
- if (exp > 9) {
- do {
- *--t = to_char(exp % 10);
- } while ((exp /= 10) > 9);
- *--t = to_char(exp);
- for (; t < expbuf + MAXEXP; *p++ = *t++);
- }
- else {
- *p++ = '0';
- *p++ = to_char(exp);
- }
- return (p);
- }
- #endif /* FLOATING_POINT */
-