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*34320Sbostic static char sccsid[] = "@(#)vfprintf.c 5.16 (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"; 6534319Sbostic for (buf = sbuf, cnt = 0;; ++fmt) { 6634318Sbostic n = fp->_cnt; 6734318Sbostic for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) 6834318Sbostic if (--n < 0 6934318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE 7034318Sbostic && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 7134318Sbostic #endif 7234318Sbostic || ch == '\n' && fp->_flag&_IOLBF) { 7334318Sbostic fp->_cnt = n; 7434318Sbostic fp->_ptr = t; 7534318Sbostic (void)_flsbuf(ch, fp); 7634318Sbostic n = fp->_cnt; 7734318Sbostic t = fp->_ptr; 7834318Sbostic } 7934318Sbostic else 8034314Sbostic *t++ = ch; 8134318Sbostic fp->_cnt = n; 8234318Sbostic fp->_ptr = t; 8334318Sbostic if (!ch) 8434314Sbostic return(cnt); 8534314Sbostic 8634318Sbostic flags = width = 0; 8734233Sbostic prec = -1; 8834233Sbostic padc = ' '; 8934318Sbostic sign = '\0'; 9034226Sbostic 9134318Sbostic rflag: switch (*++fmt) { 9234318Sbostic case ' ': 9334318Sbostic sign = ' '; 9434318Sbostic goto rflag; 9534233Sbostic case '#': 9634318Sbostic flags |= ALT; 9734318Sbostic goto rflag; 9834233Sbostic case '*': 9934235Sbostic /* 10034235Sbostic * ``A negative field width argument is taken as a 10134235Sbostic * - flag followed by a positive field width.'' 10234235Sbostic * -- ANSI X3J11 10334235Sbostic * They don't exclude field widths read from args. 10434235Sbostic */ 10534235Sbostic if ((width = va_arg(argp, int)) >= 0) 10634318Sbostic goto rflag; 10734235Sbostic width = -width; 10834235Sbostic /*FALLTHROUGH*/ 10934235Sbostic case '-': 11034318Sbostic flags |= LADJUST; 11134318Sbostic goto rflag; 11234233Sbostic case '+': 11334314Sbostic sign = '+'; 11434318Sbostic goto rflag; 11534233Sbostic case '.': 11634235Sbostic if (*++fmt == '*') 11734318Sbostic n = va_arg(argp, int); 11834318Sbostic else if (isascii(*fmt) && isdigit(*fmt)) { 11934318Sbostic n = 0; 12034233Sbostic do { 12134318Sbostic n = 10 * n + *fmt - '0'; 12234318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 12334233Sbostic --fmt; 12434226Sbostic } 12534235Sbostic else { 12634318Sbostic --fmt; 12734235Sbostic prec = 0; 12834318Sbostic goto rflag; 12934235Sbostic } 13034318Sbostic prec = n < 0 ? -1 : n; 13134318Sbostic goto rflag; 13234233Sbostic case '0': 13334233Sbostic padc = '0'; 13434235Sbostic /*FALLTHROUGH*/ 13534233Sbostic case '1': case '2': case '3': case '4': 13634233Sbostic case '5': case '6': case '7': case '8': case '9': 13734318Sbostic n = 0; 13834233Sbostic do { 13934318Sbostic n = 10 * n + *fmt - '0'; 14034318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 14134318Sbostic width = n; 14234233Sbostic --fmt; 14334319Sbostic goto rflag; 14434235Sbostic case 'L': 14534318Sbostic /* 14634318Sbostic * C doesn't have a long double; use long for now. 14734318Sbostic * flags |= LONGDBL; 14834318Sbostic */ 14934318Sbostic flags |= LONGINT; 15034318Sbostic goto rflag; 15134235Sbostic case 'h': 15234318Sbostic flags |= SHORTINT; 15334318Sbostic goto rflag; 15434233Sbostic case 'l': 15534318Sbostic flags |= LONGINT; 15634318Sbostic goto rflag; 15734314Sbostic case 'c': 15834319Sbostic buf[0] = va_arg(argp, int); 15934314Sbostic size = 1; 16034319Sbostic t = buf; 16134314Sbostic goto pforw; 16234314Sbostic case 'd': 16334318Sbostic case 'i': 16434318Sbostic ARG(); 16534318Sbostic if ((long)_ulong < 0) { 16634318Sbostic _ulong = -_ulong; 16734314Sbostic sign = '-'; 16834241Sbostic } 16934314Sbostic if (sign) 17034314Sbostic PUTC(sign); 17134241Sbostic base = 10; 17234314Sbostic goto num; 17334261Sbostic case 'e': 17434236Sbostic case 'E': 17534235Sbostic case 'f': 17634261Sbostic case 'g': 17734243Sbostic case 'G': 17834243Sbostic _double = va_arg(argp, double); 17934319Sbostic size = _cvt(_double, prec, *fmt); 18034319Sbostic t = buf; 18134314Sbostic goto pforw; 18234235Sbostic case 'n': 18334318Sbostic if (flags&LONGDBL || flags&LONGINT) 18434318Sbostic *va_arg(argp, long *) = cnt; 18534318Sbostic else if (flags&SHORTINT) 18634318Sbostic *va_arg(argp, short *) = cnt; 18734318Sbostic else 18834318Sbostic *va_arg(argp, int *) = cnt; 18934235Sbostic break; 19034226Sbostic case 'o': 19134318Sbostic ARG(); 19234226Sbostic base = 8; 19334314Sbostic goto num; 19434235Sbostic case 'p': 195*34320Sbostic /* 196*34320Sbostic * the argument shall be a pointer to void. The value 197*34320Sbostic * of the pointer is converted to a sequence of 198*34320Sbostic * printable characters, in an implementation-defined 199*34320Sbostic * manner. 200*34320Sbostic */ 201*34320Sbostic _ulong = (u_long)va_arg(argp, void *); 202*34320Sbostic base = 16; 203*34320Sbostic goto num; 20434226Sbostic case 's': 20534314Sbostic if (!(t = va_arg(argp, char *))) 20634314Sbostic t = "(null)"; 20734316Sbostic if ((size = strlen(t)) > prec && prec >= 0) 20834314Sbostic size = prec; 20934318Sbostic pforw: if (!(flags&LADJUST) && width) 21034314Sbostic for (n = size; n++ < width;) 21134314Sbostic PUTC(padc); 21234314Sbostic if (fp->_cnt - (n = size) >= 0) { 21334314Sbostic cnt += n; 21434314Sbostic fp->_cnt -= n; 21534314Sbostic bcopy(t, fp->_ptr, n); 21634314Sbostic fp->_ptr += n; 21734233Sbostic } 21834314Sbostic else for (; n--; ++t) 21934314Sbostic PUTC(*t); 22034318Sbostic if (flags&LADJUST) 22134314Sbostic while (width-- > size) 22234314Sbostic PUTC(padc); 22334226Sbostic break; 22434226Sbostic case 'u': 22534318Sbostic ARG(); 22634226Sbostic base = 10; 22734314Sbostic goto num; 22834226Sbostic case 'X': 22934226Sbostic digs = "0123456789ABCDEF"; 23034233Sbostic /*FALLTHROUGH*/ 23134226Sbostic case 'x': 23234318Sbostic ARG(); 23334314Sbostic base = 16; 23434314Sbostic /* alternate form for hex; leading 0x/X */ 23534318Sbostic if (flags&ALT && _ulong) { 23634263Sbostic PUTC('0'); 23734263Sbostic PUTC(*fmt); 23834233Sbostic } 23934319Sbostic num: t = buf + MAXDIGIT - 1; 24034233Sbostic do { 24134314Sbostic *t-- = digs[_ulong % base]; 24234314Sbostic _ulong /= base; 24334314Sbostic } while(_ulong); 24434314Sbostic digs = "0123456789abcdef"; 24534319Sbostic size = buf + MAXDIGIT - 1 - t; 24634314Sbostic if (size >= prec) { 24734314Sbostic /* alternate form for octal; leading 0 */ 24834318Sbostic if (t[1] != '0' && flags&ALT && *fmt == 'o') { 24934314Sbostic *t-- = '0'; 25034314Sbostic ++size; 25134314Sbostic } 25234314Sbostic } 25334314Sbostic else 25434314Sbostic for (; size < prec; ++size) 25534314Sbostic *t-- = '0'; 25634318Sbostic if (!(flags&LADJUST)) 25734314Sbostic while (size++ < width) 25834263Sbostic PUTC(padc); 25934319Sbostic while (++t < buf + MAXDIGIT) 26034314Sbostic PUTC(*t); 26134235Sbostic for (; width > size; --width) 26234263Sbostic PUTC(padc); 26334226Sbostic break; 26434233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 26534314Sbostic return(cnt); 26634226Sbostic default: 26734263Sbostic PUTC(*fmt); 26834226Sbostic } 26934226Sbostic } 27034314Sbostic /*NOTREACHED*/ 27134226Sbostic } 27234242Sbostic 27334261Sbostic #define EFORMAT 0x01 27434261Sbostic #define FFORMAT 0x02 27534261Sbostic #define GFORMAT 0x04 27634314Sbostic #define DEFPREC 6 27734261Sbostic 27834319Sbostic static 27934319Sbostic _cvt(number, prec, fmtch) 28034242Sbostic double number; 28134261Sbostic register int prec; 28234319Sbostic char fmtch; 28334242Sbostic { 28434248Sbostic register char *p; 28534261Sbostic register int expcnt, format; 28634319Sbostic static int maxprec = MAXPREC; 28734248Sbostic double fract, integer, tmp, modf(); 28834261Sbostic int decpt; 28934319Sbostic char *endp, *savep, *startp, *malloc(); 29034242Sbostic 29134314Sbostic if (prec == -1) 29234242Sbostic prec = DEFPREC; 29334242Sbostic 29434319Sbostic /* allocate space for large precision */ 29534319Sbostic if (prec > maxprec) 29634319Sbostic buf = malloc((u_int)((maxprec = prec) + MAXFRAC + 1)); 29734319Sbostic 29834319Sbostic startp = buf; 29934314Sbostic if (number < 0) { 30034248Sbostic *startp++ = '-'; 30134248Sbostic number = -number; 30234248Sbostic } 30334314Sbostic else if (sign) 30434318Sbostic *startp++ = sign; 30534242Sbostic 30634261Sbostic switch(fmtch) { 30734261Sbostic case 'e': 30834261Sbostic case 'E': 30934261Sbostic format = EFORMAT; 31034261Sbostic break; 31134261Sbostic case 'f': 31234261Sbostic format = FFORMAT; 31334261Sbostic break; 31434261Sbostic case 'g': 31534261Sbostic case 'G': 31634261Sbostic format = GFORMAT; 31734261Sbostic fmtch -= 2; 31834261Sbostic } 31934261Sbostic 32034248Sbostic /* 32134248Sbostic * if the alternate flag is set, or, at least one digit of precision 32234248Sbostic * was requested, add a decimal point, unless it's the g/G format 32334314Sbostic * in which case we require two digits of precision, as it counts 32434248Sbostic * precision differently. 32534248Sbostic */ 32634318Sbostic decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 32734248Sbostic 32834248Sbostic expcnt = 0; 32934319Sbostic p = buf + maxprec + MAXFRAC; 33034319Sbostic endp = p + 1; 33134248Sbostic fract = modf(number, &integer); 33234248Sbostic if (integer) { 33334248Sbostic register char *p2; 33434248Sbostic 33534248Sbostic /* get integer part of number; count decimal places */ 33634248Sbostic for (; integer; ++expcnt) { 33734248Sbostic tmp = modf(integer / 10, &integer); 33834248Sbostic *p-- = (int)((tmp + .03) * 10) + '0'; 33934242Sbostic } 34034248Sbostic 34134248Sbostic /* copy, in reverse order, to start of buffer */ 34234248Sbostic p2 = startp; 34334248Sbostic *p2++ = *++p; 34434248Sbostic 34534248Sbostic /* 34634248Sbostic * if the format is g/G, and the resulting exponent will be 34734248Sbostic * greater than the precision, use e/E format. If e/E format, 34834248Sbostic * put in a decimal point as needed, and decrement precision 34934248Sbostic * count for each digit after the decimal point. 35034248Sbostic */ 35134248Sbostic if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 35234248Sbostic if (format&GFORMAT) { 35334248Sbostic format |= EFORMAT; 35434248Sbostic 35534248Sbostic /* first digit is precision for g/G format */ 35634248Sbostic if (prec) 35734248Sbostic --prec; 35834248Sbostic } 35934248Sbostic if (decpt) 36034248Sbostic *p2++ = '.'; 36134248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 36234248Sbostic 36334318Sbostic /* precision ran out, round */ 36434248Sbostic if (p < endp) { 36534248Sbostic if (*p > '4') { 36634248Sbostic for (savep = p2--;; *p2-- = '0') { 36734248Sbostic if (*p2 == '.') 36834248Sbostic --p2; 36934248Sbostic if (++*p2 <= '9') 37034248Sbostic break; 37134248Sbostic } 37234248Sbostic p2 = savep; 37334248Sbostic } 37434248Sbostic fract = 0; 37534248Sbostic } 37634242Sbostic } 37734248Sbostic /* 37834314Sbostic * g/G in f format; if out of precision, replace digits with 37934314Sbostic * zeroes, note, have to round first. 38034248Sbostic */ 38134248Sbostic else if (format&GFORMAT) { 38234248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 38334248Sbostic /* precision ran out; round and then add zeroes */ 38434248Sbostic if (p < endp) { 38534248Sbostic if (*p > '4') { 38634248Sbostic for (savep = p2--; ++*p2 > '9'; 38734248Sbostic *p2-- = '0'); 38834248Sbostic p2 = savep; 38934248Sbostic } 39034248Sbostic do { 39134248Sbostic *p2++ = '0'; 39234248Sbostic } while (++p < endp); 39334248Sbostic fract = 0; 39434248Sbostic } 39534248Sbostic if (decpt) 39634248Sbostic *p2++ = '.'; 39734242Sbostic } 39834248Sbostic /* f format */ 39934248Sbostic else { 40034248Sbostic for (; ++p < endp; *p2++ = *p); 40134248Sbostic if (decpt) 40234248Sbostic *p2++ = '.'; 40334248Sbostic } 40434248Sbostic p = p2; 40534248Sbostic } 40634248Sbostic /* 40734318Sbostic * if no fraction, the number was zero, and if no precision, can't 40834318Sbostic * show anything after the decimal point. 40934248Sbostic */ 41034248Sbostic else if (!fract || !prec) { 41134248Sbostic *startp++ = '0'; 41234318Sbostic if (decpt && !(format&GFORMAT)) 41334248Sbostic *startp++ = '.'; 41434318Sbostic *startp = '\0'; 41534319Sbostic return(startp - buf); 41634248Sbostic } 41734248Sbostic /* 41834248Sbostic * if the format is g/G, and the resulting exponent will be less than 41934248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 42034248Sbostic */ 42134248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 42234248Sbostic format |= EFORMAT; 42334248Sbostic if (fract) 42434248Sbostic for (p = startp; fract;) { 42534248Sbostic fract = modf(fract * 10, &tmp); 42634248Sbostic if (!tmp) { 42734248Sbostic --expcnt; 42834248Sbostic continue; 42934248Sbostic } 43034248Sbostic *p++ = (int)tmp + '0'; 43134248Sbostic break; 43234248Sbostic } 43334242Sbostic else 43434248Sbostic *p++ = '0'; 43534248Sbostic 43634248Sbostic /* g/G format, decrement precision for first digit */ 43734248Sbostic if (format&GFORMAT && prec) 43834248Sbostic --prec; 43934248Sbostic 44034248Sbostic /* add decimal after first non-zero digit */ 44134248Sbostic if (decpt) 44234248Sbostic *p++ = '.'; 44334242Sbostic } 44434248Sbostic /* 44534248Sbostic * f format or g/G printed as f format; don't worry about decimal 44634248Sbostic * point, if g/G format doesn't need it, will get stripped later. 44734248Sbostic */ 44834242Sbostic else { 44934248Sbostic p = startp; 45034248Sbostic *p++ = '0'; 45134248Sbostic *p++ = '.'; 45234248Sbostic } 45334248Sbostic 45434319Sbostic /* finish out requested precision */ 45534319Sbostic while (fract && prec-- > 0) { 45634319Sbostic fract = modf(fract * 10, &tmp); 45734319Sbostic *p++ = (int)tmp + '0'; 45834319Sbostic } 45934319Sbostic while (prec-- > 0) 46034319Sbostic *p++ = '0'; 46134248Sbostic 46234248Sbostic /* 46334248Sbostic * if any fractional value left, "round" it back up to the beginning 46434248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 46534248Sbostic * decimal point. 46634248Sbostic */ 46734248Sbostic if (fract) { 46834248Sbostic (void)modf(fract * 10, &tmp); 46934248Sbostic if (tmp > 4) { 47034248Sbostic for (savep = p--;; *p-- = '0') { 47134248Sbostic if (*p == '.') 47234248Sbostic --p; 47334248Sbostic if (p == startp) { 47434248Sbostic *p = '1'; 47534248Sbostic ++expcnt; 47634248Sbostic break; 47734248Sbostic } 47834248Sbostic if (++*p <= '9') 47934248Sbostic break; 48034242Sbostic } 48134248Sbostic p = savep; 48234242Sbostic } 48334248Sbostic } 48434248Sbostic 48534248Sbostic /* 48634248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 48734248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 48834248Sbostic */ 48934318Sbostic if (!(flags&ALT)) { 49034248Sbostic if (format&GFORMAT) 49134248Sbostic for (; p[-1] == '0'; --p); 49234248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 49334248Sbostic --p; 49434248Sbostic } 49534248Sbostic 49634248Sbostic /* if an e/E format, add exponent */ 49734248Sbostic if (format&EFORMAT) { 49834248Sbostic *p++ = fmtch; 49934248Sbostic if (--expcnt < 0) { 50034248Sbostic expcnt = -expcnt; 50134248Sbostic *p++ = '-'; 50234242Sbostic } 50334248Sbostic else 50434248Sbostic *p++ = '+'; 50534248Sbostic *p++ = expcnt / 10 + '0'; 50634248Sbostic *p++ = expcnt % 10 + '0'; 50734242Sbostic } 50834319Sbostic return(p - buf); 50934242Sbostic } 510