home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / EMXLIB8F.ZIP / EMX / LIB / IO / OUTPUT.C < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-02  |  14.7 KB  |  479 lines

  1. /* output.c (emx+gcc) -- Copyright (c) 1990-1993 by Eberhard Mattes */
  2.  
  3. #include <sys/emx.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <stdarg.h>
  7. #include <string.h>
  8. #include <limits.h>
  9. #include <math.h>
  10.  
  11. #define MAX_FRACT 20        /* 64 * log 2 */
  12. #define MAX_MANT  309       /* extended format isn't used */
  13. #define DEFAULT_PREC 6
  14.  
  15. #define BEGIN do {
  16. #define END   } while (0)
  17.  
  18. #define PUTC(c) BEGIN \
  19.                  if (putc (c, stream) == EOF) \
  20.                    return (-1); \
  21.                  ++count; \
  22.                 END
  23.  
  24. static void conv_real (char *dst, double x, int prec, int fixed, int force_dp,
  25.                        int *prec_out, int *exp_out);
  26.  
  27. int _output (FILE *stream, const char *format, char *arg_ptr)
  28. {
  29.   char just, sign, blank, hash, size, upper, number, integer, pad, cont, fix;
  30.   char *s, tmp[66], prefix[8], *pfx_ptr, *pfx_start, tbuf[40];
  31.   int width, prec, count, *ptr, n, i, pn, zn, sn, tzn, neg;
  32.   long long lln;
  33.   unsigned long long ulln;
  34.   double dn;
  35.   char dbuf[MAX_MANT+MAX_FRACT+10];
  36.   int de;
  37.  
  38.   count = 0;
  39.   while (*format != 0)
  40.     if (*format != '%' || format[1] == '%')
  41.       {
  42.         PUTC (*format);
  43.         if (*format == '%') ++format;
  44.         ++format;
  45.       }
  46.     else
  47.       {
  48.         just = sign = blank = hash = upper = FALSE;
  49.         width = 0; prec = -1; size = 0; tzn = 0; pad = ' ';
  50.         cont = TRUE;
  51.         while (cont)
  52.           {
  53.             ++format;
  54.             switch (*format)
  55.               {
  56.               case '-': just = TRUE; break;
  57.               case '+': sign = TRUE; break;
  58.               case '0': pad = '0'; break;
  59.               case ' ': blank = TRUE; break;
  60.               case '#': hash = TRUE; break;
  61.               default: cont = FALSE; break;
  62.               }
  63.           }
  64.         if (*format == '*')
  65.           {
  66.             ++format;
  67.             width = va_arg (arg_ptr, int);
  68.             if (width < 0)
  69.               {
  70.                 width = -width; just = TRUE;
  71.               }
  72.           }
  73.         else
  74.           while (*format >= '0' && *format <= '9')
  75.             {
  76.               width = width * 10 + (*format - '0');
  77.               ++format;
  78.             }
  79.         if (*format == '.')
  80.           {
  81.             pad = ' ';
  82.             ++format;
  83.             if (*format == '*')
  84.               {
  85.                 ++format;
  86.                 prec = va_arg (arg_ptr, int);
  87.                 if (prec < 0) prec = -1;
  88.               }
  89.             else
  90.               {
  91.                 prec = 0;
  92.                 while (*format >= '0' && *format <= '9')
  93.                   {
  94.                     prec = prec * 10 + (*format - '0');
  95.                     ++format;
  96.                   }
  97.               }
  98.           }
  99.         if (*format == 'h' || *format == 'l' || *format == 'L')
  100.           {
  101.             size = *format++;
  102.             if (size == 'l' && *format == 'l')
  103.               {
  104.                 size = 'L'; ++format;
  105.               }
  106.           }
  107.         s = NULL; number = TRUE; integer = TRUE; sn = 0;
  108.         pfx_ptr = pfx_start = prefix+1;
  109.         tbuf[0] = 0;
  110.         switch (*format)
  111.           {
  112.           case 'n':
  113.             ptr = va_arg (arg_ptr, int *);
  114.             *ptr = count;
  115.             goto next;
  116.           case 'c':
  117.             number = FALSE;
  118.             tmp[0] = (char)va_arg (arg_ptr, int);
  119.             s = tmp; sn = 1;
  120.             break;
  121.           case 'u':
  122.             if (size == 'L')
  123.               {
  124.                 ulln = va_arg (arg_ptr, unsigned long long);
  125.                 s = _ulltoa (ulln, tmp, 10);
  126.               }
  127.             else
  128.               {
  129.                 n = va_arg (arg_ptr, int);
  130.                 if (size == 'h') n = (unsigned short)n;
  131.                 s = _ultoa (n, tmp, 10);
  132.               }
  133.             sn = strlen (s);
  134.             break;
  135.           case 'd':
  136.           case 'i':
  137.             if (size == 'L')
  138.               {
  139.                 lln = va_arg (arg_ptr, long long);
  140.                 s = _lltoa (lln, tmp, 10);
  141.               }
  142.             else
  143.               {
  144.                 n = va_arg (arg_ptr, int);
  145.                 if (size == 'h') n = (short)n;
  146.                 s = _itoa (n, tmp, 10);
  147.               }
  148.             sn = strlen (s);
  149.             break;
  150.           case 'p':
  151.           case 'x':
  152.           case 'X':
  153.             if (size == 'L')
  154.               {
  155.                 ulln = va_arg (arg_ptr, unsigned long long);
  156.                 s = _ulltoa (ulln, tmp, 16);
  157.                 n = (ulln != 0);
  158.               }
  159.             else
  160.               {
  161.                 n = va_arg (arg_ptr, int);
  162.                 if (size == 'h') n = (unsigned short)n;
  163.                 s = _itoa (n, tmp, 16);
  164.               }
  165.             sn = strlen (s);
  166.             if (*format == 'X')
  167.               strupr (tmp);
  168.             if (n != 0 && hash)
  169.               {
  170.                 *pfx_ptr++ = '0';
  171.                 *pfx_ptr++ = 'x';
  172.               }
  173.             break;
  174.           case 'o':
  175.             if (size == 'L')
  176.               {
  177.                 ulln = va_arg (arg_ptr, unsigned long long);
  178.                 s = _ulltoa (ulln, tmp, 8);
  179.                 n = (ulln != 0);
  180.               }
  181.             else
  182.               {
  183.                 n = va_arg (arg_ptr, int);
  184.                 if (size == 'h') n = (unsigned short)n;
  185.                 s = _itoa (n, tmp, 8);
  186.               }
  187.             sn = strlen (s);
  188.             if (n != 0 && hash)
  189.               *pfx_ptr++ = '0';
  190.             break;
  191.           case 'g':
  192.           case 'G':
  193.           case 'e':
  194.           case 'E':
  195.           case 'f':
  196.             if (prec == -1) prec = DEFAULT_PREC;
  197.             dn = va_arg (arg_ptr, double);
  198.             tzn = prec;
  199.             if (prec > MAX_FRACT) prec = MAX_FRACT;
  200.             switch (_fxam (dn))
  201.               {
  202.               case FX_P_NAN:
  203.               case FX_N_NAN:      s = "#NAN"; break;
  204.               case FX_P_INFINITY: s = "#INF"; break;
  205.               case FX_N_INFINITY: s = "-#INF"; break;
  206.               case FX_P_EMPTY:    s = "#EMP"; break;
  207.               case FX_N_EMPTY:    s = "-#EMP"; break;
  208.               case FX_P_DENORMAL: s = "#DEN"; break;
  209.               case FX_N_DENORMAL: s = "-#DEN"; break;
  210.               default:
  211.                 s = NULL;
  212.                 break;
  213.               }
  214.             if (s != NULL)
  215.               {
  216.                 sn = strlen (s); prec = tzn = 0;
  217.                 break;
  218.               }
  219.             neg = (dn < 0.0);
  220.             if (neg) dn = -dn;
  221.             switch (*format)
  222.               {
  223.               case 'f':
  224.                 conv_real (dbuf, dn, prec, TRUE, hash, &i, &de);
  225.                 tzn -= prec - i;
  226.                 break;
  227.               case 'g':
  228.               case 'G':
  229.                 if (dn == 0.0)
  230.                   {
  231.                     dbuf[1] = '0';
  232.                     dbuf[2] = 0;
  233.                     if (!hash) tzn = 0;
  234.                     break;
  235.                   }
  236.                 conv_real (dbuf, dn, prec+1, FALSE, FALSE, &tzn, &de);
  237.                 fix = !(de < -4 || de > prec);
  238.                 if (fix)
  239.                   {
  240.                     conv_real (dbuf, dn, prec, TRUE, hash, &i, &de);
  241.                     tzn -= prec - i;
  242.                   }
  243.                 if (!hash)
  244.                   {
  245.                     i = strlen (dbuf+1);
  246.                     while (i > 1 && dbuf[i] == '0') --i;
  247.                     if (i > 1 && dbuf[i] == '.') --i;
  248.                     dbuf[i+1] = 0;
  249.                     tzn = 0;
  250.                   }
  251.                 if (!fix) goto fmt_e;
  252.                 break;
  253.               case 'e':
  254.               case 'E':
  255.                 if (dn == 0.0)
  256.                   {
  257.                     s = dbuf + 1;
  258.                     *s++ = '0';
  259.                     if (prec != 0 || hash) *s++ = '.';
  260.                     *s = 0; de = 0;
  261.                   }
  262.                 else
  263.                   conv_real (dbuf, dn, prec+1, FALSE, FALSE, &tzn, &de);
  264. fmt_e:
  265.                 tbuf[0] = (*format >= 'A' && *format <= 'Z' ? 'E' : 'e');
  266.                 tbuf[1] = (de < 0 ? '-' : '+');
  267.                 if (de < 0) de = -de;
  268.                 tbuf[2] = (char)((de / 100) % 10) + '0';
  269.                 tbuf[3] = (char)((de / 10) % 10) + '0';
  270.                 tbuf[4] = (char)(de % 10) + '0';
  271.                 tbuf[5] = 0;
  272.                 break;
  273.               }
  274.             if (neg)
  275.               {
  276.                 dbuf[0] = '-';
  277.                 s = dbuf;
  278.               }
  279.             else
  280.               s = dbuf + 1;
  281.             sn = strlen (s); prec = 0;
  282.             break;
  283.           case 's':
  284.             number = FALSE;
  285.             s = va_arg (arg_ptr, char *);
  286.             if (s == NULL) s = "(null)";
  287.             sn = strlen (s);
  288.             if (prec > 0 && prec < sn)
  289.               sn = prec;
  290.             break;
  291.           default:
  292.             if (size != 0)
  293.               --format;
  294.             PUTC (*format);
  295.             break;
  296.           }
  297.         if (s != NULL)
  298.           {
  299.             if (number && sn > 0)
  300.               {
  301.                 if (*s == '-')
  302.                   {
  303.                     ++s; --sn;
  304.                     *--pfx_start = '-';
  305.                   }
  306.                 else if (sign)
  307.                   *--pfx_start = '+';
  308.                 else if (blank)
  309.                   *--pfx_start = ' ';
  310.               }
  311.             zn = 0;
  312.             if (number && integer && prec > 0)
  313.               {
  314.                 zn = prec - sn;
  315.                 if (zn < 0) zn = 0;
  316.               }
  317.             pn = pfx_ptr - pfx_start;
  318.             if (tzn < 0) tzn = 0;
  319.             n = pn + zn + sn + tzn + strlen (tbuf);
  320.             if (number && pad == '0' && n < width && !just)
  321.               {
  322.                 zn += width - n;
  323.                 n = width;
  324.               }
  325.             if (n < width && !just)
  326.               for (i = n; i < width; ++i)
  327.                 PUTC (pad);
  328.             for (i = 0; i < pn; ++i)
  329.               {
  330.                 PUTC (*pfx_start); ++pfx_start;
  331.               }
  332.             for (i = 0; i < zn; ++i)
  333.               PUTC ('0');
  334.             for (i = 0; i < sn; ++i)
  335.               {
  336.                 PUTC (*s); ++s;
  337.               }
  338.             for (i = 0; i < tzn; ++i)
  339.               PUTC ('0');
  340.             for (i = 0; tbuf[i] != 0; ++i)
  341.               PUTC (tbuf[i]);
  342.             if (n < width && just)
  343.               for (i = n; i < width; ++i)
  344.                 PUTC (' ');
  345.           }
  346. next:
  347.         ++format;
  348.       }
  349.   return (count);
  350. }
  351.  
  352.  
  353. /* Now comes the hard part.  Convert X to a string of digits.  Store the
  354.    resulting string to DST.  DST[0] is reserved for the sign character
  355.    (and used internally for rounding) -- it is not set by this
  356.    function: the string starts at DST+1.  PREC is the precision, ie,
  357.    the number of decimal digits after the decimal point.  FIXED is
  358.    non-zero for fixed-point formats.  If FORCE_DP is non-zero, the
  359.    result will always contain a decimal point.  The resulting number of
  360.    decimal digits after the decimal point is returned in *PREC_OUT.
  361.    The exponent to be applied to the string is returned in *EXP_OUT
  362.    (used only if FIXED is 0).
  363.  
  364.    Algorithm:
  365.  
  366.    - Convert the integer part of X into a string of digits by repeated
  367.      division by 10.  After this step, I contains the number of digits
  368.      (integer part), the digit string is stored in TMP (I characters),
  369.      EXP contains the number of digits (integer part) - 1.
  370.      LEADING_ZERO is set FALSE if a fixed-point format is used (FIXED
  371.      non-zero) or if the integer part is non-zero.
  372.  
  373.    - Copy the digit string (integer part) to DST.  A decimal point is
  374.      added for fixed-point formats if FORCE_DP is non-zero or if PREC
  375.      is positive.  For exponential formats, a decimal point is not
  376.      added -- it will be inserted later between the first and the
  377.      second digits.  PREC is adjusted to indicate the number of digits
  378.      remaining to be computed.
  379.  
  380.    - The fractional part is converted into a string of digits until it
  381.      is zero or the precision PREC is exhausted.  This is done by
  382.      repeated multiplication by 10.  Leading zeros are suppressed (see
  383.      note about LEADING_ZERO above) adjusting EXP appropriately.
  384.  
  385.    - If the remaining fractional part is non-zero, rounding is
  386.      applied.  This is done by computing the next digit and comparing
  387.      it to 5: If it is greater than or equal to 5, the "number" (digit
  388.      string) is "incremented by 1" -- ignoring the decimal point.  This
  389.      operation may overflow into the first character of the
  390.      destination string which is reserved for this reason (and for
  391.      storing the sign character).  On overflow the string is moved by
  392.      one character to free the first character.
  393.  
  394.    - For exponential formats, the decimal point is inserted after the
  395.      first digit of the string.
  396.  
  397.   This algorithm is somewhat inaccurate.  Letting the coprocessor
  398.   convert the number to BCD should improve accuracy.  Another approach
  399.   is to do all the computations using 80-bit floating point numbers. */
  400.  
  401. static void conv_real (char *dst, double x, int prec, int fixed, int force_dp,
  402.                        int *prec_out, int *exp_out)
  403. {
  404.   double df, dt, di;
  405.   char tmp[MAX_MANT+MAX_FRACT+10], c, *start;
  406.   int i, exp, leading_zero;
  407.  
  408.   start = dst++;
  409.   df = modf (x, &di);
  410.   i = 0; exp = -1; leading_zero = !fixed;
  411.   while (di != 0.0)
  412.     {
  413.       dt = modf (di / 10.0, &di);
  414.       tmp[i++] = (char)(dt * 10.0 + 0.125) + '0';
  415.       ++exp; leading_zero = FALSE;
  416.     }
  417.   if (fixed)
  418.     {
  419.       if (i == 0) *dst++ = '0';
  420.       while (i != 0)
  421.         *dst++ = tmp[--i];
  422.       if (prec > 0 || force_dp) *dst++ = '.';
  423.     }
  424.   else
  425.     while (i != 0 && prec > 0)
  426.       {
  427.         *dst++ = tmp[--i];
  428.         --prec;
  429.       }
  430.   while (df != 0.0 && prec > 0)
  431.     {
  432.       df = modf (df * 10.0, &dt);
  433.       c = (char)dt + '0';
  434.       if (c != '0' || !leading_zero)
  435.         {
  436.           *dst++ = c; leading_zero = FALSE;
  437.           --prec;
  438.         }
  439.       else
  440.         --exp;
  441.     }
  442.   *dst = 0;
  443.   if (df != 0.0)
  444.     {
  445.       df = modf (df * 10.0, &dt);
  446.       c = (char)dt + '0';
  447.       if (c >= '5')
  448.         {
  449.           *start = '0';
  450.           while (dst != start)
  451.             {
  452.               --dst;
  453.               if (*dst == '.') continue;
  454.               if (*dst != '9')
  455.                 {
  456.                   ++*dst; break;
  457.                 }
  458.               *dst = '0';
  459.             }
  460.           if (dst == start)
  461.             {
  462.               i = strlen (start);
  463.               memmove (start+1, start, i+1);
  464.               if (!fixed)
  465.                 {
  466.                   start[i] = 0; ++exp;
  467.                 }
  468.             }
  469.         }
  470.     }
  471.   if (!fixed)
  472.     {
  473.       memmove (start+3, start+2, strlen (start+1));
  474.       start[2] = '.';
  475.     }
  476.   *prec_out = prec;
  477.   *exp_out = exp;
  478. }
  479.