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 6*34825Sbostic * provided that the above copyright notice and this paragraph are 7*34825Sbostic * duplicated in all such forms and that any documentation, 8*34825Sbostic * advertising materials, and other materials related to such 9*34825Sbostic * distribution and use acknowledge that the software was developed 10*34825Sbostic * by the University of California, Berkeley. The name of the 11*34825Sbostic * University may not be used to endorse or promote products derived 12*34825Sbostic * from this software without specific prior written permission. 13*34825Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34825Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34825Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1634226Sbostic */ 1734226Sbostic 1834233Sbostic #if defined(LIBC_SCCS) && !defined(lint) 19*34825Sbostic static char sccsid[] = "@(#)vfprintf.c 5.35 (Berkeley) 06/27/88"; 2034233Sbostic #endif /* LIBC_SCCS and not lint */ 2134233Sbostic 2234261Sbostic #include <sys/types.h> 2334233Sbostic #include <varargs.h> 2434226Sbostic #include <stdio.h> 2534233Sbostic #include <ctype.h> 2634226Sbostic 2734328Sbostic /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ 2834328Sbostic #define MAXEXP 308 2934328Sbostic /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ 3034328Sbostic #define MAXFRACT 39 3134226Sbostic 3234624Sbostic #define DEFPREC 6 3334624Sbostic 3434328Sbostic #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 3534328Sbostic 3634427Sbostic #define PUTC(ch) (void) putc(ch, fp) 3734236Sbostic 3834318Sbostic #define ARG() \ 3934318Sbostic _ulong = flags&LONGINT ? va_arg(argp, long) : \ 4034318Sbostic flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 4134235Sbostic 4234331Sbostic #define todigit(c) ((c) - '0') 4334331Sbostic #define tochar(n) ((n) + '0') 4434331Sbostic 4534318Sbostic /* have to deal with the negative buffer count kludge */ 4634318Sbostic #define NEGATIVE_COUNT_KLUDGE 4734318Sbostic 4834318Sbostic #define LONGINT 0x01 /* long integer */ 4934318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 5034318Sbostic #define SHORTINT 0x04 /* short integer */ 5134318Sbostic #define ALT 0x08 /* alternate form */ 5234318Sbostic #define LADJUST 0x10 /* left adjustment */ 5334427Sbostic #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 5434427Sbostic #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 5534318Sbostic 5634323Sbostic _doprnt(fmt0, argp, fp) 5734323Sbostic u_char *fmt0; 5834233Sbostic va_list argp; 5934235Sbostic register FILE *fp; 6034226Sbostic { 6134427Sbostic register u_char *fmt; /* format string */ 6234427Sbostic register int ch; /* character from fmt */ 6334427Sbostic register int cnt; /* return value accumulator */ 6434427Sbostic register int n; /* random handy integer */ 6534427Sbostic register char *t; /* buffer pointer */ 6634427Sbostic double _double; /* double precision arguments %[eEfgG] */ 6734427Sbostic u_long _ulong; /* integer arguments %[diouxX] */ 6834624Sbostic int base; /* base for [diouxX] conversion */ 6934624Sbostic int dprec; /* decimal precision in [diouxX] */ 7034624Sbostic int fieldsz; /* field size expanded by sign, etc */ 7134427Sbostic int flags; /* flags as above */ 7234427Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 7334427Sbostic int prec; /* precision from format (%.3d), or -1 */ 7434624Sbostic int realsz; /* field size expanded by decimal precision */ 7534427Sbostic int size; /* size of converted field or string */ 7634624Sbostic int width; /* width from format (%8d), or 0 */ 7734669Sbostic char sign; /* sign prefix (' ', '+', '-', or \0) */ 7834669Sbostic char softsign; /* temporary negative sign for floats */ 7934427Sbostic char *digs; /* digits for [diouxX] conversion */ 8034427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 8134226Sbostic 8234428Sbostic if (fp->_flag & _IORW) { 8334428Sbostic fp->_flag |= _IOWRT; 8434428Sbostic fp->_flag &= ~(_IOEOF|_IOREAD); 8534428Sbostic } 8634428Sbostic if ((fp->_flag & _IOWRT) == 0) 8734428Sbostic return (EOF); 8834428Sbostic 8934323Sbostic fmt = fmt0; 9034243Sbostic digs = "0123456789abcdef"; 9134322Sbostic for (cnt = 0;; ++fmt) { 9234318Sbostic n = fp->_cnt; 9334427Sbostic for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%'; 9434427Sbostic ++cnt, ++fmt) 9534318Sbostic if (--n < 0 9634318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE 9734318Sbostic && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 9834318Sbostic #endif 9934427Sbostic || ch == '\n' && fp->_flag & _IOLBF) { 10034318Sbostic fp->_cnt = n; 10134475Sbostic fp->_ptr = t; 10234427Sbostic (void) _flsbuf((u_char)ch, fp); 10334318Sbostic n = fp->_cnt; 10434427Sbostic t = (char *)fp->_ptr; 10534427Sbostic } else 10634314Sbostic *t++ = ch; 10734318Sbostic fp->_cnt = n; 10834475Sbostic fp->_ptr = t; 10934318Sbostic if (!ch) 11034427Sbostic return (cnt); 11134314Sbostic 11234672Sbostic flags = 0; dprec = 0; fpprec = 0; width = 0; 11334233Sbostic prec = -1; 11434318Sbostic sign = '\0'; 11534226Sbostic 11634318Sbostic rflag: switch (*++fmt) { 11734318Sbostic case ' ': 11834669Sbostic /* 11934669Sbostic * ``If the space and + flags both appear, the space 12034669Sbostic * flag will be ignored.'' 12134669Sbostic * -- ANSI X3J11 12234669Sbostic */ 12334669Sbostic if (!sign) 12434669Sbostic sign = ' '; 12534318Sbostic goto rflag; 12634233Sbostic case '#': 12734318Sbostic flags |= ALT; 12834318Sbostic goto rflag; 12934233Sbostic case '*': 13034235Sbostic /* 13134235Sbostic * ``A negative field width argument is taken as a 13234235Sbostic * - flag followed by a positive field width.'' 13334235Sbostic * -- ANSI X3J11 13434235Sbostic * They don't exclude field widths read from args. 13534235Sbostic */ 13634235Sbostic if ((width = va_arg(argp, int)) >= 0) 13734318Sbostic goto rflag; 13834235Sbostic width = -width; 13934427Sbostic /* FALLTHROUGH */ 14034235Sbostic case '-': 14134318Sbostic flags |= LADJUST; 14234318Sbostic goto rflag; 14334233Sbostic case '+': 14434314Sbostic sign = '+'; 14534318Sbostic goto rflag; 14634233Sbostic case '.': 14734235Sbostic if (*++fmt == '*') 14834318Sbostic n = va_arg(argp, int); 14934427Sbostic else { 15034318Sbostic n = 0; 15134427Sbostic while (isascii(*fmt) && isdigit(*fmt)) 15234427Sbostic n = 10 * n + todigit(*fmt++); 15334233Sbostic --fmt; 15434226Sbostic } 15534318Sbostic prec = n < 0 ? -1 : n; 15634318Sbostic goto rflag; 15734233Sbostic case '0': 15834427Sbostic /* 15934427Sbostic * ``Note that 0 is taken as a flag, not as the 16034427Sbostic * beginning of a field width.'' 16134427Sbostic * -- ANSI X3J11 16234427Sbostic */ 16334427Sbostic flags |= ZEROPAD; 16434427Sbostic goto rflag; 16534233Sbostic case '1': case '2': case '3': case '4': 16634233Sbostic case '5': case '6': case '7': case '8': case '9': 16734318Sbostic n = 0; 16834233Sbostic do { 16934331Sbostic n = 10 * n + todigit(*fmt); 17034318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 17134318Sbostic width = n; 17234233Sbostic --fmt; 17334319Sbostic goto rflag; 17434235Sbostic case 'L': 17534329Sbostic flags |= LONGDBL; 17634318Sbostic goto rflag; 17734235Sbostic case 'h': 17834318Sbostic flags |= SHORTINT; 17934318Sbostic goto rflag; 18034233Sbostic case 'l': 18134318Sbostic flags |= LONGINT; 18234318Sbostic goto rflag; 18334314Sbostic case 'c': 18434427Sbostic *(t = buf) = va_arg(argp, int); 18534314Sbostic size = 1; 18634427Sbostic sign = '\0'; 18734314Sbostic goto pforw; 18834624Sbostic case 'D': 18934624Sbostic flags |= LONGINT; 19034624Sbostic /*FALLTHROUGH*/ 19134314Sbostic case 'd': 19234318Sbostic case 'i': 19334318Sbostic ARG(); 19434318Sbostic if ((long)_ulong < 0) { 19534318Sbostic _ulong = -_ulong; 19634314Sbostic sign = '-'; 19734241Sbostic } 19834241Sbostic base = 10; 19934327Sbostic goto number; 20034261Sbostic case 'e': 20134236Sbostic case 'E': 20234235Sbostic case 'f': 20334261Sbostic case 'g': 20434243Sbostic case 'G': 20534243Sbostic _double = va_arg(argp, double); 20634328Sbostic /* 20734669Sbostic * don't do unrealistic precision; just pad it with 20834669Sbostic * zeroes later, so buffer size stays rational. 20934328Sbostic */ 21034328Sbostic if (prec > MAXFRACT) { 21134475Sbostic if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) 21234475Sbostic fpprec = prec - MAXFRACT; 21334328Sbostic prec = MAXFRACT; 21434328Sbostic } 21534624Sbostic else if (prec == -1) 21634624Sbostic prec = DEFPREC; 21734669Sbostic /* 21834669Sbostic * softsign avoids negative 0 if _double is < 0 and 21934669Sbostic * no significant digits will be shown 22034669Sbostic */ 22134624Sbostic if (_double < 0) { 22234669Sbostic softsign = '-'; 22334624Sbostic _double = -_double; 22434624Sbostic } 22534669Sbostic else 22634669Sbostic softsign = 0; 22734624Sbostic /* 22834669Sbostic * cvt may have to round up past the "start" of the 22934624Sbostic * buffer, i.e. ``intf("%.2f", (double)9.999);''; 23034624Sbostic * if the first char isn't NULL, it did. 23134624Sbostic */ 23234624Sbostic *buf = NULL; 23334669Sbostic size = cvt(_double, prec, flags, &softsign, *fmt, buf, 23434624Sbostic buf + sizeof(buf)); 23534669Sbostic if (softsign) 23634669Sbostic sign = '-'; 23734624Sbostic t = *buf ? buf : buf + 1; 23834314Sbostic goto pforw; 23934235Sbostic case 'n': 24034427Sbostic if (flags & LONGINT) 24134318Sbostic *va_arg(argp, long *) = cnt; 24234427Sbostic else if (flags & SHORTINT) 24334318Sbostic *va_arg(argp, short *) = cnt; 24434318Sbostic else 24534318Sbostic *va_arg(argp, int *) = cnt; 24634235Sbostic break; 24734624Sbostic case 'O': 24834624Sbostic flags |= LONGINT; 24934624Sbostic /*FALLTHROUGH*/ 25034226Sbostic case 'o': 25134318Sbostic ARG(); 25234226Sbostic base = 8; 25334327Sbostic goto nosign; 25434235Sbostic case 'p': 25534320Sbostic /* 25634321Sbostic * ``The argument shall be a pointer to void. The 25734321Sbostic * value of the pointer is converted to a sequence 25834321Sbostic * of printable characters, in an implementation- 25934321Sbostic * defined manner.'' 26034321Sbostic * -- ANSI X3J11 26134320Sbostic */ 26234427Sbostic /* NOSTRICT */ 26334320Sbostic _ulong = (u_long)va_arg(argp, void *); 26434320Sbostic base = 16; 26534327Sbostic goto nosign; 26634226Sbostic case 's': 26734314Sbostic if (!(t = va_arg(argp, char *))) 26834314Sbostic t = "(null)"; 26934321Sbostic if (prec >= 0) { 27034321Sbostic /* 27134321Sbostic * can't use strlen; can only look for the 27234321Sbostic * NUL in the first `prec' characters, and 27334321Sbostic * strlen() will go further. 27434321Sbostic */ 27534321Sbostic char *p, *memchr(); 27634321Sbostic 27734321Sbostic if (p = memchr(t, 0, prec)) { 27834321Sbostic size = p - t; 27934321Sbostic if (size > prec) 28034321Sbostic size = prec; 28134427Sbostic } else 28234321Sbostic size = prec; 28334427Sbostic } else 28434321Sbostic size = strlen(t); 28534427Sbostic sign = '\0'; 28634328Sbostic goto pforw; 28734624Sbostic case 'U': 28834624Sbostic flags |= LONGINT; 28934624Sbostic /*FALLTHROUGH*/ 29034226Sbostic case 'u': 29134318Sbostic ARG(); 29234226Sbostic base = 10; 29334327Sbostic goto nosign; 29434226Sbostic case 'X': 29534226Sbostic digs = "0123456789ABCDEF"; 29634427Sbostic /* FALLTHROUGH */ 29734226Sbostic case 'x': 29834318Sbostic ARG(); 29934314Sbostic base = 16; 30034326Sbostic /* leading 0x/X only if non-zero */ 30134427Sbostic if (flags & ALT && _ulong != 0) 30234427Sbostic flags |= HEXPREFIX; 30334327Sbostic 30434327Sbostic /* unsigned conversions */ 30534427Sbostic nosign: sign = '\0'; 30634326Sbostic /* 30734330Sbostic * ``... diouXx conversions ... if a precision is 30834330Sbostic * specified, the 0 flag will be ignored.'' 30934330Sbostic * -- ANSI X3J11 31034330Sbostic */ 31134427Sbostic number: if ((dprec = prec) >= 0) 31234427Sbostic flags &= ~ZEROPAD; 31334427Sbostic 31434330Sbostic /* 31534326Sbostic * ``The result of converting a zero value with an 31634326Sbostic * explicit precision of zero is no characters.'' 31734326Sbostic * -- ANSI X3J11 31834326Sbostic */ 31934427Sbostic t = buf + BUF; 32034427Sbostic if (_ulong != 0 || prec != 0) { 32134427Sbostic do { 32234427Sbostic *--t = digs[_ulong % base]; 32334427Sbostic _ulong /= base; 32434427Sbostic } while (_ulong); 32534427Sbostic digs = "0123456789abcdef"; 32634427Sbostic if (flags & ALT && base == 8 && *t != '0') 32734427Sbostic *--t = '0'; /* octal leading 0 */ 32834330Sbostic } 32934427Sbostic size = buf + BUF - t; 33034327Sbostic 33134427Sbostic pforw: 33234427Sbostic /* 33334624Sbostic * All reasonable formats wind up here. At this point, 33434624Sbostic * `t' points to a string which (if not flags&LADJUST) 33534624Sbostic * should be padded out to `width' places. If 33634624Sbostic * flags&ZEROPAD, it should first be prefixed by any 33734624Sbostic * sign or other prefix; otherwise, it should be blank 33834624Sbostic * padded before the prefix is emitted. After any 33934624Sbostic * left-hand padding and prefixing, emit zeroes 34034624Sbostic * required by a decimal [diouxX] precision, then print 34134624Sbostic * the string proper, then emit zeroes required by any 34234624Sbostic * leftover floating precision; finally, if LADJUST, 34334624Sbostic * pad with blanks. 34434427Sbostic */ 34534327Sbostic 34634624Sbostic /* 34734624Sbostic * compute actual size, so we know how much to pad 34834624Sbostic * fieldsz excludes decimal prec; realsz includes it 34934624Sbostic */ 35034427Sbostic fieldsz = size + fpprec; 35134427Sbostic if (sign) 35234427Sbostic fieldsz++; 35334427Sbostic if (flags & HEXPREFIX) 35434427Sbostic fieldsz += 2; 35534427Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 35634327Sbostic 35734427Sbostic /* right-adjusting blank padding */ 35834427Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0 && width) 35934427Sbostic for (n = realsz; n < width; n++) 36034427Sbostic PUTC(' '); 36134427Sbostic /* prefix */ 36234427Sbostic if (sign) 36334427Sbostic PUTC(sign); 36434427Sbostic if (flags & HEXPREFIX) { 36534427Sbostic PUTC('0'); 36634427Sbostic PUTC((char)*fmt); 36734327Sbostic } 36834427Sbostic /* right-adjusting zero padding */ 36934427Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 37034427Sbostic for (n = realsz; n < width; n++) 37134427Sbostic PUTC('0'); 37234427Sbostic /* leading zeroes from decimal precision */ 37334427Sbostic for (n = fieldsz; n < dprec; n++) 37434427Sbostic PUTC('0'); 37534327Sbostic 37634427Sbostic /* the string or number proper */ 37734427Sbostic if (fp->_cnt - (n = size) >= 0 && 37834427Sbostic (fp->_flag & _IOLBF) == 0) { 37934328Sbostic fp->_cnt -= n; 38034427Sbostic bcopy(t, (char *)fp->_ptr, n); 38134328Sbostic fp->_ptr += n; 38234427Sbostic } else 38334427Sbostic while (--n >= 0) 38434427Sbostic PUTC(*t++); 38534427Sbostic /* trailing f.p. zeroes */ 38634427Sbostic while (--fpprec >= 0) 38734328Sbostic PUTC('0'); 38834427Sbostic /* left-adjusting padding (always blank) */ 38934427Sbostic if (flags & LADJUST) 39034427Sbostic for (n = realsz; n < width; n++) 39134328Sbostic PUTC(' '); 39234427Sbostic /* finally, adjust cnt */ 39334427Sbostic cnt += width > realsz ? width : realsz; 39434226Sbostic break; 39534427Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 39634427Sbostic return (cnt); 39734226Sbostic default: 39834427Sbostic PUTC((char)*fmt); 39934427Sbostic cnt++; 40034226Sbostic } 40134226Sbostic } 40234427Sbostic /* NOTREACHED */ 40334226Sbostic } 40434242Sbostic 40534624Sbostic static 40634669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp) 40734242Sbostic double number; 40834261Sbostic register int prec; 40934323Sbostic int flags; 41034323Sbostic u_char fmtch; 41134669Sbostic char *signp, *startp, *endp; 41234242Sbostic { 41334331Sbostic register char *p, *t; 41434672Sbostic register double fract; 41534624Sbostic int dotrim, expcnt, gformat; 41634672Sbostic double integer, tmp, modf(); 41734669Sbostic char *exponent(), *round(); 41834242Sbostic 41934624Sbostic dotrim = expcnt = gformat = 0; 42034624Sbostic fract = modf(number, &integer); 42134242Sbostic 42234624Sbostic /* get an extra slot for rounding. */ 42334624Sbostic t = ++startp; 42434624Sbostic 42534624Sbostic /* 42634624Sbostic * get integer portion of number; put into the end of the buffer; the 42734624Sbostic * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 42834624Sbostic */ 42934624Sbostic for (p = endp - 1; integer; ++expcnt) { 43034624Sbostic tmp = modf(integer / 10, &integer); 43134624Sbostic *p-- = tochar((int)((tmp + .01) * 10)); 43234248Sbostic } 43334261Sbostic switch(fmtch) { 43434261Sbostic case 'f': 43534624Sbostic /* reverse integer into beginning of buffer */ 43634624Sbostic if (expcnt) 43734624Sbostic for (; ++p < endp; *t++ = *p); 43834624Sbostic else 43934624Sbostic *t++ = '0'; 44034248Sbostic /* 44134624Sbostic * if precision required or alternate flag set, add in a 44234624Sbostic * decimal point. 44334248Sbostic */ 44434624Sbostic if (prec || flags&ALT) 44534624Sbostic *t++ = '.'; 44634624Sbostic /* if requires more precision and some fraction left */ 44734624Sbostic if (fract) { 44834624Sbostic if (prec) 44934624Sbostic do { 45034624Sbostic fract = modf(fract * 10, &tmp); 45134624Sbostic *t++ = tochar((int)tmp); 45234624Sbostic } while (--prec && fract); 45334624Sbostic if (fract) 45434669Sbostic startp = round(fract, (int *)NULL, startp, 45534669Sbostic t - 1, (char)0, signp); 45634624Sbostic } 45734624Sbostic for (; prec--; *t++ = '0'); 45834624Sbostic break; 45934624Sbostic case 'e': 46034624Sbostic case 'E': 46134624Sbostic eformat: if (expcnt) { 46234624Sbostic *t++ = *++p; 46334624Sbostic if (prec || flags&ALT) 46434331Sbostic *t++ = '.'; 46534624Sbostic /* if requires more precision and some integer left */ 46634624Sbostic for (; prec && ++p < endp; --prec) 46734624Sbostic *t++ = *p; 46834624Sbostic /* 46934624Sbostic * if done precision and more of the integer component, 47034624Sbostic * round using it; adjust fract so we don't re-round 47134624Sbostic * later. 47234624Sbostic */ 47334624Sbostic if (!prec && ++p < endp) { 47434248Sbostic fract = 0; 47534669Sbostic startp = round((double)0, &expcnt, startp, 47634669Sbostic t - 1, *p, signp); 47734248Sbostic } 47834624Sbostic /* adjust expcnt for digit in front of decimal */ 47934624Sbostic --expcnt; 48034242Sbostic } 48134624Sbostic /* until first fractional digit, decrement exponent */ 48234624Sbostic else if (fract) { 48334624Sbostic /* adjust expcnt for digit in front of decimal */ 48434624Sbostic for (expcnt = -1;; --expcnt) { 48534624Sbostic fract = modf(fract * 10, &tmp); 48634624Sbostic if (tmp) 48734624Sbostic break; 48834248Sbostic } 48934624Sbostic *t++ = tochar((int)tmp); 49034624Sbostic if (prec || flags&ALT) 49134331Sbostic *t++ = '.'; 49234242Sbostic } 49334248Sbostic else { 49434624Sbostic *t++ = '0'; 49534624Sbostic if (prec || flags&ALT) 49634331Sbostic *t++ = '.'; 49734248Sbostic } 49834624Sbostic /* if requires more precision and some fraction left */ 49934624Sbostic if (fract) { 50034624Sbostic if (prec) 50134624Sbostic do { 50234624Sbostic fract = modf(fract * 10, &tmp); 50334624Sbostic *t++ = tochar((int)tmp); 50434624Sbostic } while (--prec && fract); 50534624Sbostic if (fract) 50634669Sbostic startp = round(fract, &expcnt, startp, 50734669Sbostic t - 1, (char)0, signp); 50834584Sbostic } 50934624Sbostic /* if requires more precision */ 51034624Sbostic for (; prec--; *t++ = '0'); 51134624Sbostic 51234624Sbostic /* unless alternate flag, trim any g/G format trailing 0's */ 51334624Sbostic if (gformat && !(flags&ALT)) { 51434624Sbostic while (t > startp && *--t == '0'); 51534624Sbostic if (*t == '.') 51634624Sbostic --t; 51734624Sbostic ++t; 51834624Sbostic } 51934624Sbostic t = exponent(t, expcnt, fmtch); 52034624Sbostic break; 52134624Sbostic case 'g': 52234624Sbostic case 'G': 52334624Sbostic /* a precision of 0 is treated as a precision of 1. */ 52434624Sbostic if (!prec) 52534624Sbostic ++prec; 52634624Sbostic /* 52734624Sbostic * ``The style used depends on the value converted; style e 52834624Sbostic * will be used only if the exponent resulting from the 52934624Sbostic * conversion is less than -4 or greater than the precision.'' 53034624Sbostic * -- ANSI X3J11 53134624Sbostic */ 53234624Sbostic if (expcnt > prec || !expcnt && fract && fract < .0001) { 53334624Sbostic /* 53434624Sbostic * g/G format counts "significant digits, not digits of 53534624Sbostic * precision; for the e/E format, this just causes an 53634624Sbostic * off-by-one problem, i.e. g/G considers the digit 53734624Sbostic * before the decimal point significant and e/E doesn't 53834624Sbostic * count it as precision. 53934624Sbostic */ 54034624Sbostic --prec; 54134624Sbostic fmtch -= 2; /* G->E, g->e */ 54234624Sbostic gformat = 1; 54334624Sbostic goto eformat; 54434624Sbostic } 54534624Sbostic /* 54634624Sbostic * reverse integer into beginning of buffer, 54734624Sbostic * note, decrement precision 54834624Sbostic */ 54934624Sbostic if (expcnt) 55034624Sbostic for (; ++p < endp; *t++ = *p, --prec); 55134624Sbostic else 55234624Sbostic *t++ = '0'; 55334624Sbostic /* 55434624Sbostic * if precision required or alternate flag set, add in a 55534624Sbostic * decimal point. If no digits yet, add in leading 0. 55634624Sbostic */ 55734624Sbostic if (prec || flags&ALT) { 55834624Sbostic dotrim = 1; 55934624Sbostic *t++ = '.'; 56034624Sbostic } 56134624Sbostic else 56234624Sbostic dotrim = 0; 56334624Sbostic /* if requires more precision and some fraction left */ 56434624Sbostic if (fract) { 56534624Sbostic if (prec) { 56634624Sbostic do { 56734624Sbostic fract = modf(fract * 10, &tmp); 56834624Sbostic *t++ = tochar((int)tmp); 56934624Sbostic } while(!tmp); 57034624Sbostic while (--prec && fract) { 57134624Sbostic fract = modf(fract * 10, &tmp); 57234624Sbostic *t++ = tochar((int)tmp); 57334248Sbostic } 57434248Sbostic } 57534624Sbostic if (fract) 57634669Sbostic startp = round(fract, (int *)NULL, startp, 57734669Sbostic t - 1, (char)0, signp); 57834624Sbostic } 57934624Sbostic /* alternate format, adds 0's for precision, else trim 0's */ 58034624Sbostic if (flags&ALT) 58134624Sbostic for (; prec--; *t++ = '0'); 58234624Sbostic else if (dotrim) { 58334624Sbostic while (t > startp && *--t == '0'); 58434624Sbostic if (*t != '.') 58534624Sbostic ++t; 58634624Sbostic } 58734624Sbostic } 58834624Sbostic return(t - startp); 58934624Sbostic } 59034248Sbostic 59134624Sbostic static char * 59234669Sbostic round(fract, exp, start, end, ch, signp) 59334624Sbostic double fract; 59434669Sbostic int *exp; 59534624Sbostic register char *start, *end; 59634669Sbostic char ch, *signp; 59734624Sbostic { 59834624Sbostic double tmp; 59934248Sbostic 60034624Sbostic if (fract) 60134248Sbostic (void)modf(fract * 10, &tmp); 60234624Sbostic else 60334624Sbostic tmp = todigit(ch); 60434624Sbostic if (tmp > 4) 60534624Sbostic for (;; --end) { 60634624Sbostic if (*end == '.') 60734624Sbostic --end; 60834624Sbostic if (++*end <= '9') 60934624Sbostic break; 61034624Sbostic *end = '0'; 61134624Sbostic if (end == start) { 61234669Sbostic if (exp) { /* e/E; increment exponent */ 61334669Sbostic *end = '1'; 61434669Sbostic ++*exp; 61534669Sbostic } 61634669Sbostic else { /* f; add extra digit */ 61734669Sbostic *--end = '1'; 61834669Sbostic --start; 61934669Sbostic } 62034624Sbostic break; 62134242Sbostic } 62234242Sbostic } 62334669Sbostic /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 62434669Sbostic else if (*signp == '-') 62534669Sbostic for (;; --end) { 62634669Sbostic if (*end == '.') 62734669Sbostic --end; 62834669Sbostic if (*end != '0') 62934669Sbostic break; 63034669Sbostic if (end == start) 63134669Sbostic *signp = 0; 63234669Sbostic } 63334624Sbostic return(start); 63434624Sbostic } 63534248Sbostic 63634624Sbostic static char * 63734624Sbostic exponent(p, exp, fmtch) 63834624Sbostic register char *p; 63934624Sbostic register int exp; 64034624Sbostic u_char fmtch; 64134624Sbostic { 64234624Sbostic register char *t; 64334624Sbostic char expbuf[MAXEXP]; 64434248Sbostic 64534624Sbostic *p++ = fmtch; 64634624Sbostic if (exp < 0) { 64734624Sbostic exp = -exp; 64834624Sbostic *p++ = '-'; 64934242Sbostic } 65034624Sbostic else 65134624Sbostic *p++ = '+'; 65234624Sbostic t = expbuf + MAXEXP; 65334624Sbostic if (exp > 9) { 65434624Sbostic do { 65534624Sbostic *--t = tochar(exp % 10); 65634624Sbostic } while ((exp /= 10) > 9); 65734624Sbostic *--t = tochar(exp); 65834624Sbostic for (; t < expbuf + MAXEXP; *p++ = *t++); 65934624Sbostic } 66034624Sbostic else { 66134624Sbostic *p++ = '0'; 66234624Sbostic *p++ = tochar(exp); 66334624Sbostic } 66434323Sbostic return(p); 66534242Sbostic } 666