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*34322Sbostic static char sccsid[] = "@(#)vfprintf.c 5.18 (Berkeley) 05/17/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 2234319Sbostic /* 2334319Sbostic * To handle arbitrary floating point precision, the buffer has to hold the 2434319Sbostic * number, a decimal point, and N precision digits. We can't just truncate 2534319Sbostic * at some point is that the lower-level math routines may very well be 2634319Sbostic * repeatedly returning some small fraction. A 128 bit fraction can be 2734319Sbostic * represented in 39 decimal digits. Guess a max of 40 digits of precision, 2834319Sbostic * and add one for the decimal point. 2934319Sbostic */ 3034319Sbostic #define MAXFRAC 39 3134319Sbostic #define MAXPREC 40 3234319Sbostic #define MAXDIGIT (MAXFRAC + MAXPREC + 1) 3334226Sbostic 3434263Sbostic #define PUTC(ch) {++cnt; putc(ch, fp);} 3534236Sbostic 3634318Sbostic #define ARG() \ 3734318Sbostic _ulong = flags&LONGINT ? va_arg(argp, long) : \ 3834318Sbostic flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 3934235Sbostic 4034318Sbostic /* have to deal with the negative buffer count kludge */ 4134318Sbostic #define NEGATIVE_COUNT_KLUDGE 4234318Sbostic 4334318Sbostic #define LONGINT 0x01 /* long integer */ 4434318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 4534318Sbostic #define SHORTINT 0x04 /* short integer */ 4634318Sbostic #define ALT 0x08 /* alternate form */ 4734318Sbostic #define LADJUST 0x10 /* left adjustment */ 4834318Sbostic static int flags; 4934318Sbostic 5034319Sbostic static char sign, *buf; 5134248Sbostic 5234235Sbostic x_doprnt(fmt, argp, fp) 5334233Sbostic register char *fmt; 5434233Sbostic va_list argp; 5534235Sbostic register FILE *fp; 5634226Sbostic { 5734314Sbostic register int cnt, n; 5834314Sbostic register char ch, *t; 5934235Sbostic double _double; 6034314Sbostic u_long _ulong; 6134318Sbostic int base, width, prec, size; 6234319Sbostic char padc, *digs, sbuf[MAXDIGIT]; 6334226Sbostic 6434243Sbostic digs = "0123456789abcdef"; 65*34322Sbostic if (!buf) 66*34322Sbostic buf = sbuf; 67*34322Sbostic for (cnt = 0;; ++fmt) { 6834318Sbostic n = fp->_cnt; 6934318Sbostic for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) 7034318Sbostic if (--n < 0 7134318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE 7234318Sbostic && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 7334318Sbostic #endif 7434318Sbostic || ch == '\n' && fp->_flag&_IOLBF) { 7534318Sbostic fp->_cnt = n; 7634318Sbostic fp->_ptr = t; 7734318Sbostic (void)_flsbuf(ch, fp); 7834318Sbostic n = fp->_cnt; 7934318Sbostic t = fp->_ptr; 8034318Sbostic } 8134318Sbostic else 8234314Sbostic *t++ = ch; 8334318Sbostic fp->_cnt = n; 8434318Sbostic fp->_ptr = t; 8534318Sbostic if (!ch) 8634314Sbostic return(cnt); 8734314Sbostic 8834318Sbostic flags = width = 0; 8934233Sbostic prec = -1; 9034233Sbostic padc = ' '; 9134318Sbostic sign = '\0'; 9234226Sbostic 9334318Sbostic rflag: switch (*++fmt) { 9434318Sbostic case ' ': 9534318Sbostic sign = ' '; 9634318Sbostic goto rflag; 9734233Sbostic case '#': 9834318Sbostic flags |= ALT; 9934318Sbostic goto rflag; 10034233Sbostic case '*': 10134235Sbostic /* 10234235Sbostic * ``A negative field width argument is taken as a 10334235Sbostic * - flag followed by a positive field width.'' 10434235Sbostic * -- ANSI X3J11 10534235Sbostic * They don't exclude field widths read from args. 10634235Sbostic */ 10734235Sbostic if ((width = va_arg(argp, int)) >= 0) 10834318Sbostic goto rflag; 10934235Sbostic width = -width; 11034235Sbostic /*FALLTHROUGH*/ 11134235Sbostic case '-': 11234318Sbostic flags |= LADJUST; 11334318Sbostic goto rflag; 11434233Sbostic case '+': 11534314Sbostic sign = '+'; 11634318Sbostic goto rflag; 11734233Sbostic case '.': 11834235Sbostic if (*++fmt == '*') 11934318Sbostic n = va_arg(argp, int); 12034318Sbostic else if (isascii(*fmt) && isdigit(*fmt)) { 12134318Sbostic n = 0; 12234233Sbostic do { 12334318Sbostic n = 10 * n + *fmt - '0'; 12434318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 12534233Sbostic --fmt; 12634226Sbostic } 12734235Sbostic else { 12834318Sbostic --fmt; 12934235Sbostic prec = 0; 13034318Sbostic goto rflag; 13134235Sbostic } 13234318Sbostic prec = n < 0 ? -1 : n; 13334318Sbostic goto rflag; 13434233Sbostic case '0': 13534233Sbostic padc = '0'; 13634235Sbostic /*FALLTHROUGH*/ 13734233Sbostic case '1': case '2': case '3': case '4': 13834233Sbostic case '5': case '6': case '7': case '8': case '9': 13934318Sbostic n = 0; 14034233Sbostic do { 14134318Sbostic n = 10 * n + *fmt - '0'; 14234318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 14334318Sbostic width = n; 14434233Sbostic --fmt; 14534319Sbostic goto rflag; 14634235Sbostic case 'L': 14734318Sbostic /* 14834318Sbostic * C doesn't have a long double; use long for now. 14934318Sbostic * flags |= LONGDBL; 15034318Sbostic */ 15134318Sbostic flags |= LONGINT; 15234318Sbostic goto rflag; 15334235Sbostic case 'h': 15434318Sbostic flags |= SHORTINT; 15534318Sbostic goto rflag; 15634233Sbostic case 'l': 15734318Sbostic flags |= LONGINT; 15834318Sbostic goto rflag; 15934314Sbostic case 'c': 16034319Sbostic buf[0] = va_arg(argp, int); 16134314Sbostic size = 1; 16234319Sbostic t = buf; 16334314Sbostic goto pforw; 16434314Sbostic case 'd': 16534318Sbostic case 'i': 16634318Sbostic ARG(); 16734318Sbostic if ((long)_ulong < 0) { 16834318Sbostic _ulong = -_ulong; 16934314Sbostic sign = '-'; 17034241Sbostic } 17134314Sbostic if (sign) 17234314Sbostic PUTC(sign); 17334241Sbostic base = 10; 17434314Sbostic goto num; 17534261Sbostic case 'e': 17634236Sbostic case 'E': 17734235Sbostic case 'f': 17834261Sbostic case 'g': 17934243Sbostic case 'G': 18034243Sbostic _double = va_arg(argp, double); 18134319Sbostic size = _cvt(_double, prec, *fmt); 18234319Sbostic t = buf; 18334314Sbostic goto pforw; 18434235Sbostic case 'n': 18534318Sbostic if (flags&LONGDBL || flags&LONGINT) 18634318Sbostic *va_arg(argp, long *) = cnt; 18734318Sbostic else if (flags&SHORTINT) 18834318Sbostic *va_arg(argp, short *) = cnt; 18934318Sbostic else 19034318Sbostic *va_arg(argp, int *) = cnt; 19134235Sbostic break; 19234226Sbostic case 'o': 19334318Sbostic ARG(); 19434226Sbostic base = 8; 19534314Sbostic goto num; 19634235Sbostic case 'p': 19734320Sbostic /* 19834321Sbostic * ``The argument shall be a pointer to void. The 19934321Sbostic * value of the pointer is converted to a sequence 20034321Sbostic * of printable characters, in an implementation- 20134321Sbostic * defined manner.'' 20234321Sbostic * -- ANSI X3J11 20334320Sbostic */ 20434320Sbostic _ulong = (u_long)va_arg(argp, void *); 20534320Sbostic base = 16; 20634320Sbostic goto num; 20734226Sbostic case 's': 20834314Sbostic if (!(t = va_arg(argp, char *))) 20934314Sbostic t = "(null)"; 21034321Sbostic if (prec >= 0) { 21134321Sbostic /* 21234321Sbostic * can't use strlen; can only look for the 21334321Sbostic * NUL in the first `prec' characters, and 21434321Sbostic * strlen() will go further. 21534321Sbostic */ 21634321Sbostic char *p, *memchr(); 21734321Sbostic 21834321Sbostic if (p = memchr(t, 0, prec)) { 21934321Sbostic size = p - t; 22034321Sbostic if (size > prec) 22134321Sbostic size = prec; 22234321Sbostic } 22334321Sbostic else 22434321Sbostic size = prec; 22534321Sbostic } 22634321Sbostic else 22734321Sbostic size = strlen(t); 22834318Sbostic pforw: if (!(flags&LADJUST) && width) 22934314Sbostic for (n = size; n++ < width;) 23034314Sbostic PUTC(padc); 23134314Sbostic if (fp->_cnt - (n = size) >= 0) { 23234314Sbostic cnt += n; 23334314Sbostic fp->_cnt -= n; 23434314Sbostic bcopy(t, fp->_ptr, n); 23534314Sbostic fp->_ptr += n; 23634233Sbostic } 23734314Sbostic else for (; n--; ++t) 23834314Sbostic PUTC(*t); 23934318Sbostic if (flags&LADJUST) 24034314Sbostic while (width-- > size) 24134314Sbostic PUTC(padc); 24234226Sbostic break; 24334226Sbostic case 'u': 24434318Sbostic ARG(); 24534226Sbostic base = 10; 24634314Sbostic goto num; 24734226Sbostic case 'X': 24834226Sbostic digs = "0123456789ABCDEF"; 24934233Sbostic /*FALLTHROUGH*/ 25034226Sbostic case 'x': 25134318Sbostic ARG(); 25234314Sbostic base = 16; 25334314Sbostic /* alternate form for hex; leading 0x/X */ 25434318Sbostic if (flags&ALT && _ulong) { 25534263Sbostic PUTC('0'); 25634263Sbostic PUTC(*fmt); 25734233Sbostic } 25834319Sbostic num: t = buf + MAXDIGIT - 1; 25934233Sbostic do { 26034314Sbostic *t-- = digs[_ulong % base]; 26134314Sbostic _ulong /= base; 26234314Sbostic } while(_ulong); 26334314Sbostic digs = "0123456789abcdef"; 26434319Sbostic size = buf + MAXDIGIT - 1 - t; 26534314Sbostic if (size >= prec) { 26634314Sbostic /* alternate form for octal; leading 0 */ 26734318Sbostic if (t[1] != '0' && flags&ALT && *fmt == 'o') { 26834314Sbostic *t-- = '0'; 26934314Sbostic ++size; 27034314Sbostic } 27134314Sbostic } 27234314Sbostic else 27334314Sbostic for (; size < prec; ++size) 27434314Sbostic *t-- = '0'; 27534318Sbostic if (!(flags&LADJUST)) 27634314Sbostic while (size++ < width) 27734263Sbostic PUTC(padc); 27834319Sbostic while (++t < buf + MAXDIGIT) 27934314Sbostic PUTC(*t); 28034235Sbostic for (; width > size; --width) 28134263Sbostic PUTC(padc); 28234226Sbostic break; 28334233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 28434314Sbostic return(cnt); 28534226Sbostic default: 28634263Sbostic PUTC(*fmt); 28734226Sbostic } 28834226Sbostic } 28934314Sbostic /*NOTREACHED*/ 29034226Sbostic } 29134242Sbostic 29234261Sbostic #define EFORMAT 0x01 29334261Sbostic #define FFORMAT 0x02 29434261Sbostic #define GFORMAT 0x04 29534314Sbostic #define DEFPREC 6 29634261Sbostic 29734319Sbostic static 29834319Sbostic _cvt(number, prec, fmtch) 29934242Sbostic double number; 30034261Sbostic register int prec; 30134319Sbostic char fmtch; 30234242Sbostic { 30334248Sbostic register char *p; 30434261Sbostic register int expcnt, format; 30534319Sbostic static int maxprec = MAXPREC; 30634248Sbostic double fract, integer, tmp, modf(); 30734261Sbostic int decpt; 30834319Sbostic char *endp, *savep, *startp, *malloc(); 30934242Sbostic 31034314Sbostic if (prec == -1) 31134242Sbostic prec = DEFPREC; 31234242Sbostic 31334319Sbostic /* allocate space for large precision */ 31434319Sbostic if (prec > maxprec) 31534319Sbostic buf = malloc((u_int)((maxprec = prec) + MAXFRAC + 1)); 31634319Sbostic 31734319Sbostic startp = buf; 31834314Sbostic if (number < 0) { 31934248Sbostic *startp++ = '-'; 32034248Sbostic number = -number; 32134248Sbostic } 32234314Sbostic else if (sign) 32334318Sbostic *startp++ = sign; 32434242Sbostic 32534261Sbostic switch(fmtch) { 32634261Sbostic case 'e': 32734261Sbostic case 'E': 32834261Sbostic format = EFORMAT; 32934261Sbostic break; 33034261Sbostic case 'f': 33134261Sbostic format = FFORMAT; 33234261Sbostic break; 33334261Sbostic case 'g': 33434261Sbostic case 'G': 33534261Sbostic format = GFORMAT; 33634261Sbostic fmtch -= 2; 33734261Sbostic } 33834261Sbostic 33934248Sbostic /* 34034248Sbostic * if the alternate flag is set, or, at least one digit of precision 34134248Sbostic * was requested, add a decimal point, unless it's the g/G format 34234314Sbostic * in which case we require two digits of precision, as it counts 34334248Sbostic * precision differently. 34434248Sbostic */ 34534318Sbostic decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 34634248Sbostic 34734248Sbostic expcnt = 0; 34834319Sbostic p = buf + maxprec + MAXFRAC; 34934319Sbostic endp = p + 1; 35034248Sbostic fract = modf(number, &integer); 35134248Sbostic if (integer) { 35234248Sbostic register char *p2; 35334248Sbostic 35434248Sbostic /* get integer part of number; count decimal places */ 35534248Sbostic for (; integer; ++expcnt) { 35634248Sbostic tmp = modf(integer / 10, &integer); 35734248Sbostic *p-- = (int)((tmp + .03) * 10) + '0'; 35834242Sbostic } 35934248Sbostic 36034248Sbostic /* copy, in reverse order, to start of buffer */ 36134248Sbostic p2 = startp; 36234248Sbostic *p2++ = *++p; 36334248Sbostic 36434248Sbostic /* 36534248Sbostic * if the format is g/G, and the resulting exponent will be 36634248Sbostic * greater than the precision, use e/E format. If e/E format, 36734248Sbostic * put in a decimal point as needed, and decrement precision 36834248Sbostic * count for each digit after the decimal point. 36934248Sbostic */ 37034248Sbostic if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 37134248Sbostic if (format&GFORMAT) { 37234248Sbostic format |= EFORMAT; 37334248Sbostic 37434248Sbostic /* first digit is precision for g/G format */ 37534248Sbostic if (prec) 37634248Sbostic --prec; 37734248Sbostic } 37834248Sbostic if (decpt) 37934248Sbostic *p2++ = '.'; 38034248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 38134248Sbostic 38234318Sbostic /* precision ran out, round */ 38334248Sbostic if (p < endp) { 38434248Sbostic if (*p > '4') { 38534248Sbostic for (savep = p2--;; *p2-- = '0') { 38634248Sbostic if (*p2 == '.') 38734248Sbostic --p2; 38834248Sbostic if (++*p2 <= '9') 38934248Sbostic break; 39034248Sbostic } 39134248Sbostic p2 = savep; 39234248Sbostic } 39334248Sbostic fract = 0; 39434248Sbostic } 39534242Sbostic } 39634248Sbostic /* 39734314Sbostic * g/G in f format; if out of precision, replace digits with 39834314Sbostic * zeroes, note, have to round first. 39934248Sbostic */ 40034248Sbostic else if (format&GFORMAT) { 40134248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 40234248Sbostic /* precision ran out; round and then add zeroes */ 40334248Sbostic if (p < endp) { 40434248Sbostic if (*p > '4') { 40534248Sbostic for (savep = p2--; ++*p2 > '9'; 40634248Sbostic *p2-- = '0'); 40734248Sbostic p2 = savep; 40834248Sbostic } 40934248Sbostic do { 41034248Sbostic *p2++ = '0'; 41134248Sbostic } while (++p < endp); 41234248Sbostic fract = 0; 41334248Sbostic } 41434248Sbostic if (decpt) 41534248Sbostic *p2++ = '.'; 41634242Sbostic } 41734248Sbostic /* f format */ 41834248Sbostic else { 41934248Sbostic for (; ++p < endp; *p2++ = *p); 42034248Sbostic if (decpt) 42134248Sbostic *p2++ = '.'; 42234248Sbostic } 42334248Sbostic p = p2; 42434248Sbostic } 42534248Sbostic /* 42634318Sbostic * if no fraction, the number was zero, and if no precision, can't 42734318Sbostic * show anything after the decimal point. 42834248Sbostic */ 42934248Sbostic else if (!fract || !prec) { 43034248Sbostic *startp++ = '0'; 43134318Sbostic if (decpt && !(format&GFORMAT)) 43234248Sbostic *startp++ = '.'; 43334318Sbostic *startp = '\0'; 43434319Sbostic return(startp - buf); 43534248Sbostic } 43634248Sbostic /* 43734248Sbostic * if the format is g/G, and the resulting exponent will be less than 43834248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 43934248Sbostic */ 44034248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 44134248Sbostic format |= EFORMAT; 44234248Sbostic if (fract) 44334248Sbostic for (p = startp; fract;) { 44434248Sbostic fract = modf(fract * 10, &tmp); 44534248Sbostic if (!tmp) { 44634248Sbostic --expcnt; 44734248Sbostic continue; 44834248Sbostic } 44934248Sbostic *p++ = (int)tmp + '0'; 45034248Sbostic break; 45134248Sbostic } 45234242Sbostic else 45334248Sbostic *p++ = '0'; 45434248Sbostic 45534248Sbostic /* g/G format, decrement precision for first digit */ 45634248Sbostic if (format&GFORMAT && prec) 45734248Sbostic --prec; 45834248Sbostic 45934248Sbostic /* add decimal after first non-zero digit */ 46034248Sbostic if (decpt) 46134248Sbostic *p++ = '.'; 46234242Sbostic } 46334248Sbostic /* 46434248Sbostic * f format or g/G printed as f format; don't worry about decimal 46534248Sbostic * point, if g/G format doesn't need it, will get stripped later. 46634248Sbostic */ 46734242Sbostic else { 46834248Sbostic p = startp; 46934248Sbostic *p++ = '0'; 47034248Sbostic *p++ = '.'; 47134248Sbostic } 47234248Sbostic 47334319Sbostic /* finish out requested precision */ 47434319Sbostic while (fract && prec-- > 0) { 47534319Sbostic fract = modf(fract * 10, &tmp); 47634319Sbostic *p++ = (int)tmp + '0'; 47734319Sbostic } 47834319Sbostic while (prec-- > 0) 47934319Sbostic *p++ = '0'; 48034248Sbostic 48134248Sbostic /* 48234248Sbostic * if any fractional value left, "round" it back up to the beginning 48334248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 48434248Sbostic * decimal point. 48534248Sbostic */ 48634248Sbostic if (fract) { 48734248Sbostic (void)modf(fract * 10, &tmp); 48834248Sbostic if (tmp > 4) { 48934248Sbostic for (savep = p--;; *p-- = '0') { 49034248Sbostic if (*p == '.') 49134248Sbostic --p; 49234248Sbostic if (p == startp) { 49334248Sbostic *p = '1'; 49434248Sbostic ++expcnt; 49534248Sbostic break; 49634248Sbostic } 49734248Sbostic if (++*p <= '9') 49834248Sbostic break; 49934242Sbostic } 50034248Sbostic p = savep; 50134242Sbostic } 50234248Sbostic } 50334248Sbostic 50434248Sbostic /* 50534248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 50634248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 50734248Sbostic */ 50834318Sbostic if (!(flags&ALT)) { 50934248Sbostic if (format&GFORMAT) 51034248Sbostic for (; p[-1] == '0'; --p); 51134248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 51234248Sbostic --p; 51334248Sbostic } 51434248Sbostic 51534248Sbostic /* if an e/E format, add exponent */ 51634248Sbostic if (format&EFORMAT) { 51734248Sbostic *p++ = fmtch; 51834248Sbostic if (--expcnt < 0) { 51934248Sbostic expcnt = -expcnt; 52034248Sbostic *p++ = '-'; 52134242Sbostic } 52234248Sbostic else 52334248Sbostic *p++ = '+'; 52434248Sbostic *p++ = expcnt / 10 + '0'; 52534248Sbostic *p++ = expcnt % 10 + '0'; 52634242Sbostic } 52734319Sbostic return(p - buf); 52834242Sbostic } 529