134226Sbostic /* 234233Sbostic * Copyright (c) 1988 Regents of the University of California. 334233Sbostic * All rights reserved. 434226Sbostic * 534233Sbostic * Redistribution and use in source and binary forms are permitted 634233Sbostic * provided that this notice is preserved and that due credit is given 734233Sbostic * to the University of California at Berkeley. The name of the University 834233Sbostic * may not be used to endorse or promote products derived from this 934233Sbostic * software without specific prior written permission. This software 1034233Sbostic * is provided ``as is'' without express or implied warranty. 1134226Sbostic */ 1234226Sbostic 1334233Sbostic #if defined(LIBC_SCCS) && !defined(lint) 14*34584Sbostic static char sccsid[] = "@(#)vfprintf.c 5.31 (Berkeley) 06/01/88"; 1534233Sbostic #endif /* LIBC_SCCS and not lint */ 1634233Sbostic 1734261Sbostic #include <sys/types.h> 1834233Sbostic #include <varargs.h> 1934226Sbostic #include <stdio.h> 2034233Sbostic #include <ctype.h> 2134226Sbostic 2234328Sbostic /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ 2334328Sbostic #define MAXEXP 308 2434328Sbostic /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ 2534328Sbostic #define MAXFRACT 39 2634226Sbostic 2734328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 2834328Sbostic 2934427Sbostic #define PUTC(ch) (void) putc(ch, fp) 3034236Sbostic 3134318Sbostic #define ARG() \ 3234318Sbostic _ulong = flags&LONGINT ? va_arg(argp, long) : \ 3334318Sbostic flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 3434235Sbostic 3534331Sbostic #define todigit(c) ((c) - '0') 3634331Sbostic #define tochar(n) ((n) + '0') 3734331Sbostic 3834318Sbostic /* have to deal with the negative buffer count kludge */ 3934318Sbostic #define NEGATIVE_COUNT_KLUDGE 4034318Sbostic 4134318Sbostic #define LONGINT 0x01 /* long integer */ 4234318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 4334318Sbostic #define SHORTINT 0x04 /* short integer */ 4434318Sbostic #define ALT 0x08 /* alternate form */ 4534318Sbostic #define LADJUST 0x10 /* left adjustment */ 4634427Sbostic #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 4734427Sbostic #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 4834318Sbostic 4934323Sbostic _doprnt(fmt0, argp, fp) 5034323Sbostic u_char *fmt0; 5134233Sbostic va_list argp; 5234235Sbostic register FILE *fp; 5334226Sbostic { 5434427Sbostic register u_char *fmt; /* format string */ 5534427Sbostic register int ch; /* character from fmt */ 5634427Sbostic register int cnt; /* return value accumulator */ 5734427Sbostic register int n; /* random handy integer */ 5834427Sbostic register char *t; /* buffer pointer */ 5934427Sbostic double _double; /* double precision arguments %[eEfgG] */ 6034427Sbostic u_long _ulong; /* integer arguments %[diouxX] */ 6134427Sbostic int flags; /* flags as above */ 6234427Sbostic int dprec; /* decimal precision in [diouxX] */ 6334427Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 6434427Sbostic int width; /* width from format (%8d), or 0 */ 6534427Sbostic int prec; /* precision from format (%.3d), or -1 */ 6634427Sbostic int size; /* size of converted field or string */ 6734427Sbostic int fieldsz; /* field size expanded by sign, etc */ 6834427Sbostic int realsz; /* field size expanded by decimal precision */ 6934427Sbostic char sign; /* sign prefix (+ - or \0) */ 7034427Sbostic int base; /* base for [diouxX] conversion */ 7134427Sbostic char *digs; /* digits for [diouxX] conversion */ 7234427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 7334427Sbostic char *_cvt(); /* handles [eEfgG] formats */ 7434226Sbostic 7534428Sbostic if (fp->_flag & _IORW) { 7634428Sbostic fp->_flag |= _IOWRT; 7734428Sbostic fp->_flag &= ~(_IOEOF|_IOREAD); 7834428Sbostic } 7934428Sbostic if ((fp->_flag & _IOWRT) == 0) 8034428Sbostic return (EOF); 8134428Sbostic 8234323Sbostic fmt = fmt0; 8334243Sbostic digs = "0123456789abcdef"; 8434322Sbostic for (cnt = 0;; ++fmt) { 8534318Sbostic n = fp->_cnt; 8634427Sbostic for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%'; 8734427Sbostic ++cnt, ++fmt) 8834318Sbostic if (--n < 0 8934318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE 9034318Sbostic && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 9134318Sbostic #endif 9234427Sbostic || ch == '\n' && fp->_flag & _IOLBF) { 9334318Sbostic fp->_cnt = n; 9434475Sbostic fp->_ptr = t; 9534427Sbostic (void) _flsbuf((u_char)ch, fp); 9634318Sbostic n = fp->_cnt; 9734427Sbostic t = (char *)fp->_ptr; 9834427Sbostic } else 9934314Sbostic *t++ = ch; 10034318Sbostic fp->_cnt = n; 10134475Sbostic fp->_ptr = t; 10234318Sbostic if (!ch) 10334427Sbostic return (cnt); 10434314Sbostic 10534427Sbostic flags = dprec = fpprec = width = 0; 10634233Sbostic prec = -1; 10734318Sbostic sign = '\0'; 10834226Sbostic 10934318Sbostic rflag: switch (*++fmt) { 11034318Sbostic case ' ': 11134318Sbostic sign = ' '; 11234318Sbostic goto rflag; 11334233Sbostic case '#': 11434318Sbostic flags |= ALT; 11534318Sbostic goto rflag; 11634233Sbostic case '*': 11734235Sbostic /* 11834235Sbostic * ``A negative field width argument is taken as a 11934235Sbostic * - flag followed by a positive field width.'' 12034235Sbostic * -- ANSI X3J11 12134235Sbostic * They don't exclude field widths read from args. 12234235Sbostic */ 12334235Sbostic if ((width = va_arg(argp, int)) >= 0) 12434318Sbostic goto rflag; 12534235Sbostic width = -width; 12634427Sbostic /* FALLTHROUGH */ 12734235Sbostic case '-': 12834318Sbostic flags |= LADJUST; 12934318Sbostic goto rflag; 13034233Sbostic case '+': 13134314Sbostic sign = '+'; 13234318Sbostic goto rflag; 13334233Sbostic case '.': 13434235Sbostic if (*++fmt == '*') 13534318Sbostic n = va_arg(argp, int); 13634427Sbostic else { 13734318Sbostic n = 0; 13834427Sbostic while (isascii(*fmt) && isdigit(*fmt)) 13934427Sbostic n = 10 * n + todigit(*fmt++); 14034233Sbostic --fmt; 14134226Sbostic } 14234318Sbostic prec = n < 0 ? -1 : n; 14334318Sbostic goto rflag; 14434233Sbostic case '0': 14534427Sbostic /* 14634427Sbostic * ``Note that 0 is taken as a flag, not as the 14734427Sbostic * beginning of a field width.'' 14834427Sbostic * -- ANSI X3J11 14934427Sbostic */ 15034427Sbostic flags |= ZEROPAD; 15134427Sbostic goto rflag; 15234233Sbostic case '1': case '2': case '3': case '4': 15334233Sbostic case '5': case '6': case '7': case '8': case '9': 15434318Sbostic n = 0; 15534233Sbostic do { 15634331Sbostic n = 10 * n + todigit(*fmt); 15734318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 15834318Sbostic width = n; 15934233Sbostic --fmt; 16034319Sbostic goto rflag; 16134235Sbostic case 'L': 16234329Sbostic flags |= LONGDBL; 16334318Sbostic goto rflag; 16434235Sbostic case 'h': 16534318Sbostic flags |= SHORTINT; 16634318Sbostic goto rflag; 16734233Sbostic case 'l': 16834318Sbostic flags |= LONGINT; 16934318Sbostic goto rflag; 17034314Sbostic case 'c': 17134427Sbostic *(t = buf) = va_arg(argp, int); 17234314Sbostic size = 1; 17334427Sbostic sign = '\0'; 17434314Sbostic goto pforw; 17534314Sbostic case 'd': 17634318Sbostic case 'i': 17734318Sbostic ARG(); 17834318Sbostic if ((long)_ulong < 0) { 17934318Sbostic _ulong = -_ulong; 18034314Sbostic sign = '-'; 18134241Sbostic } 18234241Sbostic base = 10; 18334327Sbostic goto number; 18434261Sbostic case 'e': 18534236Sbostic case 'E': 18634235Sbostic case 'f': 18734261Sbostic case 'g': 18834243Sbostic case 'G': 18934243Sbostic _double = va_arg(argp, double); 19034328Sbostic /* 19134328Sbostic * don't bother to do unrealistic precision; just 19234328Sbostic * pad it with zeroes later. This keeps buffer size 19334328Sbostic * rational. 19434328Sbostic */ 19534328Sbostic if (prec > MAXFRACT) { 19634475Sbostic if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) 19734475Sbostic fpprec = prec - MAXFRACT; 19834328Sbostic prec = MAXFRACT; 19934328Sbostic } 20034319Sbostic t = buf; 20134427Sbostic size = _cvt(_double, prec, flags, *fmt, &sign, 20234427Sbostic t, t + sizeof(buf)) - t; 20334314Sbostic goto pforw; 20434235Sbostic case 'n': 20534427Sbostic if (flags & LONGINT) 20634318Sbostic *va_arg(argp, long *) = cnt; 20734427Sbostic else if (flags & SHORTINT) 20834318Sbostic *va_arg(argp, short *) = cnt; 20934318Sbostic else 21034318Sbostic *va_arg(argp, int *) = cnt; 21134235Sbostic break; 21234226Sbostic case 'o': 21334318Sbostic ARG(); 21434226Sbostic base = 8; 21534327Sbostic goto nosign; 21634235Sbostic case 'p': 21734320Sbostic /* 21834321Sbostic * ``The argument shall be a pointer to void. The 21934321Sbostic * value of the pointer is converted to a sequence 22034321Sbostic * of printable characters, in an implementation- 22134321Sbostic * defined manner.'' 22234321Sbostic * -- ANSI X3J11 22334320Sbostic */ 22434427Sbostic /* NOSTRICT */ 22534320Sbostic _ulong = (u_long)va_arg(argp, void *); 22634320Sbostic base = 16; 22734327Sbostic goto nosign; 22834226Sbostic case 's': 22934314Sbostic if (!(t = va_arg(argp, char *))) 23034314Sbostic t = "(null)"; 23134321Sbostic if (prec >= 0) { 23234321Sbostic /* 23334321Sbostic * can't use strlen; can only look for the 23434321Sbostic * NUL in the first `prec' characters, and 23534321Sbostic * strlen() will go further. 23634321Sbostic */ 23734321Sbostic char *p, *memchr(); 23834321Sbostic 23934321Sbostic if (p = memchr(t, 0, prec)) { 24034321Sbostic size = p - t; 24134321Sbostic if (size > prec) 24234321Sbostic size = prec; 24334427Sbostic } else 24434321Sbostic size = prec; 24534427Sbostic } else 24634321Sbostic size = strlen(t); 24734427Sbostic sign = '\0'; 24834328Sbostic goto pforw; 24934226Sbostic case 'u': 25034318Sbostic ARG(); 25134226Sbostic base = 10; 25234327Sbostic goto nosign; 25334226Sbostic case 'X': 25434226Sbostic digs = "0123456789ABCDEF"; 25534427Sbostic /* FALLTHROUGH */ 25634226Sbostic case 'x': 25734318Sbostic ARG(); 25834314Sbostic base = 16; 25934326Sbostic /* leading 0x/X only if non-zero */ 26034427Sbostic if (flags & ALT && _ulong != 0) 26134427Sbostic flags |= HEXPREFIX; 26234327Sbostic 26334327Sbostic /* unsigned conversions */ 26434427Sbostic nosign: sign = '\0'; 26534326Sbostic /* 26634330Sbostic * ``... diouXx conversions ... if a precision is 26734330Sbostic * specified, the 0 flag will be ignored.'' 26834330Sbostic * -- ANSI X3J11 26934330Sbostic */ 27034427Sbostic number: if ((dprec = prec) >= 0) 27134427Sbostic flags &= ~ZEROPAD; 27234427Sbostic 27334330Sbostic /* 27434326Sbostic * ``The result of converting a zero value with an 27534326Sbostic * explicit precision of zero is no characters.'' 27634326Sbostic * -- ANSI X3J11 27734326Sbostic */ 27834427Sbostic t = buf + BUF; 27934427Sbostic if (_ulong != 0 || prec != 0) { 28034427Sbostic do { 28134427Sbostic *--t = digs[_ulong % base]; 28234427Sbostic _ulong /= base; 28334427Sbostic } while (_ulong); 28434427Sbostic digs = "0123456789abcdef"; 28534427Sbostic if (flags & ALT && base == 8 && *t != '0') 28634427Sbostic *--t = '0'; /* octal leading 0 */ 28734330Sbostic } 28834427Sbostic size = buf + BUF - t; 28934327Sbostic 29034427Sbostic pforw: 29134427Sbostic /* 29234427Sbostic * All reasonable formats wind up here. At this 29334427Sbostic * point, `t' points to a string which (if not 29434427Sbostic * flags&LADJUST) should be padded out to `width' 29534427Sbostic * places. If flags&ZEROPAD, it should first be 29634427Sbostic * prefixed by any sign or other prefix; otherwise, 29734427Sbostic * it should be blank padded before the prefix is 29834427Sbostic * emitted. After any left-hand padding and 29934427Sbostic * prefixing, emit zeroes required by a decimal 30034427Sbostic * [diouxX] precision, then print the string proper, 30134427Sbostic * then emit zeroes required by any leftover floating 30234427Sbostic * precision; finally, if LADJUST, pad with blanks. 30334427Sbostic */ 30434327Sbostic 30534427Sbostic /* compute actual size, so we know how much to pad */ 30634427Sbostic /* this code is not terribly satisfactory */ 30734427Sbostic /* fieldsz excludes decimal prec; realsz includes it */ 30834427Sbostic fieldsz = size + fpprec; 30934427Sbostic if (sign) 31034427Sbostic fieldsz++; 31134427Sbostic if (flags & HEXPREFIX) 31234427Sbostic fieldsz += 2; 31334427Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 31434327Sbostic 31534427Sbostic /* right-adjusting blank padding */ 31634427Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0 && width) 31734427Sbostic for (n = realsz; n < width; n++) 31834427Sbostic PUTC(' '); 31934427Sbostic /* prefix */ 32034427Sbostic if (sign) 32134427Sbostic PUTC(sign); 32234427Sbostic if (flags & HEXPREFIX) { 32334427Sbostic PUTC('0'); 32434427Sbostic PUTC((char)*fmt); 32534327Sbostic } 32634427Sbostic /* right-adjusting zero padding */ 32734427Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 32834427Sbostic for (n = realsz; n < width; n++) 32934427Sbostic PUTC('0'); 33034427Sbostic /* leading zeroes from decimal precision */ 33134427Sbostic for (n = fieldsz; n < dprec; n++) 33234427Sbostic PUTC('0'); 33334327Sbostic 33434427Sbostic /* the string or number proper */ 33534427Sbostic if (fp->_cnt - (n = size) >= 0 && 33634427Sbostic (fp->_flag & _IOLBF) == 0) { 33734328Sbostic fp->_cnt -= n; 33834427Sbostic bcopy(t, (char *)fp->_ptr, n); 33934328Sbostic fp->_ptr += n; 34034427Sbostic } else 34134427Sbostic while (--n >= 0) 34234427Sbostic PUTC(*t++); 34334427Sbostic /* trailing f.p. zeroes */ 34434427Sbostic while (--fpprec >= 0) 34534328Sbostic PUTC('0'); 34634427Sbostic /* left-adjusting padding (always blank) */ 34734427Sbostic if (flags & LADJUST) 34834427Sbostic for (n = realsz; n < width; n++) 34934328Sbostic PUTC(' '); 35034427Sbostic 35134427Sbostic /* finally, adjust cnt */ 35234427Sbostic cnt += width > realsz ? width : realsz; 35334226Sbostic break; 35434427Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 35534427Sbostic return (cnt); 35634226Sbostic default: 35734427Sbostic PUTC((char)*fmt); 35834427Sbostic cnt++; 35934226Sbostic } 36034226Sbostic } 36134427Sbostic /* NOTREACHED */ 36234226Sbostic } 36334242Sbostic 36434261Sbostic #define EFORMAT 0x01 36534261Sbostic #define FFORMAT 0x02 36634261Sbostic #define GFORMAT 0x04 36734314Sbostic #define DEFPREC 6 36834261Sbostic 36934323Sbostic static char * 37034427Sbostic _cvt(number, prec, flags, fmtch, sign, startp, endp) 37134242Sbostic double number; 37234261Sbostic register int prec; 37334323Sbostic int flags; 37434323Sbostic u_char fmtch; 37534427Sbostic char *sign, *startp, *endp; 37634242Sbostic { 37734331Sbostic register char *p, *t; 37834261Sbostic register int expcnt, format; 37934248Sbostic double fract, integer, tmp, modf(); 38034261Sbostic int decpt; 38134331Sbostic char *savep, exponent[MAXEXP]; 38234242Sbostic 38334314Sbostic if (prec == -1) 38434242Sbostic prec = DEFPREC; 38534242Sbostic 38634314Sbostic if (number < 0) { 38734327Sbostic *sign = '-'; 38834248Sbostic number = -number; 38934248Sbostic } 39034242Sbostic 39134261Sbostic switch(fmtch) { 39234261Sbostic case 'e': 39334261Sbostic case 'E': 39434261Sbostic format = EFORMAT; 39534261Sbostic break; 39634261Sbostic case 'f': 39734261Sbostic format = FFORMAT; 39834261Sbostic break; 39934261Sbostic case 'g': 40034261Sbostic case 'G': 40134261Sbostic format = GFORMAT; 40234261Sbostic fmtch -= 2; 40334261Sbostic } 40434261Sbostic 40534248Sbostic /* 40634248Sbostic * if the alternate flag is set, or, at least one digit of precision 40734248Sbostic * was requested, add a decimal point, unless it's the g/G format 40834314Sbostic * in which case we require two digits of precision, as it counts 40934248Sbostic * precision differently. 41034248Sbostic */ 41134318Sbostic decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 41234248Sbostic 41334248Sbostic expcnt = 0; 41434323Sbostic p = endp - 1; 41534248Sbostic fract = modf(number, &integer); 41634248Sbostic if (integer) { 41734248Sbostic /* get integer part of number; count decimal places */ 41834248Sbostic for (; integer; ++expcnt) { 41934248Sbostic tmp = modf(integer / 10, &integer); 42034331Sbostic *p-- = tochar((int)((tmp + .03) * 10)); 42134242Sbostic } 42234248Sbostic 42334248Sbostic /* copy, in reverse order, to start of buffer */ 42434331Sbostic t = startp; 42534331Sbostic *t++ = *++p; 42634248Sbostic 42734248Sbostic /* 42834248Sbostic * if the format is g/G, and the resulting exponent will be 42934248Sbostic * greater than the precision, use e/E format. If e/E format, 43034248Sbostic * put in a decimal point as needed, and decrement precision 43134248Sbostic * count for each digit after the decimal point. 43234248Sbostic */ 43334248Sbostic if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 43434248Sbostic if (format&GFORMAT) { 43534248Sbostic format |= EFORMAT; 43634248Sbostic 43734248Sbostic /* first digit is precision for g/G format */ 43834248Sbostic if (prec) 43934248Sbostic --prec; 44034248Sbostic } 44134248Sbostic if (decpt) 44234331Sbostic *t++ = '.'; 44334331Sbostic for (; ++p < endp && prec; --prec, *t++ = *p); 44434248Sbostic 44534318Sbostic /* precision ran out, round */ 44634248Sbostic if (p < endp) { 44734248Sbostic if (*p > '4') { 44834331Sbostic for (savep = t--;; *t-- = '0') { 44934331Sbostic if (*t == '.') 45034331Sbostic --t; 45134331Sbostic if (++*t <= '9') 45234248Sbostic break; 45334248Sbostic } 45434331Sbostic t = savep; 45534248Sbostic } 45634248Sbostic fract = 0; 45734248Sbostic } 45834242Sbostic } 45934248Sbostic /* 46034314Sbostic * g/G in f format; if out of precision, replace digits with 46134314Sbostic * zeroes, note, have to round first. 46234248Sbostic */ 46334248Sbostic else if (format&GFORMAT) { 46434331Sbostic for (; ++p < endp && prec; --prec, *t++ = *p); 46534248Sbostic /* precision ran out; round and then add zeroes */ 46634248Sbostic if (p < endp) { 46734248Sbostic if (*p > '4') { 46834331Sbostic for (savep = t--; ++*t > '9'; 46934331Sbostic *t-- = '0'); 47034331Sbostic t = savep; 47134248Sbostic } 47234248Sbostic do { 47334331Sbostic *t++ = '0'; 47434248Sbostic } while (++p < endp); 47534248Sbostic fract = 0; 47634248Sbostic } 47734248Sbostic if (decpt) 47834331Sbostic *t++ = '.'; 47934242Sbostic } 48034248Sbostic /* f format */ 48134248Sbostic else { 48234331Sbostic for (; ++p < endp; *t++ = *p); 48334248Sbostic if (decpt) 48434331Sbostic *t++ = '.'; 48534248Sbostic } 48634331Sbostic p = t; 48734248Sbostic } 48834248Sbostic /* 48934318Sbostic * if no fraction, the number was zero, and if no precision, can't 49034318Sbostic * show anything after the decimal point. 49134248Sbostic */ 49234248Sbostic else if (!fract || !prec) { 49334248Sbostic *startp++ = '0'; 494*34584Sbostic if (decpt && !(format&GFORMAT)) { 49534248Sbostic *startp++ = '.'; 496*34584Sbostic while (prec-- > 0) 497*34584Sbostic *startp++ = '0'; 498*34584Sbostic } 49934318Sbostic *startp = '\0'; 50034323Sbostic return(startp); 50134248Sbostic } 50234248Sbostic /* 50334248Sbostic * if the format is g/G, and the resulting exponent will be less than 50434248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 50534248Sbostic */ 50634248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 50734248Sbostic format |= EFORMAT; 50834248Sbostic if (fract) 50934248Sbostic for (p = startp; fract;) { 51034248Sbostic fract = modf(fract * 10, &tmp); 51134248Sbostic if (!tmp) { 51234248Sbostic --expcnt; 51334248Sbostic continue; 51434248Sbostic } 51534331Sbostic *p++ = tochar((int)tmp); 51634248Sbostic break; 51734248Sbostic } 51834242Sbostic else 51934248Sbostic *p++ = '0'; 52034248Sbostic 52134248Sbostic /* g/G format, decrement precision for first digit */ 52234248Sbostic if (format&GFORMAT && prec) 52334248Sbostic --prec; 52434248Sbostic 52534248Sbostic /* add decimal after first non-zero digit */ 52634248Sbostic if (decpt) 52734248Sbostic *p++ = '.'; 52834242Sbostic } 52934248Sbostic /* 53034248Sbostic * f format or g/G printed as f format; don't worry about decimal 53134248Sbostic * point, if g/G format doesn't need it, will get stripped later. 53234248Sbostic */ 53334242Sbostic else { 53434248Sbostic p = startp; 53534248Sbostic *p++ = '0'; 53634248Sbostic *p++ = '.'; 53734248Sbostic } 53834248Sbostic 53934319Sbostic /* finish out requested precision */ 54034319Sbostic while (fract && prec-- > 0) { 54134319Sbostic fract = modf(fract * 10, &tmp); 54234331Sbostic *p++ = tochar((int)tmp); 54334319Sbostic } 54434319Sbostic while (prec-- > 0) 54534319Sbostic *p++ = '0'; 54634248Sbostic 54734248Sbostic /* 54834248Sbostic * if any fractional value left, "round" it back up to the beginning 54934248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 55034248Sbostic * decimal point. 55134248Sbostic */ 55234248Sbostic if (fract) { 55334248Sbostic (void)modf(fract * 10, &tmp); 55434248Sbostic if (tmp > 4) { 55534248Sbostic for (savep = p--;; *p-- = '0') { 55634248Sbostic if (*p == '.') 55734248Sbostic --p; 55834248Sbostic if (p == startp) { 55934248Sbostic *p = '1'; 56034248Sbostic ++expcnt; 56134248Sbostic break; 56234248Sbostic } 56334248Sbostic if (++*p <= '9') 56434248Sbostic break; 56534242Sbostic } 56634248Sbostic p = savep; 56734242Sbostic } 56834248Sbostic } 56934248Sbostic 57034248Sbostic /* 57134248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 57234248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 57334248Sbostic */ 57434318Sbostic if (!(flags&ALT)) { 57534248Sbostic if (format&GFORMAT) 57634248Sbostic for (; p[-1] == '0'; --p); 57734248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 57834248Sbostic --p; 57934248Sbostic } 58034248Sbostic 58134248Sbostic /* if an e/E format, add exponent */ 58234248Sbostic if (format&EFORMAT) { 58334248Sbostic *p++ = fmtch; 58434248Sbostic if (--expcnt < 0) { 58534248Sbostic expcnt = -expcnt; 58634248Sbostic *p++ = '-'; 58734242Sbostic } 58834248Sbostic else 58934248Sbostic *p++ = '+'; 59034331Sbostic t = exponent + MAXEXP; 59134331Sbostic if (expcnt > 9) { 59234331Sbostic do { 59334331Sbostic *--t = tochar(expcnt % 10); 59434331Sbostic } while ((expcnt /= 10) > 9); 59534331Sbostic *--t = tochar(expcnt); 59634331Sbostic for (; t < exponent + MAXEXP; *p++ = *t++); 59734331Sbostic } 59834331Sbostic else { 59934331Sbostic *p++ = '0'; 60034331Sbostic *p++ = tochar(expcnt); 60134331Sbostic } 60234242Sbostic } 60334323Sbostic return(p); 60434242Sbostic } 605