handle exponents greater than 2 digits
[unix-history] / usr / src / lib / libc / stdio / vfprintf.c
index fe87b11..4985e70 100644 (file)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)vfprintf.c 5.5 (Berkeley) %G%";
+static char sccsid[] = "@(#)vfprintf.c 5.27 (Berkeley) %G%";
 #endif /* LIBC_SCCS and not lint */
 
 #endif /* LIBC_SCCS and not lint */
 
-#include <sys/param.h>
+#include <sys/types.h>
 #include <varargs.h>
 #include <stdio.h>
 #include <ctype.h>
 
 #include <varargs.h>
 #include <stdio.h>
 #include <ctype.h>
 
-#define        MAXBUF          120                     /* should hold any number */
-#define        DEFPREC         6                       /* default precision */
+/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
+#define        MAXEXP          308
+/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
+#define        MAXFRACT        39
 
 
-#define        PUTC(ch, fd)    {++cnt; putc(ch, fd);}
+#define        BUF             (MAXEXP+MAXFRACT+1)     /* + decimal point */
 
 
-#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);
+#define        PUTC(ch)        {++cnt; putc((char)ch, fp);}
 
 
-x_doprnt(fmt, argp, fp)
-       register char *fmt;
+#define        ARG() \
+       _ulong = flags&LONGINT ? va_arg(argp, long) : \
+           flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
+
+#define        todigit(c)      ((c) - '0')
+#define        tochar(n)       ((n) + '0')
+
+/* have to deal with the negative buffer count kludge */
+#define        NEGATIVE_COUNT_KLUDGE
+
+#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 */
+
+_doprnt(fmt0, argp, fp)
+       u_char *fmt0;
        va_list argp;
        register FILE *fp;
 {
        va_list argp;
        register FILE *fp;
 {
-       register u_long reg_ulong;
-       register long reg_long;
-       register int base;
-       register char *digs, *bp, *t, padc;
+       register u_char *fmt;
+       register int ch, cnt, n;
+       register char *t;
        double _double;
        double _double;
-       char argsize, printsign, buf[MAXBUF], *ecvt(), *fcvt();
-       int alternate, cnt, decpt, n, ladjust, width, prec, sign, size;
+       u_long _ulong;
+       int base, flags, fpprec, prec, size, width;
+       char padc, sign, *digs, buf[BUF], *_cvt();
 
 
-       for (cnt = 0; *fmt; ++fmt) {
-               if (*fmt != '%') {
-                       PUTC(*fmt, fp);
-                       continue;
-               }
+       fmt = fmt0;
+       digs = "0123456789abcdef";
+       for (cnt = 0;; ++fmt) {
+               n = fp->_cnt;
+               for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
+                       if (--n < 0
+#ifdef NEGATIVE_COUNT_KLUDGE
+                           && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
+#endif
+                           || ch == '\n' && fp->_flag&_IOLBF) {
+                               fp->_cnt = n;
+                               fp->_ptr = t;
+                               (void)_flsbuf(ch, fp);
+                               n = fp->_cnt;
+                               t = fp->_ptr;
+                       }
+                       else
+                               *t++ = ch;
+               fp->_cnt = n;
+               fp->_ptr = t;
+               if (!ch)
+                       return(cnt);
 
 
-               alternate = ladjust = width = 0;
+               flags = fpprec = width = 0;
                prec = -1;
                padc = ' ';
                prec = -1;
                padc = ' ';
-               argsize = printsign = '\0';
+               sign = '\0';
 
 
-flags:         switch (*++fmt) {
+rflag:         switch (*++fmt) {
+               case ' ':
+                       sign = ' ';
+                       goto rflag;
                case '#':
                case '#':
-                       alternate = 1;
-                       goto flags;
-               case '%':                       /* "%#%" prints as "%" */
-                       PUTC('%', fp);
-                       continue;
+                       flags |= ALT;
+                       goto rflag;
                case '*':
                        /*
                         * ``A negative field width argument is taken as a
                case '*':
                        /*
                         * ``A negative field width argument is taken as a
@@ -70,233 +100,484 @@ flags:           switch (*++fmt) {
                         * They don't exclude field widths read from args.
                         */
                        if ((width = va_arg(argp, int)) >= 0)
                         * They don't exclude field widths read from args.
                         */
                        if ((width = va_arg(argp, int)) >= 0)
-                               goto flags;
+                               goto rflag;
                        width = -width;
                        /*FALLTHROUGH*/
                case '-':
                        width = -width;
                        /*FALLTHROUGH*/
                case '-':
-                       ladjust = 1;
-                       goto flags;
+                       flags |= LADJUST;
+                       goto rflag;
                case '+':
                case '+':
-                       printsign = '+';
-                       goto flags;
+                       sign = '+';
+                       goto rflag;
                case '.':
                        if (*++fmt == '*')
                case '.':
                        if (*++fmt == '*')
-                               prec = va_arg(argp, int);
-                       else if (isdigit(*fmt)) {
-                               prec = 0;
+                               n = va_arg(argp, int);
+                       else if (isascii(*fmt) && isdigit(*fmt)) {
+                               n = 0;
                                do {
                                do {
-                                       prec = 10 * prec + *fmt - '0';
-                               } while isdigit(*++fmt);
+                                       n = 10 * n + todigit(*fmt);
+                               } while (isascii(*++fmt) && isdigit(*fmt));
                                --fmt;
                        }
                        else {
                                --fmt;
                        }
                        else {
-                               prec = 0;
                                --fmt;
                                --fmt;
-                               goto flags;
+                               prec = 0;
+                               goto rflag;
                        }
                        }
-                       if (prec < 0)
-                               prec = -1;
-                       goto flags;
+                       prec = n < 0 ? -1 : n;
+                       goto rflag;
                case '0':
                        padc = '0';
                        /*FALLTHROUGH*/
                case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                case '0':
                        padc = '0';
                        /*FALLTHROUGH*/
                case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
+                       n = 0;
                        do {
                        do {
-                               width = 10 * width + *fmt - '0';
-                       } while isdigit(*++fmt);
+                               n = 10 * n + todigit(*fmt);
+                       } while (isascii(*++fmt) && isdigit(*fmt));
+                       width = n;
                        --fmt;
                        --fmt;
+                       goto rflag;
                case 'L':
                case 'L':
-                       argsize |= LONGDBL;
-                       goto flags;
+                       flags |= LONGDBL;
+                       goto rflag;
                case 'h':
                case 'h':
-                       argsize |= SHORTINT;
-                       goto flags;
+                       flags |= SHORTINT;
+                       goto rflag;
                case 'l':
                case 'l':
-                       argsize |= LONGINT;
-                       goto flags;
-               }
-
-               digs = "0123456789abcdef";
-
-               switch (*fmt) {
+                       flags |= LONGINT;
+                       goto rflag;
                case 'c':
                case 'c':
-                       PUTC(va_arg(argp, int), fp);
-                       break;
+                       buf[0] = va_arg(argp, int);
+                       size = 1;
+                       t = buf;
+                       goto pforw;
                case 'd':
                case 'i':
                case 'd':
                case 'i':
-                       GETARG(reg_long);
-                       if (reg_long < 0) {
-                               reg_ulong = -reg_long;
-                               printsign = '-';
-                       }
-                       else {
-                               reg_ulong = reg_long;
+                       ARG();
+                       if ((long)_ulong < 0) {
+                               _ulong = -_ulong;
+                               sign = '-';
                        }
                        }
-                       if (printsign)
-                               PUTC(printsign, fp);
                        base = 10;
                        base = 10;
-                       goto num1;
-               case 'E':
+                       goto number;
                case 'e':
                case 'e':
-                       if (prec == -1)
-                               prec = DEFPREC;
-                       _double = va_arg(argp, double);
-                       t = fcvt(_double, prec + 1, &decpt, &sign);
-gise:                  bp = buf;
-                       *bp++ = *t ? *t++ : '0';
-                       if (alternate || prec > 0)
-                               *bp++ = '.';
-                       while (prec--)
-                               *bp++ = *t ? *t++ : '0';
-                       *bp++ = *fmt;
-                       if (decpt > 0 || !_double) {
-                               *bp++ = '+';
-                               --decpt;
-                       }
-                       else {
-                               *bp++ = '-';
-                               decpt = -decpt + 1;
-                       }
-                       /* exponents <= 99 in ANSI X3J11 */
-                       *bp++ = (int)(decpt / 10) + '0';
-                       *bp++ = (int)(decpt % 10) + '0';
-                       goto pbuf;
+               case 'E':
                case 'f':
                case 'f':
-                       if (prec == -1)
-                               prec = DEFPREC;
-                       _double = va_arg(argp, double);
-                       t = fcvt(_double, prec + 1, &decpt, &sign);
-gisf:                  bp = buf;
-                       if (decpt >= 0)
-                               for (;;) {
-                                       *bp++ = *t ? *t++ : '0';
-                                       if (!--decpt)
-                                               break;
-                               }
-                       if (alternate || prec > 0) {
-                               if (decpt < 0)
-                                       *bp++ = '0';
-                               *bp++ = '.';
-                       }
-                       while (decpt++) {
-                               *bp++ = '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);
-                       break;
+               case 'g':
                case 'G':
                case 'G':
-               case 'g': {
-                       int gotoe;
-
-                       if (prec == -1)
-                               prec = DEFPREC;
                        _double = va_arg(argp, double);
                        _double = va_arg(argp, double);
-                       t = fcvt(_double, prec + 1, &decpt, &sign);
-                       gotoe = decpt > prec;
-                       if (!alternate) {
-                               for (bp = t + prec + decpt; prec &&
-                                   *--bp == '0'; --prec);
+                       /*
+                        * don't bother to do unrealistic precision; just
+                        * pad it with zeroes later.  This keeps buffer size
+                        * rational.
+                        */
+                       if (prec > MAXFRACT) {
+                               fpprec = prec - MAXFRACT;
+                               prec = MAXFRACT;
                        }
                        }
-                       if (gotoe || decpt < -3) {
-                               *fmt -= 2;
-                               goto gise;
+                       size = _cvt(_double, prec, flags, *fmt, padc, &sign,
+                           buf, buf + sizeof(buf)) - buf;
+                       t = buf;
+                       /*
+                        * zero-padded sign put out here; blank padded sign
+                        * placed in number in _cvt().
+                        */
+                       if (sign && padc == '0') {
+                               PUTC(sign);
+                               --width;
                        }
                        }
-                       --*fmt;
-                       goto gisf;
-               }
+                       goto pforw;
                case 'n':
                case 'n':
-                       *(va_arg(argp, int *)) = cnt;
+                       if (flags&LONGINT)
+                               *va_arg(argp, long *) = cnt;
+                       else if (flags&SHORTINT)
+                               *va_arg(argp, short *) = cnt;
+                       else
+                               *va_arg(argp, int *) = cnt;
                        break;
                case 'o':
                        break;
                case 'o':
-                       GETARG(reg_ulong);
+                       ARG();
                        base = 8;
                        base = 8;
-                       if (!reg_ulong || !alternate)
-                               goto num1;
-                       bp = buf + sizeof(buf) - 1;
-                       do {
-                               *bp-- = digs[reg_ulong % base];
-                               reg_ulong /= base;
-                       } while(reg_ulong);
-                       size = &buf[sizeof(buf) - 1] - bp;
-                       if (size < --width && !ladjust)
-                               do {
-                                       PUTC(padc, fp);
-                               } while (--width > size);
-                       PUTC('0', fp);
-                       goto num2;
-                       break;
+                       goto nosign;
                case 'p':
                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(argp, void *);
+                       base = 16;
+                       goto nosign;
                case 's':
                case 's':
-                       if (!(bp = va_arg(argp, char *)))
-                               bp = "(null)";
-                       if (width > 0 && !ladjust) {
-                               char *savep;
-
-                               savep = bp;
-                               for (n = 0; *bp && (prec < 0 || n < prec);
-                                   n++, bp++);
-                               bp = savep;
-                               while (n++ < width)
-                                       PUTC(' ', fp);
-                       }
-                       for (n = 0; *bp; ++bp) {
-                               if (++n > prec && prec >= 0)
-                                       break;
-                               PUTC(*bp, fp);
+                       if (!(t = va_arg(argp, char *)))
+                               t = "(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();
+
+                               if (p = memchr(t, 0, prec)) {
+                                       size = p - t;
+                                       if (size > prec)
+                                               size = prec;
+                               }
+                               else
+                                       size = prec;
                        }
                        }
-                       if (n < width && ladjust)
-                               do {
-                                       PUTC(' ', fp);
-                               } while (++n < width);
-                       break;
+                       else
+                               size = strlen(t);
+                       goto pforw;
                case 'u':
                case 'u':
-                       GETARG(reg_ulong);
+                       ARG();
                        base = 10;
                        base = 10;
-                       goto num1;
+                       goto nosign;
                case 'X':
                        digs = "0123456789ABCDEF";
                        /*FALLTHROUGH*/
                case 'x':
                case 'X':
                        digs = "0123456789ABCDEF";
                        /*FALLTHROUGH*/
                case 'x':
-                       GETARG(reg_ulong);
-                       if (alternate && reg_ulong) {
-                               PUTC('0', fp);
-                               PUTC(*fmt, fp);
-                       }
+                       ARG();
                        base = 16;
                        base = 16;
-num1:                  bp = buf + sizeof(buf) - 1;
+                       /* leading 0x/X only if non-zero */
+                       if (!_ulong)
+                               flags &= ~ALT;
+
+                       /* unsigned conversions */
+nosign:                        sign = NULL;
+                       /*
+                        * ``... diouXx conversions ... if a precision is
+                        * specified, the 0 flag will be ignored.''
+                        *      -- ANSI X3J11
+                        */
+number:                        if (prec >= 0)
+                               padc = ' ';
+                       /*
+                        * ``The result of converting a zero value with an
+                        * explicit precision of zero is no characters.''
+                        *      -- ANSI X3J11
+                        */
+                       if (!_ulong && !prec) {
+                               size = 0;
+                               goto pforw;
+                       }
+
+                       t = buf + BUF - 1;
                        do {
                        do {
-                               *bp-- = digs[reg_ulong % base];
-                               reg_ulong /= base;
-                       } while(reg_ulong);
-                       size = &buf[sizeof(buf) - 1] - bp;
-                       for (; size < prec; *bp-- = '0', ++size);
-                       if (size < width && !ladjust)
-                               do {
-                                       PUTC(padc, fp);
-                               } while (--width > size);
-num2:                  while (++bp != &buf[MAXBUF])
-                               PUTC(*bp, fp);
-                       for (; width > size; --width)
-                               PUTC(padc, fp);
+                               *t-- = digs[_ulong % base];
+                               _ulong /= base;
+                       } while(_ulong);
+                       for (size = buf + BUF - 1 - t; size < prec; ++size)
+                               *t-- = '0';
+                       digs = "0123456789abcdef";
+
+                       /* alternate mode for hex and octal numbers */
+                       if (flags&ALT)
+                               switch (base) {
+                               case 16:
+                                       /* avoid "00000x35" */
+                                       if (padc == ' ') {
+                                               *t-- = *fmt;
+                                               *t-- = '0';
+                                               size += 2;
+                                       }
+                                       else {
+                                               PUTC('0');
+                                               PUTC(*fmt);
+                                               width -= 2;
+                                       }
+                                       break;
+                               case 8:
+                                       if (t[1] != '0') {
+                                               *t-- = '0';
+                                               ++size;
+                                       }
+                                       break;
+                               }
+
+                       if (sign) {
+                               /* avoid "0000-3" */
+                               if (padc == ' ') {
+                                       *t-- = sign;
+                                       ++size;
+                               }
+                               else {
+                                       PUTC(sign);
+                                       --width;
+                               }
+                       }
+                       ++t;
+
+pforw:                 if (!(flags&LADJUST) && width)
+                               for (n = size + fpprec; n++ < width;)
+                                       PUTC(padc);
+                       if (fp->_cnt - (n = size) >= 0) {
+                               cnt += n;
+                               fp->_cnt -= n;
+                               bcopy(t, fp->_ptr, n);
+                               fp->_ptr += n;
+                       }
+                       else for (; n--; ++t)
+                               PUTC(*t);
+                       while (fpprec--)
+                               PUTC('0');
+                       if (flags&LADJUST)
+                               for (n = size + fpprec; ++n < width;)
+                                       PUTC(' ');
                        break;
                case '\0':              /* "%?" prints ?, unless ? is NULL */
                        break;
                case '\0':              /* "%?" prints ?, unless ? is NULL */
-                       return(ferror(fp) ? -1 : cnt);
+                       return(cnt);
                default:
                default:
-                       PUTC(*fmt, fp);
+                       PUTC(*fmt);
+               }
+       }
+       /*NOTREACHED*/
+}
+
+#define        EFORMAT 0x01
+#define        FFORMAT 0x02
+#define        GFORMAT 0x04
+#define        DEFPREC 6
+
+static char *
+_cvt(number, prec, flags, fmtch, padc, sign, startp, endp)
+       double number;
+       register int prec;
+       int flags;
+       u_char fmtch;
+       char padc, *sign, *startp, *endp;
+{
+       register char *p, *t;
+       register int expcnt, format;
+       double fract, integer, tmp, modf();
+       int decpt;
+       char *savep, exponent[MAXEXP];
+
+       if (prec == -1)
+               prec = DEFPREC;
+
+       if (number < 0) {
+               *sign = '-';
+               number = -number;
+       }
+
+       /* if blank padded, add sign in as part of the number */
+       if (*sign && padc == ' ')
+               *startp++ = *sign;
+
+       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, as it counts
+        * precision differently.
+        */
+       decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
+
+       expcnt = 0;
+       p = endp - 1;
+       fract = modf(number, &integer);
+       if (integer) {
+               /* get integer part of number; count decimal places */
+               for (; integer; ++expcnt) {
+                       tmp = modf(integer / 10, &integer);
+                       *p-- = tochar((int)((tmp + .03) * 10));
+               }
+
+               /* copy, in reverse order, to start of buffer */
+               t = startp;
+               *t++ = *++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)
+                               *t++ = '.';
+                       for (; ++p < endp && prec; --prec, *t++ = *p);
+
+                       /* precision ran out, round */
+                       if (p < endp) {
+                               if (*p > '4') {
+                                       for (savep = t--;; *t-- = '0') {
+                                               if (*t == '.')
+                                                       --t;
+                                               if (++*t <= '9')
+                                                       break;
+                                       }
+                                       t = savep;
+                               }
+                               fract = 0;
+                       }
+               }
+               /*
+                * g/G in f format; if out of precision, replace digits with
+                * zeroes, note, have to round first.
+                */
+               else if (format&GFORMAT) {
+                       for (; ++p < endp && prec; --prec, *t++ = *p);
+                       /* precision ran out; round and then add zeroes */
+                       if (p < endp) {
+                               if (*p > '4') {
+                                       for (savep = t--; ++*t > '9';
+                                           *t-- = '0');
+                                       t = savep;
+                               }
+                               do {
+                                       *t++ = '0';
+                               } while (++p < endp);
+                               fract = 0;
+                       }
+                       if (decpt)
+                               *t++ = '.';
+               }
+               /* f format */
+               else {
+                       for (; ++p < endp; *t++ = *p);
+                       if (decpt)
+                               *t++ = '.';
+               }
+               p = t;
+       }
+       /*
+        * 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 && !(format&GFORMAT))
+                       *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++ = tochar((int)tmp);
+                               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 */
+       while (fract && prec-- > 0) {
+               fract = modf(fract * 10, &tmp);
+               *p++ = tochar((int)tmp);
+       }
+       while (prec-- > 0)
+               *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 (!(flags&ALT)) {
+               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++ = '+';
+               t = exponent + MAXEXP;
+               if (expcnt > 9) {
+                       do {
+                               *--t = tochar(expcnt % 10);
+                       } while ((expcnt /= 10) > 9);
+                       *--t = tochar(expcnt);
+                       for (; t < exponent + MAXEXP; *p++ = *t++);
+               }
+               else {
+                       *p++ = '0';
+                       *p++ = tochar(expcnt);
                }
        }
                }
        }
-       return(ferror(fp) ? -1 : cnt);
+       return(p);
 }
 }