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*53810Sbostic static char sccsid[] = "@(#)vfprintf.c 5.49 (Berkeley) 06/02/92"; 1334233Sbostic #endif /* LIBC_SCCS and not lint */ 1434233Sbostic 1546076Sbostic /* 1646076Sbostic * Actual printf innards. 1746076Sbostic * 1846076Sbostic * This code is large and complicated... 1946076Sbostic */ 2046076Sbostic 2134261Sbostic #include <sys/types.h> 2246611Sbostic #include <math.h> 2346076Sbostic #include <stdio.h> 2446076Sbostic #include <string.h> 2546076Sbostic #if __STDC__ 2646076Sbostic #include <stdarg.h> 2746076Sbostic #else 2834233Sbostic #include <varargs.h> 2946076Sbostic #endif 3046076Sbostic #include "local.h" 3146076Sbostic #include "fvwrite.h" 3234226Sbostic 3350438Sbostic /* Define FLOATING_POINT to get floating point. */ 3446076Sbostic #define FLOATING_POINT 3534226Sbostic 3646076Sbostic /* 3746076Sbostic * Flush out all the vectors defined by the given uio, 3846076Sbostic * then reset it so that it can be reused. 3946076Sbostic */ 4046180Storek static int 4146076Sbostic __sprint(fp, uio) 4246076Sbostic FILE *fp; 4346076Sbostic register struct __suio *uio; 4446076Sbostic { 4546076Sbostic register int err; 4646076Sbostic 4746076Sbostic if (uio->uio_resid == 0) { 4846076Sbostic uio->uio_iovcnt = 0; 4946076Sbostic return (0); 5046076Sbostic } 5146076Sbostic err = __sfvwrite(fp, uio); 5246076Sbostic uio->uio_resid = 0; 5346076Sbostic uio->uio_iovcnt = 0; 5446076Sbostic return (err); 5546076Sbostic } 5646076Sbostic 5746076Sbostic /* 5846076Sbostic * Helper function for `fprintf to unbuffered unix file': creates a 5946076Sbostic * temporary buffer. We only work on write-only files; this avoids 6046076Sbostic * worries about ungetc buffers and so forth. 6146076Sbostic */ 6246180Storek static int 6346076Sbostic __sbprintf(fp, fmt, ap) 6446076Sbostic register FILE *fp; 6546180Storek const char *fmt; 6646076Sbostic va_list ap; 6746076Sbostic { 6846076Sbostic int ret; 6946076Sbostic FILE fake; 7046076Sbostic unsigned char buf[BUFSIZ]; 7146076Sbostic 7246076Sbostic /* copy the important variables */ 7346076Sbostic fake._flags = fp->_flags & ~__SNBF; 7446076Sbostic fake._file = fp->_file; 7546076Sbostic fake._cookie = fp->_cookie; 7646076Sbostic fake._write = fp->_write; 7746076Sbostic 7846076Sbostic /* set up the buffer */ 7946076Sbostic fake._bf._base = fake._p = buf; 8046076Sbostic fake._bf._size = fake._w = sizeof(buf); 8146076Sbostic fake._lbfsize = 0; /* not actually used, but Just In Case */ 8246076Sbostic 8346076Sbostic /* do the work, then copy any error status */ 8446076Sbostic ret = vfprintf(&fake, fmt, ap); 8546076Sbostic if (ret >= 0 && fflush(&fake)) 8646076Sbostic ret = EOF; 8746076Sbostic if (fake._flags & __SERR) 8846076Sbostic fp->_flags |= __SERR; 8946076Sbostic return (ret); 9046076Sbostic } 9146076Sbostic 9246076Sbostic 9346076Sbostic #ifdef FLOATING_POINT 9446180Storek #include "floatio.h" 9546076Sbostic 9634328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 9746076Sbostic #define DEFPREC 6 9834328Sbostic 9946076Sbostic static int cvt(); 10034236Sbostic 10146076Sbostic #else /* no FLOATING_POINT */ 10234235Sbostic 10346076Sbostic #define BUF 40 10434331Sbostic 10546076Sbostic #endif /* FLOATING_POINT */ 10634318Sbostic 10746076Sbostic 10846076Sbostic /* 10946076Sbostic * Macros for converting digits to letters and vice versa 11046076Sbostic */ 11146076Sbostic #define to_digit(c) ((c) - '0') 11246076Sbostic #define is_digit(c) ((unsigned)to_digit(c) <= 9) 11346076Sbostic #define to_char(n) ((n) + '0') 11446076Sbostic 11546076Sbostic /* 11646076Sbostic * Flags used during conversion. 11746076Sbostic */ 118*53810Sbostic #define ALT 0x001 /* alternate form */ 119*53810Sbostic #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ 120*53810Sbostic #define LADJUST 0x004 /* left adjustment */ 121*53810Sbostic #define LONGDBL 0x008 /* long double; unimplemented */ 122*53810Sbostic #define LONGINT 0x010 /* long integer */ 123*53810Sbostic #define QUADINT 0x020 /* quad integer */ 124*53810Sbostic #define SHORTINT 0x040 /* short integer */ 125*53810Sbostic #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ 12634318Sbostic 12746180Storek int 12846076Sbostic vfprintf(fp, fmt0, ap) 12946076Sbostic FILE *fp; 13046180Storek const char *fmt0; 13146076Sbostic va_list ap; 13234226Sbostic { 13346076Sbostic register char *fmt; /* format string */ 13434427Sbostic register int ch; /* character from fmt */ 13546076Sbostic register int n; /* handy integer (short term usage) */ 13646076Sbostic register char *cp; /* handy char pointer (short term usage) */ 13746076Sbostic register struct __siov *iovp;/* for PRINT macro */ 13846076Sbostic register int flags; /* flags as above */ 13946076Sbostic int ret; /* return value accumulator */ 14046076Sbostic int width; /* width from format (%8d), or 0 */ 14146076Sbostic int prec; /* precision from format (%.3d), or -1 */ 14246076Sbostic char sign; /* sign prefix (' ', '+', '-', or \0) */ 14346076Sbostic #ifdef FLOATING_POINT 14446076Sbostic char softsign; /* temporary negative sign for floats */ 14534427Sbostic double _double; /* double precision arguments %[eEfgG] */ 14646076Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 14746076Sbostic #endif 148*53810Sbostic u_quad_t _uquad; /* integer arguments %[diouxX] */ 14946076Sbostic enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 15046076Sbostic int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 15134624Sbostic int fieldsz; /* field size expanded by sign, etc */ 15246076Sbostic int realsz; /* field size expanded by dprec */ 15334427Sbostic int size; /* size of converted field or string */ 15446076Sbostic char *xdigs; /* digits for [xX] conversion */ 15546076Sbostic #define NIOV 8 15646076Sbostic struct __suio uio; /* output information: summary */ 15746076Sbostic struct __siov iov[NIOV];/* ... and individual io vectors */ 15834427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 15946076Sbostic char ox[2]; /* space for 0x hex-prefix */ 16034226Sbostic 16146076Sbostic /* 162*53810Sbostic * Choose PADSIZE to trade efficiency vs. size. If larger printf 163*53810Sbostic * fields occur frequently, increase PADSIZE and make the initialisers 164*53810Sbostic * below longer. 16546076Sbostic */ 16646076Sbostic #define PADSIZE 16 /* pad chunk size */ 16746076Sbostic static char blanks[PADSIZE] = 16846076Sbostic {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 16946076Sbostic static char zeroes[PADSIZE] = 17046076Sbostic {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 17146076Sbostic 17246076Sbostic /* 17346076Sbostic * BEWARE, these `goto error' on error, and PAD uses `n'. 17446076Sbostic */ 17546076Sbostic #define PRINT(ptr, len) { \ 17646076Sbostic iovp->iov_base = (ptr); \ 17746076Sbostic iovp->iov_len = (len); \ 17846076Sbostic uio.uio_resid += (len); \ 17946076Sbostic iovp++; \ 18046076Sbostic if (++uio.uio_iovcnt >= NIOV) { \ 18146076Sbostic if (__sprint(fp, &uio)) \ 18246076Sbostic goto error; \ 18346076Sbostic iovp = iov; \ 18446076Sbostic } \ 18546076Sbostic } 18646076Sbostic #define PAD(howmany, with) { \ 18746076Sbostic if ((n = (howmany)) > 0) { \ 18846076Sbostic while (n > PADSIZE) { \ 18946076Sbostic PRINT(with, PADSIZE); \ 19046076Sbostic n -= PADSIZE; \ 19146076Sbostic } \ 19246076Sbostic PRINT(with, n); \ 19346076Sbostic } \ 19446076Sbostic } 19546076Sbostic #define FLUSH() { \ 19646076Sbostic if (uio.uio_resid && __sprint(fp, &uio)) \ 19746076Sbostic goto error; \ 19846076Sbostic uio.uio_iovcnt = 0; \ 19946076Sbostic iovp = iov; \ 20046076Sbostic } 20146076Sbostic 20246076Sbostic /* 20346076Sbostic * To extend shorts properly, we need both signed and unsigned 20446076Sbostic * argument extraction methods. 20546076Sbostic */ 20646076Sbostic #define SARG() \ 207*53810Sbostic (flags&QUADINT ? va_arg(ap, quad_t) : \ 208*53810Sbostic flags&LONGINT ? va_arg(ap, long) : \ 20946076Sbostic flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ 21046076Sbostic (long)va_arg(ap, int)) 21146076Sbostic #define UARG() \ 212*53810Sbostic (flags&QUADINT ? va_arg(ap, u_quad_t) : \ 213*53810Sbostic flags&LONGINT ? va_arg(ap, u_long) : \ 21446076Sbostic flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ 21546076Sbostic (u_long)va_arg(ap, u_int)) 21646076Sbostic 21746076Sbostic /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 21846076Sbostic if (cantwrite(fp)) 21934428Sbostic return (EOF); 22034428Sbostic 22146076Sbostic /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 22246076Sbostic if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 22346076Sbostic fp->_file >= 0) 22446076Sbostic return (__sbprintf(fp, fmt0, ap)); 22546076Sbostic 22646076Sbostic fmt = (char *)fmt0; 22746076Sbostic uio.uio_iov = iovp = iov; 22846076Sbostic uio.uio_resid = 0; 22946076Sbostic uio.uio_iovcnt = 0; 23046076Sbostic ret = 0; 23146076Sbostic 23246076Sbostic /* 23346076Sbostic * Scan the format for conversions (`%' character). 23446076Sbostic */ 23546076Sbostic for (;;) { 23646076Sbostic for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 23746076Sbostic /* void */; 23846076Sbostic if ((n = fmt - cp) != 0) { 23946076Sbostic PRINT(cp, n); 24046076Sbostic ret += n; 24146076Sbostic } 24246076Sbostic if (ch == '\0') 24346076Sbostic goto done; 24446076Sbostic fmt++; /* skip over '%' */ 24546076Sbostic 24646076Sbostic flags = 0; 24746076Sbostic dprec = 0; 24846076Sbostic #ifdef FLOATING_POINT 24946076Sbostic fpprec = 0; 25034318Sbostic #endif 25146076Sbostic width = 0; 25234233Sbostic prec = -1; 25334318Sbostic sign = '\0'; 25434226Sbostic 25546076Sbostic rflag: ch = *fmt++; 25646076Sbostic reswitch: switch (ch) { 25734318Sbostic case ' ': 25834669Sbostic /* 25934669Sbostic * ``If the space and + flags both appear, the space 26034669Sbostic * flag will be ignored.'' 26134669Sbostic * -- ANSI X3J11 26234669Sbostic */ 26334669Sbostic if (!sign) 26434669Sbostic sign = ' '; 26534318Sbostic goto rflag; 26634233Sbostic case '#': 26734318Sbostic flags |= ALT; 26834318Sbostic goto rflag; 26934233Sbostic case '*': 27034235Sbostic /* 27134235Sbostic * ``A negative field width argument is taken as a 27246076Sbostic * - flag followed by a positive field width.'' 27334235Sbostic * -- ANSI X3J11 27434235Sbostic * They don't exclude field widths read from args. 27534235Sbostic */ 27646076Sbostic if ((width = va_arg(ap, int)) >= 0) 27734318Sbostic goto rflag; 27834235Sbostic width = -width; 27934427Sbostic /* FALLTHROUGH */ 28034235Sbostic case '-': 28134318Sbostic flags |= LADJUST; 28234318Sbostic goto rflag; 28334233Sbostic case '+': 28434314Sbostic sign = '+'; 28534318Sbostic goto rflag; 28634233Sbostic case '.': 28746076Sbostic if ((ch = *fmt++) == '*') { 28846076Sbostic n = va_arg(ap, int); 28946076Sbostic prec = n < 0 ? -1 : n; 29046076Sbostic goto rflag; 29134226Sbostic } 29246076Sbostic n = 0; 29346076Sbostic while (is_digit(ch)) { 29446076Sbostic n = 10 * n + to_digit(ch); 29546076Sbostic ch = *fmt++; 29646076Sbostic } 29734318Sbostic prec = n < 0 ? -1 : n; 29846076Sbostic goto reswitch; 29934233Sbostic case '0': 30034427Sbostic /* 30134427Sbostic * ``Note that 0 is taken as a flag, not as the 30234427Sbostic * beginning of a field width.'' 30334427Sbostic * -- ANSI X3J11 30434427Sbostic */ 30534427Sbostic flags |= ZEROPAD; 30634427Sbostic goto rflag; 30734233Sbostic case '1': case '2': case '3': case '4': 30834233Sbostic case '5': case '6': case '7': case '8': case '9': 30934318Sbostic n = 0; 31034233Sbostic do { 31146076Sbostic n = 10 * n + to_digit(ch); 31246076Sbostic ch = *fmt++; 31346076Sbostic } while (is_digit(ch)); 31434318Sbostic width = n; 31546076Sbostic goto reswitch; 31646076Sbostic #ifdef FLOATING_POINT 31734235Sbostic case 'L': 31834329Sbostic flags |= LONGDBL; 31934318Sbostic goto rflag; 32046076Sbostic #endif 32134235Sbostic case 'h': 32234318Sbostic flags |= SHORTINT; 32334318Sbostic goto rflag; 32434233Sbostic case 'l': 32534318Sbostic flags |= LONGINT; 32634318Sbostic goto rflag; 327*53810Sbostic case 'q': 328*53810Sbostic flags |= QUADINT; 329*53810Sbostic goto rflag; 33034314Sbostic case 'c': 33146076Sbostic *(cp = buf) = va_arg(ap, int); 33234314Sbostic size = 1; 33334427Sbostic sign = '\0'; 33446076Sbostic break; 33534624Sbostic case 'D': 33634624Sbostic flags |= LONGINT; 33734624Sbostic /*FALLTHROUGH*/ 33834314Sbostic case 'd': 33934318Sbostic case 'i': 340*53810Sbostic _uquad = SARG(); 341*53810Sbostic if ((quad_t)_uquad < 0) { 342*53810Sbostic _uquad = -_uquad; 34334314Sbostic sign = '-'; 34434241Sbostic } 34546076Sbostic base = DEC; 34634327Sbostic goto number; 34746076Sbostic #ifdef FLOATING_POINT 34834261Sbostic case 'e': 34934236Sbostic case 'E': 35034235Sbostic case 'f': 35134261Sbostic case 'g': 35234243Sbostic case 'G': 35346076Sbostic _double = va_arg(ap, double); 35446126Storek /* do this before tricky precision changes */ 35547607Sbostic if (isinf(_double)) { 35647607Sbostic if (_double < 0) 35747607Sbostic sign = '-'; 35847607Sbostic cp = "Inf"; 35947607Sbostic size = 3; 36046126Storek break; 36146126Storek } 36247607Sbostic if (isnan(_double)) { 36347607Sbostic cp = "NaN"; 36447607Sbostic size = 3; 36547607Sbostic break; 36647607Sbostic } 36734328Sbostic /* 36834669Sbostic * don't do unrealistic precision; just pad it with 36934669Sbostic * zeroes later, so buffer size stays rational. 37034328Sbostic */ 37134328Sbostic if (prec > MAXFRACT) { 37246076Sbostic if (ch != 'g' && ch != 'G' || (flags&ALT)) 37334475Sbostic fpprec = prec - MAXFRACT; 37434328Sbostic prec = MAXFRACT; 37546076Sbostic } else if (prec == -1) 37634624Sbostic prec = DEFPREC; 37734669Sbostic /* 37846076Sbostic * cvt may have to round up before the "start" of 37946076Sbostic * its buffer, i.e. ``intf("%.2f", (double)9.999);''; 38046076Sbostic * if the first character is still NUL, it did. 38146076Sbostic * softsign avoids negative 0 if _double < 0 but 38246076Sbostic * no significant digits will be shown. 38334669Sbostic */ 38446076Sbostic cp = buf; 38546076Sbostic *cp = '\0'; 38646076Sbostic size = cvt(_double, prec, flags, &softsign, ch, 38746076Sbostic cp, buf + sizeof(buf)); 38834669Sbostic if (softsign) 38934669Sbostic sign = '-'; 39046076Sbostic if (*cp == '\0') 39146076Sbostic cp++; 39246076Sbostic break; 39346076Sbostic #endif /* FLOATING_POINT */ 39434235Sbostic case 'n': 395*53810Sbostic if (flags & QUADINT) 396*53810Sbostic *va_arg(ap, quad_t *) = ret; 397*53810Sbostic else if (flags & LONGINT) 39846076Sbostic *va_arg(ap, long *) = ret; 39934427Sbostic else if (flags & SHORTINT) 40046076Sbostic *va_arg(ap, short *) = ret; 40134318Sbostic else 40246076Sbostic *va_arg(ap, int *) = ret; 40346076Sbostic continue; /* no output */ 40434624Sbostic case 'O': 40534624Sbostic flags |= LONGINT; 40634624Sbostic /*FALLTHROUGH*/ 40734226Sbostic case 'o': 408*53810Sbostic _uquad = UARG(); 40946076Sbostic base = OCT; 41034327Sbostic goto nosign; 41134235Sbostic case 'p': 41234320Sbostic /* 41334321Sbostic * ``The argument shall be a pointer to void. The 41434321Sbostic * value of the pointer is converted to a sequence 41534321Sbostic * of printable characters, in an implementation- 41634321Sbostic * defined manner.'' 41734321Sbostic * -- ANSI X3J11 41834320Sbostic */ 41934427Sbostic /* NOSTRICT */ 420*53810Sbostic _uquad = (u_quad_t)va_arg(ap, void *); 42146076Sbostic base = HEX; 42246076Sbostic xdigs = "0123456789abcdef"; 42346076Sbostic flags |= HEXPREFIX; 42446076Sbostic ch = 'x'; 42534327Sbostic goto nosign; 42634226Sbostic case 's': 42746076Sbostic if ((cp = va_arg(ap, char *)) == NULL) 42846076Sbostic cp = "(null)"; 42934321Sbostic if (prec >= 0) { 43034321Sbostic /* 43134321Sbostic * can't use strlen; can only look for the 43234321Sbostic * NUL in the first `prec' characters, and 43334321Sbostic * strlen() will go further. 43434321Sbostic */ 43546076Sbostic char *p = memchr(cp, 0, prec); 43634321Sbostic 43746076Sbostic if (p != NULL) { 43846076Sbostic size = p - cp; 43934321Sbostic if (size > prec) 44034321Sbostic size = prec; 44134427Sbostic } else 44234321Sbostic size = prec; 44334427Sbostic } else 44446076Sbostic size = strlen(cp); 44534427Sbostic sign = '\0'; 44646076Sbostic break; 44734624Sbostic case 'U': 44834624Sbostic flags |= LONGINT; 44934624Sbostic /*FALLTHROUGH*/ 45034226Sbostic case 'u': 451*53810Sbostic _uquad = UARG(); 45246076Sbostic base = DEC; 45334327Sbostic goto nosign; 45434226Sbostic case 'X': 45546076Sbostic xdigs = "0123456789ABCDEF"; 45646076Sbostic goto hex; 45734226Sbostic case 'x': 45846076Sbostic xdigs = "0123456789abcdef"; 459*53810Sbostic hex: _uquad = UARG(); 46046076Sbostic base = HEX; 46134326Sbostic /* leading 0x/X only if non-zero */ 462*53810Sbostic if (flags & ALT && _uquad != 0) 46334427Sbostic flags |= HEXPREFIX; 46434327Sbostic 46534327Sbostic /* unsigned conversions */ 46634427Sbostic nosign: sign = '\0'; 46734326Sbostic /* 46834330Sbostic * ``... diouXx conversions ... if a precision is 46934330Sbostic * specified, the 0 flag will be ignored.'' 47034330Sbostic * -- ANSI X3J11 47134330Sbostic */ 47234427Sbostic number: if ((dprec = prec) >= 0) 47334427Sbostic flags &= ~ZEROPAD; 47434427Sbostic 47534330Sbostic /* 47634326Sbostic * ``The result of converting a zero value with an 47734326Sbostic * explicit precision of zero is no characters.'' 47834326Sbostic * -- ANSI X3J11 47934326Sbostic */ 48046076Sbostic cp = buf + BUF; 481*53810Sbostic if (_uquad != 0 || prec != 0) { 48246076Sbostic /* 483*53810Sbostic * Unsigned mod is hard, and unsigned mod 48446076Sbostic * by a constant is easier than that by 48546076Sbostic * a variable; hence this switch. 48646076Sbostic */ 48746076Sbostic switch (base) { 48846076Sbostic case OCT: 48946076Sbostic do { 490*53810Sbostic *--cp = to_char(_uquad & 7); 491*53810Sbostic _uquad >>= 3; 492*53810Sbostic } while (_uquad); 49346076Sbostic /* handle octal leading 0 */ 49446076Sbostic if (flags & ALT && *cp != '0') 49546076Sbostic *--cp = '0'; 49646076Sbostic break; 49734327Sbostic 49846076Sbostic case DEC: 49946076Sbostic /* many numbers are 1 digit */ 500*53810Sbostic while (_uquad >= 10) { 501*53810Sbostic *--cp = to_char(_uquad % 10); 502*53810Sbostic _uquad /= 10; 50346076Sbostic } 504*53810Sbostic *--cp = to_char(_uquad); 50546076Sbostic break; 50634327Sbostic 50746076Sbostic case HEX: 50846076Sbostic do { 509*53810Sbostic *--cp = xdigs[_uquad & 15]; 510*53810Sbostic _uquad >>= 4; 511*53810Sbostic } while (_uquad); 51246076Sbostic break; 51334327Sbostic 51446076Sbostic default: 51546076Sbostic cp = "bug in vfprintf: bad base"; 51646180Storek size = strlen(cp); 51746076Sbostic goto skipsize; 51846076Sbostic } 51934327Sbostic } 52046076Sbostic size = buf + BUF - cp; 52146076Sbostic skipsize: 52234226Sbostic break; 52346076Sbostic default: /* "%?" prints ?, unless ? is NUL */ 52446076Sbostic if (ch == '\0') 52546076Sbostic goto done; 52646076Sbostic /* pretend it was %c with argument ch */ 52746076Sbostic cp = buf; 52846076Sbostic *cp = ch; 52946076Sbostic size = 1; 53046076Sbostic sign = '\0'; 53146076Sbostic break; 53234226Sbostic } 53346076Sbostic 53446076Sbostic /* 53546076Sbostic * All reasonable formats wind up here. At this point, 53646076Sbostic * `cp' points to a string which (if not flags&LADJUST) 53746076Sbostic * should be padded out to `width' places. If 53846076Sbostic * flags&ZEROPAD, it should first be prefixed by any 53946076Sbostic * sign or other prefix; otherwise, it should be blank 54046076Sbostic * padded before the prefix is emitted. After any 54146076Sbostic * left-hand padding and prefixing, emit zeroes 54246076Sbostic * required by a decimal [diouxX] precision, then print 54346076Sbostic * the string proper, then emit zeroes required by any 54446076Sbostic * leftover floating precision; finally, if LADJUST, 54546076Sbostic * pad with blanks. 54646076Sbostic */ 54746076Sbostic 54846076Sbostic /* 54946076Sbostic * compute actual size, so we know how much to pad. 55046076Sbostic * fieldsz excludes decimal prec; realsz includes it 55146076Sbostic */ 55246076Sbostic #ifdef FLOATING_POINT 55346076Sbostic fieldsz = size + fpprec; 55446076Sbostic #else 55546076Sbostic fieldsz = size; 55646076Sbostic #endif 55746076Sbostic if (sign) 55846076Sbostic fieldsz++; 55946076Sbostic else if (flags & HEXPREFIX) 56046076Sbostic fieldsz += 2; 56146076Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 56246076Sbostic 56346076Sbostic /* right-adjusting blank padding */ 56446076Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0) 56546076Sbostic PAD(width - realsz, blanks); 56646076Sbostic 56746076Sbostic /* prefix */ 56846076Sbostic if (sign) { 56946076Sbostic PRINT(&sign, 1); 57046076Sbostic } else if (flags & HEXPREFIX) { 57146076Sbostic ox[0] = '0'; 57246076Sbostic ox[1] = ch; 57346076Sbostic PRINT(ox, 2); 57446076Sbostic } 57546076Sbostic 57646076Sbostic /* right-adjusting zero padding */ 57746076Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 57846076Sbostic PAD(width - realsz, zeroes); 57946076Sbostic 58046076Sbostic /* leading zeroes from decimal precision */ 58146076Sbostic PAD(dprec - fieldsz, zeroes); 58246076Sbostic 58346076Sbostic /* the string or number proper */ 58446076Sbostic PRINT(cp, size); 58546076Sbostic 58646076Sbostic #ifdef FLOATING_POINT 58746076Sbostic /* trailing f.p. zeroes */ 58846076Sbostic PAD(fpprec, zeroes); 58946076Sbostic #endif 59046076Sbostic 59146076Sbostic /* left-adjusting padding (always blank) */ 59246076Sbostic if (flags & LADJUST) 59346076Sbostic PAD(width - realsz, blanks); 59446076Sbostic 59546076Sbostic /* finally, adjust ret */ 59646076Sbostic ret += width > realsz ? width : realsz; 59746076Sbostic 59846076Sbostic FLUSH(); /* copy out the I/O vectors */ 59934226Sbostic } 60046076Sbostic done: 60146076Sbostic FLUSH(); 60246076Sbostic error: 60346076Sbostic return (__sferror(fp) ? EOF : ret); 60434427Sbostic /* NOTREACHED */ 60534226Sbostic } 60634242Sbostic 60746076Sbostic #ifdef FLOATING_POINT 60846180Storek #include <math.h> 60946180Storek 61046076Sbostic static char *exponent(); 61146076Sbostic static char *round(); 61246076Sbostic 61346180Storek static int 61434669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp) 61534242Sbostic double number; 61634261Sbostic register int prec; 61734323Sbostic int flags; 61846076Sbostic char *signp; 61946076Sbostic int fmtch; 62046076Sbostic char *startp, *endp; 62134242Sbostic { 62234331Sbostic register char *p, *t; 62334672Sbostic register double fract; 62434624Sbostic int dotrim, expcnt, gformat; 62546180Storek double integer, tmp; 62634242Sbostic 62746076Sbostic dotrim = expcnt = gformat = 0; 62846076Sbostic if (number < 0) { 62946076Sbostic number = -number; 63046076Sbostic *signp = '-'; 63146076Sbostic } else 63246076Sbostic *signp = 0; 63344426Sbostic 63434624Sbostic fract = modf(number, &integer); 63534242Sbostic 63634624Sbostic /* get an extra slot for rounding. */ 63734624Sbostic t = ++startp; 63834624Sbostic 63934624Sbostic /* 64034624Sbostic * get integer portion of number; put into the end of the buffer; the 64134624Sbostic * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 64234624Sbostic */ 64334624Sbostic for (p = endp - 1; integer; ++expcnt) { 64434624Sbostic tmp = modf(integer / 10, &integer); 64546076Sbostic *p-- = to_char((int)((tmp + .01) * 10)); 64634248Sbostic } 64746076Sbostic switch (fmtch) { 64834261Sbostic case 'f': 64934624Sbostic /* reverse integer into beginning of buffer */ 65034624Sbostic if (expcnt) 65134624Sbostic for (; ++p < endp; *t++ = *p); 65234624Sbostic else 65334624Sbostic *t++ = '0'; 65434248Sbostic /* 65534624Sbostic * if precision required or alternate flag set, add in a 65634624Sbostic * decimal point. 65734248Sbostic */ 65834624Sbostic if (prec || flags&ALT) 65934624Sbostic *t++ = '.'; 66034624Sbostic /* if requires more precision and some fraction left */ 66134624Sbostic if (fract) { 66234624Sbostic if (prec) 66334624Sbostic do { 66434624Sbostic fract = modf(fract * 10, &tmp); 66546076Sbostic *t++ = to_char((int)tmp); 66634624Sbostic } while (--prec && fract); 66734624Sbostic if (fract) 66834669Sbostic startp = round(fract, (int *)NULL, startp, 66934669Sbostic t - 1, (char)0, signp); 67034624Sbostic } 67134624Sbostic for (; prec--; *t++ = '0'); 67234624Sbostic break; 67334624Sbostic case 'e': 67434624Sbostic case 'E': 67534624Sbostic eformat: if (expcnt) { 67634624Sbostic *t++ = *++p; 67734624Sbostic if (prec || flags&ALT) 67834331Sbostic *t++ = '.'; 67934624Sbostic /* if requires more precision and some integer left */ 68034624Sbostic for (; prec && ++p < endp; --prec) 68134624Sbostic *t++ = *p; 68234624Sbostic /* 68334624Sbostic * if done precision and more of the integer component, 68434624Sbostic * round using it; adjust fract so we don't re-round 68534624Sbostic * later. 68634624Sbostic */ 68734624Sbostic if (!prec && ++p < endp) { 68834248Sbostic fract = 0; 68934669Sbostic startp = round((double)0, &expcnt, startp, 69034669Sbostic t - 1, *p, signp); 69134248Sbostic } 69234624Sbostic /* adjust expcnt for digit in front of decimal */ 69334624Sbostic --expcnt; 69434242Sbostic } 69534624Sbostic /* until first fractional digit, decrement exponent */ 69634624Sbostic else if (fract) { 69734624Sbostic /* adjust expcnt for digit in front of decimal */ 69834624Sbostic for (expcnt = -1;; --expcnt) { 69934624Sbostic fract = modf(fract * 10, &tmp); 70034624Sbostic if (tmp) 70134624Sbostic break; 70234248Sbostic } 70346076Sbostic *t++ = to_char((int)tmp); 70434624Sbostic if (prec || flags&ALT) 70534331Sbostic *t++ = '.'; 70634242Sbostic } 70734248Sbostic else { 70834624Sbostic *t++ = '0'; 70934624Sbostic if (prec || flags&ALT) 71034331Sbostic *t++ = '.'; 71134248Sbostic } 71234624Sbostic /* if requires more precision and some fraction left */ 71334624Sbostic if (fract) { 71434624Sbostic if (prec) 71534624Sbostic do { 71634624Sbostic fract = modf(fract * 10, &tmp); 71746076Sbostic *t++ = to_char((int)tmp); 71834624Sbostic } while (--prec && fract); 71934624Sbostic if (fract) 72034669Sbostic startp = round(fract, &expcnt, startp, 72134669Sbostic t - 1, (char)0, signp); 72234584Sbostic } 72334624Sbostic /* if requires more precision */ 72434624Sbostic for (; prec--; *t++ = '0'); 72534624Sbostic 72634624Sbostic /* unless alternate flag, trim any g/G format trailing 0's */ 72734624Sbostic if (gformat && !(flags&ALT)) { 72834624Sbostic while (t > startp && *--t == '0'); 72934624Sbostic if (*t == '.') 73034624Sbostic --t; 73134624Sbostic ++t; 73234624Sbostic } 73334624Sbostic t = exponent(t, expcnt, fmtch); 73434624Sbostic break; 73534624Sbostic case 'g': 73634624Sbostic case 'G': 73734624Sbostic /* a precision of 0 is treated as a precision of 1. */ 73834624Sbostic if (!prec) 73934624Sbostic ++prec; 74034624Sbostic /* 74134624Sbostic * ``The style used depends on the value converted; style e 74234624Sbostic * will be used only if the exponent resulting from the 74334624Sbostic * conversion is less than -4 or greater than the precision.'' 74434624Sbostic * -- ANSI X3J11 74534624Sbostic */ 74634624Sbostic if (expcnt > prec || !expcnt && fract && fract < .0001) { 74734624Sbostic /* 74834624Sbostic * g/G format counts "significant digits, not digits of 74934624Sbostic * precision; for the e/E format, this just causes an 75034624Sbostic * off-by-one problem, i.e. g/G considers the digit 75134624Sbostic * before the decimal point significant and e/E doesn't 75234624Sbostic * count it as precision. 75334624Sbostic */ 75434624Sbostic --prec; 75534624Sbostic fmtch -= 2; /* G->E, g->e */ 75634624Sbostic gformat = 1; 75734624Sbostic goto eformat; 75834624Sbostic } 75934624Sbostic /* 76034624Sbostic * reverse integer into beginning of buffer, 76134624Sbostic * note, decrement precision 76234624Sbostic */ 76334624Sbostic if (expcnt) 76434624Sbostic for (; ++p < endp; *t++ = *p, --prec); 76534624Sbostic else 76634624Sbostic *t++ = '0'; 76734624Sbostic /* 76834624Sbostic * if precision required or alternate flag set, add in a 76934624Sbostic * decimal point. If no digits yet, add in leading 0. 77034624Sbostic */ 77134624Sbostic if (prec || flags&ALT) { 77234624Sbostic dotrim = 1; 77334624Sbostic *t++ = '.'; 77434624Sbostic } 77534624Sbostic else 77634624Sbostic dotrim = 0; 77734624Sbostic /* if requires more precision and some fraction left */ 77834624Sbostic if (fract) { 77934624Sbostic if (prec) { 78034624Sbostic do { 78134624Sbostic fract = modf(fract * 10, &tmp); 78246076Sbostic *t++ = to_char((int)tmp); 78334624Sbostic } while(!tmp); 78434624Sbostic while (--prec && fract) { 78534624Sbostic fract = modf(fract * 10, &tmp); 78646076Sbostic *t++ = to_char((int)tmp); 78734248Sbostic } 78834248Sbostic } 78934624Sbostic if (fract) 79034669Sbostic startp = round(fract, (int *)NULL, startp, 79134669Sbostic t - 1, (char)0, signp); 79234624Sbostic } 79334624Sbostic /* alternate format, adds 0's for precision, else trim 0's */ 79434624Sbostic if (flags&ALT) 79534624Sbostic for (; prec--; *t++ = '0'); 79634624Sbostic else if (dotrim) { 79734624Sbostic while (t > startp && *--t == '0'); 79834624Sbostic if (*t != '.') 79934624Sbostic ++t; 80034624Sbostic } 80134624Sbostic } 80246076Sbostic return (t - startp); 80334624Sbostic } 80434248Sbostic 80534624Sbostic static char * 80634669Sbostic round(fract, exp, start, end, ch, signp) 80734624Sbostic double fract; 80834669Sbostic int *exp; 80934624Sbostic register char *start, *end; 81034669Sbostic char ch, *signp; 81134624Sbostic { 81234624Sbostic double tmp; 81334248Sbostic 81434624Sbostic if (fract) 81546180Storek (void)modf(fract * 10, &tmp); 81634624Sbostic else 81746076Sbostic tmp = to_digit(ch); 81834624Sbostic if (tmp > 4) 81934624Sbostic for (;; --end) { 82034624Sbostic if (*end == '.') 82134624Sbostic --end; 82234624Sbostic if (++*end <= '9') 82334624Sbostic break; 82434624Sbostic *end = '0'; 82534624Sbostic if (end == start) { 82634669Sbostic if (exp) { /* e/E; increment exponent */ 82734669Sbostic *end = '1'; 82834669Sbostic ++*exp; 82934669Sbostic } 83034669Sbostic else { /* f; add extra digit */ 83146076Sbostic *--end = '1'; 83246076Sbostic --start; 83334669Sbostic } 83434624Sbostic break; 83534242Sbostic } 83634242Sbostic } 83734669Sbostic /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 83834669Sbostic else if (*signp == '-') 83934669Sbostic for (;; --end) { 84034669Sbostic if (*end == '.') 84134669Sbostic --end; 84234669Sbostic if (*end != '0') 84334669Sbostic break; 84434669Sbostic if (end == start) 84534669Sbostic *signp = 0; 84634669Sbostic } 84746076Sbostic return (start); 84834624Sbostic } 84934248Sbostic 85034624Sbostic static char * 85134624Sbostic exponent(p, exp, fmtch) 85234624Sbostic register char *p; 85334624Sbostic register int exp; 85446076Sbostic int fmtch; 85534624Sbostic { 85634624Sbostic register char *t; 85734624Sbostic char expbuf[MAXEXP]; 85834248Sbostic 85934624Sbostic *p++ = fmtch; 86034624Sbostic if (exp < 0) { 86134624Sbostic exp = -exp; 86234624Sbostic *p++ = '-'; 86334242Sbostic } 86434624Sbostic else 86534624Sbostic *p++ = '+'; 86634624Sbostic t = expbuf + MAXEXP; 86734624Sbostic if (exp > 9) { 86834624Sbostic do { 86946076Sbostic *--t = to_char(exp % 10); 87034624Sbostic } while ((exp /= 10) > 9); 87146076Sbostic *--t = to_char(exp); 87234624Sbostic for (; t < expbuf + MAXEXP; *p++ = *t++); 87334624Sbostic } 87434624Sbostic else { 87534624Sbostic *p++ = '0'; 87646076Sbostic *p++ = to_char(exp); 87734624Sbostic } 87846076Sbostic return (p); 87934242Sbostic } 88046076Sbostic #endif /* FLOATING_POINT */ 881