1*46076Sbostic /*- 2*46076Sbostic * Copyright (c) 1990 The Regents of the University of California. 334233Sbostic * All rights reserved. 434226Sbostic * 5*46076Sbostic * This code is derived from software contributed to Berkeley by 6*46076Sbostic * Chris Torek. 7*46076Sbostic * 842631Sbostic * %sccs.include.redist.c% 934226Sbostic */ 1034226Sbostic 1134233Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*46076Sbostic static char sccsid[] = "@(#)vfprintf.c 5.40 (Berkeley) 01/20/91"; 1334233Sbostic #endif /* LIBC_SCCS and not lint */ 1434233Sbostic 15*46076Sbostic /* 16*46076Sbostic * Actual printf innards. 17*46076Sbostic * 18*46076Sbostic * This code is large and complicated... 19*46076Sbostic */ 20*46076Sbostic 2134261Sbostic #include <sys/types.h> 22*46076Sbostic #include <stdio.h> 23*46076Sbostic #include <string.h> 24*46076Sbostic #if __STDC__ 25*46076Sbostic #include <stdarg.h> 26*46076Sbostic #else 2734233Sbostic #include <varargs.h> 28*46076Sbostic #endif 29*46076Sbostic #include "local.h" 30*46076Sbostic #include "fvwrite.h" 3134226Sbostic 32*46076Sbostic /* 33*46076Sbostic * Define FLOATING_POINT to get floating point. 34*46076Sbostic * Define CSH to get a csh-specific version (grr). 35*46076Sbostic */ 36*46076Sbostic #ifndef CSH 37*46076Sbostic #define FLOATING_POINT 38*46076Sbostic #endif 3934226Sbostic 40*46076Sbostic /* end of configuration stuff */ 4134624Sbostic 42*46076Sbostic 43*46076Sbostic #ifdef CSH 44*46076Sbostic /* 45*46076Sbostic * C shell hacks. Ick, gag. 46*46076Sbostic */ 47*46076Sbostic #undef BUFSIZ 48*46076Sbostic #include "sh.h" 49*46076Sbostic 50*46076Sbostic printf(fmt, args) 51*46076Sbostic char *fmt; 52*46076Sbostic { 53*46076Sbostic FILE f; 54*46076Sbostic 55*46076Sbostic f._flags = __SWR; 56*46076Sbostic return (vfprintf(&f, fmt, &args)); 57*46076Sbostic } 58*46076Sbostic 59*46076Sbostic #define __sprint(fp, uio) cshprintv(uio) 60*46076Sbostic 61*46076Sbostic cshprintv(uio) 62*46076Sbostic register struct __suio *uio; 63*46076Sbostic { 64*46076Sbostic register char *p; 65*46076Sbostic register int n, ch, iovcnt; 66*46076Sbostic register struct __siov *iov = uio->uio_iov; 67*46076Sbostic 68*46076Sbostic for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) { 69*46076Sbostic for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) { 70*46076Sbostic #ifdef CSHPUTCHAR 71*46076Sbostic ch = *p++; 72*46076Sbostic CSHPUTCHAR; /* this horrid macro uses `ch' */ 73*46076Sbostic #else 74*46076Sbostic #undef putchar 75*46076Sbostic putchar(*p++); 76*46076Sbostic #endif 77*46076Sbostic } 78*46076Sbostic } 79*46076Sbostic uio->uio_resid = 0; 80*46076Sbostic uio->uio_iovcnt = 0; 81*46076Sbostic return (0); 82*46076Sbostic } 83*46076Sbostic 84*46076Sbostic #else /* CSH */ 85*46076Sbostic 86*46076Sbostic /* 87*46076Sbostic * Flush out all the vectors defined by the given uio, 88*46076Sbostic * then reset it so that it can be reused. 89*46076Sbostic */ 90*46076Sbostic static 91*46076Sbostic __sprint(fp, uio) 92*46076Sbostic FILE *fp; 93*46076Sbostic register struct __suio *uio; 94*46076Sbostic { 95*46076Sbostic register int err; 96*46076Sbostic 97*46076Sbostic if (uio->uio_resid == 0) { 98*46076Sbostic uio->uio_iovcnt = 0; 99*46076Sbostic return (0); 100*46076Sbostic } 101*46076Sbostic err = __sfvwrite(fp, uio); 102*46076Sbostic uio->uio_resid = 0; 103*46076Sbostic uio->uio_iovcnt = 0; 104*46076Sbostic return (err); 105*46076Sbostic } 106*46076Sbostic 107*46076Sbostic /* 108*46076Sbostic * Helper function for `fprintf to unbuffered unix file': creates a 109*46076Sbostic * temporary buffer. We only work on write-only files; this avoids 110*46076Sbostic * worries about ungetc buffers and so forth. 111*46076Sbostic */ 112*46076Sbostic static 113*46076Sbostic __sbprintf(fp, fmt, ap) 114*46076Sbostic register FILE *fp; 115*46076Sbostic char *fmt; 116*46076Sbostic va_list ap; 117*46076Sbostic { 118*46076Sbostic int ret; 119*46076Sbostic FILE fake; 120*46076Sbostic unsigned char buf[BUFSIZ]; 121*46076Sbostic 122*46076Sbostic /* copy the important variables */ 123*46076Sbostic fake._flags = fp->_flags & ~__SNBF; 124*46076Sbostic fake._file = fp->_file; 125*46076Sbostic fake._cookie = fp->_cookie; 126*46076Sbostic fake._write = fp->_write; 127*46076Sbostic 128*46076Sbostic /* set up the buffer */ 129*46076Sbostic fake._bf._base = fake._p = buf; 130*46076Sbostic fake._bf._size = fake._w = sizeof(buf); 131*46076Sbostic fake._lbfsize = 0; /* not actually used, but Just In Case */ 132*46076Sbostic 133*46076Sbostic /* do the work, then copy any error status */ 134*46076Sbostic ret = vfprintf(&fake, fmt, ap); 135*46076Sbostic if (ret >= 0 && fflush(&fake)) 136*46076Sbostic ret = EOF; 137*46076Sbostic if (fake._flags & __SERR) 138*46076Sbostic fp->_flags |= __SERR; 139*46076Sbostic return (ret); 140*46076Sbostic } 141*46076Sbostic 142*46076Sbostic #endif /* CSH */ 143*46076Sbostic 144*46076Sbostic 145*46076Sbostic #ifdef FLOATING_POINT 146*46076Sbostic 147*46076Sbostic #include "floatio.h" 14834328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 149*46076Sbostic #define DEFPREC 6 15034328Sbostic 151*46076Sbostic static int cvt(); 15234236Sbostic 153*46076Sbostic #else /* no FLOATING_POINT */ 15434235Sbostic 155*46076Sbostic #define BUF 40 15634331Sbostic 157*46076Sbostic #endif /* FLOATING_POINT */ 15834318Sbostic 159*46076Sbostic 160*46076Sbostic /* 161*46076Sbostic * Macros for converting digits to letters and vice versa 162*46076Sbostic */ 163*46076Sbostic #define to_digit(c) ((c) - '0') 164*46076Sbostic #define is_digit(c) ((unsigned)to_digit(c) <= 9) 165*46076Sbostic #define to_char(n) ((n) + '0') 166*46076Sbostic 167*46076Sbostic /* 168*46076Sbostic * Flags used during conversion. 169*46076Sbostic */ 17034318Sbostic #define LONGINT 0x01 /* long integer */ 17134318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 17234318Sbostic #define SHORTINT 0x04 /* short integer */ 17334318Sbostic #define ALT 0x08 /* alternate form */ 17434318Sbostic #define LADJUST 0x10 /* left adjustment */ 17534427Sbostic #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 17634427Sbostic #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 17734318Sbostic 178*46076Sbostic vfprintf(fp, fmt0, ap) 179*46076Sbostic FILE *fp; 180*46076Sbostic char *fmt0; 181*46076Sbostic #if tahoe 182*46076Sbostic register /* technically illegal, since we do not know what type va_list is */ 183*46076Sbostic #endif 184*46076Sbostic va_list ap; 18534226Sbostic { 186*46076Sbostic register char *fmt; /* format string */ 18734427Sbostic register int ch; /* character from fmt */ 188*46076Sbostic register int n; /* handy integer (short term usage) */ 189*46076Sbostic register char *cp; /* handy char pointer (short term usage) */ 190*46076Sbostic register struct __siov *iovp;/* for PRINT macro */ 191*46076Sbostic register int flags; /* flags as above */ 192*46076Sbostic int ret; /* return value accumulator */ 193*46076Sbostic int width; /* width from format (%8d), or 0 */ 194*46076Sbostic int prec; /* precision from format (%.3d), or -1 */ 195*46076Sbostic char sign; /* sign prefix (' ', '+', '-', or \0) */ 196*46076Sbostic #ifdef FLOATING_POINT 197*46076Sbostic char softsign; /* temporary negative sign for floats */ 19834427Sbostic double _double; /* double precision arguments %[eEfgG] */ 199*46076Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 200*46076Sbostic #endif 20134427Sbostic u_long _ulong; /* integer arguments %[diouxX] */ 202*46076Sbostic enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 203*46076Sbostic int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 20434624Sbostic int fieldsz; /* field size expanded by sign, etc */ 205*46076Sbostic int realsz; /* field size expanded by dprec */ 20634427Sbostic int size; /* size of converted field or string */ 207*46076Sbostic char *xdigs; /* digits for [xX] conversion */ 208*46076Sbostic #define NIOV 8 209*46076Sbostic struct __suio uio; /* output information: summary */ 210*46076Sbostic struct __siov iov[NIOV];/* ... and individual io vectors */ 21134427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 212*46076Sbostic char ox[2]; /* space for 0x hex-prefix */ 21334226Sbostic 214*46076Sbostic /* 215*46076Sbostic * Choose PADSIZE to trade efficiency vs size. If larger 216*46076Sbostic * printf fields occur frequently, increase PADSIZE (and make 217*46076Sbostic * the initialisers below longer). 218*46076Sbostic */ 219*46076Sbostic #define PADSIZE 16 /* pad chunk size */ 220*46076Sbostic static char blanks[PADSIZE] = 221*46076Sbostic {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 222*46076Sbostic static char zeroes[PADSIZE] = 223*46076Sbostic {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 224*46076Sbostic 225*46076Sbostic /* 226*46076Sbostic * BEWARE, these `goto error' on error, and PAD uses `n'. 227*46076Sbostic */ 228*46076Sbostic #define PRINT(ptr, len) { \ 229*46076Sbostic iovp->iov_base = (ptr); \ 230*46076Sbostic iovp->iov_len = (len); \ 231*46076Sbostic uio.uio_resid += (len); \ 232*46076Sbostic iovp++; \ 233*46076Sbostic if (++uio.uio_iovcnt >= NIOV) { \ 234*46076Sbostic if (__sprint(fp, &uio)) \ 235*46076Sbostic goto error; \ 236*46076Sbostic iovp = iov; \ 237*46076Sbostic } \ 238*46076Sbostic } 239*46076Sbostic #define PAD(howmany, with) { \ 240*46076Sbostic if ((n = (howmany)) > 0) { \ 241*46076Sbostic while (n > PADSIZE) { \ 242*46076Sbostic PRINT(with, PADSIZE); \ 243*46076Sbostic n -= PADSIZE; \ 244*46076Sbostic } \ 245*46076Sbostic PRINT(with, n); \ 246*46076Sbostic } \ 247*46076Sbostic } 248*46076Sbostic #define FLUSH() { \ 249*46076Sbostic if (uio.uio_resid && __sprint(fp, &uio)) \ 250*46076Sbostic goto error; \ 251*46076Sbostic uio.uio_iovcnt = 0; \ 252*46076Sbostic iovp = iov; \ 253*46076Sbostic } 254*46076Sbostic 255*46076Sbostic /* 256*46076Sbostic * To extend shorts properly, we need both signed and unsigned 257*46076Sbostic * argument extraction methods. 258*46076Sbostic */ 259*46076Sbostic #define SARG() \ 260*46076Sbostic (flags&LONGINT ? va_arg(ap, long) : \ 261*46076Sbostic flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ 262*46076Sbostic (long)va_arg(ap, int)) 263*46076Sbostic #define UARG() \ 264*46076Sbostic (flags&LONGINT ? va_arg(ap, u_long) : \ 265*46076Sbostic flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ 266*46076Sbostic (u_long)va_arg(ap, u_int)) 267*46076Sbostic 268*46076Sbostic #ifndef CSH 269*46076Sbostic /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 270*46076Sbostic if (cantwrite(fp)) 27134428Sbostic return (EOF); 27234428Sbostic 273*46076Sbostic /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 274*46076Sbostic if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 275*46076Sbostic fp->_file >= 0) 276*46076Sbostic return (__sbprintf(fp, fmt0, ap)); 277*46076Sbostic #endif /* CSH */ 278*46076Sbostic 279*46076Sbostic fmt = (char *)fmt0; 280*46076Sbostic uio.uio_iov = iovp = iov; 281*46076Sbostic uio.uio_resid = 0; 282*46076Sbostic uio.uio_iovcnt = 0; 283*46076Sbostic ret = 0; 284*46076Sbostic 285*46076Sbostic /* 286*46076Sbostic * Scan the format for conversions (`%' character). 287*46076Sbostic */ 288*46076Sbostic for (;;) { 289*46076Sbostic for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 290*46076Sbostic /* void */; 291*46076Sbostic if ((n = fmt - cp) != 0) { 292*46076Sbostic PRINT(cp, n); 293*46076Sbostic ret += n; 294*46076Sbostic } 295*46076Sbostic if (ch == '\0') 296*46076Sbostic goto done; 297*46076Sbostic fmt++; /* skip over '%' */ 298*46076Sbostic 299*46076Sbostic flags = 0; 300*46076Sbostic dprec = 0; 301*46076Sbostic #ifdef FLOATING_POINT 302*46076Sbostic fpprec = 0; 30334318Sbostic #endif 304*46076Sbostic width = 0; 30534233Sbostic prec = -1; 30634318Sbostic sign = '\0'; 30734226Sbostic 308*46076Sbostic rflag: ch = *fmt++; 309*46076Sbostic reswitch: switch (ch) { 31034318Sbostic case ' ': 31134669Sbostic /* 31234669Sbostic * ``If the space and + flags both appear, the space 31334669Sbostic * flag will be ignored.'' 31434669Sbostic * -- ANSI X3J11 31534669Sbostic */ 31634669Sbostic if (!sign) 31734669Sbostic sign = ' '; 31834318Sbostic goto rflag; 31934233Sbostic case '#': 32034318Sbostic flags |= ALT; 32134318Sbostic goto rflag; 32234233Sbostic case '*': 32334235Sbostic /* 32434235Sbostic * ``A negative field width argument is taken as a 325*46076Sbostic * - flag followed by a positive field width.'' 32634235Sbostic * -- ANSI X3J11 32734235Sbostic * They don't exclude field widths read from args. 32834235Sbostic */ 329*46076Sbostic if ((width = va_arg(ap, int)) >= 0) 33034318Sbostic goto rflag; 33134235Sbostic width = -width; 33234427Sbostic /* FALLTHROUGH */ 33334235Sbostic case '-': 33434318Sbostic flags |= LADJUST; 33534318Sbostic goto rflag; 33634233Sbostic case '+': 33734314Sbostic sign = '+'; 33834318Sbostic goto rflag; 33934233Sbostic case '.': 340*46076Sbostic if ((ch = *fmt++) == '*') { 341*46076Sbostic n = va_arg(ap, int); 342*46076Sbostic prec = n < 0 ? -1 : n; 343*46076Sbostic goto rflag; 34434226Sbostic } 345*46076Sbostic n = 0; 346*46076Sbostic while (is_digit(ch)) { 347*46076Sbostic n = 10 * n + to_digit(ch); 348*46076Sbostic ch = *fmt++; 349*46076Sbostic } 35034318Sbostic prec = n < 0 ? -1 : n; 351*46076Sbostic goto reswitch; 35234233Sbostic case '0': 35334427Sbostic /* 35434427Sbostic * ``Note that 0 is taken as a flag, not as the 35534427Sbostic * beginning of a field width.'' 35634427Sbostic * -- ANSI X3J11 35734427Sbostic */ 35834427Sbostic flags |= ZEROPAD; 35934427Sbostic goto rflag; 36034233Sbostic case '1': case '2': case '3': case '4': 36134233Sbostic case '5': case '6': case '7': case '8': case '9': 36234318Sbostic n = 0; 36334233Sbostic do { 364*46076Sbostic n = 10 * n + to_digit(ch); 365*46076Sbostic ch = *fmt++; 366*46076Sbostic } while (is_digit(ch)); 36734318Sbostic width = n; 368*46076Sbostic goto reswitch; 369*46076Sbostic #ifdef FLOATING_POINT 37034235Sbostic case 'L': 37134329Sbostic flags |= LONGDBL; 37234318Sbostic goto rflag; 373*46076Sbostic #endif 37434235Sbostic case 'h': 37534318Sbostic flags |= SHORTINT; 37634318Sbostic goto rflag; 37734233Sbostic case 'l': 37834318Sbostic flags |= LONGINT; 37934318Sbostic goto rflag; 38034314Sbostic case 'c': 381*46076Sbostic *(cp = buf) = va_arg(ap, int); 38234314Sbostic size = 1; 38334427Sbostic sign = '\0'; 384*46076Sbostic break; 38534624Sbostic case 'D': 38634624Sbostic flags |= LONGINT; 38734624Sbostic /*FALLTHROUGH*/ 38834314Sbostic case 'd': 38934318Sbostic case 'i': 390*46076Sbostic _ulong = SARG(); 39134318Sbostic if ((long)_ulong < 0) { 39234318Sbostic _ulong = -_ulong; 39334314Sbostic sign = '-'; 39434241Sbostic } 395*46076Sbostic base = DEC; 39634327Sbostic goto number; 397*46076Sbostic #ifdef FLOATING_POINT 39834261Sbostic case 'e': 39934236Sbostic case 'E': 40034235Sbostic case 'f': 40134261Sbostic case 'g': 40234243Sbostic case 'G': 403*46076Sbostic _double = va_arg(ap, double); 40434328Sbostic /* 40534669Sbostic * don't do unrealistic precision; just pad it with 40634669Sbostic * zeroes later, so buffer size stays rational. 40734328Sbostic */ 40834328Sbostic if (prec > MAXFRACT) { 409*46076Sbostic if (ch != 'g' && ch != 'G' || (flags&ALT)) 41034475Sbostic fpprec = prec - MAXFRACT; 41134328Sbostic prec = MAXFRACT; 412*46076Sbostic } else if (prec == -1) 41334624Sbostic prec = DEFPREC; 41434669Sbostic /* 415*46076Sbostic * cvt may have to round up before the "start" of 416*46076Sbostic * its buffer, i.e. ``intf("%.2f", (double)9.999);''; 417*46076Sbostic * if the first character is still NUL, it did. 418*46076Sbostic * softsign avoids negative 0 if _double < 0 but 419*46076Sbostic * no significant digits will be shown. 42034669Sbostic */ 421*46076Sbostic cp = buf; 422*46076Sbostic *cp = '\0'; 423*46076Sbostic size = cvt(_double, prec, flags, &softsign, ch, 424*46076Sbostic cp, buf + sizeof(buf)); 42534669Sbostic if (softsign) 42634669Sbostic sign = '-'; 427*46076Sbostic if (*cp == '\0') 428*46076Sbostic cp++; 429*46076Sbostic break; 430*46076Sbostic #endif /* FLOATING_POINT */ 43134235Sbostic case 'n': 43234427Sbostic if (flags & LONGINT) 433*46076Sbostic *va_arg(ap, long *) = ret; 43434427Sbostic else if (flags & SHORTINT) 435*46076Sbostic *va_arg(ap, short *) = ret; 43634318Sbostic else 437*46076Sbostic *va_arg(ap, int *) = ret; 438*46076Sbostic continue; /* no output */ 43934624Sbostic case 'O': 44034624Sbostic flags |= LONGINT; 44134624Sbostic /*FALLTHROUGH*/ 44234226Sbostic case 'o': 443*46076Sbostic _ulong = UARG(); 444*46076Sbostic base = OCT; 44534327Sbostic goto nosign; 44634235Sbostic case 'p': 44734320Sbostic /* 44834321Sbostic * ``The argument shall be a pointer to void. The 44934321Sbostic * value of the pointer is converted to a sequence 45034321Sbostic * of printable characters, in an implementation- 45134321Sbostic * defined manner.'' 45234321Sbostic * -- ANSI X3J11 45334320Sbostic */ 45434427Sbostic /* NOSTRICT */ 455*46076Sbostic _ulong = (u_long)va_arg(ap, void *); 456*46076Sbostic base = HEX; 457*46076Sbostic xdigs = "0123456789abcdef"; 458*46076Sbostic flags |= HEXPREFIX; 459*46076Sbostic ch = 'x'; 46034327Sbostic goto nosign; 46134226Sbostic case 's': 462*46076Sbostic if ((cp = va_arg(ap, char *)) == NULL) 463*46076Sbostic cp = "(null)"; 46434321Sbostic if (prec >= 0) { 46534321Sbostic /* 46634321Sbostic * can't use strlen; can only look for the 46734321Sbostic * NUL in the first `prec' characters, and 46834321Sbostic * strlen() will go further. 46934321Sbostic */ 470*46076Sbostic char *p = memchr(cp, 0, prec); 47134321Sbostic 472*46076Sbostic if (p != NULL) { 473*46076Sbostic size = p - cp; 47434321Sbostic if (size > prec) 47534321Sbostic size = prec; 47634427Sbostic } else 47734321Sbostic size = prec; 47834427Sbostic } else 479*46076Sbostic size = strlen(cp); 48034427Sbostic sign = '\0'; 481*46076Sbostic break; 48234624Sbostic case 'U': 48334624Sbostic flags |= LONGINT; 48434624Sbostic /*FALLTHROUGH*/ 48534226Sbostic case 'u': 486*46076Sbostic _ulong = UARG(); 487*46076Sbostic base = DEC; 48834327Sbostic goto nosign; 48934226Sbostic case 'X': 490*46076Sbostic xdigs = "0123456789ABCDEF"; 491*46076Sbostic goto hex; 49234226Sbostic case 'x': 493*46076Sbostic xdigs = "0123456789abcdef"; 494*46076Sbostic hex: _ulong = UARG(); 495*46076Sbostic base = HEX; 49634326Sbostic /* leading 0x/X only if non-zero */ 49734427Sbostic if (flags & ALT && _ulong != 0) 49834427Sbostic flags |= HEXPREFIX; 49934327Sbostic 50034327Sbostic /* unsigned conversions */ 50134427Sbostic nosign: sign = '\0'; 50234326Sbostic /* 50334330Sbostic * ``... diouXx conversions ... if a precision is 50434330Sbostic * specified, the 0 flag will be ignored.'' 50534330Sbostic * -- ANSI X3J11 50634330Sbostic */ 50734427Sbostic number: if ((dprec = prec) >= 0) 50834427Sbostic flags &= ~ZEROPAD; 50934427Sbostic 51034330Sbostic /* 51134326Sbostic * ``The result of converting a zero value with an 51234326Sbostic * explicit precision of zero is no characters.'' 51334326Sbostic * -- ANSI X3J11 51434326Sbostic */ 515*46076Sbostic cp = buf + BUF; 51634427Sbostic if (_ulong != 0 || prec != 0) { 517*46076Sbostic /* 518*46076Sbostic * unsigned mod is hard, and unsigned mod 519*46076Sbostic * by a constant is easier than that by 520*46076Sbostic * a variable; hence this switch. 521*46076Sbostic */ 522*46076Sbostic switch (base) { 523*46076Sbostic case OCT: 524*46076Sbostic do { 525*46076Sbostic *--cp = to_char(_ulong & 7); 526*46076Sbostic _ulong >>= 3; 527*46076Sbostic } while (_ulong); 528*46076Sbostic /* handle octal leading 0 */ 529*46076Sbostic if (flags & ALT && *cp != '0') 530*46076Sbostic *--cp = '0'; 531*46076Sbostic break; 53234327Sbostic 533*46076Sbostic case DEC: 534*46076Sbostic /* many numbers are 1 digit */ 535*46076Sbostic while (_ulong >= 10) { 536*46076Sbostic *--cp = to_char(_ulong % 10); 537*46076Sbostic _ulong /= 10; 538*46076Sbostic } 539*46076Sbostic *--cp = to_char(_ulong); 540*46076Sbostic break; 54134327Sbostic 542*46076Sbostic case HEX: 543*46076Sbostic do { 544*46076Sbostic *--cp = xdigs[_ulong & 15]; 545*46076Sbostic _ulong >>= 4; 546*46076Sbostic } while (_ulong); 547*46076Sbostic break; 54834327Sbostic 549*46076Sbostic default: 550*46076Sbostic cp = "bug in vfprintf: bad base"; 551*46076Sbostic goto skipsize; 552*46076Sbostic } 55334327Sbostic } 554*46076Sbostic size = buf + BUF - cp; 555*46076Sbostic skipsize: 55634226Sbostic break; 557*46076Sbostic default: /* "%?" prints ?, unless ? is NUL */ 558*46076Sbostic if (ch == '\0') 559*46076Sbostic goto done; 560*46076Sbostic /* pretend it was %c with argument ch */ 561*46076Sbostic cp = buf; 562*46076Sbostic *cp = ch; 563*46076Sbostic size = 1; 564*46076Sbostic sign = '\0'; 565*46076Sbostic break; 56634226Sbostic } 567*46076Sbostic 568*46076Sbostic /* 569*46076Sbostic * All reasonable formats wind up here. At this point, 570*46076Sbostic * `cp' points to a string which (if not flags&LADJUST) 571*46076Sbostic * should be padded out to `width' places. If 572*46076Sbostic * flags&ZEROPAD, it should first be prefixed by any 573*46076Sbostic * sign or other prefix; otherwise, it should be blank 574*46076Sbostic * padded before the prefix is emitted. After any 575*46076Sbostic * left-hand padding and prefixing, emit zeroes 576*46076Sbostic * required by a decimal [diouxX] precision, then print 577*46076Sbostic * the string proper, then emit zeroes required by any 578*46076Sbostic * leftover floating precision; finally, if LADJUST, 579*46076Sbostic * pad with blanks. 580*46076Sbostic */ 581*46076Sbostic 582*46076Sbostic /* 583*46076Sbostic * compute actual size, so we know how much to pad. 584*46076Sbostic * fieldsz excludes decimal prec; realsz includes it 585*46076Sbostic */ 586*46076Sbostic #ifdef FLOATING_POINT 587*46076Sbostic fieldsz = size + fpprec; 588*46076Sbostic #else 589*46076Sbostic fieldsz = size; 590*46076Sbostic #endif 591*46076Sbostic if (sign) 592*46076Sbostic fieldsz++; 593*46076Sbostic else if (flags & HEXPREFIX) 594*46076Sbostic fieldsz += 2; 595*46076Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 596*46076Sbostic 597*46076Sbostic /* right-adjusting blank padding */ 598*46076Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0) 599*46076Sbostic PAD(width - realsz, blanks); 600*46076Sbostic 601*46076Sbostic /* prefix */ 602*46076Sbostic if (sign) { 603*46076Sbostic PRINT(&sign, 1); 604*46076Sbostic } else if (flags & HEXPREFIX) { 605*46076Sbostic ox[0] = '0'; 606*46076Sbostic ox[1] = ch; 607*46076Sbostic PRINT(ox, 2); 608*46076Sbostic } 609*46076Sbostic 610*46076Sbostic /* right-adjusting zero padding */ 611*46076Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 612*46076Sbostic PAD(width - realsz, zeroes); 613*46076Sbostic 614*46076Sbostic /* leading zeroes from decimal precision */ 615*46076Sbostic PAD(dprec - fieldsz, zeroes); 616*46076Sbostic 617*46076Sbostic /* the string or number proper */ 618*46076Sbostic PRINT(cp, size); 619*46076Sbostic 620*46076Sbostic #ifdef FLOATING_POINT 621*46076Sbostic /* trailing f.p. zeroes */ 622*46076Sbostic PAD(fpprec, zeroes); 623*46076Sbostic #endif 624*46076Sbostic 625*46076Sbostic /* left-adjusting padding (always blank) */ 626*46076Sbostic if (flags & LADJUST) 627*46076Sbostic PAD(width - realsz, blanks); 628*46076Sbostic 629*46076Sbostic /* finally, adjust ret */ 630*46076Sbostic ret += width > realsz ? width : realsz; 631*46076Sbostic 632*46076Sbostic FLUSH(); /* copy out the I/O vectors */ 63334226Sbostic } 634*46076Sbostic done: 635*46076Sbostic FLUSH(); 636*46076Sbostic error: 637*46076Sbostic return (__sferror(fp) ? EOF : ret); 63834427Sbostic /* NOTREACHED */ 63934226Sbostic } 64034242Sbostic 641*46076Sbostic #ifdef FLOATING_POINT 642*46076Sbostic static char *exponent(); 643*46076Sbostic static char *round(); 644*46076Sbostic 64534624Sbostic static 64634669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp) 64734242Sbostic double number; 64834261Sbostic register int prec; 64934323Sbostic int flags; 650*46076Sbostic char *signp; 651*46076Sbostic int fmtch; 652*46076Sbostic char *startp, *endp; 65334242Sbostic { 65434331Sbostic register char *p, *t; 65534672Sbostic register double fract; 65634624Sbostic int dotrim, expcnt, gformat; 65734672Sbostic double integer, tmp, modf(); 65834242Sbostic 659*46076Sbostic dotrim = expcnt = gformat = 0; 660*46076Sbostic if (number < 0) { 661*46076Sbostic number = -number; 662*46076Sbostic *signp = '-'; 663*46076Sbostic } else 664*46076Sbostic *signp = 0; 66544426Sbostic 66634624Sbostic fract = modf(number, &integer); 66734242Sbostic 66834624Sbostic /* get an extra slot for rounding. */ 66934624Sbostic t = ++startp; 67034624Sbostic 67134624Sbostic /* 67234624Sbostic * get integer portion of number; put into the end of the buffer; the 67334624Sbostic * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 67434624Sbostic */ 67534624Sbostic for (p = endp - 1; integer; ++expcnt) { 67634624Sbostic tmp = modf(integer / 10, &integer); 677*46076Sbostic *p-- = to_char((int)((tmp + .01) * 10)); 67834248Sbostic } 679*46076Sbostic switch (fmtch) { 68034261Sbostic case 'f': 68134624Sbostic /* reverse integer into beginning of buffer */ 68234624Sbostic if (expcnt) 68334624Sbostic for (; ++p < endp; *t++ = *p); 68434624Sbostic else 68534624Sbostic *t++ = '0'; 68634248Sbostic /* 68734624Sbostic * if precision required or alternate flag set, add in a 68834624Sbostic * decimal point. 68934248Sbostic */ 69034624Sbostic if (prec || flags&ALT) 69134624Sbostic *t++ = '.'; 69234624Sbostic /* if requires more precision and some fraction left */ 69334624Sbostic if (fract) { 69434624Sbostic if (prec) 69534624Sbostic do { 69634624Sbostic fract = modf(fract * 10, &tmp); 697*46076Sbostic *t++ = to_char((int)tmp); 69834624Sbostic } while (--prec && fract); 69934624Sbostic if (fract) 70034669Sbostic startp = round(fract, (int *)NULL, startp, 70134669Sbostic t - 1, (char)0, signp); 70234624Sbostic } 70334624Sbostic for (; prec--; *t++ = '0'); 70434624Sbostic break; 70534624Sbostic case 'e': 70634624Sbostic case 'E': 70734624Sbostic eformat: if (expcnt) { 70834624Sbostic *t++ = *++p; 70934624Sbostic if (prec || flags&ALT) 71034331Sbostic *t++ = '.'; 71134624Sbostic /* if requires more precision and some integer left */ 71234624Sbostic for (; prec && ++p < endp; --prec) 71334624Sbostic *t++ = *p; 71434624Sbostic /* 71534624Sbostic * if done precision and more of the integer component, 71634624Sbostic * round using it; adjust fract so we don't re-round 71734624Sbostic * later. 71834624Sbostic */ 71934624Sbostic if (!prec && ++p < endp) { 72034248Sbostic fract = 0; 72134669Sbostic startp = round((double)0, &expcnt, startp, 72234669Sbostic t - 1, *p, signp); 72334248Sbostic } 72434624Sbostic /* adjust expcnt for digit in front of decimal */ 72534624Sbostic --expcnt; 72634242Sbostic } 72734624Sbostic /* until first fractional digit, decrement exponent */ 72834624Sbostic else if (fract) { 72934624Sbostic /* adjust expcnt for digit in front of decimal */ 73034624Sbostic for (expcnt = -1;; --expcnt) { 73134624Sbostic fract = modf(fract * 10, &tmp); 73234624Sbostic if (tmp) 73334624Sbostic break; 73434248Sbostic } 735*46076Sbostic *t++ = to_char((int)tmp); 73634624Sbostic if (prec || flags&ALT) 73734331Sbostic *t++ = '.'; 73834242Sbostic } 73934248Sbostic else { 74034624Sbostic *t++ = '0'; 74134624Sbostic if (prec || flags&ALT) 74234331Sbostic *t++ = '.'; 74334248Sbostic } 74434624Sbostic /* if requires more precision and some fraction left */ 74534624Sbostic if (fract) { 74634624Sbostic if (prec) 74734624Sbostic do { 74834624Sbostic fract = modf(fract * 10, &tmp); 749*46076Sbostic *t++ = to_char((int)tmp); 75034624Sbostic } while (--prec && fract); 75134624Sbostic if (fract) 75234669Sbostic startp = round(fract, &expcnt, startp, 75334669Sbostic t - 1, (char)0, signp); 75434584Sbostic } 75534624Sbostic /* if requires more precision */ 75634624Sbostic for (; prec--; *t++ = '0'); 75734624Sbostic 75834624Sbostic /* unless alternate flag, trim any g/G format trailing 0's */ 75934624Sbostic if (gformat && !(flags&ALT)) { 76034624Sbostic while (t > startp && *--t == '0'); 76134624Sbostic if (*t == '.') 76234624Sbostic --t; 76334624Sbostic ++t; 76434624Sbostic } 76534624Sbostic t = exponent(t, expcnt, fmtch); 76634624Sbostic break; 76734624Sbostic case 'g': 76834624Sbostic case 'G': 76934624Sbostic /* a precision of 0 is treated as a precision of 1. */ 77034624Sbostic if (!prec) 77134624Sbostic ++prec; 77234624Sbostic /* 77334624Sbostic * ``The style used depends on the value converted; style e 77434624Sbostic * will be used only if the exponent resulting from the 77534624Sbostic * conversion is less than -4 or greater than the precision.'' 77634624Sbostic * -- ANSI X3J11 77734624Sbostic */ 77834624Sbostic if (expcnt > prec || !expcnt && fract && fract < .0001) { 77934624Sbostic /* 78034624Sbostic * g/G format counts "significant digits, not digits of 78134624Sbostic * precision; for the e/E format, this just causes an 78234624Sbostic * off-by-one problem, i.e. g/G considers the digit 78334624Sbostic * before the decimal point significant and e/E doesn't 78434624Sbostic * count it as precision. 78534624Sbostic */ 78634624Sbostic --prec; 78734624Sbostic fmtch -= 2; /* G->E, g->e */ 78834624Sbostic gformat = 1; 78934624Sbostic goto eformat; 79034624Sbostic } 79134624Sbostic /* 79234624Sbostic * reverse integer into beginning of buffer, 79334624Sbostic * note, decrement precision 79434624Sbostic */ 79534624Sbostic if (expcnt) 79634624Sbostic for (; ++p < endp; *t++ = *p, --prec); 79734624Sbostic else 79834624Sbostic *t++ = '0'; 79934624Sbostic /* 80034624Sbostic * if precision required or alternate flag set, add in a 80134624Sbostic * decimal point. If no digits yet, add in leading 0. 80234624Sbostic */ 80334624Sbostic if (prec || flags&ALT) { 80434624Sbostic dotrim = 1; 80534624Sbostic *t++ = '.'; 80634624Sbostic } 80734624Sbostic else 80834624Sbostic dotrim = 0; 80934624Sbostic /* if requires more precision and some fraction left */ 81034624Sbostic if (fract) { 81134624Sbostic if (prec) { 81234624Sbostic do { 81334624Sbostic fract = modf(fract * 10, &tmp); 814*46076Sbostic *t++ = to_char((int)tmp); 81534624Sbostic } while(!tmp); 81634624Sbostic while (--prec && fract) { 81734624Sbostic fract = modf(fract * 10, &tmp); 818*46076Sbostic *t++ = to_char((int)tmp); 81934248Sbostic } 82034248Sbostic } 82134624Sbostic if (fract) 82234669Sbostic startp = round(fract, (int *)NULL, startp, 82334669Sbostic t - 1, (char)0, signp); 82434624Sbostic } 82534624Sbostic /* alternate format, adds 0's for precision, else trim 0's */ 82634624Sbostic if (flags&ALT) 82734624Sbostic for (; prec--; *t++ = '0'); 82834624Sbostic else if (dotrim) { 82934624Sbostic while (t > startp && *--t == '0'); 83034624Sbostic if (*t != '.') 83134624Sbostic ++t; 83234624Sbostic } 83334624Sbostic } 834*46076Sbostic return (t - startp); 83534624Sbostic } 83634248Sbostic 83734624Sbostic static char * 83834669Sbostic round(fract, exp, start, end, ch, signp) 83934624Sbostic double fract; 84034669Sbostic int *exp; 84134624Sbostic register char *start, *end; 84234669Sbostic char ch, *signp; 84334624Sbostic { 84434624Sbostic double tmp; 84534248Sbostic 84634624Sbostic if (fract) 847*46076Sbostic (void)modf(fract * 10, &tmp); 84834624Sbostic else 849*46076Sbostic tmp = to_digit(ch); 85034624Sbostic if (tmp > 4) 85134624Sbostic for (;; --end) { 85234624Sbostic if (*end == '.') 85334624Sbostic --end; 85434624Sbostic if (++*end <= '9') 85534624Sbostic break; 85634624Sbostic *end = '0'; 85734624Sbostic if (end == start) { 85834669Sbostic if (exp) { /* e/E; increment exponent */ 85934669Sbostic *end = '1'; 86034669Sbostic ++*exp; 86134669Sbostic } 86234669Sbostic else { /* f; add extra digit */ 863*46076Sbostic *--end = '1'; 864*46076Sbostic --start; 86534669Sbostic } 86634624Sbostic break; 86734242Sbostic } 86834242Sbostic } 86934669Sbostic /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 87034669Sbostic else if (*signp == '-') 87134669Sbostic for (;; --end) { 87234669Sbostic if (*end == '.') 87334669Sbostic --end; 87434669Sbostic if (*end != '0') 87534669Sbostic break; 87634669Sbostic if (end == start) 87734669Sbostic *signp = 0; 87834669Sbostic } 879*46076Sbostic return (start); 88034624Sbostic } 88134248Sbostic 88234624Sbostic static char * 88334624Sbostic exponent(p, exp, fmtch) 88434624Sbostic register char *p; 88534624Sbostic register int exp; 886*46076Sbostic int fmtch; 88734624Sbostic { 88834624Sbostic register char *t; 88934624Sbostic char expbuf[MAXEXP]; 89034248Sbostic 89134624Sbostic *p++ = fmtch; 89234624Sbostic if (exp < 0) { 89334624Sbostic exp = -exp; 89434624Sbostic *p++ = '-'; 89534242Sbostic } 89634624Sbostic else 89734624Sbostic *p++ = '+'; 89834624Sbostic t = expbuf + MAXEXP; 89934624Sbostic if (exp > 9) { 90034624Sbostic do { 901*46076Sbostic *--t = to_char(exp % 10); 90234624Sbostic } while ((exp /= 10) > 9); 903*46076Sbostic *--t = to_char(exp); 90434624Sbostic for (; t < expbuf + MAXEXP; *p++ = *t++); 90534624Sbostic } 90634624Sbostic else { 90734624Sbostic *p++ = '0'; 908*46076Sbostic *p++ = to_char(exp); 90934624Sbostic } 910*46076Sbostic return (p); 91134242Sbostic } 912*46076Sbostic #endif /* FLOATING_POINT */ 913