minor whacks; checkpoint before speed attempt
[unix-history] / usr / src / lib / libc / stdio / vfprintf.c
index b8b875a..7b05214 100644 (file)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
  */
 
 #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 */
 
 #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        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        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;
 {
 
 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 u_long reg_ulong;
        register long reg_long;
-       register int base;
-       register char *digs, *bp, *t, padc;
        double _double;
        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 != '%') {
        for (cnt = 0; *fmt; ++fmt) {
                if (*fmt != '%') {
-                       PUTC(*fmt, fp);
+                       PUTC(*fmt);
                        continue;
                }
 
                        continue;
                }
 
@@ -62,9 +63,6 @@ flags:                switch (*++fmt) {
                case '#':
                        alternate = 1;
                        goto flags;
                case '#':
                        alternate = 1;
                        goto flags;
-               case '%':                       /* "%#%" prints as "%" */
-                       PUTC('%', fp);
-                       continue;
                case '*':
                        /*
                         * ``A negative field width argument is taken as a
                case '*':
                        /*
                         * ``A negative field width argument is taken as a
@@ -118,65 +116,13 @@ flags:            switch (*++fmt) {
                case 'l':
                        argsize |= LONGINT;
                        goto flags;
                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;
                        break;
+               }
                case 'd':
                case 'i':
                        GETARG(reg_long);
                case 'd':
                case 'i':
                        GETARG(reg_long);
@@ -188,9 +134,26 @@ pbuf:                      size = bp - buf;
                                reg_ulong = reg_long;
                        }
                        if (printsign)
                                reg_ulong = reg_long;
                        }
                        if (printsign)
-                               PUTC(printsign, fp);
+                               PUTC(printsign);
                        base = 10;
                        goto num1;
                        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;
                case 'n':
                        *(va_arg(argp, int *)) = cnt;
                        break;
@@ -207,11 +170,10 @@ pbuf:                     size = bp - buf;
                        size = &buf[sizeof(buf) - 1] - bp;
                        if (size < --width && !ladjust)
                                do {
                        size = &buf[sizeof(buf) - 1] - bp;
                        if (size < --width && !ladjust)
                                do {
-                                       PUTC(padc, fp);
+                                       PUTC(padc);
                                } while (--width > size);
                                } while (--width > size);
-                       PUTC('0', fp);
+                       PUTC('0');
                        goto num2;
                        goto num2;
-                       break;
                case 'p':
                case 's':
                        if (!(bp = va_arg(argp, char *)))
                case 'p':
                case 's':
                        if (!(bp = va_arg(argp, char *)))
@@ -224,16 +186,16 @@ pbuf:                     size = bp - buf;
                                    n++, bp++);
                                bp = savep;
                                while (n++ < width)
                                    n++, bp++);
                                bp = savep;
                                while (n++ < width)
-                                       PUTC(' ', fp);
+                                       PUTC(' ');
                        }
                        for (n = 0; *bp; ++bp) {
                                if (++n > prec && prec >= 0)
                                        break;
                        }
                        for (n = 0; *bp; ++bp) {
                                if (++n > prec && prec >= 0)
                                        break;
-                               PUTC(*bp, fp);
+                               PUTC(*bp);
                        }
                        if (n < width && ladjust)
                                do {
                        }
                        if (n < width && ladjust)
                                do {
-                                       PUTC(' ', fp);
+                                       PUTC(' ');
                                } while (++n < width);
                        break;
                case 'u':
                                } while (++n < width);
                        break;
                case 'u':
@@ -246,8 +208,8 @@ pbuf:                       size = bp - buf;
                case 'x':
                        GETARG(reg_ulong);
                        if (alternate && reg_ulong) {
                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;
                        }
                        base = 16;
 num1:                  bp = buf + sizeof(buf) - 1;
@@ -259,18 +221,254 @@ num1:                    bp = buf + sizeof(buf) - 1;
                        for (; size < prec; *bp-- = '0', ++size);
                        if (size < width && !ladjust)
                                do {
                        for (; size < prec; *bp-- = '0', ++size);
                        if (size < width && !ladjust)
                                do {
-                                       PUTC(padc, fp);
+                                       PUTC(padc);
                                } while (--width > size);
 num2:                  while (++bp != &buf[MAXBUF])
                                } while (--width > size);
 num2:                  while (++bp != &buf[MAXBUF])
-                               PUTC(*bp, fp);
+                               PUTC(*bp);
                        for (; width > size; --width)
                        for (; width > size; --width)
-                               PUTC(padc, fp);
+                               PUTC(padc);
+                       digs = "0123456789abcdef";
                        break;
                case '\0':              /* "%?" prints ?, unless ? is NULL */
                        return(ferror(fp) ? -1 : cnt);
                default:
                        break;
                case '\0':              /* "%?" prints ?, unless ? is NULL */
                        return(ferror(fp) ? -1 : cnt);
                default:
-                       PUTC(*fmt, fp);
+                       PUTC(*fmt);
                }
        }
        return(ferror(fp) ? -1 : cnt);
 }
                }
        }
        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);
+}