146076Sbostic /*- 246076Sbostic * Copyright (c) 1990 The Regents of the University of California. 334233Sbostic * All rights reserved. 434226Sbostic * 546076Sbostic * This code is derived from software contributed to Berkeley by 646076Sbostic * Chris Torek. 746076Sbostic * 842631Sbostic * %sccs.include.redist.c% 934226Sbostic */ 1034226Sbostic 1134233Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*46646Sdonn static char sccsid[] = "@(#)vfprintf.c 5.46 (Berkeley) 02/24/91"; 1334233Sbostic #endif /* LIBC_SCCS and not lint */ 1434233Sbostic 1546076Sbostic /* 1646076Sbostic * Actual printf innards. 1746076Sbostic * 1846076Sbostic * This code is large and complicated... 1946076Sbostic */ 2046076Sbostic 2134261Sbostic #include <sys/types.h> 2246611Sbostic #include <math.h> 2346076Sbostic #include <stdio.h> 2446076Sbostic #include <string.h> 2546076Sbostic #if __STDC__ 2646076Sbostic #include <stdarg.h> 2746076Sbostic #else 2834233Sbostic #include <varargs.h> 2946076Sbostic #endif 3046076Sbostic #include "local.h" 3146076Sbostic #include "fvwrite.h" 3234226Sbostic 3346076Sbostic /* 3446076Sbostic * Define FLOATING_POINT to get floating point. 3546076Sbostic * Define CSH to get a csh-specific version (grr). 3646076Sbostic */ 3746076Sbostic #ifndef CSH 3846076Sbostic #define FLOATING_POINT 3946076Sbostic #endif 4034226Sbostic 4146076Sbostic /* end of configuration stuff */ 4234624Sbostic 4346076Sbostic 4446076Sbostic #ifdef CSH 4546076Sbostic /* 4646076Sbostic * C shell hacks. Ick, gag. 4746076Sbostic */ 4846076Sbostic #undef BUFSIZ 4946076Sbostic #include "sh.h" 5046076Sbostic 5146180Storek #if __STDC__ 5246180Storek int 5346180Storek printf(const char *fmt, ...) { 5446180Storek FILE f; 55*46646Sdonn va_list ap; 56*46646Sdonn int ret; 5746180Storek 58*46646Sdonn va_start(ap, fmt); 5946180Storek f._flags = __SWR; 60*46646Sdonn f._write = NULL; 61*46646Sdonn ret = vfprintf(&f, fmt, ap); 62*46646Sdonn va_end(ap); 63*46646Sdonn return ret; 6446180Storek } 6546180Storek #else 6646180Storek int 6746076Sbostic printf(fmt, args) 6846076Sbostic char *fmt; 6946076Sbostic { 7046076Sbostic FILE f; 7146076Sbostic 7246076Sbostic f._flags = __SWR; 7346442Storek f._write = NULL; 7446076Sbostic return (vfprintf(&f, fmt, &args)); 7546076Sbostic } 7646180Storek #endif 7746076Sbostic 7846180Storek int 7946442Storek __sprint(fp, uio) 8046442Storek FILE *fp; 8146076Sbostic register struct __suio *uio; 8246076Sbostic { 8346076Sbostic register char *p; 8446076Sbostic register int n, ch, iovcnt; 8546442Storek register struct __siov *iov; 8646076Sbostic 8746442Storek /* must allow sprintf to work, might as well allow others too */ 8846442Storek if (fp->_write || fp->_flags & __SSTR) { 8946442Storek if (uio->uio_resid == 0) { 9046442Storek uio->uio_iovcnt = 0; 9146442Storek return (0); 9246442Storek } 9346442Storek n = __sfvwrite(fp, uio); 9446442Storek uio->uio_resid = 0; 9546442Storek uio->uio_iovcnt = 0; 9646442Storek return (n); 9746442Storek } 9846442Storek iov = uio->uio_iov; 9946076Sbostic for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) { 10046076Sbostic for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) { 10146076Sbostic #ifdef CSHPUTCHAR 10246076Sbostic ch = *p++; 10346076Sbostic CSHPUTCHAR; /* this horrid macro uses `ch' */ 10446076Sbostic #else 10546076Sbostic #undef putchar 10646076Sbostic putchar(*p++); 10746076Sbostic #endif 10846076Sbostic } 10946076Sbostic } 11046076Sbostic uio->uio_resid = 0; 11146076Sbostic uio->uio_iovcnt = 0; 11246076Sbostic return (0); 11346076Sbostic } 11446076Sbostic 11546076Sbostic #else /* CSH */ 11646076Sbostic 11746076Sbostic /* 11846076Sbostic * Flush out all the vectors defined by the given uio, 11946076Sbostic * then reset it so that it can be reused. 12046076Sbostic */ 12146180Storek static int 12246076Sbostic __sprint(fp, uio) 12346076Sbostic FILE *fp; 12446076Sbostic register struct __suio *uio; 12546076Sbostic { 12646076Sbostic register int err; 12746076Sbostic 12846076Sbostic if (uio->uio_resid == 0) { 12946076Sbostic uio->uio_iovcnt = 0; 13046076Sbostic return (0); 13146076Sbostic } 13246076Sbostic err = __sfvwrite(fp, uio); 13346076Sbostic uio->uio_resid = 0; 13446076Sbostic uio->uio_iovcnt = 0; 13546076Sbostic return (err); 13646076Sbostic } 13746076Sbostic 13846076Sbostic /* 13946076Sbostic * Helper function for `fprintf to unbuffered unix file': creates a 14046076Sbostic * temporary buffer. We only work on write-only files; this avoids 14146076Sbostic * worries about ungetc buffers and so forth. 14246076Sbostic */ 14346180Storek static int 14446076Sbostic __sbprintf(fp, fmt, ap) 14546076Sbostic register FILE *fp; 14646180Storek const char *fmt; 14746076Sbostic va_list ap; 14846076Sbostic { 14946076Sbostic int ret; 15046076Sbostic FILE fake; 15146076Sbostic unsigned char buf[BUFSIZ]; 15246076Sbostic 15346076Sbostic /* copy the important variables */ 15446076Sbostic fake._flags = fp->_flags & ~__SNBF; 15546076Sbostic fake._file = fp->_file; 15646076Sbostic fake._cookie = fp->_cookie; 15746076Sbostic fake._write = fp->_write; 15846076Sbostic 15946076Sbostic /* set up the buffer */ 16046076Sbostic fake._bf._base = fake._p = buf; 16146076Sbostic fake._bf._size = fake._w = sizeof(buf); 16246076Sbostic fake._lbfsize = 0; /* not actually used, but Just In Case */ 16346076Sbostic 16446076Sbostic /* do the work, then copy any error status */ 16546076Sbostic ret = vfprintf(&fake, fmt, ap); 16646076Sbostic if (ret >= 0 && fflush(&fake)) 16746076Sbostic ret = EOF; 16846076Sbostic if (fake._flags & __SERR) 16946076Sbostic fp->_flags |= __SERR; 17046076Sbostic return (ret); 17146076Sbostic } 17246076Sbostic 17346076Sbostic #endif /* CSH */ 17446076Sbostic 17546076Sbostic 17646076Sbostic #ifdef FLOATING_POINT 17746180Storek #include "floatio.h" 17846076Sbostic 17934328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 18046076Sbostic #define DEFPREC 6 18134328Sbostic 18246076Sbostic static int cvt(); 18346126Storek #if defined(hp300) || defined(sparc) 18446126Storek static char *isspecial(); 18546126Storek #endif 18634236Sbostic 18746076Sbostic #else /* no FLOATING_POINT */ 18834235Sbostic 18946076Sbostic #define BUF 40 19034331Sbostic 19146076Sbostic #endif /* FLOATING_POINT */ 19234318Sbostic 19346076Sbostic 19446076Sbostic /* 19546076Sbostic * Macros for converting digits to letters and vice versa 19646076Sbostic */ 19746076Sbostic #define to_digit(c) ((c) - '0') 19846076Sbostic #define is_digit(c) ((unsigned)to_digit(c) <= 9) 19946076Sbostic #define to_char(n) ((n) + '0') 20046076Sbostic 20146076Sbostic /* 20246076Sbostic * Flags used during conversion. 20346076Sbostic */ 20434318Sbostic #define LONGINT 0x01 /* long integer */ 20534318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 20634318Sbostic #define SHORTINT 0x04 /* short integer */ 20734318Sbostic #define ALT 0x08 /* alternate form */ 20834318Sbostic #define LADJUST 0x10 /* left adjustment */ 20934427Sbostic #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 21034427Sbostic #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 21134318Sbostic 21246180Storek int 21346076Sbostic vfprintf(fp, fmt0, ap) 21446076Sbostic FILE *fp; 21546180Storek const char *fmt0; 21646076Sbostic #if tahoe 21746076Sbostic register /* technically illegal, since we do not know what type va_list is */ 21846076Sbostic #endif 21946076Sbostic va_list ap; 22034226Sbostic { 22146076Sbostic register char *fmt; /* format string */ 22234427Sbostic register int ch; /* character from fmt */ 22346076Sbostic register int n; /* handy integer (short term usage) */ 22446076Sbostic register char *cp; /* handy char pointer (short term usage) */ 22546076Sbostic register struct __siov *iovp;/* for PRINT macro */ 22646076Sbostic register int flags; /* flags as above */ 22746076Sbostic int ret; /* return value accumulator */ 22846076Sbostic int width; /* width from format (%8d), or 0 */ 22946076Sbostic int prec; /* precision from format (%.3d), or -1 */ 23046076Sbostic char sign; /* sign prefix (' ', '+', '-', or \0) */ 23146076Sbostic #ifdef FLOATING_POINT 23246076Sbostic char softsign; /* temporary negative sign for floats */ 23334427Sbostic double _double; /* double precision arguments %[eEfgG] */ 23446076Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 23546076Sbostic #endif 23634427Sbostic u_long _ulong; /* integer arguments %[diouxX] */ 23746076Sbostic enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 23846076Sbostic int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 23934624Sbostic int fieldsz; /* field size expanded by sign, etc */ 24046076Sbostic int realsz; /* field size expanded by dprec */ 24134427Sbostic int size; /* size of converted field or string */ 24246076Sbostic char *xdigs; /* digits for [xX] conversion */ 24346076Sbostic #define NIOV 8 24446076Sbostic struct __suio uio; /* output information: summary */ 24546076Sbostic struct __siov iov[NIOV];/* ... and individual io vectors */ 24634427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 24746076Sbostic char ox[2]; /* space for 0x hex-prefix */ 24834226Sbostic 24946076Sbostic /* 25046076Sbostic * Choose PADSIZE to trade efficiency vs size. If larger 25146076Sbostic * printf fields occur frequently, increase PADSIZE (and make 25246076Sbostic * the initialisers below longer). 25346076Sbostic */ 25446076Sbostic #define PADSIZE 16 /* pad chunk size */ 25546076Sbostic static char blanks[PADSIZE] = 25646076Sbostic {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 25746076Sbostic static char zeroes[PADSIZE] = 25846076Sbostic {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 25946076Sbostic 26046076Sbostic /* 26146076Sbostic * BEWARE, these `goto error' on error, and PAD uses `n'. 26246076Sbostic */ 26346076Sbostic #define PRINT(ptr, len) { \ 26446076Sbostic iovp->iov_base = (ptr); \ 26546076Sbostic iovp->iov_len = (len); \ 26646076Sbostic uio.uio_resid += (len); \ 26746076Sbostic iovp++; \ 26846076Sbostic if (++uio.uio_iovcnt >= NIOV) { \ 26946076Sbostic if (__sprint(fp, &uio)) \ 27046076Sbostic goto error; \ 27146076Sbostic iovp = iov; \ 27246076Sbostic } \ 27346076Sbostic } 27446076Sbostic #define PAD(howmany, with) { \ 27546076Sbostic if ((n = (howmany)) > 0) { \ 27646076Sbostic while (n > PADSIZE) { \ 27746076Sbostic PRINT(with, PADSIZE); \ 27846076Sbostic n -= PADSIZE; \ 27946076Sbostic } \ 28046076Sbostic PRINT(with, n); \ 28146076Sbostic } \ 28246076Sbostic } 28346076Sbostic #define FLUSH() { \ 28446076Sbostic if (uio.uio_resid && __sprint(fp, &uio)) \ 28546076Sbostic goto error; \ 28646076Sbostic uio.uio_iovcnt = 0; \ 28746076Sbostic iovp = iov; \ 28846076Sbostic } 28946076Sbostic 29046076Sbostic /* 29146076Sbostic * To extend shorts properly, we need both signed and unsigned 29246076Sbostic * argument extraction methods. 29346076Sbostic */ 29446076Sbostic #define SARG() \ 29546076Sbostic (flags&LONGINT ? va_arg(ap, long) : \ 29646076Sbostic flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ 29746076Sbostic (long)va_arg(ap, int)) 29846076Sbostic #define UARG() \ 29946076Sbostic (flags&LONGINT ? va_arg(ap, u_long) : \ 30046076Sbostic flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ 30146076Sbostic (u_long)va_arg(ap, u_int)) 30246076Sbostic 30346076Sbostic #ifndef CSH 30446076Sbostic /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 30546076Sbostic if (cantwrite(fp)) 30634428Sbostic return (EOF); 30734428Sbostic 30846076Sbostic /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 30946076Sbostic if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 31046076Sbostic fp->_file >= 0) 31146076Sbostic return (__sbprintf(fp, fmt0, ap)); 31246076Sbostic #endif /* CSH */ 31346076Sbostic 31446076Sbostic fmt = (char *)fmt0; 31546076Sbostic uio.uio_iov = iovp = iov; 31646076Sbostic uio.uio_resid = 0; 31746076Sbostic uio.uio_iovcnt = 0; 31846076Sbostic ret = 0; 31946076Sbostic 32046076Sbostic /* 32146076Sbostic * Scan the format for conversions (`%' character). 32246076Sbostic */ 32346076Sbostic for (;;) { 32446076Sbostic for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 32546076Sbostic /* void */; 32646076Sbostic if ((n = fmt - cp) != 0) { 32746076Sbostic PRINT(cp, n); 32846076Sbostic ret += n; 32946076Sbostic } 33046076Sbostic if (ch == '\0') 33146076Sbostic goto done; 33246076Sbostic fmt++; /* skip over '%' */ 33346076Sbostic 33446076Sbostic flags = 0; 33546076Sbostic dprec = 0; 33646076Sbostic #ifdef FLOATING_POINT 33746076Sbostic fpprec = 0; 33834318Sbostic #endif 33946076Sbostic width = 0; 34034233Sbostic prec = -1; 34134318Sbostic sign = '\0'; 34234226Sbostic 34346076Sbostic rflag: ch = *fmt++; 34446076Sbostic reswitch: switch (ch) { 34534318Sbostic case ' ': 34634669Sbostic /* 34734669Sbostic * ``If the space and + flags both appear, the space 34834669Sbostic * flag will be ignored.'' 34934669Sbostic * -- ANSI X3J11 35034669Sbostic */ 35134669Sbostic if (!sign) 35234669Sbostic sign = ' '; 35334318Sbostic goto rflag; 35434233Sbostic case '#': 35534318Sbostic flags |= ALT; 35634318Sbostic goto rflag; 35734233Sbostic case '*': 35834235Sbostic /* 35934235Sbostic * ``A negative field width argument is taken as a 36046076Sbostic * - flag followed by a positive field width.'' 36134235Sbostic * -- ANSI X3J11 36234235Sbostic * They don't exclude field widths read from args. 36334235Sbostic */ 36446076Sbostic if ((width = va_arg(ap, int)) >= 0) 36534318Sbostic goto rflag; 36634235Sbostic width = -width; 36734427Sbostic /* FALLTHROUGH */ 36834235Sbostic case '-': 36934318Sbostic flags |= LADJUST; 37034318Sbostic goto rflag; 37134233Sbostic case '+': 37234314Sbostic sign = '+'; 37334318Sbostic goto rflag; 37434233Sbostic case '.': 37546076Sbostic if ((ch = *fmt++) == '*') { 37646076Sbostic n = va_arg(ap, int); 37746076Sbostic prec = n < 0 ? -1 : n; 37846076Sbostic goto rflag; 37934226Sbostic } 38046076Sbostic n = 0; 38146076Sbostic while (is_digit(ch)) { 38246076Sbostic n = 10 * n + to_digit(ch); 38346076Sbostic ch = *fmt++; 38446076Sbostic } 38534318Sbostic prec = n < 0 ? -1 : n; 38646076Sbostic goto reswitch; 38734233Sbostic case '0': 38834427Sbostic /* 38934427Sbostic * ``Note that 0 is taken as a flag, not as the 39034427Sbostic * beginning of a field width.'' 39134427Sbostic * -- ANSI X3J11 39234427Sbostic */ 39334427Sbostic flags |= ZEROPAD; 39434427Sbostic goto rflag; 39534233Sbostic case '1': case '2': case '3': case '4': 39634233Sbostic case '5': case '6': case '7': case '8': case '9': 39734318Sbostic n = 0; 39834233Sbostic do { 39946076Sbostic n = 10 * n + to_digit(ch); 40046076Sbostic ch = *fmt++; 40146076Sbostic } while (is_digit(ch)); 40234318Sbostic width = n; 40346076Sbostic goto reswitch; 40446076Sbostic #ifdef FLOATING_POINT 40534235Sbostic case 'L': 40634329Sbostic flags |= LONGDBL; 40734318Sbostic goto rflag; 40846076Sbostic #endif 40934235Sbostic case 'h': 41034318Sbostic flags |= SHORTINT; 41134318Sbostic goto rflag; 41234233Sbostic case 'l': 41334318Sbostic flags |= LONGINT; 41434318Sbostic goto rflag; 41534314Sbostic case 'c': 41646076Sbostic *(cp = buf) = va_arg(ap, int); 41734314Sbostic size = 1; 41834427Sbostic sign = '\0'; 41946076Sbostic break; 42034624Sbostic case 'D': 42134624Sbostic flags |= LONGINT; 42234624Sbostic /*FALLTHROUGH*/ 42334314Sbostic case 'd': 42434318Sbostic case 'i': 42546076Sbostic _ulong = SARG(); 42634318Sbostic if ((long)_ulong < 0) { 42734318Sbostic _ulong = -_ulong; 42834314Sbostic sign = '-'; 42934241Sbostic } 43046076Sbostic base = DEC; 43134327Sbostic goto number; 43246076Sbostic #ifdef FLOATING_POINT 43334261Sbostic case 'e': 43434236Sbostic case 'E': 43534235Sbostic case 'f': 43634261Sbostic case 'g': 43734243Sbostic case 'G': 43846076Sbostic _double = va_arg(ap, double); 43946126Storek #if defined(hp300) || defined(sparc) 44046126Storek /* do this before tricky precision changes */ 44146126Storek if ((cp = isspecial(_double, &sign)) != NULL) { 44246126Storek size = strlen(cp); 44346126Storek break; 44446126Storek } 44546126Storek #endif 44634328Sbostic /* 44734669Sbostic * don't do unrealistic precision; just pad it with 44834669Sbostic * zeroes later, so buffer size stays rational. 44934328Sbostic */ 45034328Sbostic if (prec > MAXFRACT) { 45146076Sbostic if (ch != 'g' && ch != 'G' || (flags&ALT)) 45234475Sbostic fpprec = prec - MAXFRACT; 45334328Sbostic prec = MAXFRACT; 45446076Sbostic } else if (prec == -1) 45534624Sbostic prec = DEFPREC; 45634669Sbostic /* 45746076Sbostic * cvt may have to round up before the "start" of 45846076Sbostic * its buffer, i.e. ``intf("%.2f", (double)9.999);''; 45946076Sbostic * if the first character is still NUL, it did. 46046076Sbostic * softsign avoids negative 0 if _double < 0 but 46146076Sbostic * no significant digits will be shown. 46234669Sbostic */ 46346076Sbostic cp = buf; 46446076Sbostic *cp = '\0'; 46546076Sbostic size = cvt(_double, prec, flags, &softsign, ch, 46646076Sbostic cp, buf + sizeof(buf)); 46734669Sbostic if (softsign) 46834669Sbostic sign = '-'; 46946076Sbostic if (*cp == '\0') 47046076Sbostic cp++; 47146076Sbostic break; 47246076Sbostic #endif /* FLOATING_POINT */ 47334235Sbostic case 'n': 47434427Sbostic if (flags & LONGINT) 47546076Sbostic *va_arg(ap, long *) = ret; 47634427Sbostic else if (flags & SHORTINT) 47746076Sbostic *va_arg(ap, short *) = ret; 47834318Sbostic else 47946076Sbostic *va_arg(ap, int *) = ret; 48046076Sbostic continue; /* no output */ 48134624Sbostic case 'O': 48234624Sbostic flags |= LONGINT; 48334624Sbostic /*FALLTHROUGH*/ 48434226Sbostic case 'o': 48546076Sbostic _ulong = UARG(); 48646076Sbostic base = OCT; 48734327Sbostic goto nosign; 48834235Sbostic case 'p': 48934320Sbostic /* 49034321Sbostic * ``The argument shall be a pointer to void. The 49134321Sbostic * value of the pointer is converted to a sequence 49234321Sbostic * of printable characters, in an implementation- 49334321Sbostic * defined manner.'' 49434321Sbostic * -- ANSI X3J11 49534320Sbostic */ 49634427Sbostic /* NOSTRICT */ 49746076Sbostic _ulong = (u_long)va_arg(ap, void *); 49846076Sbostic base = HEX; 49946076Sbostic xdigs = "0123456789abcdef"; 50046076Sbostic flags |= HEXPREFIX; 50146076Sbostic ch = 'x'; 50234327Sbostic goto nosign; 50334226Sbostic case 's': 50446076Sbostic if ((cp = va_arg(ap, char *)) == NULL) 50546076Sbostic cp = "(null)"; 50634321Sbostic if (prec >= 0) { 50734321Sbostic /* 50834321Sbostic * can't use strlen; can only look for the 50934321Sbostic * NUL in the first `prec' characters, and 51034321Sbostic * strlen() will go further. 51134321Sbostic */ 51246076Sbostic char *p = memchr(cp, 0, prec); 51334321Sbostic 51446076Sbostic if (p != NULL) { 51546076Sbostic size = p - cp; 51634321Sbostic if (size > prec) 51734321Sbostic size = prec; 51834427Sbostic } else 51934321Sbostic size = prec; 52034427Sbostic } else 52146076Sbostic size = strlen(cp); 52234427Sbostic sign = '\0'; 52346076Sbostic break; 52434624Sbostic case 'U': 52534624Sbostic flags |= LONGINT; 52634624Sbostic /*FALLTHROUGH*/ 52734226Sbostic case 'u': 52846076Sbostic _ulong = UARG(); 52946076Sbostic base = DEC; 53034327Sbostic goto nosign; 53134226Sbostic case 'X': 53246076Sbostic xdigs = "0123456789ABCDEF"; 53346076Sbostic goto hex; 53434226Sbostic case 'x': 53546076Sbostic xdigs = "0123456789abcdef"; 53646076Sbostic hex: _ulong = UARG(); 53746076Sbostic base = HEX; 53834326Sbostic /* leading 0x/X only if non-zero */ 53934427Sbostic if (flags & ALT && _ulong != 0) 54034427Sbostic flags |= HEXPREFIX; 54134327Sbostic 54234327Sbostic /* unsigned conversions */ 54334427Sbostic nosign: sign = '\0'; 54434326Sbostic /* 54534330Sbostic * ``... diouXx conversions ... if a precision is 54634330Sbostic * specified, the 0 flag will be ignored.'' 54734330Sbostic * -- ANSI X3J11 54834330Sbostic */ 54934427Sbostic number: if ((dprec = prec) >= 0) 55034427Sbostic flags &= ~ZEROPAD; 55134427Sbostic 55234330Sbostic /* 55334326Sbostic * ``The result of converting a zero value with an 55434326Sbostic * explicit precision of zero is no characters.'' 55534326Sbostic * -- ANSI X3J11 55634326Sbostic */ 55746076Sbostic cp = buf + BUF; 55834427Sbostic if (_ulong != 0 || prec != 0) { 55946076Sbostic /* 56046076Sbostic * unsigned mod is hard, and unsigned mod 56146076Sbostic * by a constant is easier than that by 56246076Sbostic * a variable; hence this switch. 56346076Sbostic */ 56446076Sbostic switch (base) { 56546076Sbostic case OCT: 56646076Sbostic do { 56746076Sbostic *--cp = to_char(_ulong & 7); 56846076Sbostic _ulong >>= 3; 56946076Sbostic } while (_ulong); 57046076Sbostic /* handle octal leading 0 */ 57146076Sbostic if (flags & ALT && *cp != '0') 57246076Sbostic *--cp = '0'; 57346076Sbostic break; 57434327Sbostic 57546076Sbostic case DEC: 57646076Sbostic /* many numbers are 1 digit */ 57746076Sbostic while (_ulong >= 10) { 57846076Sbostic *--cp = to_char(_ulong % 10); 57946076Sbostic _ulong /= 10; 58046076Sbostic } 58146076Sbostic *--cp = to_char(_ulong); 58246076Sbostic break; 58334327Sbostic 58446076Sbostic case HEX: 58546076Sbostic do { 58646076Sbostic *--cp = xdigs[_ulong & 15]; 58746076Sbostic _ulong >>= 4; 58846076Sbostic } while (_ulong); 58946076Sbostic break; 59034327Sbostic 59146076Sbostic default: 59246076Sbostic cp = "bug in vfprintf: bad base"; 59346180Storek size = strlen(cp); 59446076Sbostic goto skipsize; 59546076Sbostic } 59634327Sbostic } 59746076Sbostic size = buf + BUF - cp; 59846076Sbostic skipsize: 59934226Sbostic break; 60046076Sbostic default: /* "%?" prints ?, unless ? is NUL */ 60146076Sbostic if (ch == '\0') 60246076Sbostic goto done; 60346076Sbostic /* pretend it was %c with argument ch */ 60446076Sbostic cp = buf; 60546076Sbostic *cp = ch; 60646076Sbostic size = 1; 60746076Sbostic sign = '\0'; 60846076Sbostic break; 60934226Sbostic } 61046076Sbostic 61146076Sbostic /* 61246076Sbostic * All reasonable formats wind up here. At this point, 61346076Sbostic * `cp' points to a string which (if not flags&LADJUST) 61446076Sbostic * should be padded out to `width' places. If 61546076Sbostic * flags&ZEROPAD, it should first be prefixed by any 61646076Sbostic * sign or other prefix; otherwise, it should be blank 61746076Sbostic * padded before the prefix is emitted. After any 61846076Sbostic * left-hand padding and prefixing, emit zeroes 61946076Sbostic * required by a decimal [diouxX] precision, then print 62046076Sbostic * the string proper, then emit zeroes required by any 62146076Sbostic * leftover floating precision; finally, if LADJUST, 62246076Sbostic * pad with blanks. 62346076Sbostic */ 62446076Sbostic 62546076Sbostic /* 62646076Sbostic * compute actual size, so we know how much to pad. 62746076Sbostic * fieldsz excludes decimal prec; realsz includes it 62846076Sbostic */ 62946076Sbostic #ifdef FLOATING_POINT 63046076Sbostic fieldsz = size + fpprec; 63146076Sbostic #else 63246076Sbostic fieldsz = size; 63346076Sbostic #endif 63446076Sbostic if (sign) 63546076Sbostic fieldsz++; 63646076Sbostic else if (flags & HEXPREFIX) 63746076Sbostic fieldsz += 2; 63846076Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 63946076Sbostic 64046076Sbostic /* right-adjusting blank padding */ 64146076Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0) 64246076Sbostic PAD(width - realsz, blanks); 64346076Sbostic 64446076Sbostic /* prefix */ 64546076Sbostic if (sign) { 64646076Sbostic PRINT(&sign, 1); 64746076Sbostic } else if (flags & HEXPREFIX) { 64846076Sbostic ox[0] = '0'; 64946076Sbostic ox[1] = ch; 65046076Sbostic PRINT(ox, 2); 65146076Sbostic } 65246076Sbostic 65346076Sbostic /* right-adjusting zero padding */ 65446076Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 65546076Sbostic PAD(width - realsz, zeroes); 65646076Sbostic 65746076Sbostic /* leading zeroes from decimal precision */ 65846076Sbostic PAD(dprec - fieldsz, zeroes); 65946076Sbostic 66046076Sbostic /* the string or number proper */ 66146076Sbostic PRINT(cp, size); 66246076Sbostic 66346076Sbostic #ifdef FLOATING_POINT 66446076Sbostic /* trailing f.p. zeroes */ 66546076Sbostic PAD(fpprec, zeroes); 66646076Sbostic #endif 66746076Sbostic 66846076Sbostic /* left-adjusting padding (always blank) */ 66946076Sbostic if (flags & LADJUST) 67046076Sbostic PAD(width - realsz, blanks); 67146076Sbostic 67246076Sbostic /* finally, adjust ret */ 67346076Sbostic ret += width > realsz ? width : realsz; 67446076Sbostic 67546076Sbostic FLUSH(); /* copy out the I/O vectors */ 67634226Sbostic } 67746076Sbostic done: 67846076Sbostic FLUSH(); 67946076Sbostic error: 68046076Sbostic return (__sferror(fp) ? EOF : ret); 68134427Sbostic /* NOTREACHED */ 68234226Sbostic } 68334242Sbostic 68446076Sbostic #ifdef FLOATING_POINT 68546180Storek #include <math.h> 68646180Storek 68746076Sbostic static char *exponent(); 68846076Sbostic static char *round(); 68946076Sbostic 69046126Storek #if defined(hp300) || defined(sparc) 69146126Storek /* 69246126Storek * Check for special IEEE format values (NaN, Inf). 69346126Storek */ 69446126Storek static char * 69546126Storek isspecial(d, signp) 69646126Storek double d; 69746161Storek char *signp; 69846126Storek { 69946126Storek register struct IEEEdp { 70046126Storek unsigned sign:1; 70146126Storek unsigned exp:11; 70246126Storek unsigned manh:20; 70346126Storek unsigned manl:32; 70446126Storek } *ip = (struct IEEEdp *)&d; 70546126Storek 70646126Storek if (ip->exp != 0x7ff) 70746126Storek return (NULL); 70846126Storek if (ip->sign) 70946126Storek *signp = '-'; 71046161Storek return (ip->manh || ip->manl ? "NaN" : "Inf"); 71146126Storek } 71246126Storek #endif /* hp300 or sparc */ 71346126Storek 71446180Storek static int 71534669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp) 71634242Sbostic double number; 71734261Sbostic register int prec; 71834323Sbostic int flags; 71946076Sbostic char *signp; 72046076Sbostic int fmtch; 72146076Sbostic char *startp, *endp; 72234242Sbostic { 72334331Sbostic register char *p, *t; 72434672Sbostic register double fract; 72534624Sbostic int dotrim, expcnt, gformat; 72646180Storek double integer, tmp; 72734242Sbostic 72846076Sbostic dotrim = expcnt = gformat = 0; 72946076Sbostic if (number < 0) { 73046076Sbostic number = -number; 73146076Sbostic *signp = '-'; 73246076Sbostic } else 73346076Sbostic *signp = 0; 73444426Sbostic 73534624Sbostic fract = modf(number, &integer); 73634242Sbostic 73734624Sbostic /* get an extra slot for rounding. */ 73834624Sbostic t = ++startp; 73934624Sbostic 74034624Sbostic /* 74134624Sbostic * get integer portion of number; put into the end of the buffer; the 74234624Sbostic * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 74334624Sbostic */ 74434624Sbostic for (p = endp - 1; integer; ++expcnt) { 74534624Sbostic tmp = modf(integer / 10, &integer); 74646076Sbostic *p-- = to_char((int)((tmp + .01) * 10)); 74734248Sbostic } 74846076Sbostic switch (fmtch) { 74934261Sbostic case 'f': 75034624Sbostic /* reverse integer into beginning of buffer */ 75134624Sbostic if (expcnt) 75234624Sbostic for (; ++p < endp; *t++ = *p); 75334624Sbostic else 75434624Sbostic *t++ = '0'; 75534248Sbostic /* 75634624Sbostic * if precision required or alternate flag set, add in a 75734624Sbostic * decimal point. 75834248Sbostic */ 75934624Sbostic if (prec || flags&ALT) 76034624Sbostic *t++ = '.'; 76134624Sbostic /* if requires more precision and some fraction left */ 76234624Sbostic if (fract) { 76334624Sbostic if (prec) 76434624Sbostic do { 76534624Sbostic fract = modf(fract * 10, &tmp); 76646076Sbostic *t++ = to_char((int)tmp); 76734624Sbostic } while (--prec && fract); 76834624Sbostic if (fract) 76934669Sbostic startp = round(fract, (int *)NULL, startp, 77034669Sbostic t - 1, (char)0, signp); 77134624Sbostic } 77234624Sbostic for (; prec--; *t++ = '0'); 77334624Sbostic break; 77434624Sbostic case 'e': 77534624Sbostic case 'E': 77634624Sbostic eformat: if (expcnt) { 77734624Sbostic *t++ = *++p; 77834624Sbostic if (prec || flags&ALT) 77934331Sbostic *t++ = '.'; 78034624Sbostic /* if requires more precision and some integer left */ 78134624Sbostic for (; prec && ++p < endp; --prec) 78234624Sbostic *t++ = *p; 78334624Sbostic /* 78434624Sbostic * if done precision and more of the integer component, 78534624Sbostic * round using it; adjust fract so we don't re-round 78634624Sbostic * later. 78734624Sbostic */ 78834624Sbostic if (!prec && ++p < endp) { 78934248Sbostic fract = 0; 79034669Sbostic startp = round((double)0, &expcnt, startp, 79134669Sbostic t - 1, *p, signp); 79234248Sbostic } 79334624Sbostic /* adjust expcnt for digit in front of decimal */ 79434624Sbostic --expcnt; 79534242Sbostic } 79634624Sbostic /* until first fractional digit, decrement exponent */ 79734624Sbostic else if (fract) { 79834624Sbostic /* adjust expcnt for digit in front of decimal */ 79934624Sbostic for (expcnt = -1;; --expcnt) { 80034624Sbostic fract = modf(fract * 10, &tmp); 80134624Sbostic if (tmp) 80234624Sbostic break; 80334248Sbostic } 80446076Sbostic *t++ = to_char((int)tmp); 80534624Sbostic if (prec || flags&ALT) 80634331Sbostic *t++ = '.'; 80734242Sbostic } 80834248Sbostic else { 80934624Sbostic *t++ = '0'; 81034624Sbostic if (prec || flags&ALT) 81134331Sbostic *t++ = '.'; 81234248Sbostic } 81334624Sbostic /* if requires more precision and some fraction left */ 81434624Sbostic if (fract) { 81534624Sbostic if (prec) 81634624Sbostic do { 81734624Sbostic fract = modf(fract * 10, &tmp); 81846076Sbostic *t++ = to_char((int)tmp); 81934624Sbostic } while (--prec && fract); 82034624Sbostic if (fract) 82134669Sbostic startp = round(fract, &expcnt, startp, 82234669Sbostic t - 1, (char)0, signp); 82334584Sbostic } 82434624Sbostic /* if requires more precision */ 82534624Sbostic for (; prec--; *t++ = '0'); 82634624Sbostic 82734624Sbostic /* unless alternate flag, trim any g/G format trailing 0's */ 82834624Sbostic if (gformat && !(flags&ALT)) { 82934624Sbostic while (t > startp && *--t == '0'); 83034624Sbostic if (*t == '.') 83134624Sbostic --t; 83234624Sbostic ++t; 83334624Sbostic } 83434624Sbostic t = exponent(t, expcnt, fmtch); 83534624Sbostic break; 83634624Sbostic case 'g': 83734624Sbostic case 'G': 83834624Sbostic /* a precision of 0 is treated as a precision of 1. */ 83934624Sbostic if (!prec) 84034624Sbostic ++prec; 84134624Sbostic /* 84234624Sbostic * ``The style used depends on the value converted; style e 84334624Sbostic * will be used only if the exponent resulting from the 84434624Sbostic * conversion is less than -4 or greater than the precision.'' 84534624Sbostic * -- ANSI X3J11 84634624Sbostic */ 84734624Sbostic if (expcnt > prec || !expcnt && fract && fract < .0001) { 84834624Sbostic /* 84934624Sbostic * g/G format counts "significant digits, not digits of 85034624Sbostic * precision; for the e/E format, this just causes an 85134624Sbostic * off-by-one problem, i.e. g/G considers the digit 85234624Sbostic * before the decimal point significant and e/E doesn't 85334624Sbostic * count it as precision. 85434624Sbostic */ 85534624Sbostic --prec; 85634624Sbostic fmtch -= 2; /* G->E, g->e */ 85734624Sbostic gformat = 1; 85834624Sbostic goto eformat; 85934624Sbostic } 86034624Sbostic /* 86134624Sbostic * reverse integer into beginning of buffer, 86234624Sbostic * note, decrement precision 86334624Sbostic */ 86434624Sbostic if (expcnt) 86534624Sbostic for (; ++p < endp; *t++ = *p, --prec); 86634624Sbostic else 86734624Sbostic *t++ = '0'; 86834624Sbostic /* 86934624Sbostic * if precision required or alternate flag set, add in a 87034624Sbostic * decimal point. If no digits yet, add in leading 0. 87134624Sbostic */ 87234624Sbostic if (prec || flags&ALT) { 87334624Sbostic dotrim = 1; 87434624Sbostic *t++ = '.'; 87534624Sbostic } 87634624Sbostic else 87734624Sbostic dotrim = 0; 87834624Sbostic /* if requires more precision and some fraction left */ 87934624Sbostic if (fract) { 88034624Sbostic if (prec) { 88134624Sbostic do { 88234624Sbostic fract = modf(fract * 10, &tmp); 88346076Sbostic *t++ = to_char((int)tmp); 88434624Sbostic } while(!tmp); 88534624Sbostic while (--prec && fract) { 88634624Sbostic fract = modf(fract * 10, &tmp); 88746076Sbostic *t++ = to_char((int)tmp); 88834248Sbostic } 88934248Sbostic } 89034624Sbostic if (fract) 89134669Sbostic startp = round(fract, (int *)NULL, startp, 89234669Sbostic t - 1, (char)0, signp); 89334624Sbostic } 89434624Sbostic /* alternate format, adds 0's for precision, else trim 0's */ 89534624Sbostic if (flags&ALT) 89634624Sbostic for (; prec--; *t++ = '0'); 89734624Sbostic else if (dotrim) { 89834624Sbostic while (t > startp && *--t == '0'); 89934624Sbostic if (*t != '.') 90034624Sbostic ++t; 90134624Sbostic } 90234624Sbostic } 90346076Sbostic return (t - startp); 90434624Sbostic } 90534248Sbostic 90634624Sbostic static char * 90734669Sbostic round(fract, exp, start, end, ch, signp) 90834624Sbostic double fract; 90934669Sbostic int *exp; 91034624Sbostic register char *start, *end; 91134669Sbostic char ch, *signp; 91234624Sbostic { 91334624Sbostic double tmp; 91434248Sbostic 91534624Sbostic if (fract) 91646180Storek (void)modf(fract * 10, &tmp); 91734624Sbostic else 91846076Sbostic tmp = to_digit(ch); 91934624Sbostic if (tmp > 4) 92034624Sbostic for (;; --end) { 92134624Sbostic if (*end == '.') 92234624Sbostic --end; 92334624Sbostic if (++*end <= '9') 92434624Sbostic break; 92534624Sbostic *end = '0'; 92634624Sbostic if (end == start) { 92734669Sbostic if (exp) { /* e/E; increment exponent */ 92834669Sbostic *end = '1'; 92934669Sbostic ++*exp; 93034669Sbostic } 93134669Sbostic else { /* f; add extra digit */ 93246076Sbostic *--end = '1'; 93346076Sbostic --start; 93434669Sbostic } 93534624Sbostic break; 93634242Sbostic } 93734242Sbostic } 93834669Sbostic /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 93934669Sbostic else if (*signp == '-') 94034669Sbostic for (;; --end) { 94134669Sbostic if (*end == '.') 94234669Sbostic --end; 94334669Sbostic if (*end != '0') 94434669Sbostic break; 94534669Sbostic if (end == start) 94634669Sbostic *signp = 0; 94734669Sbostic } 94846076Sbostic return (start); 94934624Sbostic } 95034248Sbostic 95134624Sbostic static char * 95234624Sbostic exponent(p, exp, fmtch) 95334624Sbostic register char *p; 95434624Sbostic register int exp; 95546076Sbostic int fmtch; 95634624Sbostic { 95734624Sbostic register char *t; 95834624Sbostic char expbuf[MAXEXP]; 95934248Sbostic 96034624Sbostic *p++ = fmtch; 96134624Sbostic if (exp < 0) { 96234624Sbostic exp = -exp; 96334624Sbostic *p++ = '-'; 96434242Sbostic } 96534624Sbostic else 96634624Sbostic *p++ = '+'; 96734624Sbostic t = expbuf + MAXEXP; 96834624Sbostic if (exp > 9) { 96934624Sbostic do { 97046076Sbostic *--t = to_char(exp % 10); 97134624Sbostic } while ((exp /= 10) > 9); 97246076Sbostic *--t = to_char(exp); 97334624Sbostic for (; t < expbuf + MAXEXP; *p++ = *t++); 97434624Sbostic } 97534624Sbostic else { 97634624Sbostic *p++ = '0'; 97746076Sbostic *p++ = to_char(exp); 97834624Sbostic } 97946076Sbostic return (p); 98034242Sbostic } 98146076Sbostic #endif /* FLOATING_POINT */ 982