home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
OS2
/
EMXFIX04.ZIP
/
OUTPUT.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-24
|
23KB
|
873 lines
/* output.c (emx+gcc) -- Copyright (c) 1990-1994 by Eberhard Mattes */
#include <sys/emx.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <setjmp.h>
#include <float.h>
#include <limits.h>
#define DEFAULT_PREC 6
#define BEGIN do {
#define END } while (0)
#define PUTC(V,C) BEGIN \
if (putc (C, (V)->stream) == EOF) \
longjmp ((V)->jerror, 1); \
++(V)->count; \
END
#define STRLEN(S) (((S) == NULL) ? 0 : strlen (S))
/* This structure holds the local variables of _output() which are
passed to the various functions called by _output(). */
typedef struct
{
FILE *stream; /* Where output should go */
char minus; /* Non-zero if `-' flag present */
char plus; /* Non-zero if `+' flag present */
char blank; /* Non-zero if ` ' flag present */
char hash; /* Non-zero if `#' flag present */
char pad; /* Pad character (' ' or '0') */
int width; /* Field width (or 0 if none specified) */
int prec; /* Precision (or -1 if none specified) */
int lim; /* Number of significant digits */
int count; /* Number of characters printed */
jmp_buf jerror; /* Jump there on output error */
} olocal;
/* Print the first N characters of the string S. */
static void out_str (olocal *v, const char *s, int n)
{
if (n >= 16)
{
if (fwrite (s, 1, n, v->stream) != n)
longjmp (v->jerror, 1);
v->count += n;
}
else
while (n > 0)
{
PUTC (v, *s);
++s; --n;
}
}
/* Print the character C N times. */
static void out_pad (olocal *v, char c, int n)
{
char buf[256];
if (n > sizeof (buf))
{
int i;
/* Very big padding -- do 256 characters at a time. */
memset (buf, c, sizeof (buf));
while (n > 0)
{
i = sizeof (buf);
if (i > n)
i = n;
out_str (v, buf, i);
n -= i;
}
}
else if (n >= 16)
{
memset (buf, c, n);
out_str (v, buf, n);
}
else
while (n > 0)
{
PUTC (v, c);
--n;
}
}
/* Perform formatting for the "%c" and "%s" formats. The meaning of
the '0' flag is not defined by ANSI for "%c" and "%s"; we always
use blanks for padding. */
static void cvt_str (olocal *v, const char *str)
{
int str_len;
if (str == NULL)
{
/* Print "(null)" for the NULL pointer if the precision is big
enough or not specified. */
if (v->prec < 0 || v->prec >= 6)
str = "(null)";
else
str = "";
}
str_len = strlen (str);
/* Limit the number of characters printed by the precision. */
if (v->prec >= 0 && v->prec < str_len)
str_len = v->prec;
/* Print the string, using blanks for padding. */
if (str_len < v->width && !v->minus)
out_pad (v, ' ', v->width - str_len);
out_str (v, str, str_len);
if (str_len < v->width && v->minus)
out_pad (v, ' ', v->width - str_len);
}
/* Print and pad a number (which has already been converted into
strings). PFX is "0" for non-zero octal numbers, "0x" or "0X" for
non-zero hexadecimal numbers, "0." for floating point numbers in
[0.0,1.0) unless using exponential format, the significant digits
for large floating point numbers in the "%f" format, or NULL.
Insert LPAD0 zeros between PFX and STR. STR is the number. Insert
RPAD0 zeros between STR and XPS. XPS is the exponent (unless it is
NULL). IS_SIGNED is non-zero when printing a signed number. IS_NEG
is non-zero for negative numbers (the strings don't contain a
sign). */
static void cvt_number (olocal *v, const char *pfx, const char *str,
const char *xps, int lpad0, int rpad0,
int is_signed, int is_neg)
{
int sign, pfx_len, str_len, xps_len, min_len;
if (!is_signed) /* No sign for %u, %o and %x */
sign = EOF;
else if (is_neg)
sign = '-';
else if (v->plus) /* '+' overrides ' ' */
sign = '+';
else if (v->blank)
sign = ' ';
else
sign = EOF;
pfx_len = STRLEN (pfx);
str_len = strlen (str);
xps_len = STRLEN (xps);
if (lpad0 < 0)
lpad0 = 0;
if (rpad0 < 0)
rpad0 = 0;
/* Compute the minimum length required for printing the number. */
min_len = lpad0 + pfx_len + str_len + rpad0 + xps_len;
if (sign != EOF)
++min_len;
/* If padding with zeros is requested, increase LPAD0 to pad the
number on the left with zeros. Note that the `-' flag
(left-justify) turns off padding with zeros. */
if (v->pad == '0' && min_len < v->width)
{
lpad0 += v->width - min_len;
min_len = v->width;
}
/* Pad on the left with blanks. */
if (min_len < v->width && !v->minus)
out_pad (v, ' ', v->width - min_len);
/* Print the number. */
if (sign != EOF)
PUTC (v, sign);
out_str (v, pfx, pfx_len);
out_pad (v, '0', lpad0);
out_str (v, str, str_len);
out_pad (v, '0', rpad0);
out_str (v, xps, xps_len);
/* Pad on the right with blanks. */
if (min_len < v->width && v->minus)
out_pad (v, ' ', v->width - min_len);
}
/* Print and pad an integer (which has already been converted into the
string STR). PFX is "0" for non-zero octal numbers, "0x" or "0X"
for hexadecimal numbers, or NULL. ZERO is non-zero if the number
is zero. IS_NEG is non-zero if the number is negative (the string
STR doesn't contain a sign). */
static void cvt_integer (olocal *v, const char *pfx, const char *str,
int zero, int is_signed, int is_neg)
{
int lpad0;
if (zero && v->prec == 0) /* ANSI */
return;
if (v->prec >= 0) /* Ignore `0' if `-' is given for an integer */
v->pad = ' ';
lpad0 = v->prec - strlen (str);
cvt_number (v, pfx, str, NULL, lpad0, 0, is_signed, is_neg);
}
static void cvt_hex (olocal *v, char *str, char x, int zero)
{
if (x == 'X')
strupr (str);
cvt_integer (v, ((!zero && v->hash) ? "0x" : NULL),
str, zero, FALSE, FALSE);
}
static void cvt_hex_32 (olocal *v, unsigned n, char x)
{
char buf[9];
_ltoa (n, buf, 16);
cvt_hex (v, buf, x, n == 0);
}
static void cvt_hex_64 (olocal *v, unsigned long long n, char x)
{
char buf[17];
_ulltoa (n, buf, 16);
cvt_hex (v, buf, x, n == 0);
}
static void cvt_oct (olocal *v, char *str, int zero)
{
cvt_integer (v, ((!zero && v->hash) ? "0" : NULL),
str, zero, FALSE, FALSE);
}
static void cvt_oct_32 (olocal *v, unsigned n)
{
char buf[12];
_ltoa (n, buf, 8);
cvt_oct (v, buf, n == 0);
}
static void cvt_oct_64 (olocal *v, unsigned long long n)
{
char buf[23];
_ulltoa (n, buf, 8);
cvt_oct (v, buf, n == 0);
}
static void cvt_dec_32 (olocal *v, unsigned n, int is_signed, int is_neg)
{
char buf[11];
_ultoa (n, buf, 10);
cvt_integer (v, NULL, buf, n == 0, is_signed, is_neg);
}
static void cvt_dec_64 (olocal *v, unsigned long long n, int is_signed,
int is_neg)
{
char buf[21];
_ulltoa (n, buf, 10);
cvt_integer (v, NULL, buf, n == 0, is_signed, is_neg);
}
/* Print a floating point number (which has been turned into the digit
string DIGITS and the exponent XP) for the "%f" format. If IS_NEG
is non-zero, the number is negative. */
static void cvt_fixed_digits (olocal *v, char *digits, int xp, int is_neg,
int is_auto)
{
int lpad0, rpad0, len, frac;
/* We have to handle 9 cases (the examples are for DIG=4):
| Fmt | Number || digits | xp | pfx |lpad0|rpad0| Output
----+-----+-----------++--------+----+------+-----+-----+---------
(1) | .3f | 0.0001234 || "1234" | -4 | "0." | 0 | 3 | 0.000
(2) | .7f | 0.001234 || "1234" | -3 | "0." | 2 | 1 | 0.0012340
(3) | .3f | 0.001234 || "1234" | -3 | "0." | 2 | 0 | 0.001
(4) | .3f | 0.1234 || "1234" | -1 | "0." | 0 | 0 | 0.123
(5) | .3f | 1.234 || "1234" | 0 | N/A | N/A | 0 | 1.234
(6) | .1f | 12.34 || "1234" | 1 | N/A | N/A | 0 | 12.3
(7) | .3f | 123.4 || "1234" | 2 | N/A | N/A | 2 | 123.400
(8) | .3f | 1234.0 || "1234" | 3 | N/A | N/A | 3 | 1234.000
(9) | .3f | 123456.0 || "1235" | 5 |"1235"| 2 | 3 | 123500.000
*/
if (xp < 0)
{
/* Cases (1) through (4). We print "0." followed by
min (-xp - 1, v->prec)
zeros and the string of digit. */
lpad0 = -xp - 1; /* This is for cases (2) through (3) */
if (v->prec < lpad0)
lpad0 = v->prec; /* This is for case (1) */
/* Compute the number of zeros to append. */
rpad0 = v->prec - (lpad0 + DIG); /* This is for case (2) */
if (rpad0 < 0)
rpad0 = 0; /* This is for cases (1), (3) and (4) */
/* Truncate the string of digits according to the precision for
cases (1), (3) and (4). */
len = v->prec - lpad0;
if (len >= 0 && len < DIG)
digits[len] = 0;
if (is_auto)
{
rpad0 = 0;
_cvt_remove_zeros (digits, 0);
if (digits[0] == 0)
lpad0 = 0;
}
cvt_number (v, ((v->hash || digits[0] != 0 || lpad0 != 0) ? "0." : "0"),
digits, NULL, lpad0, rpad0, TRUE, is_neg);
}
else if (xp < DIG)
{
/* Cases (5) through (8). */
/* Compute the number of zeros to append and truncate the string
of digits. */
frac = DIG - xp - 1; /* Number of decimals (frac >= 0) */
rpad0 = v->prec - frac; /* For cases (5), (7) and (8) */
if (rpad0 < 0)
{
/* Case (6) */
digits[DIG + rpad0] = 0;
rpad0 = 0;
}
if (is_auto)
{
_cvt_remove_zeros (digits, xp + 1);
rpad0 = 0;
}
/* Insert the decimal point. */
if (v->hash || digits[xp + 1] != 0 || rpad0 != 0)
{
memmove (digits + xp + 2, digits + xp + 1, DIG - xp);
digits[xp + 1] = '.';
}
cvt_number (v, NULL, digits, NULL, 0, rpad0, TRUE, is_neg);
}
else
{
/* Case (9). */
lpad0 = xp - DIG + 1;
rpad0 = (is_auto ? 0 : v->prec);
cvt_number (v, digits, ((v->hash || rpad0 != 0) ? "." : ""),
NULL, lpad0, rpad0, TRUE, is_neg);
}
}
/* Print a floating point number (which has been turned into the digit
string DIGITS and the exponent XP) for the "%e" and "%g" formats.
If IS_NEG is non-zero, the number is negative. XP_CHAR is 'e' or
'E'. If IS_AUTO is non-zero, we should omit trailing zeros for
the "%g" format. */
static void cvt_exp_digits (olocal *v, char *digits, int xp, int is_neg,
char xp_char, int is_auto)
{
int i, rpad0;
char xps_buf[10];
xps_buf[0] = xp_char;
xps_buf[1] = '+';
if (xp < 0)
{
xps_buf[1] = '-';
xp = -xp;
}
i = 2;
if (xp >= 1000)
xps_buf[i++] = (char)((xp / 1000) % 10) + '0';
if (xp >= 100)
xps_buf[i++] = (char)((xp / 100) % 10) + '0';
xps_buf[i++] = (char)((xp / 10) % 10) + '0';
xps_buf[i++] = (char)(xp % 10) + '0';
xps_buf[i] = 0;
/* Insert decimal point. */
if (v->prec == 0 && !v->hash)
{
digits[1] = 0;
rpad0 = 0;
}
else
{
memmove (digits + 2, digits + 1, DIG);
digits[1] = '.';
if (v->prec >= DIG)
rpad0 = 1 + v->prec - DIG;
else
{
rpad0 = 0;
digits[2 + v->prec] = 0;
}
if (is_auto)
{
_cvt_remove_zeros (digits, 2);
if (digits[2] == 0)
digits[1] = 0;
rpad0 = 0;
}
}
cvt_number (v, NULL, digits, xps_buf, 0, rpad0, TRUE, is_neg);
}
/* Perform formatting for the "%f" format. */
static void cvt_fixed (olocal *v, DOUBLE x, int is_neg)
{
if (x == 0.0)
cvt_number (v, NULL, ((v->hash || v->prec > 0) ? "0." : "0"),
NULL, 0, v->prec, TRUE, is_neg);
else
{
char digits[DIG+2];
int xp;
xp = _cvt_float_decimal (x, digits);
_cvt_round (digits, &xp, xp + 1 + v->prec, v->lim);
cvt_fixed_digits (v, digits, xp, is_neg, FALSE);
}
}
/* Perform formatting for the "%e" format. XP_CHAR is 'e' or 'E'. */
static void cvt_exp (olocal *v, DOUBLE x, int is_neg, char xp_char)
{
if (x == 0.0)
{
static char xps_buf[] = "e+00";
xps_buf[0] = xp_char;
cvt_number (v, NULL, ((v->hash || v->prec > 0) ? "0." : "0"),
xps_buf, 0, v->prec, TRUE, FALSE);
}
else
{
char digits[DIG+2];
int xp;
xp = _cvt_float_decimal (x, digits);
_cvt_round (digits, &xp, v->prec + 1, v->lim);
cvt_exp_digits (v, digits, xp, is_neg, xp_char, FALSE);
}
}
/* Perform formatting for the "%g" format. XP_CHAR is 'e' or 'E'. */
static void cvt_auto (olocal *v, DOUBLE x, int is_neg, char xp_char)
{
/* A precision of zero is treated as a precision of 1. Note that
the precision defines the number of significant digits, not the
number of decimals! */
if (v->prec == 0)
v->prec = 1;
/* 0.0 is treated specially as _cvt_float_decimal etc. cannot handle
that case. */
if (x == 0.0)
cvt_number (v, NULL, (v->hash ? "0." : "0"), NULL,
0, (v->hash ? v->prec : 0), TRUE, FALSE);
else
{
char digits[DIG+2];
int xp;
/* Convert the number to decimal and round it according to the
precision (number of significant digits). */
xp = _cvt_float_decimal (x, digits);
_cvt_round (digits, &xp, v->prec, v->lim);
/* If the exponent (of the "%e" format) is less than -4 or
greater than or equal to the precision, use the "%e"
format. Otherwise, use the "%f" format. */
if (xp < -4 || xp >= v->prec)
{
/* Adjust the precision to indicate the number of
decimals. */
--v->prec;
/* Treat "%#g" like "%e" (except for the precision). "%g"
(without `#') removes trailing zeros. */
cvt_exp_digits (v, digits, xp, is_neg, xp_char, !v->hash);
}
else
{
/* Compute the number of decimals from the exponent and the
precision. We must not remove trailing zeros from the
integer part of the number! */
v->prec -= xp + 1;
if (v->prec < 0)
v->prec = 0;
cvt_fixed_digits (v, digits, xp, is_neg, !v->hash);
}
}
}
/* Print the floating point number X. LIM is the number of
significant digits of X (depending on the type of X), FMT is the
format character from the format string. */
static void cvt_float (olocal *v, DOUBLE x, int lim, char fmt)
{
const char *s;
int is_neg;
s = _cvt_nan (FXAM (x));
if (s != NULL)
{
v->prec = -1; /* TODO: Is this correct? */
cvt_str (v, s); /* TODO: Is this correct? */
return;
}
v->lim = lim;
if (v->prec < 0)
v->prec = DEFAULT_PREC;
is_neg = (x < 0.0);
if (is_neg)
x = -x;
switch (fmt)
{
case 'f':
cvt_fixed (v, x, is_neg);
break;
case 'g':
cvt_auto (v, x, is_neg, 'e');
break;
case 'G':
cvt_auto (v, x, is_neg, 'E');
break;
case 'e':
cvt_exp (v, x, is_neg, 'e');
break;
case 'E':
cvt_exp (v, x, is_neg, 'E');
break;
default:
abort ();
}
}
/* This is the working horse for printf() and friends. */
int _output (FILE *stream, const char *format, char *arg_ptr)
{
olocal v;
char size, cont;
/* Initialize local variables. */
v.stream = stream;
v.count = 0;
/* Return -1 on output error. */
if (setjmp (v.jerror) != 0)
return (-1);
/* Interpret the string. */
while (*format != 0)
if (*format != '%')
{
PUTC (&v, *format);
++format;
}
else if (format[1] == '%')
{
PUTC (&v, '%');
format += 2;
}
else
{
v.minus = v.plus = v.blank = v.hash = FALSE;
v.width = 0; v.prec = -1; size = 0; v.pad = ' ';
cont = TRUE;
do
{
++format;
switch (*format)
{
case '-':
v.minus = TRUE;
break;
case '+':
v.plus = TRUE;
break;
case '0':
v.pad = '0';
break;
case ' ':
v.blank = TRUE;
break;
case '#':
v.hash = TRUE;
break;
default:
cont = FALSE;
break;
}
} while (cont);
/* `-' overrides `0' */
if (v.minus)
v.pad = ' ';
/* Field width */
if (*format == '*')
{
++format;
v.width = va_arg (arg_ptr, int);
if (v.width < 0)
{
v.width = -v.width;
v.minus = TRUE;
}
}
else
while (*format >= '0' && *format <= '9')
{
v.width = v.width * 10 + (*format - '0');
++format;
}
/* Precision */
if (*format == '.')
{
++format;
if (*format == '*')
{
++format;
v.prec = va_arg (arg_ptr, int);
if (v.prec < 0)
v.prec = -1; /* We don't need this */
}
else
{
v.prec = 0;
while (*format >= '0' && *format <= '9')
{
v.prec = v.prec * 10 + (*format - '0');
++format;
}
}
}
/* Size */
if (*format == 'h' || *format == 'l' || *format == 'L')
{
size = *format++;
if (size == 'l' && *format == 'l')
{
size = 'L'; ++format;
}
}
/* Format */
switch (*format)
{
case 0:
return (v.count);
case 'n':
if (size == 'L')
{
long long *ptr = va_arg (arg_ptr, long long *);
*ptr = v.count;
}
else if (size == 'h')
{
short *ptr = va_arg (arg_ptr, short *);
*ptr = v.count;
}
else
{
int *ptr = va_arg (arg_ptr, int *);
*ptr = v.count;
}
break;
case 'c':
{
char c;
c = (char)va_arg (arg_ptr, int);
v.prec = 1;
cvt_str (&v, &c);
}
break;
case 's':
cvt_str (&v, va_arg (arg_ptr, const char *));
break;
case 'd':
case 'i':
if (size == 'L')
{
long long n = va_arg (arg_ptr, long long);
if (n < 0)
cvt_dec_64 (&v, -n, TRUE, TRUE);
else
cvt_dec_64 (&v, n, TRUE, FALSE);
}
else
{
int n = va_arg (arg_ptr, int);
if (size == 'h')
n = (short)n;
if (n < 0)
cvt_dec_32 (&v, -n, TRUE, TRUE);
else
cvt_dec_32 (&v, n, TRUE, FALSE);
}
break;
case 'u':
if (size == 'L')
cvt_dec_64 (&v, va_arg (arg_ptr, unsigned long long),
FALSE, FALSE);
else
{
unsigned n = va_arg (arg_ptr, unsigned);
if (size == 'h')
n = (unsigned short)n;
cvt_dec_32 (&v, n, FALSE, FALSE);
}
break;
case 'p':
v.hash = TRUE;
cvt_hex_32 (&v, va_arg (arg_ptr, unsigned), 'x');
break;
case 'x':
case 'X':
if (size == 'L')
cvt_hex_64 (&v, va_arg (arg_ptr, unsigned long long), *format);
else
{
unsigned n = va_arg (arg_ptr, unsigned);
if (size == 'h')
n = (unsigned short)n;
cvt_hex_32 (&v, n, *format);
}
break;
case 'o':
if (size == 'L')
cvt_oct_64 (&v, va_arg (arg_ptr, unsigned long long));
else
{
unsigned n = va_arg (arg_ptr, unsigned);
if (size == 'h')
n = (unsigned short)n;
cvt_oct_32 (&v, n);
}
break;
case 'g':
case 'G':
case 'e':
case 'E':
case 'f':
if (size == 'L')
{
DOUBLE x = va_arg (arg_ptr, long double);
cvt_float (&v, x, DIG, *format);
}
else
{
double x = va_arg (arg_ptr, double);
cvt_float (&v, x, DBL_DIG, *format);
}
break;
default:
/* TODO: print the last letter only or all the characters? */
out_str (&v, format, 1);
break;
}
++format;
}
/* Return the number of characters printed. */
return (v.count);
}