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*46180Storek static char sccsid[] = "@(#)vfprintf.c 5.43 (Berkeley) 01/31/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 50*46180Storek #if __STDC__ 51*46180Storek int 52*46180Storek printf(const char *fmt, ...) { 53*46180Storek FILE f; 54*46180Storek 55*46180Storek f._flags = __SWR; 56*46180Storek return (vfprintf(&f, fmt, (va_list)(&fmt + 1))); 57*46180Storek } 58*46180Storek #else 59*46180Storek int 6046076Sbostic printf(fmt, args) 6146076Sbostic char *fmt; 6246076Sbostic { 6346076Sbostic FILE f; 6446076Sbostic 6546076Sbostic f._flags = __SWR; 6646076Sbostic return (vfprintf(&f, fmt, &args)); 6746076Sbostic } 68*46180Storek #endif 6946076Sbostic 7046076Sbostic #define __sprint(fp, uio) cshprintv(uio) 7146076Sbostic 72*46180Storek int 7346076Sbostic cshprintv(uio) 7446076Sbostic register struct __suio *uio; 7546076Sbostic { 7646076Sbostic register char *p; 7746076Sbostic register int n, ch, iovcnt; 7846076Sbostic register struct __siov *iov = uio->uio_iov; 7946076Sbostic 8046076Sbostic for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) { 8146076Sbostic for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) { 8246076Sbostic #ifdef CSHPUTCHAR 8346076Sbostic ch = *p++; 8446076Sbostic CSHPUTCHAR; /* this horrid macro uses `ch' */ 8546076Sbostic #else 8646076Sbostic #undef putchar 8746076Sbostic putchar(*p++); 8846076Sbostic #endif 8946076Sbostic } 9046076Sbostic } 9146076Sbostic uio->uio_resid = 0; 9246076Sbostic uio->uio_iovcnt = 0; 9346076Sbostic return (0); 9446076Sbostic } 9546076Sbostic 9646076Sbostic #else /* CSH */ 9746076Sbostic 9846076Sbostic /* 9946076Sbostic * Flush out all the vectors defined by the given uio, 10046076Sbostic * then reset it so that it can be reused. 10146076Sbostic */ 102*46180Storek static int 10346076Sbostic __sprint(fp, uio) 10446076Sbostic FILE *fp; 10546076Sbostic register struct __suio *uio; 10646076Sbostic { 10746076Sbostic register int err; 10846076Sbostic 10946076Sbostic if (uio->uio_resid == 0) { 11046076Sbostic uio->uio_iovcnt = 0; 11146076Sbostic return (0); 11246076Sbostic } 11346076Sbostic err = __sfvwrite(fp, uio); 11446076Sbostic uio->uio_resid = 0; 11546076Sbostic uio->uio_iovcnt = 0; 11646076Sbostic return (err); 11746076Sbostic } 11846076Sbostic 11946076Sbostic /* 12046076Sbostic * Helper function for `fprintf to unbuffered unix file': creates a 12146076Sbostic * temporary buffer. We only work on write-only files; this avoids 12246076Sbostic * worries about ungetc buffers and so forth. 12346076Sbostic */ 124*46180Storek static int 12546076Sbostic __sbprintf(fp, fmt, ap) 12646076Sbostic register FILE *fp; 127*46180Storek const char *fmt; 12846076Sbostic va_list ap; 12946076Sbostic { 13046076Sbostic int ret; 13146076Sbostic FILE fake; 13246076Sbostic unsigned char buf[BUFSIZ]; 13346076Sbostic 13446076Sbostic /* copy the important variables */ 13546076Sbostic fake._flags = fp->_flags & ~__SNBF; 13646076Sbostic fake._file = fp->_file; 13746076Sbostic fake._cookie = fp->_cookie; 13846076Sbostic fake._write = fp->_write; 13946076Sbostic 14046076Sbostic /* set up the buffer */ 14146076Sbostic fake._bf._base = fake._p = buf; 14246076Sbostic fake._bf._size = fake._w = sizeof(buf); 14346076Sbostic fake._lbfsize = 0; /* not actually used, but Just In Case */ 14446076Sbostic 14546076Sbostic /* do the work, then copy any error status */ 14646076Sbostic ret = vfprintf(&fake, fmt, ap); 14746076Sbostic if (ret >= 0 && fflush(&fake)) 14846076Sbostic ret = EOF; 14946076Sbostic if (fake._flags & __SERR) 15046076Sbostic fp->_flags |= __SERR; 15146076Sbostic return (ret); 15246076Sbostic } 15346076Sbostic 15446076Sbostic #endif /* CSH */ 15546076Sbostic 15646076Sbostic 15746076Sbostic #ifdef FLOATING_POINT 158*46180Storek #include "floatio.h" 15946076Sbostic 16034328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 16146076Sbostic #define DEFPREC 6 16234328Sbostic 16346076Sbostic static int cvt(); 16446126Storek #if defined(hp300) || defined(sparc) 16546126Storek static char *isspecial(); 16646126Storek #endif 16734236Sbostic 16846076Sbostic #else /* no FLOATING_POINT */ 16934235Sbostic 17046076Sbostic #define BUF 40 17134331Sbostic 17246076Sbostic #endif /* FLOATING_POINT */ 17334318Sbostic 17446076Sbostic 17546076Sbostic /* 17646076Sbostic * Macros for converting digits to letters and vice versa 17746076Sbostic */ 17846076Sbostic #define to_digit(c) ((c) - '0') 17946076Sbostic #define is_digit(c) ((unsigned)to_digit(c) <= 9) 18046076Sbostic #define to_char(n) ((n) + '0') 18146076Sbostic 18246076Sbostic /* 18346076Sbostic * Flags used during conversion. 18446076Sbostic */ 18534318Sbostic #define LONGINT 0x01 /* long integer */ 18634318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 18734318Sbostic #define SHORTINT 0x04 /* short integer */ 18834318Sbostic #define ALT 0x08 /* alternate form */ 18934318Sbostic #define LADJUST 0x10 /* left adjustment */ 19034427Sbostic #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 19134427Sbostic #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 19234318Sbostic 193*46180Storek int 19446076Sbostic vfprintf(fp, fmt0, ap) 19546076Sbostic FILE *fp; 196*46180Storek const char *fmt0; 19746076Sbostic #if tahoe 19846076Sbostic register /* technically illegal, since we do not know what type va_list is */ 19946076Sbostic #endif 20046076Sbostic va_list ap; 20134226Sbostic { 20246076Sbostic register char *fmt; /* format string */ 20334427Sbostic register int ch; /* character from fmt */ 20446076Sbostic register int n; /* handy integer (short term usage) */ 20546076Sbostic register char *cp; /* handy char pointer (short term usage) */ 20646076Sbostic register struct __siov *iovp;/* for PRINT macro */ 20746076Sbostic register int flags; /* flags as above */ 20846076Sbostic int ret; /* return value accumulator */ 20946076Sbostic int width; /* width from format (%8d), or 0 */ 21046076Sbostic int prec; /* precision from format (%.3d), or -1 */ 21146076Sbostic char sign; /* sign prefix (' ', '+', '-', or \0) */ 21246076Sbostic #ifdef FLOATING_POINT 21346076Sbostic char softsign; /* temporary negative sign for floats */ 21434427Sbostic double _double; /* double precision arguments %[eEfgG] */ 21546076Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 21646076Sbostic #endif 21734427Sbostic u_long _ulong; /* integer arguments %[diouxX] */ 21846076Sbostic enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 21946076Sbostic int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 22034624Sbostic int fieldsz; /* field size expanded by sign, etc */ 22146076Sbostic int realsz; /* field size expanded by dprec */ 22234427Sbostic int size; /* size of converted field or string */ 22346076Sbostic char *xdigs; /* digits for [xX] conversion */ 22446076Sbostic #define NIOV 8 22546076Sbostic struct __suio uio; /* output information: summary */ 22646076Sbostic struct __siov iov[NIOV];/* ... and individual io vectors */ 22734427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 22846076Sbostic char ox[2]; /* space for 0x hex-prefix */ 22934226Sbostic 23046076Sbostic /* 23146076Sbostic * Choose PADSIZE to trade efficiency vs size. If larger 23246076Sbostic * printf fields occur frequently, increase PADSIZE (and make 23346076Sbostic * the initialisers below longer). 23446076Sbostic */ 23546076Sbostic #define PADSIZE 16 /* pad chunk size */ 23646076Sbostic static char blanks[PADSIZE] = 23746076Sbostic {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 23846076Sbostic static char zeroes[PADSIZE] = 23946076Sbostic {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 24046076Sbostic 24146076Sbostic /* 24246076Sbostic * BEWARE, these `goto error' on error, and PAD uses `n'. 24346076Sbostic */ 24446076Sbostic #define PRINT(ptr, len) { \ 24546076Sbostic iovp->iov_base = (ptr); \ 24646076Sbostic iovp->iov_len = (len); \ 24746076Sbostic uio.uio_resid += (len); \ 24846076Sbostic iovp++; \ 24946076Sbostic if (++uio.uio_iovcnt >= NIOV) { \ 25046076Sbostic if (__sprint(fp, &uio)) \ 25146076Sbostic goto error; \ 25246076Sbostic iovp = iov; \ 25346076Sbostic } \ 25446076Sbostic } 25546076Sbostic #define PAD(howmany, with) { \ 25646076Sbostic if ((n = (howmany)) > 0) { \ 25746076Sbostic while (n > PADSIZE) { \ 25846076Sbostic PRINT(with, PADSIZE); \ 25946076Sbostic n -= PADSIZE; \ 26046076Sbostic } \ 26146076Sbostic PRINT(with, n); \ 26246076Sbostic } \ 26346076Sbostic } 26446076Sbostic #define FLUSH() { \ 26546076Sbostic if (uio.uio_resid && __sprint(fp, &uio)) \ 26646076Sbostic goto error; \ 26746076Sbostic uio.uio_iovcnt = 0; \ 26846076Sbostic iovp = iov; \ 26946076Sbostic } 27046076Sbostic 27146076Sbostic /* 27246076Sbostic * To extend shorts properly, we need both signed and unsigned 27346076Sbostic * argument extraction methods. 27446076Sbostic */ 27546076Sbostic #define SARG() \ 27646076Sbostic (flags&LONGINT ? va_arg(ap, long) : \ 27746076Sbostic flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ 27846076Sbostic (long)va_arg(ap, int)) 27946076Sbostic #define UARG() \ 28046076Sbostic (flags&LONGINT ? va_arg(ap, u_long) : \ 28146076Sbostic flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ 28246076Sbostic (u_long)va_arg(ap, u_int)) 28346076Sbostic 28446076Sbostic #ifndef CSH 28546076Sbostic /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 28646076Sbostic if (cantwrite(fp)) 28734428Sbostic return (EOF); 28834428Sbostic 28946076Sbostic /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 29046076Sbostic if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 29146076Sbostic fp->_file >= 0) 29246076Sbostic return (__sbprintf(fp, fmt0, ap)); 29346076Sbostic #endif /* CSH */ 29446076Sbostic 29546076Sbostic fmt = (char *)fmt0; 29646076Sbostic uio.uio_iov = iovp = iov; 29746076Sbostic uio.uio_resid = 0; 29846076Sbostic uio.uio_iovcnt = 0; 29946076Sbostic ret = 0; 30046076Sbostic 30146076Sbostic /* 30246076Sbostic * Scan the format for conversions (`%' character). 30346076Sbostic */ 30446076Sbostic for (;;) { 30546076Sbostic for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 30646076Sbostic /* void */; 30746076Sbostic if ((n = fmt - cp) != 0) { 30846076Sbostic PRINT(cp, n); 30946076Sbostic ret += n; 31046076Sbostic } 31146076Sbostic if (ch == '\0') 31246076Sbostic goto done; 31346076Sbostic fmt++; /* skip over '%' */ 31446076Sbostic 31546076Sbostic flags = 0; 31646076Sbostic dprec = 0; 31746076Sbostic #ifdef FLOATING_POINT 31846076Sbostic fpprec = 0; 31934318Sbostic #endif 32046076Sbostic width = 0; 32134233Sbostic prec = -1; 32234318Sbostic sign = '\0'; 32334226Sbostic 32446076Sbostic rflag: ch = *fmt++; 32546076Sbostic reswitch: switch (ch) { 32634318Sbostic case ' ': 32734669Sbostic /* 32834669Sbostic * ``If the space and + flags both appear, the space 32934669Sbostic * flag will be ignored.'' 33034669Sbostic * -- ANSI X3J11 33134669Sbostic */ 33234669Sbostic if (!sign) 33334669Sbostic sign = ' '; 33434318Sbostic goto rflag; 33534233Sbostic case '#': 33634318Sbostic flags |= ALT; 33734318Sbostic goto rflag; 33834233Sbostic case '*': 33934235Sbostic /* 34034235Sbostic * ``A negative field width argument is taken as a 34146076Sbostic * - flag followed by a positive field width.'' 34234235Sbostic * -- ANSI X3J11 34334235Sbostic * They don't exclude field widths read from args. 34434235Sbostic */ 34546076Sbostic if ((width = va_arg(ap, int)) >= 0) 34634318Sbostic goto rflag; 34734235Sbostic width = -width; 34834427Sbostic /* FALLTHROUGH */ 34934235Sbostic case '-': 35034318Sbostic flags |= LADJUST; 35134318Sbostic goto rflag; 35234233Sbostic case '+': 35334314Sbostic sign = '+'; 35434318Sbostic goto rflag; 35534233Sbostic case '.': 35646076Sbostic if ((ch = *fmt++) == '*') { 35746076Sbostic n = va_arg(ap, int); 35846076Sbostic prec = n < 0 ? -1 : n; 35946076Sbostic goto rflag; 36034226Sbostic } 36146076Sbostic n = 0; 36246076Sbostic while (is_digit(ch)) { 36346076Sbostic n = 10 * n + to_digit(ch); 36446076Sbostic ch = *fmt++; 36546076Sbostic } 36634318Sbostic prec = n < 0 ? -1 : n; 36746076Sbostic goto reswitch; 36834233Sbostic case '0': 36934427Sbostic /* 37034427Sbostic * ``Note that 0 is taken as a flag, not as the 37134427Sbostic * beginning of a field width.'' 37234427Sbostic * -- ANSI X3J11 37334427Sbostic */ 37434427Sbostic flags |= ZEROPAD; 37534427Sbostic goto rflag; 37634233Sbostic case '1': case '2': case '3': case '4': 37734233Sbostic case '5': case '6': case '7': case '8': case '9': 37834318Sbostic n = 0; 37934233Sbostic do { 38046076Sbostic n = 10 * n + to_digit(ch); 38146076Sbostic ch = *fmt++; 38246076Sbostic } while (is_digit(ch)); 38334318Sbostic width = n; 38446076Sbostic goto reswitch; 38546076Sbostic #ifdef FLOATING_POINT 38634235Sbostic case 'L': 38734329Sbostic flags |= LONGDBL; 38834318Sbostic goto rflag; 38946076Sbostic #endif 39034235Sbostic case 'h': 39134318Sbostic flags |= SHORTINT; 39234318Sbostic goto rflag; 39334233Sbostic case 'l': 39434318Sbostic flags |= LONGINT; 39534318Sbostic goto rflag; 39634314Sbostic case 'c': 39746076Sbostic *(cp = buf) = va_arg(ap, int); 39834314Sbostic size = 1; 39934427Sbostic sign = '\0'; 40046076Sbostic break; 40134624Sbostic case 'D': 40234624Sbostic flags |= LONGINT; 40334624Sbostic /*FALLTHROUGH*/ 40434314Sbostic case 'd': 40534318Sbostic case 'i': 40646076Sbostic _ulong = SARG(); 40734318Sbostic if ((long)_ulong < 0) { 40834318Sbostic _ulong = -_ulong; 40934314Sbostic sign = '-'; 41034241Sbostic } 41146076Sbostic base = DEC; 41234327Sbostic goto number; 41346076Sbostic #ifdef FLOATING_POINT 41434261Sbostic case 'e': 41534236Sbostic case 'E': 41634235Sbostic case 'f': 41734261Sbostic case 'g': 41834243Sbostic case 'G': 41946076Sbostic _double = va_arg(ap, double); 42046126Storek #if defined(hp300) || defined(sparc) 42146126Storek /* do this before tricky precision changes */ 42246126Storek if ((cp = isspecial(_double, &sign)) != NULL) { 42346126Storek size = strlen(cp); 42446126Storek break; 42546126Storek } 42646126Storek #endif 42734328Sbostic /* 42834669Sbostic * don't do unrealistic precision; just pad it with 42934669Sbostic * zeroes later, so buffer size stays rational. 43034328Sbostic */ 43134328Sbostic if (prec > MAXFRACT) { 43246076Sbostic if (ch != 'g' && ch != 'G' || (flags&ALT)) 43334475Sbostic fpprec = prec - MAXFRACT; 43434328Sbostic prec = MAXFRACT; 43546076Sbostic } else if (prec == -1) 43634624Sbostic prec = DEFPREC; 43734669Sbostic /* 43846076Sbostic * cvt may have to round up before the "start" of 43946076Sbostic * its buffer, i.e. ``intf("%.2f", (double)9.999);''; 44046076Sbostic * if the first character is still NUL, it did. 44146076Sbostic * softsign avoids negative 0 if _double < 0 but 44246076Sbostic * no significant digits will be shown. 44334669Sbostic */ 44446076Sbostic cp = buf; 44546076Sbostic *cp = '\0'; 44646076Sbostic size = cvt(_double, prec, flags, &softsign, ch, 44746076Sbostic cp, buf + sizeof(buf)); 44834669Sbostic if (softsign) 44934669Sbostic sign = '-'; 45046076Sbostic if (*cp == '\0') 45146076Sbostic cp++; 45246076Sbostic break; 45346076Sbostic #endif /* FLOATING_POINT */ 45434235Sbostic case 'n': 45534427Sbostic if (flags & LONGINT) 45646076Sbostic *va_arg(ap, long *) = ret; 45734427Sbostic else if (flags & SHORTINT) 45846076Sbostic *va_arg(ap, short *) = ret; 45934318Sbostic else 46046076Sbostic *va_arg(ap, int *) = ret; 46146076Sbostic continue; /* no output */ 46234624Sbostic case 'O': 46334624Sbostic flags |= LONGINT; 46434624Sbostic /*FALLTHROUGH*/ 46534226Sbostic case 'o': 46646076Sbostic _ulong = UARG(); 46746076Sbostic base = OCT; 46834327Sbostic goto nosign; 46934235Sbostic case 'p': 47034320Sbostic /* 47134321Sbostic * ``The argument shall be a pointer to void. The 47234321Sbostic * value of the pointer is converted to a sequence 47334321Sbostic * of printable characters, in an implementation- 47434321Sbostic * defined manner.'' 47534321Sbostic * -- ANSI X3J11 47634320Sbostic */ 47734427Sbostic /* NOSTRICT */ 47846076Sbostic _ulong = (u_long)va_arg(ap, void *); 47946076Sbostic base = HEX; 48046076Sbostic xdigs = "0123456789abcdef"; 48146076Sbostic flags |= HEXPREFIX; 48246076Sbostic ch = 'x'; 48334327Sbostic goto nosign; 48434226Sbostic case 's': 48546076Sbostic if ((cp = va_arg(ap, char *)) == NULL) 48646076Sbostic cp = "(null)"; 48734321Sbostic if (prec >= 0) { 48834321Sbostic /* 48934321Sbostic * can't use strlen; can only look for the 49034321Sbostic * NUL in the first `prec' characters, and 49134321Sbostic * strlen() will go further. 49234321Sbostic */ 49346076Sbostic char *p = memchr(cp, 0, prec); 49434321Sbostic 49546076Sbostic if (p != NULL) { 49646076Sbostic size = p - cp; 49734321Sbostic if (size > prec) 49834321Sbostic size = prec; 49934427Sbostic } else 50034321Sbostic size = prec; 50134427Sbostic } else 50246076Sbostic size = strlen(cp); 50334427Sbostic sign = '\0'; 50446076Sbostic break; 50534624Sbostic case 'U': 50634624Sbostic flags |= LONGINT; 50734624Sbostic /*FALLTHROUGH*/ 50834226Sbostic case 'u': 50946076Sbostic _ulong = UARG(); 51046076Sbostic base = DEC; 51134327Sbostic goto nosign; 51234226Sbostic case 'X': 51346076Sbostic xdigs = "0123456789ABCDEF"; 51446076Sbostic goto hex; 51534226Sbostic case 'x': 51646076Sbostic xdigs = "0123456789abcdef"; 51746076Sbostic hex: _ulong = UARG(); 51846076Sbostic base = HEX; 51934326Sbostic /* leading 0x/X only if non-zero */ 52034427Sbostic if (flags & ALT && _ulong != 0) 52134427Sbostic flags |= HEXPREFIX; 52234327Sbostic 52334327Sbostic /* unsigned conversions */ 52434427Sbostic nosign: sign = '\0'; 52534326Sbostic /* 52634330Sbostic * ``... diouXx conversions ... if a precision is 52734330Sbostic * specified, the 0 flag will be ignored.'' 52834330Sbostic * -- ANSI X3J11 52934330Sbostic */ 53034427Sbostic number: if ((dprec = prec) >= 0) 53134427Sbostic flags &= ~ZEROPAD; 53234427Sbostic 53334330Sbostic /* 53434326Sbostic * ``The result of converting a zero value with an 53534326Sbostic * explicit precision of zero is no characters.'' 53634326Sbostic * -- ANSI X3J11 53734326Sbostic */ 53846076Sbostic cp = buf + BUF; 53934427Sbostic if (_ulong != 0 || prec != 0) { 54046076Sbostic /* 54146076Sbostic * unsigned mod is hard, and unsigned mod 54246076Sbostic * by a constant is easier than that by 54346076Sbostic * a variable; hence this switch. 54446076Sbostic */ 54546076Sbostic switch (base) { 54646076Sbostic case OCT: 54746076Sbostic do { 54846076Sbostic *--cp = to_char(_ulong & 7); 54946076Sbostic _ulong >>= 3; 55046076Sbostic } while (_ulong); 55146076Sbostic /* handle octal leading 0 */ 55246076Sbostic if (flags & ALT && *cp != '0') 55346076Sbostic *--cp = '0'; 55446076Sbostic break; 55534327Sbostic 55646076Sbostic case DEC: 55746076Sbostic /* many numbers are 1 digit */ 55846076Sbostic while (_ulong >= 10) { 55946076Sbostic *--cp = to_char(_ulong % 10); 56046076Sbostic _ulong /= 10; 56146076Sbostic } 56246076Sbostic *--cp = to_char(_ulong); 56346076Sbostic break; 56434327Sbostic 56546076Sbostic case HEX: 56646076Sbostic do { 56746076Sbostic *--cp = xdigs[_ulong & 15]; 56846076Sbostic _ulong >>= 4; 56946076Sbostic } while (_ulong); 57046076Sbostic break; 57134327Sbostic 57246076Sbostic default: 57346076Sbostic cp = "bug in vfprintf: bad base"; 574*46180Storek size = strlen(cp); 57546076Sbostic goto skipsize; 57646076Sbostic } 57734327Sbostic } 57846076Sbostic size = buf + BUF - cp; 57946076Sbostic skipsize: 58034226Sbostic break; 58146076Sbostic default: /* "%?" prints ?, unless ? is NUL */ 58246076Sbostic if (ch == '\0') 58346076Sbostic goto done; 58446076Sbostic /* pretend it was %c with argument ch */ 58546076Sbostic cp = buf; 58646076Sbostic *cp = ch; 58746076Sbostic size = 1; 58846076Sbostic sign = '\0'; 58946076Sbostic break; 59034226Sbostic } 59146076Sbostic 59246076Sbostic /* 59346076Sbostic * All reasonable formats wind up here. At this point, 59446076Sbostic * `cp' points to a string which (if not flags&LADJUST) 59546076Sbostic * should be padded out to `width' places. If 59646076Sbostic * flags&ZEROPAD, it should first be prefixed by any 59746076Sbostic * sign or other prefix; otherwise, it should be blank 59846076Sbostic * padded before the prefix is emitted. After any 59946076Sbostic * left-hand padding and prefixing, emit zeroes 60046076Sbostic * required by a decimal [diouxX] precision, then print 60146076Sbostic * the string proper, then emit zeroes required by any 60246076Sbostic * leftover floating precision; finally, if LADJUST, 60346076Sbostic * pad with blanks. 60446076Sbostic */ 60546076Sbostic 60646076Sbostic /* 60746076Sbostic * compute actual size, so we know how much to pad. 60846076Sbostic * fieldsz excludes decimal prec; realsz includes it 60946076Sbostic */ 61046076Sbostic #ifdef FLOATING_POINT 61146076Sbostic fieldsz = size + fpprec; 61246076Sbostic #else 61346076Sbostic fieldsz = size; 61446076Sbostic #endif 61546076Sbostic if (sign) 61646076Sbostic fieldsz++; 61746076Sbostic else if (flags & HEXPREFIX) 61846076Sbostic fieldsz += 2; 61946076Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 62046076Sbostic 62146076Sbostic /* right-adjusting blank padding */ 62246076Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0) 62346076Sbostic PAD(width - realsz, blanks); 62446076Sbostic 62546076Sbostic /* prefix */ 62646076Sbostic if (sign) { 62746076Sbostic PRINT(&sign, 1); 62846076Sbostic } else if (flags & HEXPREFIX) { 62946076Sbostic ox[0] = '0'; 63046076Sbostic ox[1] = ch; 63146076Sbostic PRINT(ox, 2); 63246076Sbostic } 63346076Sbostic 63446076Sbostic /* right-adjusting zero padding */ 63546076Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 63646076Sbostic PAD(width - realsz, zeroes); 63746076Sbostic 63846076Sbostic /* leading zeroes from decimal precision */ 63946076Sbostic PAD(dprec - fieldsz, zeroes); 64046076Sbostic 64146076Sbostic /* the string or number proper */ 64246076Sbostic PRINT(cp, size); 64346076Sbostic 64446076Sbostic #ifdef FLOATING_POINT 64546076Sbostic /* trailing f.p. zeroes */ 64646076Sbostic PAD(fpprec, zeroes); 64746076Sbostic #endif 64846076Sbostic 64946076Sbostic /* left-adjusting padding (always blank) */ 65046076Sbostic if (flags & LADJUST) 65146076Sbostic PAD(width - realsz, blanks); 65246076Sbostic 65346076Sbostic /* finally, adjust ret */ 65446076Sbostic ret += width > realsz ? width : realsz; 65546076Sbostic 65646076Sbostic FLUSH(); /* copy out the I/O vectors */ 65734226Sbostic } 65846076Sbostic done: 65946076Sbostic FLUSH(); 66046076Sbostic error: 66146076Sbostic return (__sferror(fp) ? EOF : ret); 66234427Sbostic /* NOTREACHED */ 66334226Sbostic } 66434242Sbostic 66546076Sbostic #ifdef FLOATING_POINT 666*46180Storek #include <math.h> 667*46180Storek 66846076Sbostic static char *exponent(); 66946076Sbostic static char *round(); 67046076Sbostic 67146126Storek #if defined(hp300) || defined(sparc) 67246126Storek /* 67346126Storek * Check for special IEEE format values (NaN, Inf). 67446126Storek */ 67546126Storek static char * 67646126Storek isspecial(d, signp) 67746126Storek double d; 67846161Storek char *signp; 67946126Storek { 68046126Storek register struct IEEEdp { 68146126Storek unsigned sign:1; 68246126Storek unsigned exp:11; 68346126Storek unsigned manh:20; 68446126Storek unsigned manl:32; 68546126Storek } *ip = (struct IEEEdp *)&d; 68646126Storek 68746126Storek if (ip->exp != 0x7ff) 68846126Storek return (NULL); 68946126Storek if (ip->sign) 69046126Storek *signp = '-'; 69146161Storek return (ip->manh || ip->manl ? "NaN" : "Inf"); 69246126Storek } 69346126Storek #endif /* hp300 or sparc */ 69446126Storek 695*46180Storek static int 69634669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp) 69734242Sbostic double number; 69834261Sbostic register int prec; 69934323Sbostic int flags; 70046076Sbostic char *signp; 70146076Sbostic int fmtch; 70246076Sbostic char *startp, *endp; 70334242Sbostic { 70434331Sbostic register char *p, *t; 70534672Sbostic register double fract; 70634624Sbostic int dotrim, expcnt, gformat; 707*46180Storek double integer, tmp; 70834242Sbostic 70946076Sbostic dotrim = expcnt = gformat = 0; 71046076Sbostic if (number < 0) { 71146076Sbostic number = -number; 71246076Sbostic *signp = '-'; 71346076Sbostic } else 71446076Sbostic *signp = 0; 71544426Sbostic 71634624Sbostic fract = modf(number, &integer); 71734242Sbostic 71834624Sbostic /* get an extra slot for rounding. */ 71934624Sbostic t = ++startp; 72034624Sbostic 72134624Sbostic /* 72234624Sbostic * get integer portion of number; put into the end of the buffer; the 72334624Sbostic * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 72434624Sbostic */ 72534624Sbostic for (p = endp - 1; integer; ++expcnt) { 72634624Sbostic tmp = modf(integer / 10, &integer); 72746076Sbostic *p-- = to_char((int)((tmp + .01) * 10)); 72834248Sbostic } 72946076Sbostic switch (fmtch) { 73034261Sbostic case 'f': 73134624Sbostic /* reverse integer into beginning of buffer */ 73234624Sbostic if (expcnt) 73334624Sbostic for (; ++p < endp; *t++ = *p); 73434624Sbostic else 73534624Sbostic *t++ = '0'; 73634248Sbostic /* 73734624Sbostic * if precision required or alternate flag set, add in a 73834624Sbostic * decimal point. 73934248Sbostic */ 74034624Sbostic if (prec || flags&ALT) 74134624Sbostic *t++ = '.'; 74234624Sbostic /* if requires more precision and some fraction left */ 74334624Sbostic if (fract) { 74434624Sbostic if (prec) 74534624Sbostic do { 74634624Sbostic fract = modf(fract * 10, &tmp); 74746076Sbostic *t++ = to_char((int)tmp); 74834624Sbostic } while (--prec && fract); 74934624Sbostic if (fract) 75034669Sbostic startp = round(fract, (int *)NULL, startp, 75134669Sbostic t - 1, (char)0, signp); 75234624Sbostic } 75334624Sbostic for (; prec--; *t++ = '0'); 75434624Sbostic break; 75534624Sbostic case 'e': 75634624Sbostic case 'E': 75734624Sbostic eformat: if (expcnt) { 75834624Sbostic *t++ = *++p; 75934624Sbostic if (prec || flags&ALT) 76034331Sbostic *t++ = '.'; 76134624Sbostic /* if requires more precision and some integer left */ 76234624Sbostic for (; prec && ++p < endp; --prec) 76334624Sbostic *t++ = *p; 76434624Sbostic /* 76534624Sbostic * if done precision and more of the integer component, 76634624Sbostic * round using it; adjust fract so we don't re-round 76734624Sbostic * later. 76834624Sbostic */ 76934624Sbostic if (!prec && ++p < endp) { 77034248Sbostic fract = 0; 77134669Sbostic startp = round((double)0, &expcnt, startp, 77234669Sbostic t - 1, *p, signp); 77334248Sbostic } 77434624Sbostic /* adjust expcnt for digit in front of decimal */ 77534624Sbostic --expcnt; 77634242Sbostic } 77734624Sbostic /* until first fractional digit, decrement exponent */ 77834624Sbostic else if (fract) { 77934624Sbostic /* adjust expcnt for digit in front of decimal */ 78034624Sbostic for (expcnt = -1;; --expcnt) { 78134624Sbostic fract = modf(fract * 10, &tmp); 78234624Sbostic if (tmp) 78334624Sbostic break; 78434248Sbostic } 78546076Sbostic *t++ = to_char((int)tmp); 78634624Sbostic if (prec || flags&ALT) 78734331Sbostic *t++ = '.'; 78834242Sbostic } 78934248Sbostic else { 79034624Sbostic *t++ = '0'; 79134624Sbostic if (prec || flags&ALT) 79234331Sbostic *t++ = '.'; 79334248Sbostic } 79434624Sbostic /* if requires more precision and some fraction left */ 79534624Sbostic if (fract) { 79634624Sbostic if (prec) 79734624Sbostic do { 79834624Sbostic fract = modf(fract * 10, &tmp); 79946076Sbostic *t++ = to_char((int)tmp); 80034624Sbostic } while (--prec && fract); 80134624Sbostic if (fract) 80234669Sbostic startp = round(fract, &expcnt, startp, 80334669Sbostic t - 1, (char)0, signp); 80434584Sbostic } 80534624Sbostic /* if requires more precision */ 80634624Sbostic for (; prec--; *t++ = '0'); 80734624Sbostic 80834624Sbostic /* unless alternate flag, trim any g/G format trailing 0's */ 80934624Sbostic if (gformat && !(flags&ALT)) { 81034624Sbostic while (t > startp && *--t == '0'); 81134624Sbostic if (*t == '.') 81234624Sbostic --t; 81334624Sbostic ++t; 81434624Sbostic } 81534624Sbostic t = exponent(t, expcnt, fmtch); 81634624Sbostic break; 81734624Sbostic case 'g': 81834624Sbostic case 'G': 81934624Sbostic /* a precision of 0 is treated as a precision of 1. */ 82034624Sbostic if (!prec) 82134624Sbostic ++prec; 82234624Sbostic /* 82334624Sbostic * ``The style used depends on the value converted; style e 82434624Sbostic * will be used only if the exponent resulting from the 82534624Sbostic * conversion is less than -4 or greater than the precision.'' 82634624Sbostic * -- ANSI X3J11 82734624Sbostic */ 82834624Sbostic if (expcnt > prec || !expcnt && fract && fract < .0001) { 82934624Sbostic /* 83034624Sbostic * g/G format counts "significant digits, not digits of 83134624Sbostic * precision; for the e/E format, this just causes an 83234624Sbostic * off-by-one problem, i.e. g/G considers the digit 83334624Sbostic * before the decimal point significant and e/E doesn't 83434624Sbostic * count it as precision. 83534624Sbostic */ 83634624Sbostic --prec; 83734624Sbostic fmtch -= 2; /* G->E, g->e */ 83834624Sbostic gformat = 1; 83934624Sbostic goto eformat; 84034624Sbostic } 84134624Sbostic /* 84234624Sbostic * reverse integer into beginning of buffer, 84334624Sbostic * note, decrement precision 84434624Sbostic */ 84534624Sbostic if (expcnt) 84634624Sbostic for (; ++p < endp; *t++ = *p, --prec); 84734624Sbostic else 84834624Sbostic *t++ = '0'; 84934624Sbostic /* 85034624Sbostic * if precision required or alternate flag set, add in a 85134624Sbostic * decimal point. If no digits yet, add in leading 0. 85234624Sbostic */ 85334624Sbostic if (prec || flags&ALT) { 85434624Sbostic dotrim = 1; 85534624Sbostic *t++ = '.'; 85634624Sbostic } 85734624Sbostic else 85834624Sbostic dotrim = 0; 85934624Sbostic /* if requires more precision and some fraction left */ 86034624Sbostic if (fract) { 86134624Sbostic if (prec) { 86234624Sbostic do { 86334624Sbostic fract = modf(fract * 10, &tmp); 86446076Sbostic *t++ = to_char((int)tmp); 86534624Sbostic } while(!tmp); 86634624Sbostic while (--prec && fract) { 86734624Sbostic fract = modf(fract * 10, &tmp); 86846076Sbostic *t++ = to_char((int)tmp); 86934248Sbostic } 87034248Sbostic } 87134624Sbostic if (fract) 87234669Sbostic startp = round(fract, (int *)NULL, startp, 87334669Sbostic t - 1, (char)0, signp); 87434624Sbostic } 87534624Sbostic /* alternate format, adds 0's for precision, else trim 0's */ 87634624Sbostic if (flags&ALT) 87734624Sbostic for (; prec--; *t++ = '0'); 87834624Sbostic else if (dotrim) { 87934624Sbostic while (t > startp && *--t == '0'); 88034624Sbostic if (*t != '.') 88134624Sbostic ++t; 88234624Sbostic } 88334624Sbostic } 88446076Sbostic return (t - startp); 88534624Sbostic } 88634248Sbostic 88734624Sbostic static char * 88834669Sbostic round(fract, exp, start, end, ch, signp) 88934624Sbostic double fract; 89034669Sbostic int *exp; 89134624Sbostic register char *start, *end; 89234669Sbostic char ch, *signp; 89334624Sbostic { 89434624Sbostic double tmp; 89534248Sbostic 89634624Sbostic if (fract) 897*46180Storek (void)modf(fract * 10, &tmp); 89834624Sbostic else 89946076Sbostic tmp = to_digit(ch); 90034624Sbostic if (tmp > 4) 90134624Sbostic for (;; --end) { 90234624Sbostic if (*end == '.') 90334624Sbostic --end; 90434624Sbostic if (++*end <= '9') 90534624Sbostic break; 90634624Sbostic *end = '0'; 90734624Sbostic if (end == start) { 90834669Sbostic if (exp) { /* e/E; increment exponent */ 90934669Sbostic *end = '1'; 91034669Sbostic ++*exp; 91134669Sbostic } 91234669Sbostic else { /* f; add extra digit */ 91346076Sbostic *--end = '1'; 91446076Sbostic --start; 91534669Sbostic } 91634624Sbostic break; 91734242Sbostic } 91834242Sbostic } 91934669Sbostic /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 92034669Sbostic else if (*signp == '-') 92134669Sbostic for (;; --end) { 92234669Sbostic if (*end == '.') 92334669Sbostic --end; 92434669Sbostic if (*end != '0') 92534669Sbostic break; 92634669Sbostic if (end == start) 92734669Sbostic *signp = 0; 92834669Sbostic } 92946076Sbostic return (start); 93034624Sbostic } 93134248Sbostic 93234624Sbostic static char * 93334624Sbostic exponent(p, exp, fmtch) 93434624Sbostic register char *p; 93534624Sbostic register int exp; 93646076Sbostic int fmtch; 93734624Sbostic { 93834624Sbostic register char *t; 93934624Sbostic char expbuf[MAXEXP]; 94034248Sbostic 94134624Sbostic *p++ = fmtch; 94234624Sbostic if (exp < 0) { 94334624Sbostic exp = -exp; 94434624Sbostic *p++ = '-'; 94534242Sbostic } 94634624Sbostic else 94734624Sbostic *p++ = '+'; 94834624Sbostic t = expbuf + MAXEXP; 94934624Sbostic if (exp > 9) { 95034624Sbostic do { 95146076Sbostic *--t = to_char(exp % 10); 95234624Sbostic } while ((exp /= 10) > 9); 95346076Sbostic *--t = to_char(exp); 95434624Sbostic for (; t < expbuf + MAXEXP; *p++ = *t++); 95534624Sbostic } 95634624Sbostic else { 95734624Sbostic *p++ = '0'; 95846076Sbostic *p++ = to_char(exp); 95934624Sbostic } 96046076Sbostic return (p); 96134242Sbostic } 96246076Sbostic #endif /* FLOATING_POINT */ 963