home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / LIBSRC.ZOO / libsrc / stdio / doprnt.c < prev    next >
Text File  |  1992-01-18  |  20KB  |  667 lines

  1. /*
  2.  * Copyright (c) 1988 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #if defined(LIBC_SCCS) && !defined(lint)
  19. static char sccsid[] = "@(#)doprnt.c   5.35 (Berkeley) 6/27/88";
  20. #endif /* LIBC_SCCS and not lint */
  21.  
  22. #include <sys/types.h>
  23. #include <varargs.h>
  24. #include <stdio.h>
  25. #include <ctype.h>
  26.  
  27. /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
  28. #define   MAXEXP      308
  29. /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
  30. #define   MAXFRACT   39
  31.  
  32. #define   DEFPREC      6
  33.  
  34. #define   BUF      (MAXEXP+MAXFRACT+1)   /* + decimal point */
  35.  
  36. #define   PUTC(ch)   (void) putc(ch, fp)
  37.  
  38. #define   ARG() \
  39.    _ulong = flags&LONGINT ? va_arg(argp, long) : \
  40.        flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
  41.  
  42. #define   todigit(c)   ((c) - '0')
  43. #define   tochar(n)   ((n) + '0')
  44.  
  45. /* have to deal with the negative buffer count kludge */
  46. #define   NEGATIVE_COUNT_KLUDGE
  47.  
  48. #define   LONGINT      0x01      /* long integer */
  49. #define   LONGDBL      0x02      /* long double; unimplemented */
  50. #define   SHORTINT   0x04      /* short integer */
  51. #define   ALT      0x08      /* alternate form */
  52. #define   LADJUST      0x10      /* left adjustment */
  53. #define   ZEROPAD      0x20      /* zero (as opposed to blank) pad */
  54. #define   HEXPREFIX   0x40      /* add 0x or 0X prefix */
  55.  
  56. _doprnt(fmt0, argp, fp)
  57.    u_char *fmt0;
  58.    va_list argp;
  59.    register FILE *fp;
  60. {
  61.    register u_char *fmt;   /* format string */
  62.    register int ch;   /* character from fmt */
  63.    register int cnt;   /* return value accumulator */
  64.    register int n;      /* random handy integer */
  65.    register char *t;   /* buffer pointer */
  66.    double _double;      /* double precision arguments %[eEfgG] */
  67.    u_long _ulong;      /* integer arguments %[diouxX] */
  68.    int base;      /* base for [diouxX] conversion */
  69.    int dprec;      /* decimal precision in [diouxX] */
  70.    int fieldsz;      /* field size expanded by sign, etc */
  71.    int flags;      /* flags as above */
  72.    int fpprec;      /* `extra' floating precision in [eEfgG] */
  73.    int prec;      /* precision from format (%.3d), or -1 */
  74.    int realsz;      /* field size expanded by decimal precision */
  75.    int size;      /* size of converted field or string */
  76.    int width;      /* width from format (%8d), or 0 */
  77.    char sign;      /* sign prefix (' ', '+', '-', or \0) */
  78.    char softsign;      /* temporary negative sign for floats */
  79.    char *digs;      /* digits for [diouxX] conversion */
  80.    char buf[BUF];      /* space for %c, %[diouxX], %[eEfgG] */
  81.  
  82.    if (fp->_flag & _IORW) {
  83.       fp->_flag |= _IOWRT;
  84.       fp->_flag &= ~(_IOEOF|_IOREAD);
  85.    }
  86.    if ((fp->_flag & _IOWRT) == 0)
  87.       return (EOF);
  88.  
  89.    fmt = fmt0;
  90.    digs = "0123456789abcdef";
  91.    for (cnt = 0;; ++fmt) {
  92.       n = fp->_cnt;
  93.       for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
  94.            ++cnt, ++fmt)
  95.          if (--n < 0
  96. #ifdef NEGATIVE_COUNT_KLUDGE
  97.              && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
  98. #endif
  99.              || ch == '\n' && fp->_flag & _IOLBF) {
  100.             fp->_cnt = n;
  101.             fp->_ptr = t;
  102.             (void) _flsbuf((u_char)ch, fp);
  103.             n = fp->_cnt;
  104.             t = (char *)fp->_ptr;
  105.          } else
  106.             *t++ = ch;
  107.       fp->_cnt = n;
  108.       fp->_ptr = t;
  109.       if (!ch)
  110.          return (cnt);
  111.  
  112.       flags = 0; dprec = 0; fpprec = 0; width = 0;
  113.       prec = -1;
  114.       sign = '\0';
  115.  
  116. rflag:      switch (*++fmt) {
  117.       case ' ':
  118.          /*
  119.           * ``If the space and + flags both appear, the space
  120.           * flag will be ignored.''
  121.           *   -- ANSI X3J11
  122.           */
  123.          if (!sign)
  124.             sign = ' ';
  125.          goto rflag;
  126.       case '#':
  127.          flags |= ALT;
  128.          goto rflag;
  129.       case '*':
  130.          /*
  131.           * ``A negative field width argument is taken as a
  132.           * - flag followed by a  positive field width.''
  133.           *   -- ANSI X3J11
  134.           * They don't exclude field widths read from args.
  135.           */
  136.          if ((width = va_arg(argp, int)) >= 0)
  137.             goto rflag;
  138.          width = -width;
  139.          /* FALLTHROUGH */
  140.       case '-':
  141.          flags |= LADJUST;
  142.          goto rflag;
  143.       case '+':
  144.          sign = '+';
  145.          goto rflag;
  146.       case '.':
  147.          if (*++fmt == '*')
  148.             n = va_arg(argp, int);
  149.          else {
  150.             n = 0;
  151.             while (isascii(*fmt) && isdigit(*fmt))
  152.                n = 10 * n + todigit(*fmt++);
  153.             --fmt;
  154.          }
  155.          prec = n < 0 ? -1 : n;
  156.          goto rflag;
  157.       case '0':
  158.          /*
  159.           * ``Note that 0 is taken as a flag, not as the
  160.           * beginning of a field width.''
  161.           *   -- ANSI X3J11
  162.           */
  163.          flags |= ZEROPAD;
  164.          goto rflag;
  165.       case '1': case '2': case '3': case '4':
  166.       case '5': case '6': case '7': case '8': case '9':
  167.          n = 0;
  168.          do {
  169.             n = 10 * n + todigit(*fmt);
  170.          } while (isascii(*++fmt) && isdigit(*fmt));
  171.          width = n;
  172.          --fmt;
  173.          goto rflag;
  174.       case 'L':
  175.          flags |= LONGDBL;
  176.          goto rflag;
  177.       case 'h':
  178.          flags |= SHORTINT;
  179.          goto rflag;
  180.       case 'l':
  181.          flags |= LONGINT;
  182.          goto rflag;
  183.       case 'c':
  184.          *(t = buf) = va_arg(argp, int);
  185.          size = 1;
  186.          sign = '\0';
  187.          goto pforw;
  188.       case 'D':
  189.          flags |= LONGINT;
  190.          /*FALLTHROUGH*/
  191.       case 'd':
  192.       case 'i':
  193.          ARG();
  194.          if ((long)_ulong < 0) {
  195.             _ulong = -_ulong;
  196.             sign = '-';
  197.          }
  198.          base = 10;
  199.          goto number;
  200.       case 'e':
  201.       case 'E':
  202.       case 'f':
  203.       case 'g':
  204.       case 'G':
  205.          _double = va_arg(argp, double);
  206.          /*
  207.           * don't do unrealistic precision; just pad it with
  208.           * zeroes later, so buffer size stays rational.
  209.           */
  210.          if (prec > MAXFRACT) {
  211.             if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
  212.                fpprec = prec - MAXFRACT;
  213.             prec = MAXFRACT;
  214.          }
  215.          else if (prec == -1)
  216.             prec = DEFPREC;
  217.          /*
  218.           * softsign avoids negative 0 if _double is < 0 and
  219.           * no significant digits will be shown
  220.           */
  221.          if (_double < 0) {
  222.             softsign = '-';
  223.             _double = -_double;
  224.          }
  225.          else
  226.             softsign = 0;
  227.          /*
  228.           * cvt may have to round up past the "start" of the
  229.           * buffer, i.e. ``intf("%.2f", (double)9.999);'';
  230.           * if the first char isn't NULL, it did.
  231.           */
  232.          *buf = NULL;
  233.          size = cvt(_double, prec, flags, &softsign, *fmt, buf,
  234.              buf + sizeof(buf));
  235.          if (softsign)
  236.             sign = '-';
  237.          t = *buf ? buf : buf + 1;
  238.          goto pforw;
  239.       case 'n':
  240.          if (flags & LONGINT)
  241.             *va_arg(argp, long *) = cnt;
  242.          else if (flags & SHORTINT)
  243.             *va_arg(argp, short *) = cnt;
  244.          else
  245.             *va_arg(argp, int *) = cnt;
  246.          break;
  247.       case 'O':
  248.          flags |= LONGINT;
  249.          /*FALLTHROUGH*/
  250.       case 'o':
  251.          ARG();
  252.          base = 8;
  253.          goto nosign;
  254.       case 'p':
  255.          /*
  256.           * ``The argument shall be a pointer to void.  The
  257.           * value of the pointer is converted to a sequence
  258.           * of printable characters, in an implementation-
  259.           * defined manner.''
  260.           *   -- ANSI X3J11
  261.           */
  262.          /* NOSTRICT */
  263.          _ulong = (u_long)va_arg(argp, void *);
  264.          base = 16;
  265.          goto nosign;
  266.       case 's':
  267.          if (!(t = va_arg(argp, char *)))
  268.             t = "(null)";
  269.          if (prec >= 0) {
  270.             /*
  271.              * can't use strlen; can only look for the
  272.              * NUL in the first `prec' characters, and
  273.              * strlen() will go further.
  274.              */
  275.             char *p, *memchr();
  276.  
  277.             if (p = memchr(t, 0, prec)) {
  278.                size = p - t;
  279.                if (size > prec)
  280.                   size = prec;
  281.             } else
  282.                size = prec;
  283.          } else
  284.             size = strlen(t);
  285.          sign = '\0';
  286.          goto pforw;
  287.       case 'U':
  288.          flags |= LONGINT;
  289.          /*FALLTHROUGH*/
  290.       case 'u':
  291.          ARG();
  292.          base = 10;
  293.          goto nosign;
  294.       case 'X':
  295.          digs = "0123456789ABCDEF";
  296.          /* FALLTHROUGH */
  297.       case 'x':
  298.          ARG();
  299.          base = 16;
  300.          /* leading 0x/X only if non-zero */
  301.          if (flags & ALT && _ulong != 0)
  302.             flags |= HEXPREFIX;
  303.  
  304.          /* unsigned conversions */
  305. nosign:         sign = '\0';
  306.          /*
  307.           * ``... diouXx conversions ... if a precision is
  308.           * specified, the 0 flag will be ignored.''
  309.           *   -- ANSI X3J11
  310.           */
  311. number:         if ((dprec = prec) >= 0)
  312.             flags &= ~ZEROPAD;
  313.  
  314.          /*
  315.           * ``The result of converting a zero value with an
  316.           * explicit precision of zero is no characters.''
  317.           *   -- ANSI X3J11
  318.           */
  319.          t = buf + BUF;
  320.          if (_ulong != 0 || prec != 0) {
  321.             do {
  322.                *--t = digs[_ulong % base];
  323.                _ulong /= base;
  324.             } while (_ulong);
  325.             digs = "0123456789abcdef";
  326.             if (flags & ALT && base == 8 && *t != '0')
  327.                *--t = '0'; /* octal leading 0 */
  328.          }
  329.          size = buf + BUF - t;
  330.  
  331. pforw:
  332.          /*
  333.           * All reasonable formats wind up here.  At this point,
  334.           * `t' points to a string which (if not flags&LADJUST)
  335.           * should be padded out to `width' places.  If
  336.           * flags&ZEROPAD, it should first be prefixed by any
  337.           * sign or other prefix; otherwise, it should be blank
  338.           * padded before the prefix is emitted.  After any
  339.           * left-hand padding and prefixing, emit zeroes
  340.           * required by a decimal [diouxX] precision, then print
  341.           * the string proper, then emit zeroes required by any
  342.           * leftover floating precision; finally, if LADJUST,
  343.           * pad with blanks.
  344.           */
  345.  
  346.          /*
  347.           * compute actual size, so we know how much to pad
  348.           * fieldsz excludes decimal prec; realsz includes it
  349.           */
  350.          fieldsz = size + fpprec;
  351.          if (sign)
  352.             fieldsz++;
  353.          if (flags & HEXPREFIX)
  354.             fieldsz += 2;
  355.          realsz = dprec > fieldsz ? dprec : fieldsz;
  356.  
  357.          /* right-adjusting blank padding */
  358.          if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
  359.             for (n = realsz; n < width; n++)
  360.                PUTC(' ');
  361.          /* prefix */
  362.          if (sign)
  363.             PUTC(sign);
  364.          if (flags & HEXPREFIX) {
  365.             PUTC('0');
  366.             PUTC((char)*fmt);
  367.          }
  368.          /* right-adjusting zero padding */
  369.          if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
  370.             for (n = realsz; n < width; n++)
  371.                PUTC('0');
  372.          /* leading zeroes from decimal precision */
  373.          for (n = fieldsz; n < dprec; n++)
  374.             PUTC('0');
  375.  
  376.          /* the string or number proper */
  377.          if (fp->_cnt - (n = size) >= 0 &&
  378.              (fp->_flag & _IOLBF) == 0) {
  379.             fp->_cnt -= n;
  380.             bcopy(t, (char *)fp->_ptr, n);
  381.             fp->_ptr += n;
  382.          } else
  383.             while (--n >= 0)
  384.                PUTC(*t++);
  385.          /* trailing f.p. zeroes */
  386.          while (--fpprec >= 0)
  387.             PUTC('0');
  388.          /* left-adjusting padding (always blank) */
  389.          if (flags & LADJUST)
  390.             for (n = realsz; n < width; n++)
  391.                PUTC(' ');
  392.          /* finally, adjust cnt */
  393.          cnt += width > realsz ? width : realsz;
  394.          break;
  395.       case '\0':   /* "%?" prints ?, unless ? is NULL */
  396.          return (cnt);
  397.       default:
  398.          PUTC((char)*fmt);
  399.          cnt++;
  400.       }
  401.    }
  402.    /* NOTREACHED */
  403. }
  404.  
  405. static
  406. cvt(number, prec, flags, signp, fmtch, startp, endp)
  407.    double number;
  408.    register int prec;
  409.    int flags;
  410.    u_char fmtch;
  411.    char *signp, *startp, *endp;
  412. {
  413.    register char *p, *t;
  414.    register double fract;
  415.    int dotrim, expcnt, gformat;
  416.    double integer, tmp, modf();
  417.    char *exponent(), *round();
  418.  
  419.    dotrim = expcnt = gformat = 0;
  420.    fract = modf(number, &integer);
  421.  
  422.    /* get an extra slot for rounding. */
  423.    t = ++startp;
  424.  
  425.    /*
  426.     * get integer portion of number; put into the end of the buffer; the
  427.     * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
  428.     */
  429.    for (p = endp - 1; integer; ++expcnt) {
  430.       tmp = modf(integer / 10, &integer);
  431.       *p-- = tochar((int)((tmp + .01) * 10));
  432.    }
  433.    switch(fmtch) {
  434.    case 'f':
  435.       /* reverse integer into beginning of buffer */
  436.       if (expcnt)
  437.          for (; ++p < endp; *t++ = *p);
  438.       else
  439.          *t++ = '0';
  440.       /*
  441.        * if precision required or alternate flag set, add in a
  442.        * decimal point.
  443.        */
  444.       if (prec || flags&ALT)
  445.          *t++ = '.';
  446.       /* if requires more precision and some fraction left */
  447.       if (fract) {
  448.          if (prec)
  449.             do {
  450.                fract = modf(fract * 10, &tmp);
  451.                *t++ = tochar((int)tmp);
  452.             } while (--prec && fract);
  453.          if (fract)
  454.             startp = round(fract, (int *)NULL, startp,
  455.                 t - 1, (char)0, signp);
  456.       }
  457.       for (; prec--; *t++ = '0');
  458.       break;
  459.    case 'e':
  460.    case 'E':
  461. eformat:   if (expcnt) {
  462.          *t++ = *++p;
  463.          if (prec || flags&ALT)
  464.             *t++ = '.';
  465.          /* if requires more precision and some integer left */
  466.          for (; prec && ++p < endp; --prec)
  467.             *t++ = *p;
  468.          /*
  469.           * if done precision and more of the integer component,
  470.           * round using it; adjust fract so we don't re-round
  471.           * later.
  472.           */
  473.          if (!prec && ++p < endp) {
  474.             fract = 0;
  475.             startp = round((double)0, &expcnt, startp,
  476.                 t - 1, *p, signp);
  477.          }
  478.          /* adjust expcnt for digit in front of decimal */
  479.          --expcnt;
  480.       }
  481.       /* until first fractional digit, decrement exponent */
  482.       else if (fract) {
  483.          /* adjust expcnt for digit in front of decimal */
  484.          for (expcnt = -1;; --expcnt) {
  485.             fract = modf(fract * 10, &tmp);
  486.             if (tmp)
  487.                break;
  488.          }
  489.          *t++ = tochar((int)tmp);
  490.          if (prec || flags&ALT)
  491.             *t++ = '.';
  492.       }
  493.       else {
  494.          *t++ = '0';
  495.          if (prec || flags&ALT)
  496.             *t++ = '.';
  497.       }
  498.       /* if requires more precision and some fraction left */
  499.       if (fract) {
  500.          if (prec)
  501.             do {
  502.                fract = modf(fract * 10, &tmp);
  503.                *t++ = tochar((int)tmp);
  504.             } while (--prec && fract);
  505.          if (fract)
  506.             startp = round(fract, &expcnt, startp,
  507.                 t - 1, (char)0, signp);
  508.       }
  509.       /* if requires more precision */
  510.       for (; prec--; *t++ = '0');
  511.  
  512.       /* unless alternate flag, trim any g/G format trailing 0's */
  513.       if (gformat && !(flags&ALT)) {
  514.          while (t > startp && *--t == '0');
  515.          if (*t == '.')
  516.             --t;
  517.          ++t;
  518.       }
  519.       t = exponent(t, expcnt, fmtch);
  520.       break;
  521.    case 'g':
  522.    case 'G':
  523.       /* a precision of 0 is treated as a precision of 1. */
  524.       if (!prec)
  525.          ++prec;
  526.       /*
  527.        * ``The style used depends on the value converted; style e
  528.        * will be used only if the exponent resulting from the
  529.        * conversion is less than -4 or greater than the precision.''
  530.        *   -- ANSI X3J11
  531.        */
  532.       if (expcnt > prec || !expcnt && fract && fract < .0001) {
  533.          /*
  534.           * g/G format counts "significant digits, not digits of
  535.           * precision; for the e/E format, this just causes an
  536.           * off-by-one problem, i.e. g/G considers the digit
  537.           * before the decimal point significant and e/E doesn't
  538.           * count it as precision.
  539.           */
  540.          --prec;
  541.          fmtch -= 2;      /* G->E, g->e */
  542.          gformat = 1;
  543.          goto eformat;
  544.       }
  545.       /*
  546.        * reverse integer into beginning of buffer,
  547.        * note, decrement precision
  548.        */
  549.       if (expcnt)
  550.          for (; ++p < endp; *t++ = *p, --prec);
  551.       else
  552.          *t++ = '0';
  553.       /*
  554.        * if precision required or alternate flag set, add in a
  555.        * decimal point.  If no digits yet, add in leading 0.
  556.        */
  557.       if (prec || flags&ALT) {
  558.          dotrim = 1;
  559.          *t++ = '.';
  560.       }
  561.       else
  562.          dotrim = 0;
  563.       /* if requires more precision and some fraction left */
  564.       if (fract) {
  565.          if (prec) {
  566.             do {
  567.                fract = modf(fract * 10, &tmp);
  568.                *t++ = tochar((int)tmp);
  569.             } while(!tmp);
  570.             while (--prec && fract) {
  571.                fract = modf(fract * 10, &tmp);
  572.                *t++ = tochar((int)tmp);
  573.             }
  574.          }
  575.          if (fract)
  576.             startp = round(fract, (int *)NULL, startp,
  577.                 t - 1, (char)0, signp);
  578.       }
  579.       /* alternate format, adds 0's for precision, else trim 0's */
  580.       if (flags&ALT)
  581.          for (; prec--; *t++ = '0');
  582.       else if (dotrim) {
  583.          while (t > startp && *--t == '0');
  584.          if (*t != '.')
  585.             ++t;
  586.       }
  587.    }
  588.    return(t - startp);
  589. }
  590.  
  591. static char *
  592. round(fract, exp, start, end, ch, signp)
  593.    double fract;
  594.    int *exp;
  595.    register char *start, *end;
  596.    char ch, *signp;
  597. {
  598.    double tmp;
  599.  
  600.    if (fract)
  601.       (void)modf(fract * 10, &tmp);
  602.    else
  603.       tmp = todigit(ch);
  604.    if (tmp > 4)
  605.       for (;; --end) {
  606.          if (*end == '.')
  607.             --end;
  608.          if (++*end <= '9')
  609.             break;
  610.          *end = '0';
  611.          if (end == start) {
  612.             if (exp) {   /* e/E; increment exponent */
  613.                *end = '1';
  614.                ++*exp;
  615.             }
  616.             else {      /* f; add extra digit */
  617.                *--end = '1';
  618.                --start;
  619.             }
  620.             break;
  621.          }
  622.       }
  623.    /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
  624.    else if (*signp == '-')
  625.       for (;; --end) {
  626.          if (*end == '.')
  627.             --end;
  628.          if (*end != '0')
  629.             break;
  630.          if (end == start)
  631.             *signp = 0;
  632.       }
  633.    return(start);
  634. }
  635.  
  636. static char *
  637. exponent(p, exp, fmtch)
  638.    register char *p;
  639.    register int exp;
  640.    u_char fmtch;
  641. {
  642.    register char *t;
  643.    char expbuf[MAXEXP];
  644.  
  645.    *p++ = fmtch;
  646.    if (exp < 0) {
  647.       exp = -exp;
  648.       *p++ = '-';
  649.    }
  650.    else
  651.       *p++ = '+';
  652.    t = expbuf + MAXEXP;
  653.    if (exp > 9) {
  654.       do {
  655.          *--t = tochar(exp % 10);
  656.       } while ((exp /= 10) > 9);
  657.       *--t = tochar(exp);
  658.       for (; t < expbuf + MAXEXP; *p++ = *t++);
  659.    }
  660.    else {
  661.       *p++ = '0';
  662.       *p++ = tochar(exp);
  663.    }
  664.    return(p);
  665. }
  666.  
  667.