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 634825Sbostic * provided that the above copyright notice and this paragraph are 734825Sbostic * duplicated in all such forms and that any documentation, 834825Sbostic * advertising materials, and other materials related to such 934825Sbostic * distribution and use acknowledge that the software was developed 1034825Sbostic * by the University of California, Berkeley. The name of the 1134825Sbostic * University may not be used to endorse or promote products derived 1234825Sbostic * from this software without specific prior written permission. 1334825Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434825Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534825Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1634226Sbostic */ 1734226Sbostic 1834233Sbostic #if defined(LIBC_SCCS) && !defined(lint) 19*37234Sbostic static char sccsid[] = "@(#)vfprintf.c 5.37 (Berkeley) 03/26/89"; 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 3836090Sbostic #define ARG(basetype) \ 3936090Sbostic _ulong = flags&LONGINT ? va_arg(argp, long basetype) : \ 4036090Sbostic flags&SHORTINT ? (short basetype)va_arg(argp, int) : \ 4136090Sbostic va_arg(argp, int) 4234235Sbostic 4334331Sbostic #define todigit(c) ((c) - '0') 4434331Sbostic #define tochar(n) ((n) + '0') 4534331Sbostic 4634318Sbostic /* have to deal with the negative buffer count kludge */ 4734318Sbostic #define NEGATIVE_COUNT_KLUDGE 4834318Sbostic 4934318Sbostic #define LONGINT 0x01 /* long integer */ 5034318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 5134318Sbostic #define SHORTINT 0x04 /* short integer */ 5234318Sbostic #define ALT 0x08 /* alternate form */ 5334318Sbostic #define LADJUST 0x10 /* left adjustment */ 5434427Sbostic #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 5534427Sbostic #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 5634318Sbostic 5734323Sbostic _doprnt(fmt0, argp, fp) 5834323Sbostic u_char *fmt0; 5934233Sbostic va_list argp; 6034235Sbostic register FILE *fp; 6134226Sbostic { 6234427Sbostic register u_char *fmt; /* format string */ 6334427Sbostic register int ch; /* character from fmt */ 6434427Sbostic register int cnt; /* return value accumulator */ 6534427Sbostic register int n; /* random handy integer */ 6634427Sbostic register char *t; /* buffer pointer */ 6734427Sbostic double _double; /* double precision arguments %[eEfgG] */ 6834427Sbostic u_long _ulong; /* integer arguments %[diouxX] */ 6934624Sbostic int base; /* base for [diouxX] conversion */ 7034624Sbostic int dprec; /* decimal precision in [diouxX] */ 7134624Sbostic int fieldsz; /* field size expanded by sign, etc */ 7234427Sbostic int flags; /* flags as above */ 7334427Sbostic int fpprec; /* `extra' floating precision in [eEfgG] */ 7434427Sbostic int prec; /* precision from format (%.3d), or -1 */ 7534624Sbostic int realsz; /* field size expanded by decimal precision */ 7634427Sbostic int size; /* size of converted field or string */ 7734624Sbostic int width; /* width from format (%8d), or 0 */ 7834669Sbostic char sign; /* sign prefix (' ', '+', '-', or \0) */ 7934669Sbostic char softsign; /* temporary negative sign for floats */ 8034427Sbostic char *digs; /* digits for [diouxX] conversion */ 8134427Sbostic char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 8234226Sbostic 8334428Sbostic if (fp->_flag & _IORW) { 8434428Sbostic fp->_flag |= _IOWRT; 8534428Sbostic fp->_flag &= ~(_IOEOF|_IOREAD); 8634428Sbostic } 8734428Sbostic if ((fp->_flag & _IOWRT) == 0) 8834428Sbostic return (EOF); 8934428Sbostic 9034323Sbostic fmt = fmt0; 9134243Sbostic digs = "0123456789abcdef"; 9234322Sbostic for (cnt = 0;; ++fmt) { 9334318Sbostic n = fp->_cnt; 9434427Sbostic for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%'; 9534427Sbostic ++cnt, ++fmt) 9634318Sbostic if (--n < 0 9734318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE 9834318Sbostic && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 9934318Sbostic #endif 10034427Sbostic || ch == '\n' && fp->_flag & _IOLBF) { 10134318Sbostic fp->_cnt = n; 10234475Sbostic fp->_ptr = t; 10334427Sbostic (void) _flsbuf((u_char)ch, fp); 10434318Sbostic n = fp->_cnt; 10534427Sbostic t = (char *)fp->_ptr; 10634427Sbostic } else 10734314Sbostic *t++ = ch; 10834318Sbostic fp->_cnt = n; 10934475Sbostic fp->_ptr = t; 11034318Sbostic if (!ch) 11134427Sbostic return (cnt); 11234314Sbostic 11334672Sbostic flags = 0; dprec = 0; fpprec = 0; width = 0; 11434233Sbostic prec = -1; 11534318Sbostic sign = '\0'; 11634226Sbostic 11734318Sbostic rflag: switch (*++fmt) { 11834318Sbostic case ' ': 11934669Sbostic /* 12034669Sbostic * ``If the space and + flags both appear, the space 12134669Sbostic * flag will be ignored.'' 12234669Sbostic * -- ANSI X3J11 12334669Sbostic */ 12434669Sbostic if (!sign) 12534669Sbostic sign = ' '; 12634318Sbostic goto rflag; 12734233Sbostic case '#': 12834318Sbostic flags |= ALT; 12934318Sbostic goto rflag; 13034233Sbostic case '*': 13134235Sbostic /* 13234235Sbostic * ``A negative field width argument is taken as a 13334235Sbostic * - flag followed by a positive field width.'' 13434235Sbostic * -- ANSI X3J11 13534235Sbostic * They don't exclude field widths read from args. 13634235Sbostic */ 13734235Sbostic if ((width = va_arg(argp, int)) >= 0) 13834318Sbostic goto rflag; 13934235Sbostic width = -width; 14034427Sbostic /* FALLTHROUGH */ 14134235Sbostic case '-': 14234318Sbostic flags |= LADJUST; 14334318Sbostic goto rflag; 14434233Sbostic case '+': 14534314Sbostic sign = '+'; 14634318Sbostic goto rflag; 14734233Sbostic case '.': 14834235Sbostic if (*++fmt == '*') 14934318Sbostic n = va_arg(argp, int); 15034427Sbostic else { 15134318Sbostic n = 0; 15234427Sbostic while (isascii(*fmt) && isdigit(*fmt)) 15334427Sbostic n = 10 * n + todigit(*fmt++); 15434233Sbostic --fmt; 15534226Sbostic } 15634318Sbostic prec = n < 0 ? -1 : n; 15734318Sbostic goto rflag; 15834233Sbostic case '0': 15934427Sbostic /* 16034427Sbostic * ``Note that 0 is taken as a flag, not as the 16134427Sbostic * beginning of a field width.'' 16234427Sbostic * -- ANSI X3J11 16334427Sbostic */ 16434427Sbostic flags |= ZEROPAD; 16534427Sbostic goto rflag; 16634233Sbostic case '1': case '2': case '3': case '4': 16734233Sbostic case '5': case '6': case '7': case '8': case '9': 16834318Sbostic n = 0; 16934233Sbostic do { 17034331Sbostic n = 10 * n + todigit(*fmt); 17134318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 17234318Sbostic width = n; 17334233Sbostic --fmt; 17434319Sbostic goto rflag; 17534235Sbostic case 'L': 17634329Sbostic flags |= LONGDBL; 17734318Sbostic goto rflag; 17834235Sbostic case 'h': 17934318Sbostic flags |= SHORTINT; 18034318Sbostic goto rflag; 18134233Sbostic case 'l': 18234318Sbostic flags |= LONGINT; 18334318Sbostic goto rflag; 18434314Sbostic case 'c': 18534427Sbostic *(t = buf) = va_arg(argp, int); 18634314Sbostic size = 1; 18734427Sbostic sign = '\0'; 18834314Sbostic goto pforw; 18934624Sbostic case 'D': 19034624Sbostic flags |= LONGINT; 19134624Sbostic /*FALLTHROUGH*/ 19234314Sbostic case 'd': 19334318Sbostic case 'i': 19436090Sbostic ARG(int); 19534318Sbostic if ((long)_ulong < 0) { 19634318Sbostic _ulong = -_ulong; 19734314Sbostic sign = '-'; 19834241Sbostic } 19934241Sbostic base = 10; 20034327Sbostic goto number; 20134261Sbostic case 'e': 20234236Sbostic case 'E': 20334235Sbostic case 'f': 20434261Sbostic case 'g': 20534243Sbostic case 'G': 20634243Sbostic _double = va_arg(argp, double); 20734328Sbostic /* 20834669Sbostic * don't do unrealistic precision; just pad it with 20934669Sbostic * zeroes later, so buffer size stays rational. 21034328Sbostic */ 21134328Sbostic if (prec > MAXFRACT) { 21234475Sbostic if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) 21334475Sbostic fpprec = prec - MAXFRACT; 21434328Sbostic prec = MAXFRACT; 21534328Sbostic } 21634624Sbostic else if (prec == -1) 21734624Sbostic prec = DEFPREC; 21834669Sbostic /* 21934669Sbostic * softsign avoids negative 0 if _double is < 0 and 22034669Sbostic * no significant digits will be shown 22134669Sbostic */ 22234624Sbostic if (_double < 0) { 22334669Sbostic softsign = '-'; 22434624Sbostic _double = -_double; 22534624Sbostic } 22634669Sbostic else 22734669Sbostic softsign = 0; 22834624Sbostic /* 22934669Sbostic * cvt may have to round up past the "start" of the 23034624Sbostic * buffer, i.e. ``intf("%.2f", (double)9.999);''; 23134624Sbostic * if the first char isn't NULL, it did. 23234624Sbostic */ 23334624Sbostic *buf = NULL; 23434669Sbostic size = cvt(_double, prec, flags, &softsign, *fmt, buf, 23534624Sbostic buf + sizeof(buf)); 23634669Sbostic if (softsign) 23734669Sbostic sign = '-'; 23834624Sbostic t = *buf ? buf : buf + 1; 23934314Sbostic goto pforw; 24034235Sbostic case 'n': 24134427Sbostic if (flags & LONGINT) 24234318Sbostic *va_arg(argp, long *) = cnt; 24334427Sbostic else if (flags & SHORTINT) 24434318Sbostic *va_arg(argp, short *) = cnt; 24534318Sbostic else 24634318Sbostic *va_arg(argp, int *) = cnt; 24734235Sbostic break; 24834624Sbostic case 'O': 24934624Sbostic flags |= LONGINT; 25034624Sbostic /*FALLTHROUGH*/ 25134226Sbostic case 'o': 25236090Sbostic ARG(unsigned); 25334226Sbostic base = 8; 25434327Sbostic goto nosign; 25534235Sbostic case 'p': 25634320Sbostic /* 25734321Sbostic * ``The argument shall be a pointer to void. The 25834321Sbostic * value of the pointer is converted to a sequence 25934321Sbostic * of printable characters, in an implementation- 26034321Sbostic * defined manner.'' 26134321Sbostic * -- ANSI X3J11 26234320Sbostic */ 26334427Sbostic /* NOSTRICT */ 26434320Sbostic _ulong = (u_long)va_arg(argp, void *); 26534320Sbostic base = 16; 26634327Sbostic goto nosign; 26734226Sbostic case 's': 26834314Sbostic if (!(t = va_arg(argp, char *))) 26934314Sbostic t = "(null)"; 27034321Sbostic if (prec >= 0) { 27134321Sbostic /* 27234321Sbostic * can't use strlen; can only look for the 27334321Sbostic * NUL in the first `prec' characters, and 27434321Sbostic * strlen() will go further. 27534321Sbostic */ 27634321Sbostic char *p, *memchr(); 27734321Sbostic 27834321Sbostic if (p = memchr(t, 0, prec)) { 27934321Sbostic size = p - t; 28034321Sbostic if (size > prec) 28134321Sbostic size = prec; 28234427Sbostic } else 28334321Sbostic size = prec; 28434427Sbostic } else 28534321Sbostic size = strlen(t); 28634427Sbostic sign = '\0'; 28734328Sbostic goto pforw; 28834624Sbostic case 'U': 28934624Sbostic flags |= LONGINT; 29034624Sbostic /*FALLTHROUGH*/ 29134226Sbostic case 'u': 29236090Sbostic ARG(unsigned); 29334226Sbostic base = 10; 29434327Sbostic goto nosign; 29534226Sbostic case 'X': 29634226Sbostic digs = "0123456789ABCDEF"; 29734427Sbostic /* FALLTHROUGH */ 29834226Sbostic case 'x': 29936090Sbostic ARG(unsigned); 30034314Sbostic base = 16; 30134326Sbostic /* leading 0x/X only if non-zero */ 30234427Sbostic if (flags & ALT && _ulong != 0) 30334427Sbostic flags |= HEXPREFIX; 30434327Sbostic 30534327Sbostic /* unsigned conversions */ 30634427Sbostic nosign: sign = '\0'; 30734326Sbostic /* 30834330Sbostic * ``... diouXx conversions ... if a precision is 30934330Sbostic * specified, the 0 flag will be ignored.'' 31034330Sbostic * -- ANSI X3J11 31134330Sbostic */ 31234427Sbostic number: if ((dprec = prec) >= 0) 31334427Sbostic flags &= ~ZEROPAD; 31434427Sbostic 31534330Sbostic /* 31634326Sbostic * ``The result of converting a zero value with an 31734326Sbostic * explicit precision of zero is no characters.'' 31834326Sbostic * -- ANSI X3J11 31934326Sbostic */ 32034427Sbostic t = buf + BUF; 32134427Sbostic if (_ulong != 0 || prec != 0) { 32234427Sbostic do { 32334427Sbostic *--t = digs[_ulong % base]; 32434427Sbostic _ulong /= base; 32534427Sbostic } while (_ulong); 32634427Sbostic digs = "0123456789abcdef"; 32734427Sbostic if (flags & ALT && base == 8 && *t != '0') 32834427Sbostic *--t = '0'; /* octal leading 0 */ 32934330Sbostic } 33034427Sbostic size = buf + BUF - t; 33134327Sbostic 33234427Sbostic pforw: 33334427Sbostic /* 33434624Sbostic * All reasonable formats wind up here. At this point, 33534624Sbostic * `t' points to a string which (if not flags&LADJUST) 33634624Sbostic * should be padded out to `width' places. If 33734624Sbostic * flags&ZEROPAD, it should first be prefixed by any 33834624Sbostic * sign or other prefix; otherwise, it should be blank 33934624Sbostic * padded before the prefix is emitted. After any 34034624Sbostic * left-hand padding and prefixing, emit zeroes 34134624Sbostic * required by a decimal [diouxX] precision, then print 34234624Sbostic * the string proper, then emit zeroes required by any 34334624Sbostic * leftover floating precision; finally, if LADJUST, 34434624Sbostic * pad with blanks. 34534427Sbostic */ 34634327Sbostic 34734624Sbostic /* 34834624Sbostic * compute actual size, so we know how much to pad 34934624Sbostic * fieldsz excludes decimal prec; realsz includes it 35034624Sbostic */ 35134427Sbostic fieldsz = size + fpprec; 35234427Sbostic if (sign) 35334427Sbostic fieldsz++; 35434427Sbostic if (flags & HEXPREFIX) 35534427Sbostic fieldsz += 2; 35634427Sbostic realsz = dprec > fieldsz ? dprec : fieldsz; 35734327Sbostic 35834427Sbostic /* right-adjusting blank padding */ 35934427Sbostic if ((flags & (LADJUST|ZEROPAD)) == 0 && width) 36034427Sbostic for (n = realsz; n < width; n++) 36134427Sbostic PUTC(' '); 36234427Sbostic /* prefix */ 36334427Sbostic if (sign) 36434427Sbostic PUTC(sign); 36534427Sbostic if (flags & HEXPREFIX) { 36634427Sbostic PUTC('0'); 36734427Sbostic PUTC((char)*fmt); 36834327Sbostic } 36934427Sbostic /* right-adjusting zero padding */ 37034427Sbostic if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 37134427Sbostic for (n = realsz; n < width; n++) 37234427Sbostic PUTC('0'); 37334427Sbostic /* leading zeroes from decimal precision */ 37434427Sbostic for (n = fieldsz; n < dprec; n++) 37534427Sbostic PUTC('0'); 37634327Sbostic 37734427Sbostic /* the string or number proper */ 378*37234Sbostic n = size; 379*37234Sbostic if (fp->_cnt - n >= 0 && (fp->_flag & _IOLBF) == 0) { 38034328Sbostic fp->_cnt -= n; 38134427Sbostic bcopy(t, (char *)fp->_ptr, n); 38234328Sbostic fp->_ptr += n; 38334427Sbostic } else 38434427Sbostic while (--n >= 0) 38534427Sbostic PUTC(*t++); 38634427Sbostic /* trailing f.p. zeroes */ 38734427Sbostic while (--fpprec >= 0) 38834328Sbostic PUTC('0'); 38934427Sbostic /* left-adjusting padding (always blank) */ 39034427Sbostic if (flags & LADJUST) 39134427Sbostic for (n = realsz; n < width; n++) 39234328Sbostic PUTC(' '); 39334427Sbostic /* finally, adjust cnt */ 39434427Sbostic cnt += width > realsz ? width : realsz; 39534226Sbostic break; 39634427Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 39734427Sbostic return (cnt); 39834226Sbostic default: 39934427Sbostic PUTC((char)*fmt); 40034427Sbostic cnt++; 40134226Sbostic } 40234226Sbostic } 40334427Sbostic /* NOTREACHED */ 40434226Sbostic } 40534242Sbostic 40634624Sbostic static 40734669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp) 40834242Sbostic double number; 40934261Sbostic register int prec; 41034323Sbostic int flags; 41134323Sbostic u_char fmtch; 41234669Sbostic char *signp, *startp, *endp; 41334242Sbostic { 41434331Sbostic register char *p, *t; 41534672Sbostic register double fract; 41634624Sbostic int dotrim, expcnt, gformat; 41734672Sbostic double integer, tmp, modf(); 41834669Sbostic char *exponent(), *round(); 41934242Sbostic 42034624Sbostic dotrim = expcnt = gformat = 0; 42134624Sbostic fract = modf(number, &integer); 42234242Sbostic 42334624Sbostic /* get an extra slot for rounding. */ 42434624Sbostic t = ++startp; 42534624Sbostic 42634624Sbostic /* 42734624Sbostic * get integer portion of number; put into the end of the buffer; the 42834624Sbostic * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 42934624Sbostic */ 43034624Sbostic for (p = endp - 1; integer; ++expcnt) { 43134624Sbostic tmp = modf(integer / 10, &integer); 43234624Sbostic *p-- = tochar((int)((tmp + .01) * 10)); 43334248Sbostic } 43434261Sbostic switch(fmtch) { 43534261Sbostic case 'f': 43634624Sbostic /* reverse integer into beginning of buffer */ 43734624Sbostic if (expcnt) 43834624Sbostic for (; ++p < endp; *t++ = *p); 43934624Sbostic else 44034624Sbostic *t++ = '0'; 44134248Sbostic /* 44234624Sbostic * if precision required or alternate flag set, add in a 44334624Sbostic * decimal point. 44434248Sbostic */ 44534624Sbostic if (prec || flags&ALT) 44634624Sbostic *t++ = '.'; 44734624Sbostic /* if requires more precision and some fraction left */ 44834624Sbostic if (fract) { 44934624Sbostic if (prec) 45034624Sbostic do { 45134624Sbostic fract = modf(fract * 10, &tmp); 45234624Sbostic *t++ = tochar((int)tmp); 45334624Sbostic } while (--prec && fract); 45434624Sbostic if (fract) 45534669Sbostic startp = round(fract, (int *)NULL, startp, 45634669Sbostic t - 1, (char)0, signp); 45734624Sbostic } 45834624Sbostic for (; prec--; *t++ = '0'); 45934624Sbostic break; 46034624Sbostic case 'e': 46134624Sbostic case 'E': 46234624Sbostic eformat: if (expcnt) { 46334624Sbostic *t++ = *++p; 46434624Sbostic if (prec || flags&ALT) 46534331Sbostic *t++ = '.'; 46634624Sbostic /* if requires more precision and some integer left */ 46734624Sbostic for (; prec && ++p < endp; --prec) 46834624Sbostic *t++ = *p; 46934624Sbostic /* 47034624Sbostic * if done precision and more of the integer component, 47134624Sbostic * round using it; adjust fract so we don't re-round 47234624Sbostic * later. 47334624Sbostic */ 47434624Sbostic if (!prec && ++p < endp) { 47534248Sbostic fract = 0; 47634669Sbostic startp = round((double)0, &expcnt, startp, 47734669Sbostic t - 1, *p, signp); 47834248Sbostic } 47934624Sbostic /* adjust expcnt for digit in front of decimal */ 48034624Sbostic --expcnt; 48134242Sbostic } 48234624Sbostic /* until first fractional digit, decrement exponent */ 48334624Sbostic else if (fract) { 48434624Sbostic /* adjust expcnt for digit in front of decimal */ 48534624Sbostic for (expcnt = -1;; --expcnt) { 48634624Sbostic fract = modf(fract * 10, &tmp); 48734624Sbostic if (tmp) 48834624Sbostic break; 48934248Sbostic } 49034624Sbostic *t++ = tochar((int)tmp); 49134624Sbostic if (prec || flags&ALT) 49234331Sbostic *t++ = '.'; 49334242Sbostic } 49434248Sbostic else { 49534624Sbostic *t++ = '0'; 49634624Sbostic if (prec || flags&ALT) 49734331Sbostic *t++ = '.'; 49834248Sbostic } 49934624Sbostic /* if requires more precision and some fraction left */ 50034624Sbostic if (fract) { 50134624Sbostic if (prec) 50234624Sbostic do { 50334624Sbostic fract = modf(fract * 10, &tmp); 50434624Sbostic *t++ = tochar((int)tmp); 50534624Sbostic } while (--prec && fract); 50634624Sbostic if (fract) 50734669Sbostic startp = round(fract, &expcnt, startp, 50834669Sbostic t - 1, (char)0, signp); 50934584Sbostic } 51034624Sbostic /* if requires more precision */ 51134624Sbostic for (; prec--; *t++ = '0'); 51234624Sbostic 51334624Sbostic /* unless alternate flag, trim any g/G format trailing 0's */ 51434624Sbostic if (gformat && !(flags&ALT)) { 51534624Sbostic while (t > startp && *--t == '0'); 51634624Sbostic if (*t == '.') 51734624Sbostic --t; 51834624Sbostic ++t; 51934624Sbostic } 52034624Sbostic t = exponent(t, expcnt, fmtch); 52134624Sbostic break; 52234624Sbostic case 'g': 52334624Sbostic case 'G': 52434624Sbostic /* a precision of 0 is treated as a precision of 1. */ 52534624Sbostic if (!prec) 52634624Sbostic ++prec; 52734624Sbostic /* 52834624Sbostic * ``The style used depends on the value converted; style e 52934624Sbostic * will be used only if the exponent resulting from the 53034624Sbostic * conversion is less than -4 or greater than the precision.'' 53134624Sbostic * -- ANSI X3J11 53234624Sbostic */ 53334624Sbostic if (expcnt > prec || !expcnt && fract && fract < .0001) { 53434624Sbostic /* 53534624Sbostic * g/G format counts "significant digits, not digits of 53634624Sbostic * precision; for the e/E format, this just causes an 53734624Sbostic * off-by-one problem, i.e. g/G considers the digit 53834624Sbostic * before the decimal point significant and e/E doesn't 53934624Sbostic * count it as precision. 54034624Sbostic */ 54134624Sbostic --prec; 54234624Sbostic fmtch -= 2; /* G->E, g->e */ 54334624Sbostic gformat = 1; 54434624Sbostic goto eformat; 54534624Sbostic } 54634624Sbostic /* 54734624Sbostic * reverse integer into beginning of buffer, 54834624Sbostic * note, decrement precision 54934624Sbostic */ 55034624Sbostic if (expcnt) 55134624Sbostic for (; ++p < endp; *t++ = *p, --prec); 55234624Sbostic else 55334624Sbostic *t++ = '0'; 55434624Sbostic /* 55534624Sbostic * if precision required or alternate flag set, add in a 55634624Sbostic * decimal point. If no digits yet, add in leading 0. 55734624Sbostic */ 55834624Sbostic if (prec || flags&ALT) { 55934624Sbostic dotrim = 1; 56034624Sbostic *t++ = '.'; 56134624Sbostic } 56234624Sbostic else 56334624Sbostic dotrim = 0; 56434624Sbostic /* if requires more precision and some fraction left */ 56534624Sbostic if (fract) { 56634624Sbostic if (prec) { 56734624Sbostic do { 56834624Sbostic fract = modf(fract * 10, &tmp); 56934624Sbostic *t++ = tochar((int)tmp); 57034624Sbostic } while(!tmp); 57134624Sbostic while (--prec && fract) { 57234624Sbostic fract = modf(fract * 10, &tmp); 57334624Sbostic *t++ = tochar((int)tmp); 57434248Sbostic } 57534248Sbostic } 57634624Sbostic if (fract) 57734669Sbostic startp = round(fract, (int *)NULL, startp, 57834669Sbostic t - 1, (char)0, signp); 57934624Sbostic } 58034624Sbostic /* alternate format, adds 0's for precision, else trim 0's */ 58134624Sbostic if (flags&ALT) 58234624Sbostic for (; prec--; *t++ = '0'); 58334624Sbostic else if (dotrim) { 58434624Sbostic while (t > startp && *--t == '0'); 58534624Sbostic if (*t != '.') 58634624Sbostic ++t; 58734624Sbostic } 58834624Sbostic } 58934624Sbostic return(t - startp); 59034624Sbostic } 59134248Sbostic 59234624Sbostic static char * 59334669Sbostic round(fract, exp, start, end, ch, signp) 59434624Sbostic double fract; 59534669Sbostic int *exp; 59634624Sbostic register char *start, *end; 59734669Sbostic char ch, *signp; 59834624Sbostic { 59934624Sbostic double tmp; 60034248Sbostic 60134624Sbostic if (fract) 60234248Sbostic (void)modf(fract * 10, &tmp); 60334624Sbostic else 60434624Sbostic tmp = todigit(ch); 60534624Sbostic if (tmp > 4) 60634624Sbostic for (;; --end) { 60734624Sbostic if (*end == '.') 60834624Sbostic --end; 60934624Sbostic if (++*end <= '9') 61034624Sbostic break; 61134624Sbostic *end = '0'; 61234624Sbostic if (end == start) { 61334669Sbostic if (exp) { /* e/E; increment exponent */ 61434669Sbostic *end = '1'; 61534669Sbostic ++*exp; 61634669Sbostic } 61734669Sbostic else { /* f; add extra digit */ 61834669Sbostic *--end = '1'; 61934669Sbostic --start; 62034669Sbostic } 62134624Sbostic break; 62234242Sbostic } 62334242Sbostic } 62434669Sbostic /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 62534669Sbostic else if (*signp == '-') 62634669Sbostic for (;; --end) { 62734669Sbostic if (*end == '.') 62834669Sbostic --end; 62934669Sbostic if (*end != '0') 63034669Sbostic break; 63134669Sbostic if (end == start) 63234669Sbostic *signp = 0; 63334669Sbostic } 63434624Sbostic return(start); 63534624Sbostic } 63634248Sbostic 63734624Sbostic static char * 63834624Sbostic exponent(p, exp, fmtch) 63934624Sbostic register char *p; 64034624Sbostic register int exp; 64134624Sbostic u_char fmtch; 64234624Sbostic { 64334624Sbostic register char *t; 64434624Sbostic char expbuf[MAXEXP]; 64534248Sbostic 64634624Sbostic *p++ = fmtch; 64734624Sbostic if (exp < 0) { 64834624Sbostic exp = -exp; 64934624Sbostic *p++ = '-'; 65034242Sbostic } 65134624Sbostic else 65234624Sbostic *p++ = '+'; 65334624Sbostic t = expbuf + MAXEXP; 65434624Sbostic if (exp > 9) { 65534624Sbostic do { 65634624Sbostic *--t = tochar(exp % 10); 65734624Sbostic } while ((exp /= 10) > 9); 65834624Sbostic *--t = tochar(exp); 65934624Sbostic for (; t < expbuf + MAXEXP; *p++ = *t++); 66034624Sbostic } 66134624Sbostic else { 66234624Sbostic *p++ = '0'; 66334624Sbostic *p++ = tochar(exp); 66434624Sbostic } 66534323Sbostic return(p); 66634242Sbostic } 667