handle exponents greater than 2 digits
[unix-history] / usr / src / lib / libc / stdio / vfprintf.c
index ab3eb3b..4985e70 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)vfprintf.c 5.17 (Berkeley) %G%";
+static char sccsid[] = "@(#)vfprintf.c 5.27 (Berkeley) %G%";
 #endif /* LIBC_SCCS and not lint */
 
 #include <sys/types.h>
 #endif /* LIBC_SCCS and not lint */
 
 #include <sys/types.h>
@@ -19,24 +19,22 @@ static char sccsid[] = "@(#)vfprintf.c      5.17 (Berkeley) %G%";
 #include <stdio.h>
 #include <ctype.h>
 
 #include <stdio.h>
 #include <ctype.h>
 
-/*
- * To handle arbitrary floating point precision, the buffer has to hold the
- * number, a decimal point, and N precision digits.  We can't just truncate
- * at some point is that the lower-level math routines may very well be
- * repeatedly returning some small fraction.  A 128 bit fraction can be
- * represented in 39 decimal digits.  Guess a max of 40 digits of precision,
- * and add one for the decimal point.
- */
-#define        MAXFRAC         39
-#define        MAXPREC         40
-#define        MAXDIGIT        (MAXFRAC + MAXPREC + 1)
+/* 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        BUF             (MAXEXP+MAXFRACT+1)     /* + decimal point */
 
 
-#define        PUTC(ch)        {++cnt; putc(ch, fp);}
+#define        PUTC(ch)        {++cnt; putc((char)ch, fp);}
 
 #define        ARG() \
        _ulong = flags&LONGINT ? va_arg(argp, long) : \
            flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
 
 
 #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
 
 /* have to deal with the negative buffer count kludge */
 #define        NEGATIVE_COUNT_KLUDGE
 
@@ -45,24 +43,23 @@ static char sccsid[] = "@(#)vfprintf.c      5.17 (Berkeley) %G%";
 #define        SHORTINT        0x04            /* short integer */
 #define        ALT             0x08            /* alternate form */
 #define        LADJUST         0x10            /* left adjustment */
 #define        SHORTINT        0x04            /* short integer */
 #define        ALT             0x08            /* alternate form */
 #define        LADJUST         0x10            /* left adjustment */
-static int flags;
-
-static char sign, *buf;
 
 
-x_doprnt(fmt, argp, fp)
-       register char *fmt;
+_doprnt(fmt0, argp, fp)
+       u_char *fmt0;
        va_list argp;
        register FILE *fp;
 {
        va_list argp;
        register FILE *fp;
 {
-       register int cnt, n;
-       register char ch, *t;
+       register u_char *fmt;
+       register int ch, cnt, n;
+       register char *t;
        double _double;
        u_long _ulong;
        double _double;
        u_long _ulong;
-       int base, width, prec, size;
-       char padc, *digs, sbuf[MAXDIGIT];
+       int base, flags, fpprec, prec, size, width;
+       char padc, sign, *digs, buf[BUF], *_cvt();
 
 
+       fmt = fmt0;
        digs = "0123456789abcdef";
        digs = "0123456789abcdef";
-       for (buf = sbuf, cnt = 0;; ++fmt) {
+       for (cnt = 0;; ++fmt) {
                n = fp->_cnt;
                for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
                        if (--n < 0
                n = fp->_cnt;
                for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
                        if (--n < 0
@@ -83,7 +80,7 @@ x_doprnt(fmt, argp, fp)
                if (!ch)
                        return(cnt);
 
                if (!ch)
                        return(cnt);
 
-               flags = width = 0;
+               flags = fpprec = width = 0;
                prec = -1;
                padc = ' ';
                sign = '\0';
                prec = -1;
                padc = ' ';
                sign = '\0';
@@ -118,7 +115,7 @@ rflag:              switch (*++fmt) {
                        else if (isascii(*fmt) && isdigit(*fmt)) {
                                n = 0;
                                do {
                        else if (isascii(*fmt) && isdigit(*fmt)) {
                                n = 0;
                                do {
-                                       n = 10 * n + *fmt - '0';
+                                       n = 10 * n + todigit(*fmt);
                                } while (isascii(*++fmt) && isdigit(*fmt));
                                --fmt;
                        }
                                } while (isascii(*++fmt) && isdigit(*fmt));
                                --fmt;
                        }
@@ -136,17 +133,13 @@ rflag:            switch (*++fmt) {
                case '5': case '6': case '7': case '8': case '9':
                        n = 0;
                        do {
                case '5': case '6': case '7': case '8': case '9':
                        n = 0;
                        do {
-                               n = 10 * n + *fmt - '0';
+                               n = 10 * n + todigit(*fmt);
                        } while (isascii(*++fmt) && isdigit(*fmt));
                        width = n;
                        --fmt;
                        goto rflag;
                case 'L':
                        } while (isascii(*++fmt) && isdigit(*fmt));
                        width = n;
                        --fmt;
                        goto rflag;
                case 'L':
-                       /*
-                        * C doesn't have a long double; use long for now.
-                        * flags |= LONGDBL;
-                        */
-                       flags |= LONGINT;
+                       flags |= LONGDBL;
                        goto rflag;
                case 'h':
                        flags |= SHORTINT;
                        goto rflag;
                case 'h':
                        flags |= SHORTINT;
@@ -166,21 +159,37 @@ rflag:            switch (*++fmt) {
                                _ulong = -_ulong;
                                sign = '-';
                        }
                                _ulong = -_ulong;
                                sign = '-';
                        }
-                       if (sign)
-                               PUTC(sign);
                        base = 10;
                        base = 10;
-                       goto num;
+                       goto number;
                case 'e':
                case 'E':
                case 'f':
                case 'g':
                case 'G':
                        _double = va_arg(argp, double);
                case 'e':
                case 'E':
                case 'f':
                case 'g':
                case 'G':
                        _double = va_arg(argp, double);
-                       size = _cvt(_double, prec, *fmt);
+                       /*
+                        * 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;
+                       }
+                       size = _cvt(_double, prec, flags, *fmt, padc, &sign,
+                           buf, buf + sizeof(buf)) - buf;
                        t = buf;
                        t = buf;
+                       /*
+                        * zero-padded sign put out here; blank padded sign
+                        * placed in number in _cvt().
+                        */
+                       if (sign && padc == '0') {
+                               PUTC(sign);
+                               --width;
+                       }
                        goto pforw;
                case 'n':
                        goto pforw;
                case 'n':
-                       if (flags&LONGDBL || flags&LONGINT)
+                       if (flags&LONGINT)
                                *va_arg(argp, long *) = cnt;
                        else if (flags&SHORTINT)
                                *va_arg(argp, short *) = cnt;
                                *va_arg(argp, long *) = cnt;
                        else if (flags&SHORTINT)
                                *va_arg(argp, short *) = cnt;
@@ -190,7 +199,7 @@ rflag:              switch (*++fmt) {
                case 'o':
                        ARG();
                        base = 8;
                case 'o':
                        ARG();
                        base = 8;
-                       goto num;
+                       goto nosign;
                case 'p':
                        /*
                         * ``The argument shall be a pointer to void.  The
                case 'p':
                        /*
                         * ``The argument shall be a pointer to void.  The
@@ -199,9 +208,10 @@ rflag:             switch (*++fmt) {
                         * defined manner.''
                         *      -- ANSI X3J11
                         */
                         * defined manner.''
                         *      -- ANSI X3J11
                         */
+                       /*NOSTRICT*/
                        _ulong = (u_long)va_arg(argp, void *);
                        base = 16;
                        _ulong = (u_long)va_arg(argp, void *);
                        base = 16;
-                       goto num;
+                       goto nosign;
                case 's':
                        if (!(t = va_arg(argp, char *)))
                                t = "(null)";
                case 's':
                        if (!(t = va_arg(argp, char *)))
                                t = "(null)";
@@ -223,60 +233,102 @@ rflag:           switch (*++fmt) {
                        }
                        else
                                size = strlen(t);
                        }
                        else
                                size = strlen(t);
-pforw:                 if (!(flags&LADJUST) && width)
-                               for (n = size; 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);
-                       if (flags&LADJUST)
-                               while (width-- > size)
-                                       PUTC(padc);
-                       break;
+                       goto pforw;
                case 'u':
                        ARG();
                        base = 10;
                case 'u':
                        ARG();
                        base = 10;
-                       goto num;
+                       goto nosign;
                case 'X':
                        digs = "0123456789ABCDEF";
                        /*FALLTHROUGH*/
                case 'x':
                        ARG();
                        base = 16;
                case 'X':
                        digs = "0123456789ABCDEF";
                        /*FALLTHROUGH*/
                case 'x':
                        ARG();
                        base = 16;
-                       /* alternate form for hex; leading 0x/X */
-                       if (flags&ALT && _ulong) {
-                               PUTC('0');
-                               PUTC(*fmt);
+                       /* 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;
                        }
                        }
-num:                   t = buf + MAXDIGIT - 1;
+
+                       t = buf + BUF - 1;
                        do {
                                *t-- = digs[_ulong % base];
                                _ulong /= base;
                        } while(_ulong);
                        do {
                                *t-- = digs[_ulong % base];
                                _ulong /= base;
                        } while(_ulong);
+                       for (size = buf + BUF - 1 - t; size < prec; ++size)
+                               *t-- = '0';
                        digs = "0123456789abcdef";
                        digs = "0123456789abcdef";
-                       size = buf + MAXDIGIT - 1 - t;
-                       if (size >= prec) {
-                               /* alternate form for octal; leading 0 */
-                               if (t[1] != '0' && flags&ALT && *fmt == 'o') {
-                                       *t-- = '0';
+
+                       /* 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;
                                }
                                        ++size;
                                }
+                               else {
+                                       PUTC(sign);
+                                       --width;
+                               }
                        }
                        }
-                       else
-                               for (; size < prec; ++size)
-                                       *t-- = '0';
-                       if (!(flags&LADJUST))
-                               while (size++ < width)
+                       ++t;
+
+pforw:                 if (!(flags&LADJUST) && width)
+                               for (n = size + fpprec; n++ < width;)
                                        PUTC(padc);
                                        PUTC(padc);
-                       while (++t < buf + MAXDIGIT)
+                       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);
                                PUTC(*t);
-                       for (; width > size; --width)
-                               PUTC(padc);
+                       while (fpprec--)
+                               PUTC('0');
+                       if (flags&LADJUST)
+                               for (n = size + fpprec; ++n < width;)
+                                       PUTC(' ');
                        break;
                case '\0':              /* "%?" prints ?, unless ? is NULL */
                        return(cnt);
                        break;
                case '\0':              /* "%?" prints ?, unless ? is NULL */
                        return(cnt);
@@ -292,33 +344,31 @@ num:                      t = buf + MAXDIGIT - 1;
 #define        GFORMAT 0x04
 #define        DEFPREC 6
 
 #define        GFORMAT 0x04
 #define        DEFPREC 6
 
-static
-_cvt(number, prec, fmtch)
+static char *
+_cvt(number, prec, flags, fmtch, padc, sign, startp, endp)
        double number;
        register int prec;
        double number;
        register int prec;
-       char fmtch;
+       int flags;
+       u_char fmtch;
+       char padc, *sign, *startp, *endp;
 {
 {
-       register char *p;
+       register char *p, *t;
        register int expcnt, format;
        register int expcnt, format;
-       static int maxprec = MAXPREC;
        double fract, integer, tmp, modf();
        int decpt;
        double fract, integer, tmp, modf();
        int decpt;
-       char *endp, *savep, *startp, *malloc();
+       char *savep, exponent[MAXEXP];
 
        if (prec == -1)
                prec = DEFPREC;
 
 
        if (prec == -1)
                prec = DEFPREC;
 
-       /* allocate space for large precision */
-       if (prec > maxprec)
-               buf = malloc((u_int)((maxprec = prec) + MAXFRAC + 1));
-
-       startp = buf;
        if (number < 0) {
        if (number < 0) {
-               *startp++ = '-';
+               *sign = '-';
                number = -number;
        }
                number = -number;
        }
-       else if (sign)
-               *startp++ = sign;
+
+       /* if blank padded, add sign in as part of the number */
+       if (*sign && padc == ' ')
+               *startp++ = *sign;
 
        switch(fmtch) {
        case 'e':
 
        switch(fmtch) {
        case 'e':
@@ -343,21 +393,18 @@ _cvt(number, prec, fmtch)
        decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
 
        expcnt = 0;
        decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
 
        expcnt = 0;
-       p = buf + maxprec + MAXFRAC;
-       endp = p + 1;
+       p = endp - 1;
        fract = modf(number, &integer);
        if (integer) {
        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);
                /* get integer part of number; count decimal places */
                for (; integer; ++expcnt) {
                        tmp = modf(integer / 10, &integer);
-                       *p-- = (int)((tmp + .03) * 10) + '0';
+                       *p-- = tochar((int)((tmp + .03) * 10));
                }
 
                /* copy, in reverse order, to start of buffer */
                }
 
                /* copy, in reverse order, to start of buffer */
-               p2 = startp;
-               *p2++ = *++p;
+               t = startp;
+               *t++ = *++p;
 
                /*
                 * if the format is g/G, and the resulting exponent will be
 
                /*
                 * if the format is g/G, and the resulting exponent will be
@@ -374,19 +421,19 @@ _cvt(number, prec, fmtch)
                                        --prec;
                        }
                        if (decpt)
                                        --prec;
                        }
                        if (decpt)
-                               *p2++ = '.';
-                       for (; ++p < endp && prec; --prec, *p2++ = *p);
+                               *t++ = '.';
+                       for (; ++p < endp && prec; --prec, *t++ = *p);
 
                        /* precision ran out, round */
                        if (p < endp) {
                                if (*p > '4') {
 
                        /* precision ran out, round */
                        if (p < endp) {
                                if (*p > '4') {
-                                       for (savep = p2--;; *p2-- = '0') {
-                                               if (*p2 == '.')
-                                                       --p2;
-                                               if (++*p2 <= '9')
+                                       for (savep = t--;; *t-- = '0') {
+                                               if (*t == '.')
+                                                       --t;
+                                               if (++*t <= '9')
                                                        break;
                                        }
                                                        break;
                                        }
-                                       p2 = savep;
+                                       t = savep;
                                }
                                fract = 0;
                        }
                                }
                                fract = 0;
                        }
@@ -396,29 +443,29 @@ _cvt(number, prec, fmtch)
                 * zeroes, note, have to round first.
                 */
                else if (format&GFORMAT) {
                 * zeroes, note, have to round first.
                 */
                else if (format&GFORMAT) {
-                       for (; ++p < endp && prec; --prec, *p2++ = *p);
+                       for (; ++p < endp && prec; --prec, *t++ = *p);
                        /* precision ran out; round and then add zeroes */
                        if (p < endp) {
                                if (*p > '4') {
                        /* precision ran out; round and then add zeroes */
                        if (p < endp) {
                                if (*p > '4') {
-                                       for (savep = p2--; ++*p2 > '9';
-                                           *p2-- = '0');
-                                       p2 = savep;
+                                       for (savep = t--; ++*t > '9';
+                                           *t-- = '0');
+                                       t = savep;
                                }
                                do {
                                }
                                do {
-                                       *p2++ = '0';
+                                       *t++ = '0';
                                } while (++p < endp);
                                fract = 0;
                        }
                        if (decpt)
                                } while (++p < endp);
                                fract = 0;
                        }
                        if (decpt)
-                               *p2++ = '.';
+                               *t++ = '.';
                }
                /* f format */
                else {
                }
                /* f format */
                else {
-                       for (; ++p < endp; *p2++ = *p);
+                       for (; ++p < endp; *t++ = *p);
                        if (decpt)
                        if (decpt)
-                               *p2++ = '.';
+                               *t++ = '.';
                }
                }
-               p = p2;
+               p = t;
        }
        /*
         * if no fraction, the number was zero, and if no precision, can't
        }
        /*
         * if no fraction, the number was zero, and if no precision, can't
@@ -429,7 +476,7 @@ _cvt(number, prec, fmtch)
                if (decpt && !(format&GFORMAT))
                        *startp++ = '.';
                *startp = '\0';
                if (decpt && !(format&GFORMAT))
                        *startp++ = '.';
                *startp = '\0';
-               return(startp - buf);
+               return(startp);
        }
        /*
         * if the format is g/G, and the resulting exponent will be less than
        }
        /*
         * if the format is g/G, and the resulting exponent will be less than
@@ -444,7 +491,7 @@ _cvt(number, prec, fmtch)
                                        --expcnt;
                                        continue;
                                }
                                        --expcnt;
                                        continue;
                                }
-                               *p++ = (int)tmp + '0';
+                               *p++ = tochar((int)tmp);
                                break;
                        }
                else
                                break;
                        }
                else
@@ -471,7 +518,7 @@ _cvt(number, prec, fmtch)
        /* finish out requested precision */
        while (fract && prec-- > 0) {
                fract = modf(fract * 10, &tmp);
        /* finish out requested precision */
        while (fract && prec-- > 0) {
                fract = modf(fract * 10, &tmp);
-               *p++ = (int)tmp + '0';
+               *p++ = tochar((int)tmp);
        }
        while (prec-- > 0)
                *p++ = '0';
        }
        while (prec-- > 0)
                *p++ = '0';
@@ -519,8 +566,18 @@ _cvt(number, prec, fmtch)
                }
                else
                        *p++ = '+';
                }
                else
                        *p++ = '+';
-               *p++ = expcnt / 10 + '0';
-               *p++ = expcnt % 10 + '0';
+               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(p - buf);
+       return(p);
 }
 }