home *** CD-ROM | disk | FTP | other *** search
- /* output.c (emx+gcc) -- Copyright (c) 1990-1993 by Eberhard Mattes */
-
- #include <sys/emx.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <string.h>
- #include <limits.h>
- #include <math.h>
-
- #define MAX_FRACT 20 /* 64 * log 2 */
- #define MAX_MANT 309 /* extended format isn't used */
- #define DEFAULT_PREC 6
-
- #define BEGIN do {
- #define END } while (0)
-
- #define PUTC(c) BEGIN \
- if (putc (c, stream) == EOF) \
- return (-1); \
- ++count; \
- END
-
- static void conv_real (char *dst, double x, int prec, int fixed, int force_dp,
- int *prec_out, int *exp_out);
-
- int _output (FILE *stream, const char *format, char *arg_ptr)
- {
- char just, sign, blank, hash, size, upper, number, integer, pad, cont, fix;
- char *s, tmp[66], prefix[8], *pfx_ptr, *pfx_start, tbuf[40];
- int width, prec, count, *ptr, n, i, pn, zn, sn, tzn, neg;
- long long lln;
- unsigned long long ulln;
- double dn;
- char dbuf[MAX_MANT+MAX_FRACT+10];
- int de;
-
- count = 0;
- while (*format != 0)
- if (*format != '%' || format[1] == '%')
- {
- PUTC (*format);
- if (*format == '%') ++format;
- ++format;
- }
- else
- {
- just = sign = blank = hash = upper = FALSE;
- width = 0; prec = -1; size = 0; tzn = 0; pad = ' ';
- cont = TRUE;
- while (cont)
- {
- ++format;
- switch (*format)
- {
- case '-': just = TRUE; break;
- case '+': sign = TRUE; break;
- case '0': pad = '0'; break;
- case ' ': blank = TRUE; break;
- case '#': hash = TRUE; break;
- default: cont = FALSE; break;
- }
- }
- if (*format == '*')
- {
- ++format;
- width = va_arg (arg_ptr, int);
- if (width < 0)
- {
- width = -width; just = TRUE;
- }
- }
- else
- while (*format >= '0' && *format <= '9')
- {
- width = width * 10 + (*format - '0');
- ++format;
- }
- if (*format == '.')
- {
- pad = ' ';
- ++format;
- if (*format == '*')
- {
- ++format;
- prec = va_arg (arg_ptr, int);
- if (prec < 0) prec = -1;
- }
- else
- {
- prec = 0;
- while (*format >= '0' && *format <= '9')
- {
- prec = prec * 10 + (*format - '0');
- ++format;
- }
- }
- }
- if (*format == 'h' || *format == 'l' || *format == 'L')
- {
- size = *format++;
- if (size == 'l' && *format == 'l')
- {
- size = 'L'; ++format;
- }
- }
- s = NULL; number = TRUE; integer = TRUE; sn = 0;
- pfx_ptr = pfx_start = prefix+1;
- tbuf[0] = 0;
- switch (*format)
- {
- case 'n':
- ptr = va_arg (arg_ptr, int *);
- *ptr = count;
- goto next;
- case 'c':
- number = FALSE;
- tmp[0] = (char)va_arg (arg_ptr, int);
- s = tmp; sn = 1;
- break;
- case 'u':
- if (size == 'L')
- {
- ulln = va_arg (arg_ptr, unsigned long long);
- s = _ulltoa (ulln, tmp, 10);
- }
- else
- {
- n = va_arg (arg_ptr, int);
- if (size == 'h') n = (unsigned short)n;
- s = _ultoa (n, tmp, 10);
- }
- sn = strlen (s);
- break;
- case 'd':
- case 'i':
- if (size == 'L')
- {
- lln = va_arg (arg_ptr, long long);
- s = _lltoa (lln, tmp, 10);
- }
- else
- {
- n = va_arg (arg_ptr, int);
- if (size == 'h') n = (short)n;
- s = _itoa (n, tmp, 10);
- }
- sn = strlen (s);
- break;
- case 'p':
- case 'x':
- case 'X':
- if (size == 'L')
- {
- ulln = va_arg (arg_ptr, unsigned long long);
- s = _ulltoa (ulln, tmp, 16);
- n = (ulln != 0);
- }
- else
- {
- n = va_arg (arg_ptr, int);
- if (size == 'h') n = (unsigned short)n;
- s = _itoa (n, tmp, 16);
- }
- sn = strlen (s);
- if (*format == 'X')
- strupr (tmp);
- if (n != 0 && hash)
- {
- *pfx_ptr++ = '0';
- *pfx_ptr++ = 'x';
- }
- break;
- case 'o':
- if (size == 'L')
- {
- ulln = va_arg (arg_ptr, unsigned long long);
- s = _ulltoa (ulln, tmp, 8);
- n = (ulln != 0);
- }
- else
- {
- n = va_arg (arg_ptr, int);
- if (size == 'h') n = (unsigned short)n;
- s = _itoa (n, tmp, 8);
- }
- sn = strlen (s);
- if (n != 0 && hash)
- *pfx_ptr++ = '0';
- break;
- case 'g':
- case 'G':
- case 'e':
- case 'E':
- case 'f':
- if (prec == -1) prec = DEFAULT_PREC;
- dn = va_arg (arg_ptr, double);
- tzn = prec;
- if (prec > MAX_FRACT) prec = MAX_FRACT;
- switch (_fxam (dn))
- {
- case FX_P_NAN:
- case FX_N_NAN: s = "#NAN"; break;
- case FX_P_INFINITY: s = "#INF"; break;
- case FX_N_INFINITY: s = "-#INF"; break;
- case FX_P_EMPTY: s = "#EMP"; break;
- case FX_N_EMPTY: s = "-#EMP"; break;
- case FX_P_DENORMAL: s = "#DEN"; break;
- case FX_N_DENORMAL: s = "-#DEN"; break;
- default:
- s = NULL;
- break;
- }
- if (s != NULL)
- {
- sn = strlen (s); prec = tzn = 0;
- break;
- }
- neg = (dn < 0.0);
- if (neg) dn = -dn;
- switch (*format)
- {
- case 'f':
- conv_real (dbuf, dn, prec, TRUE, hash, &i, &de);
- tzn -= prec - i;
- break;
- case 'g':
- case 'G':
- if (dn == 0.0)
- {
- dbuf[1] = '0';
- dbuf[2] = 0;
- if (!hash) tzn = 0;
- break;
- }
- conv_real (dbuf, dn, prec+1, FALSE, FALSE, &tzn, &de);
- fix = !(de < -4 || de > prec);
- if (fix)
- {
- conv_real (dbuf, dn, prec, TRUE, hash, &i, &de);
- tzn -= prec - i;
- }
- if (!hash)
- {
- i = strlen (dbuf+1);
- while (i > 1 && dbuf[i] == '0') --i;
- if (i > 1 && dbuf[i] == '.') --i;
- dbuf[i+1] = 0;
- tzn = 0;
- }
- if (!fix) goto fmt_e;
- break;
- case 'e':
- case 'E':
- if (dn == 0.0)
- {
- s = dbuf + 1;
- *s++ = '0';
- if (prec != 0 || hash) *s++ = '.';
- *s = 0; de = 0;
- }
- else
- conv_real (dbuf, dn, prec+1, FALSE, FALSE, &tzn, &de);
- fmt_e:
- tbuf[0] = (*format >= 'A' && *format <= 'Z' ? 'E' : 'e');
- tbuf[1] = (de < 0 ? '-' : '+');
- if (de < 0) de = -de;
- tbuf[2] = (char)((de / 100) % 10) + '0';
- tbuf[3] = (char)((de / 10) % 10) + '0';
- tbuf[4] = (char)(de % 10) + '0';
- tbuf[5] = 0;
- break;
- }
- if (neg)
- {
- dbuf[0] = '-';
- s = dbuf;
- }
- else
- s = dbuf + 1;
- sn = strlen (s); prec = 0;
- break;
- case 's':
- number = FALSE;
- s = va_arg (arg_ptr, char *);
- if (s == NULL) s = "(null)";
- sn = strlen (s);
- if (prec > 0 && prec < sn)
- sn = prec;
- break;
- default:
- if (size != 0)
- --format;
- PUTC (*format);
- break;
- }
- if (s != NULL)
- {
- if (number && sn > 0)
- {
- if (*s == '-')
- {
- ++s; --sn;
- *--pfx_start = '-';
- }
- else if (sign)
- *--pfx_start = '+';
- else if (blank)
- *--pfx_start = ' ';
- }
- zn = 0;
- if (number && integer && prec > 0)
- {
- zn = prec - sn;
- if (zn < 0) zn = 0;
- }
- pn = pfx_ptr - pfx_start;
- if (tzn < 0) tzn = 0;
- n = pn + zn + sn + tzn + strlen (tbuf);
- if (number && pad == '0' && n < width && !just)
- {
- zn += width - n;
- n = width;
- }
- if (n < width && !just)
- for (i = n; i < width; ++i)
- PUTC (pad);
- for (i = 0; i < pn; ++i)
- {
- PUTC (*pfx_start); ++pfx_start;
- }
- for (i = 0; i < zn; ++i)
- PUTC ('0');
- for (i = 0; i < sn; ++i)
- {
- PUTC (*s); ++s;
- }
- for (i = 0; i < tzn; ++i)
- PUTC ('0');
- for (i = 0; tbuf[i] != 0; ++i)
- PUTC (tbuf[i]);
- if (n < width && just)
- for (i = n; i < width; ++i)
- PUTC (' ');
- }
- next:
- ++format;
- }
- return (count);
- }
-
-
- /* Now comes the hard part. Convert X to a string of digits. Store the
- resulting string to DST. DST[0] is reserved for the sign character
- (and used internally for rounding) -- it is not set by this
- function: the string starts at DST+1. PREC is the precision, ie,
- the number of decimal digits after the decimal point. FIXED is
- non-zero for fixed-point formats. If FORCE_DP is non-zero, the
- result will always contain a decimal point. The resulting number of
- decimal digits after the decimal point is returned in *PREC_OUT.
- The exponent to be applied to the string is returned in *EXP_OUT
- (used only if FIXED is 0).
-
- Algorithm:
-
- - Convert the integer part of X into a string of digits by repeated
- division by 10. After this step, I contains the number of digits
- (integer part), the digit string is stored in TMP (I characters),
- EXP contains the number of digits (integer part) - 1.
- LEADING_ZERO is set FALSE if a fixed-point format is used (FIXED
- non-zero) or if the integer part is non-zero.
-
- - Copy the digit string (integer part) to DST. A decimal point is
- added for fixed-point formats if FORCE_DP is non-zero or if PREC
- is positive. For exponential formats, a decimal point is not
- added -- it will be inserted later between the first and the
- second digits. PREC is adjusted to indicate the number of digits
- remaining to be computed.
-
- - The fractional part is converted into a string of digits until it
- is zero or the precision PREC is exhausted. This is done by
- repeated multiplication by 10. Leading zeros are suppressed (see
- note about LEADING_ZERO above) adjusting EXP appropriately.
-
- - If the remaining fractional part is non-zero, rounding is
- applied. This is done by computing the next digit and comparing
- it to 5: If it is greater than or equal to 5, the "number" (digit
- string) is "incremented by 1" -- ignoring the decimal point. This
- operation may overflow into the first character of the
- destination string which is reserved for this reason (and for
- storing the sign character). On overflow the string is moved by
- one character to free the first character.
-
- - For exponential formats, the decimal point is inserted after the
- first digit of the string.
-
- This algorithm is somewhat inaccurate. Letting the coprocessor
- convert the number to BCD should improve accuracy. Another approach
- is to do all the computations using 80-bit floating point numbers. */
-
- static void conv_real (char *dst, double x, int prec, int fixed, int force_dp,
- int *prec_out, int *exp_out)
- {
- double df, dt, di;
- char tmp[MAX_MANT+MAX_FRACT+10], c, *start;
- int i, exp, leading_zero;
-
- start = dst++;
- df = modf (x, &di);
- i = 0; exp = -1; leading_zero = !fixed;
- while (di != 0.0)
- {
- dt = modf (di / 10.0, &di);
- tmp[i++] = (char)(dt * 10.0 + 0.125) + '0';
- ++exp; leading_zero = FALSE;
- }
- if (fixed)
- {
- if (i == 0) *dst++ = '0';
- while (i != 0)
- *dst++ = tmp[--i];
- if (prec > 0 || force_dp) *dst++ = '.';
- }
- else
- while (i != 0 && prec > 0)
- {
- *dst++ = tmp[--i];
- --prec;
- }
- while (df != 0.0 && prec > 0)
- {
- df = modf (df * 10.0, &dt);
- c = (char)dt + '0';
- if (c != '0' || !leading_zero)
- {
- *dst++ = c; leading_zero = FALSE;
- --prec;
- }
- else
- --exp;
- }
- *dst = 0;
- if (df != 0.0)
- {
- df = modf (df * 10.0, &dt);
- c = (char)dt + '0';
- if (c >= '5')
- {
- *start = '0';
- while (dst != start)
- {
- --dst;
- if (*dst == '.') continue;
- if (*dst != '9')
- {
- ++*dst; break;
- }
- *dst = '0';
- }
- if (dst == start)
- {
- i = strlen (start);
- memmove (start+1, start, i+1);
- if (!fixed)
- {
- start[i] = 0; ++exp;
- }
- }
- }
- }
- if (!fixed)
- {
- memmove (start+3, start+2, strlen (start+1));
- start[2] = '.';
- }
- *prec_out = prec;
- *exp_out = exp;
- }
-