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*46611Sbostic static char sccsid[] = "@(#)vfprintf.c 5.45 (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> 22*46611Sbostic #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; 5546180Storek 5646180Storek f._flags = __SWR; 5746180Storek return (vfprintf(&f, fmt, (va_list)(&fmt + 1))); 5846180Storek } 5946180Storek #else 6046180Storek int 6146076Sbostic printf(fmt, args) 6246076Sbostic char *fmt; 6346076Sbostic { 6446076Sbostic FILE f; 6546076Sbostic 6646076Sbostic f._flags = __SWR; 6746442Storek f._write = NULL; 6846076Sbostic return (vfprintf(&f, fmt, &args)); 6946076Sbostic } 7046180Storek #endif 7146076Sbostic 7246180Storek int 7346442Storek __sprint(fp, uio) 7446442Storek FILE *fp; 7546076Sbostic register struct __suio *uio; 7646076Sbostic { 7746076Sbostic register char *p; 7846076Sbostic register int n, ch, iovcnt; 7946442Storek register struct __siov *iov; 8046076Sbostic 8146442Storek /* must allow sprintf to work, might as well allow others too */ 8246442Storek if (fp->_write || fp->_flags & __SSTR) { 8346442Storek if (uio->uio_resid == 0) { 8446442Storek uio->uio_iovcnt = 0; 8546442Storek return (0); 8646442Storek } 8746442Storek n = __sfvwrite(fp, uio); 8846442Storek uio->uio_resid = 0; 8946442Storek uio->uio_iovcnt = 0; 9046442Storek return (n); 9146442Storek } 9246442Storek iov = uio->uio_iov; 9346076Sbostic for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) { 9446076Sbostic for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) { 9546076Sbostic #ifdef CSHPUTCHAR 9646076Sbostic ch = *p++; 9746076Sbostic CSHPUTCHAR; /* this horrid macro uses `ch' */ 9846076Sbostic #else 9946076Sbostic #undef putchar 10046076Sbostic putchar(*p++); 10146076Sbostic #endif 10246076Sbostic } 10346076Sbostic } 10446076Sbostic uio->uio_resid = 0; 10546076Sbostic uio->uio_iovcnt = 0; 10646076Sbostic return (0); 10746076Sbostic } 10846076Sbostic 10946076Sbostic #else /* CSH */ 11046076Sbostic 11146076Sbostic /* 11246076Sbostic * Flush out all the vectors defined by the given uio, 11346076Sbostic * then reset it so that it can be reused. 11446076Sbostic */ 11546180Storek static int 11646076Sbostic __sprint(fp, uio) 11746076Sbostic FILE *fp; 11846076Sbostic register struct __suio *uio; 11946076Sbostic { 12046076Sbostic register int err; 12146076Sbostic 12246076Sbostic if (uio->uio_resid == 0) { 12346076Sbostic uio->uio_iovcnt = 0; 12446076Sbostic return (0); 12546076Sbostic } 12646076Sbostic err = __sfvwrite(fp, uio); 12746076Sbostic uio->uio_resid = 0; 12846076Sbostic uio->uio_iovcnt = 0; 12946076Sbostic return (err); 13046076Sbostic } 13146076Sbostic 13246076Sbostic /* 13346076Sbostic * Helper function for `fprintf to unbuffered unix file': creates a 13446076Sbostic * temporary buffer. We only work on write-only files; this avoids 13546076Sbostic * worries about ungetc buffers and so forth. 13646076Sbostic */ 13746180Storek static int 13846076Sbostic __sbprintf(fp, fmt, ap) 13946076Sbostic register FILE *fp; 14046180Storek const char *fmt; 14146076Sbostic va_list ap; 14246076Sbostic { 14346076Sbostic int ret; 14446076Sbostic FILE fake; 14546076Sbostic unsigned char buf[BUFSIZ]; 14646076Sbostic 14746076Sbostic /* copy the important variables */ 14846076Sbostic fake._flags = fp->_flags & ~__SNBF; 14946076Sbostic fake._file = fp->_file; 15046076Sbostic fake._cookie = fp->_cookie; 15146076Sbostic fake._write = fp->_write; 15246076Sbostic 15346076Sbostic /* set up the buffer */ 15446076Sbostic fake._bf._base = fake._p = buf; 15546076Sbostic fake._bf._size = fake._w = sizeof(buf); 15646076Sbostic fake._lbfsize = 0; /* not actually used, but Just In Case */ 15746076Sbostic 15846076Sbostic /* do the work, then copy any error status */ 15946076Sbostic ret = vfprintf(&fake, fmt, ap); 16046076Sbostic if (ret >= 0 && fflush(&fake)) 16146076Sbostic ret = EOF; 16246076Sbostic if (fake._flags & __SERR) 16346076Sbostic fp->_flags |= __SERR; 16446076Sbostic return (ret); 16546076Sbostic } 16646076Sbostic 16746076Sbostic #endif /* CSH */ 16846076Sbostic 16946076Sbostic 17046076Sbostic #ifdef FLOATING_POINT 17146180Storek #include "floatio.h" 17246076Sbostic 17334328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 17446076Sbostic #define DEFPREC 6 17534328Sbostic 17646076Sbostic static int cvt(); 17746126Storek #if defined(hp300) || defined(sparc) 17846126Storek static char *isspecial(); 17946126Storek #endif 18034236Sbostic 18146076Sbostic #else /* no FLOATING_POINT */ 18234235Sbostic 18346076Sbostic #define BUF 40 18434331Sbostic 18546076Sbostic #endif /* FLOATING_POINT */ 18634318Sbostic 18746076Sbostic 18846076Sbostic /* 18946076Sbostic * Macros for converting digits to letters and vice versa 19046076Sbostic */ 19146076Sbostic #define to_digit(c) ((c) - '0') 19246076Sbostic #define is_digit(c) ((unsigned)to_digit(c) <= 9) 19346076Sbostic #define to_char(n) ((n) + '0') 19446076Sbostic 19546076Sbostic /* 19646076Sbostic * Flags used during conversion. 19746076Sbostic */ 19834318Sbostic #define LONGINT 0x01 /* long integer */ 19934318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 20034318Sbostic #define SHORTINT 0x04 /* short integer */ 20134318Sbostic #define ALT 0x08 /* alternate form */ 20234318Sbostic #define LADJUST 0x10 /* left adjustment */ 20334427Sbostic #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 20434427Sbostic #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 20534318Sbostic 20646180Storek int 20746076Sbostic vfprintf(fp, fmt0, ap) 20846076Sbostic FILE *fp; 20946180Storek const char *fmt0; 21046076Sbostic #if tahoe 21146076Sbostic register /* technically illegal, since we do not know what type va_list is */ 21246076Sbostic #endif 21346076Sbostic va_list ap; 21434226Sbostic { 21546076Sbostic register char *fmt; /* format string */ 21634427Sbostic register int ch; /* character from fmt */ 21746076Sbostic register int n; /* handy integer (short term usage) */ 21846076Sbostic register char *cp; /* handy char pointer (short term usage) */ 21946076Sbostic register struct __siov *iovp;/* for PRINT macro */ 22046076Sbostic register int flags; /* flags as above */ 22146076Sbostic int ret; /* return value accumulator */ 22246076Sbostic int width; /* width from format (%8d), or 0 */ 22346076Sbostic int prec; /* precision from format (%.3d), or -1 */ 22446076Sbostic char sign; /* sign prefix (' ', '+', '-', or \0) */ 22546076Sbostic #ifdef FLOATING_POINT 22646076Sbostic char softsign; /* temporary negative sign for floats */ 22734427Sbostic double _double; /* double precision arguments %[eEfgG] */ 22846076Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 22946076Sbostic #endif 23034427Sbostic u_long _ulong; /* integer arguments %[diouxX] */ 23146076Sbostic enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 23246076Sbostic int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 23334624Sbostic int fieldsz; /* field size expanded by sign, etc */ 23446076Sbostic int realsz; /* field size expanded by dprec */ 23534427Sbostic int size; /* size of converted field or string */ 23646076Sbostic char *xdigs; /* digits for [xX] conversion */ 23746076Sbostic #define NIOV 8 23846076Sbostic struct __suio uio; /* output information: summary */ 23946076Sbostic struct __siov iov[NIOV];/* ... and individual io vectors */ 24034427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 24146076Sbostic char ox[2]; /* space for 0x hex-prefix */ 24234226Sbostic 24346076Sbostic /* 24446076Sbostic * Choose PADSIZE to trade efficiency vs size. If larger 24546076Sbostic * printf fields occur frequently, increase PADSIZE (and make 24646076Sbostic * the initialisers below longer). 24746076Sbostic */ 24846076Sbostic #define PADSIZE 16 /* pad chunk size */ 24946076Sbostic static char blanks[PADSIZE] = 25046076Sbostic {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 25146076Sbostic static char zeroes[PADSIZE] = 25246076Sbostic {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 25346076Sbostic 25446076Sbostic /* 25546076Sbostic * BEWARE, these `goto error' on error, and PAD uses `n'. 25646076Sbostic */ 25746076Sbostic #define PRINT(ptr, len) { \ 25846076Sbostic iovp->iov_base = (ptr); \ 25946076Sbostic iovp->iov_len = (len); \ 26046076Sbostic uio.uio_resid += (len); \ 26146076Sbostic iovp++; \ 26246076Sbostic if (++uio.uio_iovcnt >= NIOV) { \ 26346076Sbostic if (__sprint(fp, &uio)) \ 26446076Sbostic goto error; \ 26546076Sbostic iovp = iov; \ 26646076Sbostic } \ 26746076Sbostic } 26846076Sbostic #define PAD(howmany, with) { \ 26946076Sbostic if ((n = (howmany)) > 0) { \ 27046076Sbostic while (n > PADSIZE) { \ 27146076Sbostic PRINT(with, PADSIZE); \ 27246076Sbostic n -= PADSIZE; \ 27346076Sbostic } \ 27446076Sbostic PRINT(with, n); \ 27546076Sbostic } \ 27646076Sbostic } 27746076Sbostic #define FLUSH() { \ 27846076Sbostic if (uio.uio_resid && __sprint(fp, &uio)) \ 27946076Sbostic goto error; \ 28046076Sbostic uio.uio_iovcnt = 0; \ 28146076Sbostic iovp = iov; \ 28246076Sbostic } 28346076Sbostic 28446076Sbostic /* 28546076Sbostic * To extend shorts properly, we need both signed and unsigned 28646076Sbostic * argument extraction methods. 28746076Sbostic */ 28846076Sbostic #define SARG() \ 28946076Sbostic (flags&LONGINT ? va_arg(ap, long) : \ 29046076Sbostic flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ 29146076Sbostic (long)va_arg(ap, int)) 29246076Sbostic #define UARG() \ 29346076Sbostic (flags&LONGINT ? va_arg(ap, u_long) : \ 29446076Sbostic flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ 29546076Sbostic (u_long)va_arg(ap, u_int)) 29646076Sbostic 29746076Sbostic #ifndef CSH 29846076Sbostic /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 29946076Sbostic if (cantwrite(fp)) 30034428Sbostic return (EOF); 30134428Sbostic 30246076Sbostic /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 30346076Sbostic if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 30446076Sbostic fp->_file >= 0) 30546076Sbostic return (__sbprintf(fp, fmt0, ap)); 30646076Sbostic #endif /* CSH */ 30746076Sbostic 30846076Sbostic fmt = (char *)fmt0; 30946076Sbostic uio.uio_iov = iovp = iov; 31046076Sbostic uio.uio_resid = 0; 31146076Sbostic uio.uio_iovcnt = 0; 31246076Sbostic ret = 0; 31346076Sbostic 31446076Sbostic /* 31546076Sbostic * Scan the format for conversions (`%' character). 31646076Sbostic */ 31746076Sbostic for (;;) { 31846076Sbostic for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 31946076Sbostic /* void */; 32046076Sbostic if ((n = fmt - cp) != 0) { 32146076Sbostic PRINT(cp, n); 32246076Sbostic ret += n; 32346076Sbostic } 32446076Sbostic if (ch == '\0') 32546076Sbostic goto done; 32646076Sbostic fmt++; /* skip over '%' */ 32746076Sbostic 32846076Sbostic flags = 0; 32946076Sbostic dprec = 0; 33046076Sbostic #ifdef FLOATING_POINT 33146076Sbostic fpprec = 0; 33234318Sbostic #endif 33346076Sbostic width = 0; 33434233Sbostic prec = -1; 33534318Sbostic sign = '\0'; 33634226Sbostic 33746076Sbostic rflag: ch = *fmt++; 33846076Sbostic reswitch: switch (ch) { 33934318Sbostic case ' ': 34034669Sbostic /* 34134669Sbostic * ``If the space and + flags both appear, the space 34234669Sbostic * flag will be ignored.'' 34334669Sbostic * -- ANSI X3J11 34434669Sbostic */ 34534669Sbostic if (!sign) 34634669Sbostic sign = ' '; 34734318Sbostic goto rflag; 34834233Sbostic case '#': 34934318Sbostic flags |= ALT; 35034318Sbostic goto rflag; 35134233Sbostic case '*': 35234235Sbostic /* 35334235Sbostic * ``A negative field width argument is taken as a 35446076Sbostic * - flag followed by a positive field width.'' 35534235Sbostic * -- ANSI X3J11 35634235Sbostic * They don't exclude field widths read from args. 35734235Sbostic */ 35846076Sbostic if ((width = va_arg(ap, int)) >= 0) 35934318Sbostic goto rflag; 36034235Sbostic width = -width; 36134427Sbostic /* FALLTHROUGH */ 36234235Sbostic case '-': 36334318Sbostic flags |= LADJUST; 36434318Sbostic goto rflag; 36534233Sbostic case '+': 36634314Sbostic sign = '+'; 36734318Sbostic goto rflag; 36834233Sbostic case '.': 36946076Sbostic if ((ch = *fmt++) == '*') { 37046076Sbostic n = va_arg(ap, int); 37146076Sbostic prec = n < 0 ? -1 : n; 37246076Sbostic goto rflag; 37334226Sbostic } 37446076Sbostic n = 0; 37546076Sbostic while (is_digit(ch)) { 37646076Sbostic n = 10 * n + to_digit(ch); 37746076Sbostic ch = *fmt++; 37846076Sbostic } 37934318Sbostic prec = n < 0 ? -1 : n; 38046076Sbostic goto reswitch; 38134233Sbostic case '0': 38234427Sbostic /* 38334427Sbostic * ``Note that 0 is taken as a flag, not as the 38434427Sbostic * beginning of a field width.'' 38534427Sbostic * -- ANSI X3J11 38634427Sbostic */ 38734427Sbostic flags |= ZEROPAD; 38834427Sbostic goto rflag; 38934233Sbostic case '1': case '2': case '3': case '4': 39034233Sbostic case '5': case '6': case '7': case '8': case '9': 39134318Sbostic n = 0; 39234233Sbostic do { 39346076Sbostic n = 10 * n + to_digit(ch); 39446076Sbostic ch = *fmt++; 39546076Sbostic } while (is_digit(ch)); 39634318Sbostic width = n; 39746076Sbostic goto reswitch; 39846076Sbostic #ifdef FLOATING_POINT 39934235Sbostic case 'L': 40034329Sbostic flags |= LONGDBL; 40134318Sbostic goto rflag; 40246076Sbostic #endif 40334235Sbostic case 'h': 40434318Sbostic flags |= SHORTINT; 40534318Sbostic goto rflag; 40634233Sbostic case 'l': 40734318Sbostic flags |= LONGINT; 40834318Sbostic goto rflag; 40934314Sbostic case 'c': 41046076Sbostic *(cp = buf) = va_arg(ap, int); 41134314Sbostic size = 1; 41234427Sbostic sign = '\0'; 41346076Sbostic break; 41434624Sbostic case 'D': 41534624Sbostic flags |= LONGINT; 41634624Sbostic /*FALLTHROUGH*/ 41734314Sbostic case 'd': 41834318Sbostic case 'i': 41946076Sbostic _ulong = SARG(); 42034318Sbostic if ((long)_ulong < 0) { 42134318Sbostic _ulong = -_ulong; 42234314Sbostic sign = '-'; 42334241Sbostic } 42446076Sbostic base = DEC; 42534327Sbostic goto number; 42646076Sbostic #ifdef FLOATING_POINT 42734261Sbostic case 'e': 42834236Sbostic case 'E': 42934235Sbostic case 'f': 43034261Sbostic case 'g': 43134243Sbostic case 'G': 43246076Sbostic _double = va_arg(ap, double); 43346126Storek #if defined(hp300) || defined(sparc) 43446126Storek /* do this before tricky precision changes */ 43546126Storek if ((cp = isspecial(_double, &sign)) != NULL) { 43646126Storek size = strlen(cp); 43746126Storek break; 43846126Storek } 43946126Storek #endif 44034328Sbostic /* 44134669Sbostic * don't do unrealistic precision; just pad it with 44234669Sbostic * zeroes later, so buffer size stays rational. 44334328Sbostic */ 44434328Sbostic if (prec > MAXFRACT) { 44546076Sbostic if (ch != 'g' && ch != 'G' || (flags&ALT)) 44634475Sbostic fpprec = prec - MAXFRACT; 44734328Sbostic prec = MAXFRACT; 44846076Sbostic } else if (prec == -1) 44934624Sbostic prec = DEFPREC; 45034669Sbostic /* 45146076Sbostic * cvt may have to round up before the "start" of 45246076Sbostic * its buffer, i.e. ``intf("%.2f", (double)9.999);''; 45346076Sbostic * if the first character is still NUL, it did. 45446076Sbostic * softsign avoids negative 0 if _double < 0 but 45546076Sbostic * no significant digits will be shown. 45634669Sbostic */ 45746076Sbostic cp = buf; 45846076Sbostic *cp = '\0'; 45946076Sbostic size = cvt(_double, prec, flags, &softsign, ch, 46046076Sbostic cp, buf + sizeof(buf)); 46134669Sbostic if (softsign) 46234669Sbostic sign = '-'; 46346076Sbostic if (*cp == '\0') 46446076Sbostic cp++; 46546076Sbostic break; 46646076Sbostic #endif /* FLOATING_POINT */ 46734235Sbostic case 'n': 46834427Sbostic if (flags & LONGINT) 46946076Sbostic *va_arg(ap, long *) = ret; 47034427Sbostic else if (flags & SHORTINT) 47146076Sbostic *va_arg(ap, short *) = ret; 47234318Sbostic else 47346076Sbostic *va_arg(ap, int *) = ret; 47446076Sbostic continue; /* no output */ 47534624Sbostic case 'O': 47634624Sbostic flags |= LONGINT; 47734624Sbostic /*FALLTHROUGH*/ 47834226Sbostic case 'o': 47946076Sbostic _ulong = UARG(); 48046076Sbostic base = OCT; 48134327Sbostic goto nosign; 48234235Sbostic case 'p': 48334320Sbostic /* 48434321Sbostic * ``The argument shall be a pointer to void. The 48534321Sbostic * value of the pointer is converted to a sequence 48634321Sbostic * of printable characters, in an implementation- 48734321Sbostic * defined manner.'' 48834321Sbostic * -- ANSI X3J11 48934320Sbostic */ 49034427Sbostic /* NOSTRICT */ 49146076Sbostic _ulong = (u_long)va_arg(ap, void *); 49246076Sbostic base = HEX; 49346076Sbostic xdigs = "0123456789abcdef"; 49446076Sbostic flags |= HEXPREFIX; 49546076Sbostic ch = 'x'; 49634327Sbostic goto nosign; 49734226Sbostic case 's': 49846076Sbostic if ((cp = va_arg(ap, char *)) == NULL) 49946076Sbostic cp = "(null)"; 50034321Sbostic if (prec >= 0) { 50134321Sbostic /* 50234321Sbostic * can't use strlen; can only look for the 50334321Sbostic * NUL in the first `prec' characters, and 50434321Sbostic * strlen() will go further. 50534321Sbostic */ 50646076Sbostic char *p = memchr(cp, 0, prec); 50734321Sbostic 50846076Sbostic if (p != NULL) { 50946076Sbostic size = p - cp; 51034321Sbostic if (size > prec) 51134321Sbostic size = prec; 51234427Sbostic } else 51334321Sbostic size = prec; 51434427Sbostic } else 51546076Sbostic size = strlen(cp); 51634427Sbostic sign = '\0'; 51746076Sbostic break; 51834624Sbostic case 'U': 51934624Sbostic flags |= LONGINT; 52034624Sbostic /*FALLTHROUGH*/ 52134226Sbostic case 'u': 52246076Sbostic _ulong = UARG(); 52346076Sbostic base = DEC; 52434327Sbostic goto nosign; 52534226Sbostic case 'X': 52646076Sbostic xdigs = "0123456789ABCDEF"; 52746076Sbostic goto hex; 52834226Sbostic case 'x': 52946076Sbostic xdigs = "0123456789abcdef"; 53046076Sbostic hex: _ulong = UARG(); 53146076Sbostic base = HEX; 53234326Sbostic /* leading 0x/X only if non-zero */ 53334427Sbostic if (flags & ALT && _ulong != 0) 53434427Sbostic flags |= HEXPREFIX; 53534327Sbostic 53634327Sbostic /* unsigned conversions */ 53734427Sbostic nosign: sign = '\0'; 53834326Sbostic /* 53934330Sbostic * ``... diouXx conversions ... if a precision is 54034330Sbostic * specified, the 0 flag will be ignored.'' 54134330Sbostic * -- ANSI X3J11 54234330Sbostic */ 54334427Sbostic number: if ((dprec = prec) >= 0) 54434427Sbostic flags &= ~ZEROPAD; 54534427Sbostic 54634330Sbostic /* 54734326Sbostic * ``The result of converting a zero value with an 54834326Sbostic * explicit precision of zero is no characters.'' 54934326Sbostic * -- ANSI X3J11 55034326Sbostic */ 55146076Sbostic cp = buf + BUF; 55234427Sbostic if (_ulong != 0 || prec != 0) { 55346076Sbostic /* 55446076Sbostic * unsigned mod is hard, and unsigned mod 55546076Sbostic * by a constant is easier than that by 55646076Sbostic * a variable; hence this switch. 55746076Sbostic */ 55846076Sbostic switch (base) { 55946076Sbostic case OCT: 56046076Sbostic do { 56146076Sbostic *--cp = to_char(_ulong & 7); 56246076Sbostic _ulong >>= 3; 56346076Sbostic } while (_ulong); 56446076Sbostic /* handle octal leading 0 */ 56546076Sbostic if (flags & ALT && *cp != '0') 56646076Sbostic *--cp = '0'; 56746076Sbostic break; 56834327Sbostic 56946076Sbostic case DEC: 57046076Sbostic /* many numbers are 1 digit */ 57146076Sbostic while (_ulong >= 10) { 57246076Sbostic *--cp = to_char(_ulong % 10); 57346076Sbostic _ulong /= 10; 57446076Sbostic } 57546076Sbostic *--cp = to_char(_ulong); 57646076Sbostic break; 57734327Sbostic 57846076Sbostic case HEX: 57946076Sbostic do { 58046076Sbostic *--cp = xdigs[_ulong & 15]; 58146076Sbostic _ulong >>= 4; 58246076Sbostic } while (_ulong); 58346076Sbostic break; 58434327Sbostic 58546076Sbostic default: 58646076Sbostic cp = "bug in vfprintf: bad base"; 58746180Storek size = strlen(cp); 58846076Sbostic goto skipsize; 58946076Sbostic } 59034327Sbostic } 59146076Sbostic size = buf + BUF - cp; 59246076Sbostic skipsize: 59334226Sbostic break; 59446076Sbostic default: /* "%?" prints ?, unless ? is NUL */ 59546076Sbostic if (ch == '\0') 59646076Sbostic goto done; 59746076Sbostic /* pretend it was %c with argument ch */ 59846076Sbostic cp = buf; 59946076Sbostic *cp = ch; 60046076Sbostic size = 1; 60146076Sbostic sign = '\0'; 60246076Sbostic break; 60334226Sbostic } 60446076Sbostic 60546076Sbostic /* 60646076Sbostic * All reasonable formats wind up here. At this point, 60746076Sbostic * `cp' points to a string which (if not flags&LADJUST) 60846076Sbostic * should be padded out to `width' places. If 60946076Sbostic * flags&ZEROPAD, it should first be prefixed by any 61046076Sbostic * sign or other prefix; otherwise, it should be blank 61146076Sbostic * padded before the prefix is emitted. After any 61246076Sbostic * left-hand padding and prefixing, emit zeroes 61346076Sbostic * required by a decimal [diouxX] precision, then print 61446076Sbostic * the string proper, then emit zeroes required by any 61546076Sbostic * leftover floating precision; finally, if LADJUST, 61646076Sbostic * pad with blanks. 61746076Sbostic */ 61846076Sbostic 61946076Sbostic /* 62046076Sbostic * compute actual size, so we know how much to pad. 62146076Sbostic * fieldsz excludes decimal prec; realsz includes it 62246076Sbostic */ 62346076Sbostic #ifdef FLOATING_POINT 62446076Sbostic fieldsz = size + fpprec; 62546076Sbostic #else 62646076Sbostic fieldsz = size; 62746076Sbostic #endif 62846076Sbostic if (sign) 62946076Sbostic fieldsz++; 63046076Sbostic else if (flags & HEXPREFIX) 63146076Sbostic fieldsz += 2; 63246076Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 63346076Sbostic 63446076Sbostic /* right-adjusting blank padding */ 63546076Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0) 63646076Sbostic PAD(width - realsz, blanks); 63746076Sbostic 63846076Sbostic /* prefix */ 63946076Sbostic if (sign) { 64046076Sbostic PRINT(&sign, 1); 64146076Sbostic } else if (flags & HEXPREFIX) { 64246076Sbostic ox[0] = '0'; 64346076Sbostic ox[1] = ch; 64446076Sbostic PRINT(ox, 2); 64546076Sbostic } 64646076Sbostic 64746076Sbostic /* right-adjusting zero padding */ 64846076Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 64946076Sbostic PAD(width - realsz, zeroes); 65046076Sbostic 65146076Sbostic /* leading zeroes from decimal precision */ 65246076Sbostic PAD(dprec - fieldsz, zeroes); 65346076Sbostic 65446076Sbostic /* the string or number proper */ 65546076Sbostic PRINT(cp, size); 65646076Sbostic 65746076Sbostic #ifdef FLOATING_POINT 65846076Sbostic /* trailing f.p. zeroes */ 65946076Sbostic PAD(fpprec, zeroes); 66046076Sbostic #endif 66146076Sbostic 66246076Sbostic /* left-adjusting padding (always blank) */ 66346076Sbostic if (flags & LADJUST) 66446076Sbostic PAD(width - realsz, blanks); 66546076Sbostic 66646076Sbostic /* finally, adjust ret */ 66746076Sbostic ret += width > realsz ? width : realsz; 66846076Sbostic 66946076Sbostic FLUSH(); /* copy out the I/O vectors */ 67034226Sbostic } 67146076Sbostic done: 67246076Sbostic FLUSH(); 67346076Sbostic error: 67446076Sbostic return (__sferror(fp) ? EOF : ret); 67534427Sbostic /* NOTREACHED */ 67634226Sbostic } 67734242Sbostic 67846076Sbostic #ifdef FLOATING_POINT 67946180Storek #include <math.h> 68046180Storek 68146076Sbostic static char *exponent(); 68246076Sbostic static char *round(); 68346076Sbostic 68446126Storek #if defined(hp300) || defined(sparc) 68546126Storek /* 68646126Storek * Check for special IEEE format values (NaN, Inf). 68746126Storek */ 68846126Storek static char * 68946126Storek isspecial(d, signp) 69046126Storek double d; 69146161Storek char *signp; 69246126Storek { 69346126Storek register struct IEEEdp { 69446126Storek unsigned sign:1; 69546126Storek unsigned exp:11; 69646126Storek unsigned manh:20; 69746126Storek unsigned manl:32; 69846126Storek } *ip = (struct IEEEdp *)&d; 69946126Storek 70046126Storek if (ip->exp != 0x7ff) 70146126Storek return (NULL); 70246126Storek if (ip->sign) 70346126Storek *signp = '-'; 70446161Storek return (ip->manh || ip->manl ? "NaN" : "Inf"); 70546126Storek } 70646126Storek #endif /* hp300 or sparc */ 70746126Storek 70846180Storek static int 70934669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp) 71034242Sbostic double number; 71134261Sbostic register int prec; 71234323Sbostic int flags; 71346076Sbostic char *signp; 71446076Sbostic int fmtch; 71546076Sbostic char *startp, *endp; 71634242Sbostic { 71734331Sbostic register char *p, *t; 71834672Sbostic register double fract; 71934624Sbostic int dotrim, expcnt, gformat; 72046180Storek double integer, tmp; 72134242Sbostic 72246076Sbostic dotrim = expcnt = gformat = 0; 72346076Sbostic if (number < 0) { 72446076Sbostic number = -number; 72546076Sbostic *signp = '-'; 72646076Sbostic } else 72746076Sbostic *signp = 0; 72844426Sbostic 72934624Sbostic fract = modf(number, &integer); 73034242Sbostic 73134624Sbostic /* get an extra slot for rounding. */ 73234624Sbostic t = ++startp; 73334624Sbostic 73434624Sbostic /* 73534624Sbostic * get integer portion of number; put into the end of the buffer; the 73634624Sbostic * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 73734624Sbostic */ 73834624Sbostic for (p = endp - 1; integer; ++expcnt) { 73934624Sbostic tmp = modf(integer / 10, &integer); 74046076Sbostic *p-- = to_char((int)((tmp + .01) * 10)); 74134248Sbostic } 74246076Sbostic switch (fmtch) { 74334261Sbostic case 'f': 74434624Sbostic /* reverse integer into beginning of buffer */ 74534624Sbostic if (expcnt) 74634624Sbostic for (; ++p < endp; *t++ = *p); 74734624Sbostic else 74834624Sbostic *t++ = '0'; 74934248Sbostic /* 75034624Sbostic * if precision required or alternate flag set, add in a 75134624Sbostic * decimal point. 75234248Sbostic */ 75334624Sbostic if (prec || flags&ALT) 75434624Sbostic *t++ = '.'; 75534624Sbostic /* if requires more precision and some fraction left */ 75634624Sbostic if (fract) { 75734624Sbostic if (prec) 75834624Sbostic do { 75934624Sbostic fract = modf(fract * 10, &tmp); 76046076Sbostic *t++ = to_char((int)tmp); 76134624Sbostic } while (--prec && fract); 76234624Sbostic if (fract) 76334669Sbostic startp = round(fract, (int *)NULL, startp, 76434669Sbostic t - 1, (char)0, signp); 76534624Sbostic } 76634624Sbostic for (; prec--; *t++ = '0'); 76734624Sbostic break; 76834624Sbostic case 'e': 76934624Sbostic case 'E': 77034624Sbostic eformat: if (expcnt) { 77134624Sbostic *t++ = *++p; 77234624Sbostic if (prec || flags&ALT) 77334331Sbostic *t++ = '.'; 77434624Sbostic /* if requires more precision and some integer left */ 77534624Sbostic for (; prec && ++p < endp; --prec) 77634624Sbostic *t++ = *p; 77734624Sbostic /* 77834624Sbostic * if done precision and more of the integer component, 77934624Sbostic * round using it; adjust fract so we don't re-round 78034624Sbostic * later. 78134624Sbostic */ 78234624Sbostic if (!prec && ++p < endp) { 78334248Sbostic fract = 0; 78434669Sbostic startp = round((double)0, &expcnt, startp, 78534669Sbostic t - 1, *p, signp); 78634248Sbostic } 78734624Sbostic /* adjust expcnt for digit in front of decimal */ 78834624Sbostic --expcnt; 78934242Sbostic } 79034624Sbostic /* until first fractional digit, decrement exponent */ 79134624Sbostic else if (fract) { 79234624Sbostic /* adjust expcnt for digit in front of decimal */ 79334624Sbostic for (expcnt = -1;; --expcnt) { 79434624Sbostic fract = modf(fract * 10, &tmp); 79534624Sbostic if (tmp) 79634624Sbostic break; 79734248Sbostic } 79846076Sbostic *t++ = to_char((int)tmp); 79934624Sbostic if (prec || flags&ALT) 80034331Sbostic *t++ = '.'; 80134242Sbostic } 80234248Sbostic else { 80334624Sbostic *t++ = '0'; 80434624Sbostic if (prec || flags&ALT) 80534331Sbostic *t++ = '.'; 80634248Sbostic } 80734624Sbostic /* if requires more precision and some fraction left */ 80834624Sbostic if (fract) { 80934624Sbostic if (prec) 81034624Sbostic do { 81134624Sbostic fract = modf(fract * 10, &tmp); 81246076Sbostic *t++ = to_char((int)tmp); 81334624Sbostic } while (--prec && fract); 81434624Sbostic if (fract) 81534669Sbostic startp = round(fract, &expcnt, startp, 81634669Sbostic t - 1, (char)0, signp); 81734584Sbostic } 81834624Sbostic /* if requires more precision */ 81934624Sbostic for (; prec--; *t++ = '0'); 82034624Sbostic 82134624Sbostic /* unless alternate flag, trim any g/G format trailing 0's */ 82234624Sbostic if (gformat && !(flags&ALT)) { 82334624Sbostic while (t > startp && *--t == '0'); 82434624Sbostic if (*t == '.') 82534624Sbostic --t; 82634624Sbostic ++t; 82734624Sbostic } 82834624Sbostic t = exponent(t, expcnt, fmtch); 82934624Sbostic break; 83034624Sbostic case 'g': 83134624Sbostic case 'G': 83234624Sbostic /* a precision of 0 is treated as a precision of 1. */ 83334624Sbostic if (!prec) 83434624Sbostic ++prec; 83534624Sbostic /* 83634624Sbostic * ``The style used depends on the value converted; style e 83734624Sbostic * will be used only if the exponent resulting from the 83834624Sbostic * conversion is less than -4 or greater than the precision.'' 83934624Sbostic * -- ANSI X3J11 84034624Sbostic */ 84134624Sbostic if (expcnt > prec || !expcnt && fract && fract < .0001) { 84234624Sbostic /* 84334624Sbostic * g/G format counts "significant digits, not digits of 84434624Sbostic * precision; for the e/E format, this just causes an 84534624Sbostic * off-by-one problem, i.e. g/G considers the digit 84634624Sbostic * before the decimal point significant and e/E doesn't 84734624Sbostic * count it as precision. 84834624Sbostic */ 84934624Sbostic --prec; 85034624Sbostic fmtch -= 2; /* G->E, g->e */ 85134624Sbostic gformat = 1; 85234624Sbostic goto eformat; 85334624Sbostic } 85434624Sbostic /* 85534624Sbostic * reverse integer into beginning of buffer, 85634624Sbostic * note, decrement precision 85734624Sbostic */ 85834624Sbostic if (expcnt) 85934624Sbostic for (; ++p < endp; *t++ = *p, --prec); 86034624Sbostic else 86134624Sbostic *t++ = '0'; 86234624Sbostic /* 86334624Sbostic * if precision required or alternate flag set, add in a 86434624Sbostic * decimal point. If no digits yet, add in leading 0. 86534624Sbostic */ 86634624Sbostic if (prec || flags&ALT) { 86734624Sbostic dotrim = 1; 86834624Sbostic *t++ = '.'; 86934624Sbostic } 87034624Sbostic else 87134624Sbostic dotrim = 0; 87234624Sbostic /* if requires more precision and some fraction left */ 87334624Sbostic if (fract) { 87434624Sbostic if (prec) { 87534624Sbostic do { 87634624Sbostic fract = modf(fract * 10, &tmp); 87746076Sbostic *t++ = to_char((int)tmp); 87834624Sbostic } while(!tmp); 87934624Sbostic while (--prec && fract) { 88034624Sbostic fract = modf(fract * 10, &tmp); 88146076Sbostic *t++ = to_char((int)tmp); 88234248Sbostic } 88334248Sbostic } 88434624Sbostic if (fract) 88534669Sbostic startp = round(fract, (int *)NULL, startp, 88634669Sbostic t - 1, (char)0, signp); 88734624Sbostic } 88834624Sbostic /* alternate format, adds 0's for precision, else trim 0's */ 88934624Sbostic if (flags&ALT) 89034624Sbostic for (; prec--; *t++ = '0'); 89134624Sbostic else if (dotrim) { 89234624Sbostic while (t > startp && *--t == '0'); 89334624Sbostic if (*t != '.') 89434624Sbostic ++t; 89534624Sbostic } 89634624Sbostic } 89746076Sbostic return (t - startp); 89834624Sbostic } 89934248Sbostic 90034624Sbostic static char * 90134669Sbostic round(fract, exp, start, end, ch, signp) 90234624Sbostic double fract; 90334669Sbostic int *exp; 90434624Sbostic register char *start, *end; 90534669Sbostic char ch, *signp; 90634624Sbostic { 90734624Sbostic double tmp; 90834248Sbostic 90934624Sbostic if (fract) 91046180Storek (void)modf(fract * 10, &tmp); 91134624Sbostic else 91246076Sbostic tmp = to_digit(ch); 91334624Sbostic if (tmp > 4) 91434624Sbostic for (;; --end) { 91534624Sbostic if (*end == '.') 91634624Sbostic --end; 91734624Sbostic if (++*end <= '9') 91834624Sbostic break; 91934624Sbostic *end = '0'; 92034624Sbostic if (end == start) { 92134669Sbostic if (exp) { /* e/E; increment exponent */ 92234669Sbostic *end = '1'; 92334669Sbostic ++*exp; 92434669Sbostic } 92534669Sbostic else { /* f; add extra digit */ 92646076Sbostic *--end = '1'; 92746076Sbostic --start; 92834669Sbostic } 92934624Sbostic break; 93034242Sbostic } 93134242Sbostic } 93234669Sbostic /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 93334669Sbostic else if (*signp == '-') 93434669Sbostic for (;; --end) { 93534669Sbostic if (*end == '.') 93634669Sbostic --end; 93734669Sbostic if (*end != '0') 93834669Sbostic break; 93934669Sbostic if (end == start) 94034669Sbostic *signp = 0; 94134669Sbostic } 94246076Sbostic return (start); 94334624Sbostic } 94434248Sbostic 94534624Sbostic static char * 94634624Sbostic exponent(p, exp, fmtch) 94734624Sbostic register char *p; 94834624Sbostic register int exp; 94946076Sbostic int fmtch; 95034624Sbostic { 95134624Sbostic register char *t; 95234624Sbostic char expbuf[MAXEXP]; 95334248Sbostic 95434624Sbostic *p++ = fmtch; 95534624Sbostic if (exp < 0) { 95634624Sbostic exp = -exp; 95734624Sbostic *p++ = '-'; 95834242Sbostic } 95934624Sbostic else 96034624Sbostic *p++ = '+'; 96134624Sbostic t = expbuf + MAXEXP; 96234624Sbostic if (exp > 9) { 96334624Sbostic do { 96446076Sbostic *--t = to_char(exp % 10); 96534624Sbostic } while ((exp /= 10) > 9); 96646076Sbostic *--t = to_char(exp); 96734624Sbostic for (; t < expbuf + MAXEXP; *p++ = *t++); 96834624Sbostic } 96934624Sbostic else { 97034624Sbostic *p++ = '0'; 97146076Sbostic *p++ = to_char(exp); 97234624Sbostic } 97346076Sbostic return (p); 97434242Sbostic } 97546076Sbostic #endif /* FLOATING_POINT */ 976