home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-03-08 | 18.0 KB | 1,046 lines |
- static char sccs_id[] = "@(#) print.c 3.3 " __DATE__ " HJR";
-
- /* print.c (c) Copyright 1990 H.Rogers */
-
- /* Implements __printf(buf,format,argp) which is called by
- * all printf() functions to perform formatted output. */
-
- /* A conversion is of the form "%[flags][width][.prec][size]function".
- * For full details RTFM for printf(3). */
-
- #include <stddef.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <stdarg.h>
- #include <stdio.h>
-
- __STDIOLIB__
-
- /* To avoid truncation PRBUFSIZ should be 256+.
- * This library will *not* buffer overflow unless PRBUFSIZ < 4.
- * ANSI requires at least 509 - 512 sounds sensible (SJC). */
-
- #define PRBUFSIZ 4096
-
-
- /* The floating point functions are the most efficient they can be without
- * resorting to assuming IEEE 'D' or some other internal representation. */
-
- /* Unfortunately most software FP units are *inaccurate* - it
- * is probably worth changing these constants for architectures
- * with built in hardware FP units - make MAXPREC larger &
- * FPERR smaller for higher quality. Note that MAXPREC = 16
- * corresponds to the limits of IEEE Packed Decimal format. */
-
- #define MAXPREC 16 /* max. decimal precision for %feEgG */
- #define FPERR 1e-14 /* cutoff value for zero test */
-
- /* It's probably best to leave POINT alone... It works. */
-
- #define POINT 0.01 /* offset to compensate for rounding errors */
-
-
- /* the prototypes for the output functions */
-
- static char *__p_nout (char *, va_list *);
-
- static char *__p_char (char *, va_list *);
- static char *__p_str (char *, va_list *);
- static char *__p_sdec (char *, va_list *);
- static char *__p_uoct (char *, va_list *);
- static char *__p_udec (char *, va_list *);
- static char *__p_uhex (char *, va_list *);
- static char *__p_ffix (char *, va_list *);
- static char *__p_fexp (char *, va_list *);
- static char *__p_fmin (char *, va_list *);
- static char *__p_ptr (char *, va_list *);
-
-
- /* the buffer __prpad has to be prior to __prbuf in memory -
- * __prjust() avoids having to do any buffer copying by being
- * able to back into the __prpad area from __prbuf */
-
- static char __prpad[(PRBUFSIZ << 1) + 2];
- static char *__prbuf = __prpad + PRBUFSIZ + 1;
-
-
- /* __prebuf is set to the start of the output buffer passed to __printf() and
- * is used by __p_nout() to calculate the number of characters output */
-
- static char *__prebuf;
-
-
- #define P_FLAG0 0x0001 /* '-' */
- #define P_FLAG1 0x0002 /* '0' */
- #define P_FLAG2 0x0004 /* '+' */
- #define P_FLAG3 0x0008 /* ' ' */
- #define P_FLAG4 0x0010 /* '#' */
- #define P_FLAG5 0x0020 /* 'h' */
- #define P_FLAG6 0x0040 /* 'l' */
- #define P_FLAG7 0x0080 /* 'L' */
-
- static int __prflag;
- static unsigned int __prwidth; /* width */
- static int __prprec; /* -1 = unspecified; precision */
- static char __prfc; /* function character */
-
- static signed char __prfnc[32] = /* conversions */
- {
- -1, -1,
- 1, /* %c */
- 3, /* %d */
- 9, /* %eE */
- 8, /* %f */
- 10, /* %gG */
- -1,
- 4, /* %i */
- -1, -1, -1, -1,
- 0, /* %n */
- 5, /* %o */
- 11, /* %p */
- -1, -1,
- 2, /* %s */
- -1,
- 6, /* %u */
- -1, -1,
- 7, /* %xX */
- -1, -1, -1, -1, -1, -1, -1, -1
- };
-
- /* __prfn[] is the array of output functions called by __printf() -
- * they return a pointer to the end of the string (which is *not*
- * necessarily 0 terminated) */
-
- static char *(*__prfn[]) (char *, va_list *) =
- {
- __p_nout, /* %n no. of characters output so far */
- __p_char, /* %c character */
- __p_str, /* %s string */
- __p_sdec, /* %d signed decimal */
- __p_sdec, /* %i signed decimal */
- __p_uoct, /* %o unsigned octal */
- __p_udec, /* %u unsigned decimal */
- __p_uhex, /* %xX unsigned hex */
- __p_ffix, /* %f fixed notation double */
- __p_fexp, /* %eE exponential notation double */
- __p_fmin, /* %gG minimised space notation double */
- __p_ptr /* %p unsigned hex value of pointer */
- };
-
-
- /* __printf() */
-
- int
- __printf (char *buf, const char *format, va_list ap)
- {
- register const char *s1 = format;
- register char *s2 = buf;
-
- __prebuf = buf; /* for __p_nout() */
-
- while (*s1)
- {
- if (*s1 != '%' || *++s1 == '%') /* left to right evaluation */
- {
- *s2++ = *s1++;
- continue;
- }
-
- { /* we now have a % conversion */
- register int i;
- char *s;
-
- /* looking for "%[flags][width][.prec][size]function" */
-
- /* set flags */
-
- __prflag = 0;
- flag:
- if (*s1 == '-')
- {
- __prflag |= P_FLAG0, s1++;
- goto flag;
- }
- if (*s1 == '0')
- {
- __prflag |= P_FLAG1, s1++;
- goto flag;
- }
- if (*s1 == '+')
- {
- __prflag |= P_FLAG2, s1++;
- goto flag;
- }
- if (*s1 == ' ')
- {
- __prflag |= P_FLAG3, s1++;
- goto flag;
- }
- if (*s1 == '#')
- {
- __prflag |= P_FLAG4, s1++;
- goto flag;
- }
-
- /* set __prwidth */
-
- __prwidth = 0;
- if (isdigit (*s1))
- {
- __prwidth = (unsigned int) strtoul (s1, &s, 0);
- s1 = s;
- }
- else
- {
- if (*s1 == '*')
- {
- __prwidth = va_arg (ap, int);
- s1++;
- }
- }
- if (__prwidth > PRBUFSIZ)
- __prwidth = PRBUFSIZ;
-
- /* set __prprec */
-
- __prprec = -1;
- if (*s1 == '.')
- {
- s1++;
- if (isdigit (*s1))
- {
- __prprec = (int) strtol (s1, &s, 0);
- s1 = s;
- }
- else
- {
- if (*s1 == '*')
- {
- __prprec = va_arg (ap, int);
- s1++;
- }
- }
- }
-
- /* set size */
-
- if (*s1 == 'h')
- __prflag |= P_FLAG5, s1++;
- else if (*s1 == 'l')
- __prflag |= P_FLAG6, s1++;
- else if (*s1 == 'L')
- __prflag |= P_FLAG7, s1++;
-
- /* call appropriate output function */
-
- i = __prfnc[(_tolower (*s1) - 'a') & 31];
-
- if (i >= 0)
- {
- __prfc = *s1;
- s2 = (*__prfn[i]) (s2, (va_list *) (&ap));
- }
-
- s1++;
- }
- }
-
- *s2 = 0;
-
- return ((size_t) (s2 - buf)); /* number of characters output */
- }
-
-
- /* __prjust() */
-
- /* __prjust() justifies the string at __prbuf according to __prwidth etc.
- * it's argument is the *end* of the string at __prbuf - it returns the
- * start of the justified string. */
-
- static char *
- __prjust (register char *e)
- {
- register char *s1, *s2;
- register char p;
-
- s1 = __prbuf;
-
- p = (__prflag & P_FLAG1) ? '0' : ' ';
-
- if (__prflag & P_FLAG0)
- {
- s2 = s1 + __prwidth;
- while (e < s2)
- *e++ = ' ';
- *e = 0;
- return (s1);
- }
- else
- {
- if ((e -= __prwidth) < s1)
- {
- s2 = e;
- while (e < s1)
- *e++ = p;
- return (s2);
- }
- return (s1);
- }
- }
-
- /* __prdec() */
-
- /* __prdec() is a subroutine (called by __p_...() functions) that writes
- * a decimal number <x> backwards from <e> towards <b>, digit by digit */
-
- static char *
- __prdec (register char *b, register char *e, register unsigned int x)
- {
- *e = 0;
- do
- {
- *--e = (x % 10) + '0';
- x /= 10;
- }
- while (x && e > b);
-
- return (e);
- }
-
- /* __prexp() */
-
- /* __prexp() normalises <x> to a number of the form n.nnnn...
- * and stores the exponent in *<e> */
-
- static double
- __prexp (register double x, register int *e)
- {
- register int h;
-
- h = 0;
-
- if (x != 0)
- {
- if (x >= 10)
- {
- register double l = 0, m = 10;
-
- while (x >= m)
- {
- l = m, m *= 10;
- h++;
- }
- if (l)
- x /= l;
- }
- else
- {
- while (x < 1)
- {
- x *= 10;
- h--;
- }
- }
- }
-
- /* x is now n.nnnn... */
-
- *e = h;
- return (x);
- }
-
-
- /* __p_nout() */
-
- static char *
- __p_nout (register char *s, register va_list * ap) /* %n */
- {
- if (__prflag & P_FLAG5)
- *va_arg (*ap, short *) = (short) (s - __prebuf);
- else if (__prflag & P_FLAG6)
- *va_arg (*ap, long *) = (long) (s - __prebuf);
- else
- *va_arg (*ap, int *) = (int) (s - __prebuf);
-
- return (s);
- }
-
- /* __p_char() */
-
- static char *
- __p_char (register char *s, register va_list * ap) /* %c */
- {
- register char *b;
-
- if (!__prwidth)
- *s++ = va_arg (*ap, char);
- else
- {
- b = __prbuf;
- *b++ = va_arg (*ap, char);
- *b = 0;
- b = __prjust (b);
- while (*s = *b)
- s++, b++;
- }
-
- return (s);
- }
-
- /* __p_str() */
-
- static char *
- __p_str (register char *s, register va_list * ap) /* %s */
- {
- register char *_s;
- register char *b, *e;
- register int p;
-
- p = (__prprec < 0) ? 0 : __prprec;
- if (p > PRBUFSIZ)
- p = PRBUFSIZ;
-
- _s = va_arg (*ap, char *);
-
- /* tolerance */
-
- if (!_s)
- _s = __null;
-
- /* fast output if no width specified */
-
- if (!__prwidth)
- {
- e = s + (p ? p : PRBUFSIZ);
- while ((s < e) && (*s = *_s))
- s++, _s++;
- return (s);
- }
-
- e = (b = __prbuf) + (p ? p : PRBUFSIZ);
-
- while (b < e && (*b = *_s))
- b++, _s++;
-
- *b = 0;
- b = __prjust (b);
-
- while (*s = *b)
- s++, b++;
-
- return (s);
- }
-
- /* __p_sdec() */
-
- static char *
- __p_sdec (register char *s, register va_list * ap) /* %d */
- {
- register int x, n;
- register char *b, *e;
- register int p;
-
- p = (__prprec < 0) ? 0 : __prprec;
- if (p > PRBUFSIZ - 2)
- p = PRBUFSIZ - 2;
-
- if (__prflag & P_FLAG5)
- n = (int) va_arg (*ap, short);
- else if (__prflag & P_FLAG6)
- n = (int) va_arg (*ap, long);
- else
- n = va_arg (*ap, int);
-
- x = (n < 0) ? (-n) : n;
-
- b = __prbuf;
- e = b + PRBUFSIZ;
-
- if ((__prflag & P_FLAG2) || (n < 0))
- *b++ = (n < 0) ? '-' : '+';
- else if (__prflag & P_FLAG3)
- *b++ = ' ';
-
- e = __prdec (b, e, (unsigned int) x);
-
- if (p)
- {
- x = p - strlen (e);
- while (x > 0)
- {
- *b++ = '0';
- x--;
- }
- }
-
- while (*b = *e)
- b++, e++;
-
- if (!__prwidth)
- b = __prbuf;
- else
- b = __prjust (b);
-
- while (*s = *b)
- s++, b++;
-
- return (s);
- }
-
- /* __p_uoct() */
-
- static char *
- __p_uoct (register char *s, register va_list * ap) /* %o */
- {
- register unsigned int x, n;
- register char *b, *e;
- register int p;
-
- p = (__prprec < 0) ? 0 : __prprec;
- if (p > PRBUFSIZ - 3)
- p = PRBUFSIZ - 3;
-
- if (__prflag & P_FLAG5)
- n = (unsigned int) va_arg (*ap, unsigned short);
- else if (__prflag & P_FLAG6)
- n = (unsigned int) va_arg (*ap, unsigned long);
- else
- n = va_arg (*ap, unsigned int);
-
- x = n;
-
- b = __prbuf;
- *(e = (b + PRBUFSIZ)) = 0;
-
- do
- {
- *--e = (x & 7) + '0';
- x >>= 3;
- }
- while (b < e && x);
-
- if (__prflag & P_FLAG2)
- *b++ = '+';
- else if (__prflag & P_FLAG3)
- *b++ = ' ';
-
- if (__prflag & P_FLAG4)
- *b++ = '0';
-
- if (p)
- {
- x = p - strlen (e);
- while (x > 0)
- {
- *b++ = '0';
- x--;
- }
- }
-
- while (*b = *e)
- b++, e++;
-
- if (!__prwidth)
- b = __prbuf;
- else
- b = __prjust (b);
-
- while (*s = *b)
- s++, b++;
-
- return (s);
- }
-
- /* __p_udec() */
-
- static char *
- __p_udec (register char *s, register va_list * ap) /* %u */
- {
- register unsigned int x, n;
- register char *b, *e;
- register int p;
-
- p = (__prprec < 0) ? 0 : __prprec;
- if (p > PRBUFSIZ - 2)
- p = PRBUFSIZ - 2;
-
- if (__prflag & P_FLAG5)
- n = (unsigned int) va_arg (*ap, unsigned short);
- else if (__prflag & P_FLAG6)
- n = (unsigned int) va_arg (*ap, unsigned long);
- else
- n = va_arg (*ap, unsigned int);
-
- x = n;
-
- b = __prbuf;
- e = b + PRBUFSIZ;
-
- if (__prflag & P_FLAG2)
- *b++ = '+';
- else if (__prflag & P_FLAG3)
- *b++ = ' ';
-
- e = __prdec (b, e, x);
-
- if (p)
- {
- x = p - strlen (e);
- while (x > 0)
- {
- *b++ = '0';
- x--;
- }
- }
-
- while (*b = *e)
- b++, e++;
-
- if (!__prwidth)
- b = __prbuf;
- else
- b = __prjust (b);
-
- while (*s = *b)
- s++, b++;
-
- return (s);
- }
-
- /* __p_uhex() */
-
- static char *
- __p_uhex (register char *s, register va_list * ap) /* %xX */
- {
- register unsigned int x, n;
- register char *b, *e;
- register char *hex;
- register int p;
-
- p = (__prprec < 0) ? 0 : __prprec;
- if (p > PRBUFSIZ - 4)
- p = PRBUFSIZ - 4;
-
- hex = (__prfc == 'X') ? "0123456789ABCDEF" : "0123456789abcdef";
-
- if (__prflag & P_FLAG5)
- n = (unsigned int) va_arg (*ap, unsigned short);
- else if (__prflag & P_FLAG6)
- n = (unsigned int) va_arg (*ap, unsigned long);
- else
- n = va_arg (*ap, unsigned int);
-
- x = n;
-
- b = __prbuf;
- *(e = (b + PRBUFSIZ)) = 0;
-
- do
- {
- *--e = hex[x & 15];
- x >>= 4;
- }
- while (b < e && x);
-
- if (__prflag & P_FLAG2)
- *b++ = '+';
- else if (__prflag & P_FLAG3)
- *b++ = ' ';
-
- if ((__prflag & P_FLAG4) && n)
- {
- *b++ = '0';
- *b++ = 'x';
- }
-
- if (p)
- {
- x = p - strlen (e);
- while (x > 0)
- {
- *b++ = '0';
- x--;
- }
- }
-
- while (*b = *e)
- b++, e++;
-
- if (!__prwidth)
- b = __prbuf;
- else
- b = __prjust (b);
-
- while (*s = *b)
- s++, b++;
-
- return (s);
- }
-
- /* __p_ffix() */
-
- static char *
- __p_ffix (register char *s, register va_list * ap) /* %f */
- {
- const double point = POINT;
- const double err = FPERR;
- register double x, n, w;
- register unsigned int i;
- int h;
- register int p, m;
- register char *b, *e;
-
- if (__prflag & P_FLAG7)
- n = (double) va_arg (*ap, long double);
- else
- n = va_arg (*ap, double);
-
- b = __prbuf;
- e = b + PRBUFSIZ;
- if ((__prflag & P_FLAG2) || (n < 0))
- *b++ = (n < 0) ? '-' : '+';
- else if (__prflag & P_FLAG3)
- *b++ = ' ';
-
- x = (n < 0) ? (-n) : n;
- x = __prexp (x, &h);
- w = 1;
- while (h < 0)
- {
- w *= 10;
- h++;
- }
- x /= w;
-
- /* integer part - ensure at least 1 digit */
-
- do
- {
- x = (x - (double) (i = (unsigned int) (x + point))) * 10;
- *b++ = i + '0';
- h--;
- }
- while (b < e && h >= 0 && x > err);
- while (b < e && h >= 0)
- {
- *b++ = '0';
- h--;
- }
-
- m = (__prprec < 0) ? 6 : __prprec;
- p = (m > MAXPREC) ? MAXPREC : m;
- m -= p;
-
- /* fractional part */
-
- if (p || (__prflag & P_FLAG4))
- {
- if (b < e)
- *b++ = '.';
-
- while (b < e && p && x > err)
- {
- x = (x - (double) (i = (unsigned int) (x + point))) * 10;
- *b++ = i + '0';
- p--;
- }
-
- while (p && b < e)
- {
- *b++ = '0';
- p--;
- }
- while (m && b < e)
- {
- *b++ = '0';
- m--;
- }
- }
-
- *b = 0;
-
- if (!__prwidth)
- b = __prbuf;
- else
- b = __prjust (b);
-
- while (*s = *b)
- s++, b++;
-
- return (s);
- }
-
- /* __p_fexp() */
-
- static char *
- __p_fexp (register char *s, register va_list * ap) /* %eE */
- {
- const double point = POINT;
- const double err = FPERR;
- register double x, n;
- register unsigned int i;
- int h;
- register int p, m;
- register char *b, *e;
-
- if (__prflag & P_FLAG7)
- n = (double) va_arg (*ap, long double);
- else
- n = va_arg (*ap, double);
-
- b = __prbuf;
- e = b + PRBUFSIZ;
- if ((__prflag & P_FLAG2) || (n < 0))
- *b++ = (n < 0) ? '-' : '+';
- else if (__prflag & P_FLAG3)
- *b++ = ' ';
-
- x = (n < 0) ? (-n) : n;
- x = __prexp (x, &h);
-
- m = (__prprec < 0) ? 6 : __prprec;
- p = (m > MAXPREC) ? MAXPREC : m;
- m -= p;
-
- /* 1 digit before decimal point */
-
- x = (x - (double) (i = (unsigned int) (x + point))) * 10;
- *b++ = i + '0';
-
- /* fractional part */
-
- if (p || (__prflag & P_FLAG4))
- {
- if (b < e)
- *b++ = '.';
-
- while (b < e && p && x > err)
- {
- x = (x - (double) (i = (unsigned int) (x + point))) * 10;
- *b++ = i + '0';
- p--;
- }
-
- while (p && b < e)
- {
- *b++ = '0';
- p--;
- }
- while (m && b < e)
- {
- *b++ = '0';
- m--;
- }
- }
-
- /* exponent */
-
- if (b < e)
- *b++ = __prfc;
- if (b < e)
- *b++ = (h < 0) ? '-' : '+';
-
- if (b < e)
- e = __prdec (b, e, (h < 0) ? (unsigned int) (-h) : (unsigned int) h);
-
- while (*b = *e)
- b++, e++;
-
- if (!__prwidth)
- b = __prbuf;
- else
- b = __prjust (b);
-
- while (*s = *b)
- s++, b++;
-
- return (s);
- }
-
- /* __p_fmin() */
-
- static char *
- __p_fmin (register char *s, register va_list * ap) /* %gG */
- {
- const double point = POINT;
- const double err = FPERR;
- register double x, n, w;
- register unsigned int i;
- int h;
- register int p, m;
- register char *b, *e;
-
- if (__prflag & P_FLAG7)
- n = (double) va_arg (*ap, long double);
- else
- n = va_arg (*ap, double);
-
- b = __prbuf;
- e = b + PRBUFSIZ;
- if ((__prflag & P_FLAG2) || (n < 0))
- *b++ = (n < 0) ? '-' : '+';
- else if (__prflag & P_FLAG3)
- *b++ = ' ';
-
- /* %gG count significant figures of precision, not decimal places.
- * For %e we decrement the precision (for the digit before the decimal point).
- * For %f we subtract the exponent from the precision. */
-
- m = (__prprec < 0) ? 6 : __prprec;
- p = (m > MAXPREC) ? MAXPREC : m;
- m -= p;
-
- x = (n < 0) ? (-n) : n;
- x = __prexp (x, &h);
-
- /* "The style used depends on the value converted; style %e will be used
- * only if the exponent resulting from the conversion is less than -4 or
- * greater than the precision." - ANSI X3J11 */
-
- if (h < -4 || h > p) /* %e */
- {
- if (p)
- p--; /* just in case */
-
- /* 1 digit before decimal point */
-
- x = (x - (double) (i = (unsigned int) (x + point))) * 10;
- *b++ = i + '0';
-
- /* fractional part */
-
- if (p || (__prflag & P_FLAG4))
- {
- if (b < e)
- *b++ = '.';
-
- while (b < e && p && x > err)
- {
- x = (x - (double) (i = (unsigned int) (x + point))) * 10;
- *b++ = i + '0';
- p--;
- }
-
- if (__prflag & P_FLAG4)
- {
- while (p && b < e)
- {
- *b++ = '0';
- p--;
- }
- while (m && b < e)
- {
- *b++ = '0';
- m--;
- }
- }
- else if (b < e) /* waste trailing 0s */
- {
- while (*--b == '0');
- b++;
- if (*--b != '.')
- b++;
- }
- }
-
- /* exponent */
-
- if (b < e)
- *b++ = __prfc - ('g' - 'e');
- if (b < e)
- *b++ = (h < 0) ? '-' : '+';
-
- if (b < e)
- e = __prdec (b, e, (h < 0) ? (unsigned int) (-h) : (unsigned int) h);
-
- while (*b = *e)
- b++, e++;
- }
- else
- /* %f */
- {
- w = 1;
- while (h < 0)
- {
- w *= 10;
- h++;
- }
- x /= w;
-
- p -= h;
-
- /* integer part - ensure at least 1 digit */
-
- do
- {
- x = (x - (double) (i = (unsigned int) (x + point))) * 10;
- *b++ = i + '0';
- h--;
- }
- while (b < e && h >= 0 && x > err);
- while (b < e && h >= 0)
- {
- *b++ = '0';
- h--;
- }
-
- /* fractional part */
-
- if (p || (__prflag & P_FLAG4))
- {
- if (b < e)
- *b++ = '.';
-
- while (b < e && p && x > err)
- {
- x = (x - (double) (i = (unsigned int) (x + point))) * 10;
- *b++ = i + '0';
- p--;
- }
-
- if (__prflag & P_FLAG4)
- {
- while (p && (b < e))
- {
- *b++ = '0';
- p--;
- }
- while (m && (b < e))
- {
- *b++ = '0';
- m--;
- }
- }
- else if (b < e) /* waste trailing 0s */
- {
- while (*--b == '0');
- b++;
- if (*--b != '.')
- b++;
- }
- }
-
- *b = 0;
- }
-
- if (!__prwidth)
- b = __prbuf;
- else
- b = __prjust (b);
-
- while (*s = *b)
- s++, b++;
-
- return (s);
- }
-
- /* __p_ptr() */
-
- static char *
- __p_ptr (register char *s, register va_list * ap) /* %p */
- {
- __prflag |= P_FLAG4;
- return (__p_uhex (s, ap));
- }
-