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*46126Storek static char sccsid[] = "@(#)vfprintf.c 5.41 (Berkeley) 01/22/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> 2246076Sbostic #include <stdio.h> 2346076Sbostic #include <string.h> 2446076Sbostic #if __STDC__ 2546076Sbostic #include <stdarg.h> 2646076Sbostic #else 2734233Sbostic #include <varargs.h> 2846076Sbostic #endif 2946076Sbostic #include "local.h" 3046076Sbostic #include "fvwrite.h" 3134226Sbostic 3246076Sbostic /* 3346076Sbostic * Define FLOATING_POINT to get floating point. 3446076Sbostic * Define CSH to get a csh-specific version (grr). 3546076Sbostic */ 3646076Sbostic #ifndef CSH 3746076Sbostic #define FLOATING_POINT 3846076Sbostic #endif 3934226Sbostic 4046076Sbostic /* end of configuration stuff */ 4134624Sbostic 4246076Sbostic 4346076Sbostic #ifdef CSH 4446076Sbostic /* 4546076Sbostic * C shell hacks. Ick, gag. 4646076Sbostic */ 4746076Sbostic #undef BUFSIZ 4846076Sbostic #include "sh.h" 4946076Sbostic 5046076Sbostic printf(fmt, args) 5146076Sbostic char *fmt; 5246076Sbostic { 5346076Sbostic FILE f; 5446076Sbostic 5546076Sbostic f._flags = __SWR; 5646076Sbostic return (vfprintf(&f, fmt, &args)); 5746076Sbostic } 5846076Sbostic 5946076Sbostic #define __sprint(fp, uio) cshprintv(uio) 6046076Sbostic 6146076Sbostic cshprintv(uio) 6246076Sbostic register struct __suio *uio; 6346076Sbostic { 6446076Sbostic register char *p; 6546076Sbostic register int n, ch, iovcnt; 6646076Sbostic register struct __siov *iov = uio->uio_iov; 6746076Sbostic 6846076Sbostic for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) { 6946076Sbostic for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) { 7046076Sbostic #ifdef CSHPUTCHAR 7146076Sbostic ch = *p++; 7246076Sbostic CSHPUTCHAR; /* this horrid macro uses `ch' */ 7346076Sbostic #else 7446076Sbostic #undef putchar 7546076Sbostic putchar(*p++); 7646076Sbostic #endif 7746076Sbostic } 7846076Sbostic } 7946076Sbostic uio->uio_resid = 0; 8046076Sbostic uio->uio_iovcnt = 0; 8146076Sbostic return (0); 8246076Sbostic } 8346076Sbostic 8446076Sbostic #else /* CSH */ 8546076Sbostic 8646076Sbostic /* 8746076Sbostic * Flush out all the vectors defined by the given uio, 8846076Sbostic * then reset it so that it can be reused. 8946076Sbostic */ 9046076Sbostic static 9146076Sbostic __sprint(fp, uio) 9246076Sbostic FILE *fp; 9346076Sbostic register struct __suio *uio; 9446076Sbostic { 9546076Sbostic register int err; 9646076Sbostic 9746076Sbostic if (uio->uio_resid == 0) { 9846076Sbostic uio->uio_iovcnt = 0; 9946076Sbostic return (0); 10046076Sbostic } 10146076Sbostic err = __sfvwrite(fp, uio); 10246076Sbostic uio->uio_resid = 0; 10346076Sbostic uio->uio_iovcnt = 0; 10446076Sbostic return (err); 10546076Sbostic } 10646076Sbostic 10746076Sbostic /* 10846076Sbostic * Helper function for `fprintf to unbuffered unix file': creates a 10946076Sbostic * temporary buffer. We only work on write-only files; this avoids 11046076Sbostic * worries about ungetc buffers and so forth. 11146076Sbostic */ 11246076Sbostic static 11346076Sbostic __sbprintf(fp, fmt, ap) 11446076Sbostic register FILE *fp; 11546076Sbostic char *fmt; 11646076Sbostic va_list ap; 11746076Sbostic { 11846076Sbostic int ret; 11946076Sbostic FILE fake; 12046076Sbostic unsigned char buf[BUFSIZ]; 12146076Sbostic 12246076Sbostic /* copy the important variables */ 12346076Sbostic fake._flags = fp->_flags & ~__SNBF; 12446076Sbostic fake._file = fp->_file; 12546076Sbostic fake._cookie = fp->_cookie; 12646076Sbostic fake._write = fp->_write; 12746076Sbostic 12846076Sbostic /* set up the buffer */ 12946076Sbostic fake._bf._base = fake._p = buf; 13046076Sbostic fake._bf._size = fake._w = sizeof(buf); 13146076Sbostic fake._lbfsize = 0; /* not actually used, but Just In Case */ 13246076Sbostic 13346076Sbostic /* do the work, then copy any error status */ 13446076Sbostic ret = vfprintf(&fake, fmt, ap); 13546076Sbostic if (ret >= 0 && fflush(&fake)) 13646076Sbostic ret = EOF; 13746076Sbostic if (fake._flags & __SERR) 13846076Sbostic fp->_flags |= __SERR; 13946076Sbostic return (ret); 14046076Sbostic } 14146076Sbostic 14246076Sbostic #endif /* CSH */ 14346076Sbostic 14446076Sbostic 14546076Sbostic #ifdef FLOATING_POINT 14646076Sbostic 14746076Sbostic #include "floatio.h" 14834328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 14946076Sbostic #define DEFPREC 6 15034328Sbostic 15146076Sbostic static int cvt(); 152*46126Storek #if defined(hp300) || defined(sparc) 153*46126Storek static char *isspecial(); 154*46126Storek #endif 15534236Sbostic 15646076Sbostic #else /* no FLOATING_POINT */ 15734235Sbostic 15846076Sbostic #define BUF 40 15934331Sbostic 16046076Sbostic #endif /* FLOATING_POINT */ 16134318Sbostic 16246076Sbostic 16346076Sbostic /* 16446076Sbostic * Macros for converting digits to letters and vice versa 16546076Sbostic */ 16646076Sbostic #define to_digit(c) ((c) - '0') 16746076Sbostic #define is_digit(c) ((unsigned)to_digit(c) <= 9) 16846076Sbostic #define to_char(n) ((n) + '0') 16946076Sbostic 17046076Sbostic /* 17146076Sbostic * Flags used during conversion. 17246076Sbostic */ 17334318Sbostic #define LONGINT 0x01 /* long integer */ 17434318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 17534318Sbostic #define SHORTINT 0x04 /* short integer */ 17634318Sbostic #define ALT 0x08 /* alternate form */ 17734318Sbostic #define LADJUST 0x10 /* left adjustment */ 17834427Sbostic #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 17934427Sbostic #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 18034318Sbostic 18146076Sbostic vfprintf(fp, fmt0, ap) 18246076Sbostic FILE *fp; 18346076Sbostic char *fmt0; 18446076Sbostic #if tahoe 18546076Sbostic register /* technically illegal, since we do not know what type va_list is */ 18646076Sbostic #endif 18746076Sbostic va_list ap; 18834226Sbostic { 18946076Sbostic register char *fmt; /* format string */ 19034427Sbostic register int ch; /* character from fmt */ 19146076Sbostic register int n; /* handy integer (short term usage) */ 19246076Sbostic register char *cp; /* handy char pointer (short term usage) */ 19346076Sbostic register struct __siov *iovp;/* for PRINT macro */ 19446076Sbostic register int flags; /* flags as above */ 19546076Sbostic int ret; /* return value accumulator */ 19646076Sbostic int width; /* width from format (%8d), or 0 */ 19746076Sbostic int prec; /* precision from format (%.3d), or -1 */ 19846076Sbostic char sign; /* sign prefix (' ', '+', '-', or \0) */ 19946076Sbostic #ifdef FLOATING_POINT 20046076Sbostic char softsign; /* temporary negative sign for floats */ 20134427Sbostic double _double; /* double precision arguments %[eEfgG] */ 20246076Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 20346076Sbostic #endif 20434427Sbostic u_long _ulong; /* integer arguments %[diouxX] */ 20546076Sbostic enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 20646076Sbostic int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 20734624Sbostic int fieldsz; /* field size expanded by sign, etc */ 20846076Sbostic int realsz; /* field size expanded by dprec */ 20934427Sbostic int size; /* size of converted field or string */ 21046076Sbostic char *xdigs; /* digits for [xX] conversion */ 21146076Sbostic #define NIOV 8 21246076Sbostic struct __suio uio; /* output information: summary */ 21346076Sbostic struct __siov iov[NIOV];/* ... and individual io vectors */ 21434427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 21546076Sbostic char ox[2]; /* space for 0x hex-prefix */ 21634226Sbostic 21746076Sbostic /* 21846076Sbostic * Choose PADSIZE to trade efficiency vs size. If larger 21946076Sbostic * printf fields occur frequently, increase PADSIZE (and make 22046076Sbostic * the initialisers below longer). 22146076Sbostic */ 22246076Sbostic #define PADSIZE 16 /* pad chunk size */ 22346076Sbostic static char blanks[PADSIZE] = 22446076Sbostic {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 22546076Sbostic static char zeroes[PADSIZE] = 22646076Sbostic {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 22746076Sbostic 22846076Sbostic /* 22946076Sbostic * BEWARE, these `goto error' on error, and PAD uses `n'. 23046076Sbostic */ 23146076Sbostic #define PRINT(ptr, len) { \ 23246076Sbostic iovp->iov_base = (ptr); \ 23346076Sbostic iovp->iov_len = (len); \ 23446076Sbostic uio.uio_resid += (len); \ 23546076Sbostic iovp++; \ 23646076Sbostic if (++uio.uio_iovcnt >= NIOV) { \ 23746076Sbostic if (__sprint(fp, &uio)) \ 23846076Sbostic goto error; \ 23946076Sbostic iovp = iov; \ 24046076Sbostic } \ 24146076Sbostic } 24246076Sbostic #define PAD(howmany, with) { \ 24346076Sbostic if ((n = (howmany)) > 0) { \ 24446076Sbostic while (n > PADSIZE) { \ 24546076Sbostic PRINT(with, PADSIZE); \ 24646076Sbostic n -= PADSIZE; \ 24746076Sbostic } \ 24846076Sbostic PRINT(with, n); \ 24946076Sbostic } \ 25046076Sbostic } 25146076Sbostic #define FLUSH() { \ 25246076Sbostic if (uio.uio_resid && __sprint(fp, &uio)) \ 25346076Sbostic goto error; \ 25446076Sbostic uio.uio_iovcnt = 0; \ 25546076Sbostic iovp = iov; \ 25646076Sbostic } 25746076Sbostic 25846076Sbostic /* 25946076Sbostic * To extend shorts properly, we need both signed and unsigned 26046076Sbostic * argument extraction methods. 26146076Sbostic */ 26246076Sbostic #define SARG() \ 26346076Sbostic (flags&LONGINT ? va_arg(ap, long) : \ 26446076Sbostic flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ 26546076Sbostic (long)va_arg(ap, int)) 26646076Sbostic #define UARG() \ 26746076Sbostic (flags&LONGINT ? va_arg(ap, u_long) : \ 26846076Sbostic flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ 26946076Sbostic (u_long)va_arg(ap, u_int)) 27046076Sbostic 27146076Sbostic #ifndef CSH 27246076Sbostic /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 27346076Sbostic if (cantwrite(fp)) 27434428Sbostic return (EOF); 27534428Sbostic 27646076Sbostic /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 27746076Sbostic if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 27846076Sbostic fp->_file >= 0) 27946076Sbostic return (__sbprintf(fp, fmt0, ap)); 28046076Sbostic #endif /* CSH */ 28146076Sbostic 28246076Sbostic fmt = (char *)fmt0; 28346076Sbostic uio.uio_iov = iovp = iov; 28446076Sbostic uio.uio_resid = 0; 28546076Sbostic uio.uio_iovcnt = 0; 28646076Sbostic ret = 0; 28746076Sbostic 28846076Sbostic /* 28946076Sbostic * Scan the format for conversions (`%' character). 29046076Sbostic */ 29146076Sbostic for (;;) { 29246076Sbostic for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 29346076Sbostic /* void */; 29446076Sbostic if ((n = fmt - cp) != 0) { 29546076Sbostic PRINT(cp, n); 29646076Sbostic ret += n; 29746076Sbostic } 29846076Sbostic if (ch == '\0') 29946076Sbostic goto done; 30046076Sbostic fmt++; /* skip over '%' */ 30146076Sbostic 30246076Sbostic flags = 0; 30346076Sbostic dprec = 0; 30446076Sbostic #ifdef FLOATING_POINT 30546076Sbostic fpprec = 0; 30634318Sbostic #endif 30746076Sbostic width = 0; 30834233Sbostic prec = -1; 30934318Sbostic sign = '\0'; 31034226Sbostic 31146076Sbostic rflag: ch = *fmt++; 31246076Sbostic reswitch: switch (ch) { 31334318Sbostic case ' ': 31434669Sbostic /* 31534669Sbostic * ``If the space and + flags both appear, the space 31634669Sbostic * flag will be ignored.'' 31734669Sbostic * -- ANSI X3J11 31834669Sbostic */ 31934669Sbostic if (!sign) 32034669Sbostic sign = ' '; 32134318Sbostic goto rflag; 32234233Sbostic case '#': 32334318Sbostic flags |= ALT; 32434318Sbostic goto rflag; 32534233Sbostic case '*': 32634235Sbostic /* 32734235Sbostic * ``A negative field width argument is taken as a 32846076Sbostic * - flag followed by a positive field width.'' 32934235Sbostic * -- ANSI X3J11 33034235Sbostic * They don't exclude field widths read from args. 33134235Sbostic */ 33246076Sbostic if ((width = va_arg(ap, int)) >= 0) 33334318Sbostic goto rflag; 33434235Sbostic width = -width; 33534427Sbostic /* FALLTHROUGH */ 33634235Sbostic case '-': 33734318Sbostic flags |= LADJUST; 33834318Sbostic goto rflag; 33934233Sbostic case '+': 34034314Sbostic sign = '+'; 34134318Sbostic goto rflag; 34234233Sbostic case '.': 34346076Sbostic if ((ch = *fmt++) == '*') { 34446076Sbostic n = va_arg(ap, int); 34546076Sbostic prec = n < 0 ? -1 : n; 34646076Sbostic goto rflag; 34734226Sbostic } 34846076Sbostic n = 0; 34946076Sbostic while (is_digit(ch)) { 35046076Sbostic n = 10 * n + to_digit(ch); 35146076Sbostic ch = *fmt++; 35246076Sbostic } 35334318Sbostic prec = n < 0 ? -1 : n; 35446076Sbostic goto reswitch; 35534233Sbostic case '0': 35634427Sbostic /* 35734427Sbostic * ``Note that 0 is taken as a flag, not as the 35834427Sbostic * beginning of a field width.'' 35934427Sbostic * -- ANSI X3J11 36034427Sbostic */ 36134427Sbostic flags |= ZEROPAD; 36234427Sbostic goto rflag; 36334233Sbostic case '1': case '2': case '3': case '4': 36434233Sbostic case '5': case '6': case '7': case '8': case '9': 36534318Sbostic n = 0; 36634233Sbostic do { 36746076Sbostic n = 10 * n + to_digit(ch); 36846076Sbostic ch = *fmt++; 36946076Sbostic } while (is_digit(ch)); 37034318Sbostic width = n; 37146076Sbostic goto reswitch; 37246076Sbostic #ifdef FLOATING_POINT 37334235Sbostic case 'L': 37434329Sbostic flags |= LONGDBL; 37534318Sbostic goto rflag; 37646076Sbostic #endif 37734235Sbostic case 'h': 37834318Sbostic flags |= SHORTINT; 37934318Sbostic goto rflag; 38034233Sbostic case 'l': 38134318Sbostic flags |= LONGINT; 38234318Sbostic goto rflag; 38334314Sbostic case 'c': 38446076Sbostic *(cp = buf) = va_arg(ap, int); 38534314Sbostic size = 1; 38634427Sbostic sign = '\0'; 38746076Sbostic break; 38834624Sbostic case 'D': 38934624Sbostic flags |= LONGINT; 39034624Sbostic /*FALLTHROUGH*/ 39134314Sbostic case 'd': 39234318Sbostic case 'i': 39346076Sbostic _ulong = SARG(); 39434318Sbostic if ((long)_ulong < 0) { 39534318Sbostic _ulong = -_ulong; 39634314Sbostic sign = '-'; 39734241Sbostic } 39846076Sbostic base = DEC; 39934327Sbostic goto number; 40046076Sbostic #ifdef FLOATING_POINT 40134261Sbostic case 'e': 40234236Sbostic case 'E': 40334235Sbostic case 'f': 40434261Sbostic case 'g': 40534243Sbostic case 'G': 40646076Sbostic _double = va_arg(ap, double); 407*46126Storek #if defined(hp300) || defined(sparc) 408*46126Storek /* do this before tricky precision changes */ 409*46126Storek if ((cp = isspecial(_double, &sign)) != NULL) { 410*46126Storek size = strlen(cp); 411*46126Storek break; 412*46126Storek } 413*46126Storek #endif 41434328Sbostic /* 41534669Sbostic * don't do unrealistic precision; just pad it with 41634669Sbostic * zeroes later, so buffer size stays rational. 41734328Sbostic */ 41834328Sbostic if (prec > MAXFRACT) { 41946076Sbostic if (ch != 'g' && ch != 'G' || (flags&ALT)) 42034475Sbostic fpprec = prec - MAXFRACT; 42134328Sbostic prec = MAXFRACT; 42246076Sbostic } else if (prec == -1) 42334624Sbostic prec = DEFPREC; 42434669Sbostic /* 42546076Sbostic * cvt may have to round up before the "start" of 42646076Sbostic * its buffer, i.e. ``intf("%.2f", (double)9.999);''; 42746076Sbostic * if the first character is still NUL, it did. 42846076Sbostic * softsign avoids negative 0 if _double < 0 but 42946076Sbostic * no significant digits will be shown. 43034669Sbostic */ 43146076Sbostic cp = buf; 43246076Sbostic *cp = '\0'; 43346076Sbostic size = cvt(_double, prec, flags, &softsign, ch, 43446076Sbostic cp, buf + sizeof(buf)); 43534669Sbostic if (softsign) 43634669Sbostic sign = '-'; 43746076Sbostic if (*cp == '\0') 43846076Sbostic cp++; 43946076Sbostic break; 44046076Sbostic #endif /* FLOATING_POINT */ 44134235Sbostic case 'n': 44234427Sbostic if (flags & LONGINT) 44346076Sbostic *va_arg(ap, long *) = ret; 44434427Sbostic else if (flags & SHORTINT) 44546076Sbostic *va_arg(ap, short *) = ret; 44634318Sbostic else 44746076Sbostic *va_arg(ap, int *) = ret; 44846076Sbostic continue; /* no output */ 44934624Sbostic case 'O': 45034624Sbostic flags |= LONGINT; 45134624Sbostic /*FALLTHROUGH*/ 45234226Sbostic case 'o': 45346076Sbostic _ulong = UARG(); 45446076Sbostic base = OCT; 45534327Sbostic goto nosign; 45634235Sbostic case 'p': 45734320Sbostic /* 45834321Sbostic * ``The argument shall be a pointer to void. The 45934321Sbostic * value of the pointer is converted to a sequence 46034321Sbostic * of printable characters, in an implementation- 46134321Sbostic * defined manner.'' 46234321Sbostic * -- ANSI X3J11 46334320Sbostic */ 46434427Sbostic /* NOSTRICT */ 46546076Sbostic _ulong = (u_long)va_arg(ap, void *); 46646076Sbostic base = HEX; 46746076Sbostic xdigs = "0123456789abcdef"; 46846076Sbostic flags |= HEXPREFIX; 46946076Sbostic ch = 'x'; 47034327Sbostic goto nosign; 47134226Sbostic case 's': 47246076Sbostic if ((cp = va_arg(ap, char *)) == NULL) 47346076Sbostic cp = "(null)"; 47434321Sbostic if (prec >= 0) { 47534321Sbostic /* 47634321Sbostic * can't use strlen; can only look for the 47734321Sbostic * NUL in the first `prec' characters, and 47834321Sbostic * strlen() will go further. 47934321Sbostic */ 48046076Sbostic char *p = memchr(cp, 0, prec); 48134321Sbostic 48246076Sbostic if (p != NULL) { 48346076Sbostic size = p - cp; 48434321Sbostic if (size > prec) 48534321Sbostic size = prec; 48634427Sbostic } else 48734321Sbostic size = prec; 48834427Sbostic } else 48946076Sbostic size = strlen(cp); 49034427Sbostic sign = '\0'; 49146076Sbostic break; 49234624Sbostic case 'U': 49334624Sbostic flags |= LONGINT; 49434624Sbostic /*FALLTHROUGH*/ 49534226Sbostic case 'u': 49646076Sbostic _ulong = UARG(); 49746076Sbostic base = DEC; 49834327Sbostic goto nosign; 49934226Sbostic case 'X': 50046076Sbostic xdigs = "0123456789ABCDEF"; 50146076Sbostic goto hex; 50234226Sbostic case 'x': 50346076Sbostic xdigs = "0123456789abcdef"; 50446076Sbostic hex: _ulong = UARG(); 50546076Sbostic base = HEX; 50634326Sbostic /* leading 0x/X only if non-zero */ 50734427Sbostic if (flags & ALT && _ulong != 0) 50834427Sbostic flags |= HEXPREFIX; 50934327Sbostic 51034327Sbostic /* unsigned conversions */ 51134427Sbostic nosign: sign = '\0'; 51234326Sbostic /* 51334330Sbostic * ``... diouXx conversions ... if a precision is 51434330Sbostic * specified, the 0 flag will be ignored.'' 51534330Sbostic * -- ANSI X3J11 51634330Sbostic */ 51734427Sbostic number: if ((dprec = prec) >= 0) 51834427Sbostic flags &= ~ZEROPAD; 51934427Sbostic 52034330Sbostic /* 52134326Sbostic * ``The result of converting a zero value with an 52234326Sbostic * explicit precision of zero is no characters.'' 52334326Sbostic * -- ANSI X3J11 52434326Sbostic */ 52546076Sbostic cp = buf + BUF; 52634427Sbostic if (_ulong != 0 || prec != 0) { 52746076Sbostic /* 52846076Sbostic * unsigned mod is hard, and unsigned mod 52946076Sbostic * by a constant is easier than that by 53046076Sbostic * a variable; hence this switch. 53146076Sbostic */ 53246076Sbostic switch (base) { 53346076Sbostic case OCT: 53446076Sbostic do { 53546076Sbostic *--cp = to_char(_ulong & 7); 53646076Sbostic _ulong >>= 3; 53746076Sbostic } while (_ulong); 53846076Sbostic /* handle octal leading 0 */ 53946076Sbostic if (flags & ALT && *cp != '0') 54046076Sbostic *--cp = '0'; 54146076Sbostic break; 54234327Sbostic 54346076Sbostic case DEC: 54446076Sbostic /* many numbers are 1 digit */ 54546076Sbostic while (_ulong >= 10) { 54646076Sbostic *--cp = to_char(_ulong % 10); 54746076Sbostic _ulong /= 10; 54846076Sbostic } 54946076Sbostic *--cp = to_char(_ulong); 55046076Sbostic break; 55134327Sbostic 55246076Sbostic case HEX: 55346076Sbostic do { 55446076Sbostic *--cp = xdigs[_ulong & 15]; 55546076Sbostic _ulong >>= 4; 55646076Sbostic } while (_ulong); 55746076Sbostic break; 55834327Sbostic 55946076Sbostic default: 56046076Sbostic cp = "bug in vfprintf: bad base"; 56146076Sbostic goto skipsize; 56246076Sbostic } 56334327Sbostic } 56446076Sbostic size = buf + BUF - cp; 56546076Sbostic skipsize: 56634226Sbostic break; 56746076Sbostic default: /* "%?" prints ?, unless ? is NUL */ 56846076Sbostic if (ch == '\0') 56946076Sbostic goto done; 57046076Sbostic /* pretend it was %c with argument ch */ 57146076Sbostic cp = buf; 57246076Sbostic *cp = ch; 57346076Sbostic size = 1; 57446076Sbostic sign = '\0'; 57546076Sbostic break; 57634226Sbostic } 57746076Sbostic 57846076Sbostic /* 57946076Sbostic * All reasonable formats wind up here. At this point, 58046076Sbostic * `cp' points to a string which (if not flags&LADJUST) 58146076Sbostic * should be padded out to `width' places. If 58246076Sbostic * flags&ZEROPAD, it should first be prefixed by any 58346076Sbostic * sign or other prefix; otherwise, it should be blank 58446076Sbostic * padded before the prefix is emitted. After any 58546076Sbostic * left-hand padding and prefixing, emit zeroes 58646076Sbostic * required by a decimal [diouxX] precision, then print 58746076Sbostic * the string proper, then emit zeroes required by any 58846076Sbostic * leftover floating precision; finally, if LADJUST, 58946076Sbostic * pad with blanks. 59046076Sbostic */ 59146076Sbostic 59246076Sbostic /* 59346076Sbostic * compute actual size, so we know how much to pad. 59446076Sbostic * fieldsz excludes decimal prec; realsz includes it 59546076Sbostic */ 59646076Sbostic #ifdef FLOATING_POINT 59746076Sbostic fieldsz = size + fpprec; 59846076Sbostic #else 59946076Sbostic fieldsz = size; 60046076Sbostic #endif 60146076Sbostic if (sign) 60246076Sbostic fieldsz++; 60346076Sbostic else if (flags & HEXPREFIX) 60446076Sbostic fieldsz += 2; 60546076Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 60646076Sbostic 60746076Sbostic /* right-adjusting blank padding */ 60846076Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0) 60946076Sbostic PAD(width - realsz, blanks); 61046076Sbostic 61146076Sbostic /* prefix */ 61246076Sbostic if (sign) { 61346076Sbostic PRINT(&sign, 1); 61446076Sbostic } else if (flags & HEXPREFIX) { 61546076Sbostic ox[0] = '0'; 61646076Sbostic ox[1] = ch; 61746076Sbostic PRINT(ox, 2); 61846076Sbostic } 61946076Sbostic 62046076Sbostic /* right-adjusting zero padding */ 62146076Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 62246076Sbostic PAD(width - realsz, zeroes); 62346076Sbostic 62446076Sbostic /* leading zeroes from decimal precision */ 62546076Sbostic PAD(dprec - fieldsz, zeroes); 62646076Sbostic 62746076Sbostic /* the string or number proper */ 62846076Sbostic PRINT(cp, size); 62946076Sbostic 63046076Sbostic #ifdef FLOATING_POINT 63146076Sbostic /* trailing f.p. zeroes */ 63246076Sbostic PAD(fpprec, zeroes); 63346076Sbostic #endif 63446076Sbostic 63546076Sbostic /* left-adjusting padding (always blank) */ 63646076Sbostic if (flags & LADJUST) 63746076Sbostic PAD(width - realsz, blanks); 63846076Sbostic 63946076Sbostic /* finally, adjust ret */ 64046076Sbostic ret += width > realsz ? width : realsz; 64146076Sbostic 64246076Sbostic FLUSH(); /* copy out the I/O vectors */ 64334226Sbostic } 64446076Sbostic done: 64546076Sbostic FLUSH(); 64646076Sbostic error: 64746076Sbostic return (__sferror(fp) ? EOF : ret); 64834427Sbostic /* NOTREACHED */ 64934226Sbostic } 65034242Sbostic 65146076Sbostic #ifdef FLOATING_POINT 65246076Sbostic static char *exponent(); 65346076Sbostic static char *round(); 65446076Sbostic 655*46126Storek #if defined(hp300) || defined(sparc) 656*46126Storek /* 657*46126Storek * Check for special IEEE format values (NaN, Inf). 658*46126Storek */ 659*46126Storek static char * 660*46126Storek isspecial(d, signp) 661*46126Storek double d; 662*46126Storek char *bufp, *signp; 663*46126Storek { 664*46126Storek register struct IEEEdp { 665*46126Storek unsigned sign:1; 666*46126Storek unsigned exp:11; 667*46126Storek unsigned manh:20; 668*46126Storek unsigned manl:32; 669*46126Storek } *ip = (struct IEEEdp *)&d; 670*46126Storek 671*46126Storek if (ip->exp != 0x7ff) 672*46126Storek return (NULL); 673*46126Storek if (ip->manh || ip->manl) 674*46126Storek return ("NaN"); 675*46126Storek if (ip->sign) 676*46126Storek *signp = '-'; 677*46126Storek return ("Inf"); 678*46126Storek } 679*46126Storek #endif /* hp300 or sparc */ 680*46126Storek 68134624Sbostic static 68234669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp) 68334242Sbostic double number; 68434261Sbostic register int prec; 68534323Sbostic int flags; 68646076Sbostic char *signp; 68746076Sbostic int fmtch; 68846076Sbostic char *startp, *endp; 68934242Sbostic { 69034331Sbostic register char *p, *t; 69134672Sbostic register double fract; 69234624Sbostic int dotrim, expcnt, gformat; 69334672Sbostic double integer, tmp, modf(); 69434242Sbostic 69546076Sbostic dotrim = expcnt = gformat = 0; 69646076Sbostic if (number < 0) { 69746076Sbostic number = -number; 69846076Sbostic *signp = '-'; 69946076Sbostic } else 70046076Sbostic *signp = 0; 70144426Sbostic 70234624Sbostic fract = modf(number, &integer); 70334242Sbostic 70434624Sbostic /* get an extra slot for rounding. */ 70534624Sbostic t = ++startp; 70634624Sbostic 70734624Sbostic /* 70834624Sbostic * get integer portion of number; put into the end of the buffer; the 70934624Sbostic * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 71034624Sbostic */ 71134624Sbostic for (p = endp - 1; integer; ++expcnt) { 71234624Sbostic tmp = modf(integer / 10, &integer); 71346076Sbostic *p-- = to_char((int)((tmp + .01) * 10)); 71434248Sbostic } 71546076Sbostic switch (fmtch) { 71634261Sbostic case 'f': 71734624Sbostic /* reverse integer into beginning of buffer */ 71834624Sbostic if (expcnt) 71934624Sbostic for (; ++p < endp; *t++ = *p); 72034624Sbostic else 72134624Sbostic *t++ = '0'; 72234248Sbostic /* 72334624Sbostic * if precision required or alternate flag set, add in a 72434624Sbostic * decimal point. 72534248Sbostic */ 72634624Sbostic if (prec || flags&ALT) 72734624Sbostic *t++ = '.'; 72834624Sbostic /* if requires more precision and some fraction left */ 72934624Sbostic if (fract) { 73034624Sbostic if (prec) 73134624Sbostic do { 73234624Sbostic fract = modf(fract * 10, &tmp); 73346076Sbostic *t++ = to_char((int)tmp); 73434624Sbostic } while (--prec && fract); 73534624Sbostic if (fract) 73634669Sbostic startp = round(fract, (int *)NULL, startp, 73734669Sbostic t - 1, (char)0, signp); 73834624Sbostic } 73934624Sbostic for (; prec--; *t++ = '0'); 74034624Sbostic break; 74134624Sbostic case 'e': 74234624Sbostic case 'E': 74334624Sbostic eformat: if (expcnt) { 74434624Sbostic *t++ = *++p; 74534624Sbostic if (prec || flags&ALT) 74634331Sbostic *t++ = '.'; 74734624Sbostic /* if requires more precision and some integer left */ 74834624Sbostic for (; prec && ++p < endp; --prec) 74934624Sbostic *t++ = *p; 75034624Sbostic /* 75134624Sbostic * if done precision and more of the integer component, 75234624Sbostic * round using it; adjust fract so we don't re-round 75334624Sbostic * later. 75434624Sbostic */ 75534624Sbostic if (!prec && ++p < endp) { 75634248Sbostic fract = 0; 75734669Sbostic startp = round((double)0, &expcnt, startp, 75834669Sbostic t - 1, *p, signp); 75934248Sbostic } 76034624Sbostic /* adjust expcnt for digit in front of decimal */ 76134624Sbostic --expcnt; 76234242Sbostic } 76334624Sbostic /* until first fractional digit, decrement exponent */ 76434624Sbostic else if (fract) { 76534624Sbostic /* adjust expcnt for digit in front of decimal */ 76634624Sbostic for (expcnt = -1;; --expcnt) { 76734624Sbostic fract = modf(fract * 10, &tmp); 76834624Sbostic if (tmp) 76934624Sbostic break; 77034248Sbostic } 77146076Sbostic *t++ = to_char((int)tmp); 77234624Sbostic if (prec || flags&ALT) 77334331Sbostic *t++ = '.'; 77434242Sbostic } 77534248Sbostic else { 77634624Sbostic *t++ = '0'; 77734624Sbostic if (prec || flags&ALT) 77834331Sbostic *t++ = '.'; 77934248Sbostic } 78034624Sbostic /* if requires more precision and some fraction left */ 78134624Sbostic if (fract) { 78234624Sbostic if (prec) 78334624Sbostic do { 78434624Sbostic fract = modf(fract * 10, &tmp); 78546076Sbostic *t++ = to_char((int)tmp); 78634624Sbostic } while (--prec && fract); 78734624Sbostic if (fract) 78834669Sbostic startp = round(fract, &expcnt, startp, 78934669Sbostic t - 1, (char)0, signp); 79034584Sbostic } 79134624Sbostic /* if requires more precision */ 79234624Sbostic for (; prec--; *t++ = '0'); 79334624Sbostic 79434624Sbostic /* unless alternate flag, trim any g/G format trailing 0's */ 79534624Sbostic if (gformat && !(flags&ALT)) { 79634624Sbostic while (t > startp && *--t == '0'); 79734624Sbostic if (*t == '.') 79834624Sbostic --t; 79934624Sbostic ++t; 80034624Sbostic } 80134624Sbostic t = exponent(t, expcnt, fmtch); 80234624Sbostic break; 80334624Sbostic case 'g': 80434624Sbostic case 'G': 80534624Sbostic /* a precision of 0 is treated as a precision of 1. */ 80634624Sbostic if (!prec) 80734624Sbostic ++prec; 80834624Sbostic /* 80934624Sbostic * ``The style used depends on the value converted; style e 81034624Sbostic * will be used only if the exponent resulting from the 81134624Sbostic * conversion is less than -4 or greater than the precision.'' 81234624Sbostic * -- ANSI X3J11 81334624Sbostic */ 81434624Sbostic if (expcnt > prec || !expcnt && fract && fract < .0001) { 81534624Sbostic /* 81634624Sbostic * g/G format counts "significant digits, not digits of 81734624Sbostic * precision; for the e/E format, this just causes an 81834624Sbostic * off-by-one problem, i.e. g/G considers the digit 81934624Sbostic * before the decimal point significant and e/E doesn't 82034624Sbostic * count it as precision. 82134624Sbostic */ 82234624Sbostic --prec; 82334624Sbostic fmtch -= 2; /* G->E, g->e */ 82434624Sbostic gformat = 1; 82534624Sbostic goto eformat; 82634624Sbostic } 82734624Sbostic /* 82834624Sbostic * reverse integer into beginning of buffer, 82934624Sbostic * note, decrement precision 83034624Sbostic */ 83134624Sbostic if (expcnt) 83234624Sbostic for (; ++p < endp; *t++ = *p, --prec); 83334624Sbostic else 83434624Sbostic *t++ = '0'; 83534624Sbostic /* 83634624Sbostic * if precision required or alternate flag set, add in a 83734624Sbostic * decimal point. If no digits yet, add in leading 0. 83834624Sbostic */ 83934624Sbostic if (prec || flags&ALT) { 84034624Sbostic dotrim = 1; 84134624Sbostic *t++ = '.'; 84234624Sbostic } 84334624Sbostic else 84434624Sbostic dotrim = 0; 84534624Sbostic /* if requires more precision and some fraction left */ 84634624Sbostic if (fract) { 84734624Sbostic if (prec) { 84834624Sbostic do { 84934624Sbostic fract = modf(fract * 10, &tmp); 85046076Sbostic *t++ = to_char((int)tmp); 85134624Sbostic } while(!tmp); 85234624Sbostic while (--prec && fract) { 85334624Sbostic fract = modf(fract * 10, &tmp); 85446076Sbostic *t++ = to_char((int)tmp); 85534248Sbostic } 85634248Sbostic } 85734624Sbostic if (fract) 85834669Sbostic startp = round(fract, (int *)NULL, startp, 85934669Sbostic t - 1, (char)0, signp); 86034624Sbostic } 86134624Sbostic /* alternate format, adds 0's for precision, else trim 0's */ 86234624Sbostic if (flags&ALT) 86334624Sbostic for (; prec--; *t++ = '0'); 86434624Sbostic else if (dotrim) { 86534624Sbostic while (t > startp && *--t == '0'); 86634624Sbostic if (*t != '.') 86734624Sbostic ++t; 86834624Sbostic } 86934624Sbostic } 87046076Sbostic return (t - startp); 87134624Sbostic } 87234248Sbostic 87334624Sbostic static char * 87434669Sbostic round(fract, exp, start, end, ch, signp) 87534624Sbostic double fract; 87634669Sbostic int *exp; 87734624Sbostic register char *start, *end; 87834669Sbostic char ch, *signp; 87934624Sbostic { 88034624Sbostic double tmp; 88134248Sbostic 88234624Sbostic if (fract) 88346076Sbostic (void)modf(fract * 10, &tmp); 88434624Sbostic else 88546076Sbostic tmp = to_digit(ch); 88634624Sbostic if (tmp > 4) 88734624Sbostic for (;; --end) { 88834624Sbostic if (*end == '.') 88934624Sbostic --end; 89034624Sbostic if (++*end <= '9') 89134624Sbostic break; 89234624Sbostic *end = '0'; 89334624Sbostic if (end == start) { 89434669Sbostic if (exp) { /* e/E; increment exponent */ 89534669Sbostic *end = '1'; 89634669Sbostic ++*exp; 89734669Sbostic } 89834669Sbostic else { /* f; add extra digit */ 89946076Sbostic *--end = '1'; 90046076Sbostic --start; 90134669Sbostic } 90234624Sbostic break; 90334242Sbostic } 90434242Sbostic } 90534669Sbostic /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 90634669Sbostic else if (*signp == '-') 90734669Sbostic for (;; --end) { 90834669Sbostic if (*end == '.') 90934669Sbostic --end; 91034669Sbostic if (*end != '0') 91134669Sbostic break; 91234669Sbostic if (end == start) 91334669Sbostic *signp = 0; 91434669Sbostic } 91546076Sbostic return (start); 91634624Sbostic } 91734248Sbostic 91834624Sbostic static char * 91934624Sbostic exponent(p, exp, fmtch) 92034624Sbostic register char *p; 92134624Sbostic register int exp; 92246076Sbostic int fmtch; 92334624Sbostic { 92434624Sbostic register char *t; 92534624Sbostic char expbuf[MAXEXP]; 92634248Sbostic 92734624Sbostic *p++ = fmtch; 92834624Sbostic if (exp < 0) { 92934624Sbostic exp = -exp; 93034624Sbostic *p++ = '-'; 93134242Sbostic } 93234624Sbostic else 93334624Sbostic *p++ = '+'; 93434624Sbostic t = expbuf + MAXEXP; 93534624Sbostic if (exp > 9) { 93634624Sbostic do { 93746076Sbostic *--t = to_char(exp % 10); 93834624Sbostic } while ((exp /= 10) > 9); 93946076Sbostic *--t = to_char(exp); 94034624Sbostic for (; t < expbuf + MAXEXP; *p++ = *t++); 94134624Sbostic } 94234624Sbostic else { 94334624Sbostic *p++ = '0'; 94446076Sbostic *p++ = to_char(exp); 94534624Sbostic } 94646076Sbostic return (p); 94734242Sbostic } 94846076Sbostic #endif /* FLOATING_POINT */ 949