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*34314Sbostic static char sccsid[] = "@(#)vfprintf.c 5.11 (Berkeley) 05/16/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 22*34314Sbostic #define MAXBUF 40 2334226Sbostic 2434263Sbostic #define PUTC(ch) {++cnt; putc(ch, fp);} 2534236Sbostic 2634235Sbostic #define LONGINT 0x01 2734235Sbostic #define LONGDBL 0x02 2834235Sbostic #define SHORTINT 0x04 2934241Sbostic #define GETARG(r) \ 3034241Sbostic r = argsize&LONGINT ? va_arg(argp, long) : \ 3134241Sbostic argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 3234235Sbostic 33*34314Sbostic static int alt; 34*34314Sbostic static char sign; 3534248Sbostic 3634235Sbostic x_doprnt(fmt, argp, fp) 3734233Sbostic register char *fmt; 3834233Sbostic va_list argp; 3934235Sbostic register FILE *fp; 4034226Sbostic { 41*34314Sbostic register int cnt, n; 42*34314Sbostic register char ch, *t; 4334235Sbostic double _double; 44*34314Sbostic u_long _ulong; 45*34314Sbostic int base, ladjust, width, prec, size; 46*34314Sbostic char argsize, padc, *digs, *_cvt(), buf[MAXBUF]; 4734226Sbostic 4834243Sbostic digs = "0123456789abcdef"; 49*34314Sbostic for (cnt = 0;; ++fmt) { 50*34314Sbostic if ((n = fp->_cnt) >= 0) { 51*34314Sbostic for (t = fp->_ptr; (ch = *fmt) != '%' && ch; ++fmt) { 52*34314Sbostic if (--n < 0) 53*34314Sbostic break; 54*34314Sbostic *t++ = ch; 55*34314Sbostic } 56*34314Sbostic fp->_ptr = t; 57*34314Sbostic cnt += fp->_cnt - n; 58*34314Sbostic fp->_cnt = n; 59*34314Sbostic if (ch != '%' && ch) { 60*34314Sbostic PUTC(ch); 61*34314Sbostic continue; 62*34314Sbostic } 63*34314Sbostic } 64*34314Sbostic else for (; *fmt && *fmt != '%'; ++fmt) 6534263Sbostic PUTC(*fmt); 6634226Sbostic 67*34314Sbostic if (!*fmt) 68*34314Sbostic return(cnt); 69*34314Sbostic 70*34314Sbostic alt = ladjust = width = 0; 7134233Sbostic prec = -1; 7234233Sbostic padc = ' '; 73*34314Sbostic argsize = sign = '\0'; 74*34314Sbostic t = buf; 7534226Sbostic 7634233Sbostic flags: switch (*++fmt) { 7734233Sbostic case '#': 78*34314Sbostic alt = 1; 7934233Sbostic goto flags; 8034233Sbostic case '*': 8134235Sbostic /* 8234235Sbostic * ``A negative field width argument is taken as a 8334235Sbostic * - flag followed by a positive field width.'' 8434235Sbostic * -- ANSI X3J11 8534235Sbostic * They don't exclude field widths read from args. 8634235Sbostic */ 8734235Sbostic if ((width = va_arg(argp, int)) >= 0) 8834235Sbostic goto flags; 8934235Sbostic width = -width; 9034235Sbostic /*FALLTHROUGH*/ 9134235Sbostic case '-': 9234235Sbostic ladjust = 1; 9334233Sbostic goto flags; 9434233Sbostic case '+': 95*34314Sbostic sign = '+'; 9634233Sbostic goto flags; 9734233Sbostic case '.': 9834235Sbostic if (*++fmt == '*') 9934235Sbostic prec = va_arg(argp, int); 10034235Sbostic else if (isdigit(*fmt)) { 10134235Sbostic prec = 0; 10234233Sbostic do { 10334236Sbostic prec = 10 * prec + *fmt - '0'; 10434233Sbostic } while isdigit(*++fmt); 10534233Sbostic --fmt; 10634226Sbostic } 10734235Sbostic else { 10834235Sbostic prec = 0; 10934235Sbostic --fmt; 11034235Sbostic goto flags; 11134235Sbostic } 11234235Sbostic if (prec < 0) 11334235Sbostic prec = -1; 11434233Sbostic goto flags; 11534233Sbostic case '0': 11634233Sbostic padc = '0'; 11734235Sbostic /*FALLTHROUGH*/ 11834233Sbostic case '1': case '2': case '3': case '4': 11934233Sbostic case '5': case '6': case '7': case '8': case '9': 12034233Sbostic do { 12134236Sbostic width = 10 * width + *fmt - '0'; 12234233Sbostic } while isdigit(*++fmt); 12334233Sbostic --fmt; 12434235Sbostic case 'L': 12534235Sbostic argsize |= LONGDBL; 12634235Sbostic goto flags; 12734235Sbostic case 'h': 12834235Sbostic argsize |= SHORTINT; 12934235Sbostic goto flags; 13034233Sbostic case 'l': 13134235Sbostic argsize |= LONGINT; 13234233Sbostic goto flags; 133*34314Sbostic case 'c': 134*34314Sbostic *t = va_arg(argp, int); 135*34314Sbostic size = 1; 136*34314Sbostic goto pforw; 137*34314Sbostic case 'd': 138*34314Sbostic case 'i': { 139*34314Sbostic long reg_long; 14034226Sbostic 14134241Sbostic GETARG(reg_long); 14234241Sbostic if (reg_long < 0) { 143*34314Sbostic _ulong = -reg_long; 144*34314Sbostic sign = '-'; 14534241Sbostic } 14634241Sbostic else { 147*34314Sbostic _ulong = reg_long; 14834241Sbostic } 149*34314Sbostic if (sign) 150*34314Sbostic PUTC(sign); 15134241Sbostic base = 10; 152*34314Sbostic goto num; 153*34314Sbostic } 15434261Sbostic case 'e': 15534236Sbostic case 'E': 15634235Sbostic case 'f': 15734261Sbostic case 'g': 15834243Sbostic case 'G': 15934243Sbostic _double = va_arg(argp, double); 160*34314Sbostic size = _cvt(_double, prec, buf, buf + sizeof(buf), 161*34314Sbostic *fmt) - buf; 162*34314Sbostic goto pforw; 16334235Sbostic case 'n': 16434235Sbostic *(va_arg(argp, int *)) = cnt; 16534235Sbostic break; 16634226Sbostic case 'o': 167*34314Sbostic GETARG(_ulong); 16834226Sbostic base = 8; 169*34314Sbostic goto num; 17034235Sbostic case 'p': 17134226Sbostic case 's': 172*34314Sbostic if (!(t = va_arg(argp, char *))) 173*34314Sbostic t = "(null)"; 174*34314Sbostic if ((size = strlen(t)) < prec) 175*34314Sbostic size = prec; 176*34314Sbostic pforw: if (!ladjust && width) 177*34314Sbostic for (n = size; n++ < width;) 178*34314Sbostic PUTC(padc); 179*34314Sbostic if (fp->_cnt - (n = size) >= 0) { 180*34314Sbostic cnt += n; 181*34314Sbostic fp->_cnt -= n; 182*34314Sbostic bcopy(t, fp->_ptr, n); 183*34314Sbostic fp->_ptr += n; 18434233Sbostic } 185*34314Sbostic else for (; n--; ++t) 186*34314Sbostic PUTC(*t); 187*34314Sbostic if (ladjust) 188*34314Sbostic while (width-- > size) 189*34314Sbostic PUTC(padc); 19034226Sbostic break; 19134226Sbostic case 'u': 192*34314Sbostic GETARG(_ulong); 19334226Sbostic base = 10; 194*34314Sbostic goto num; 19534226Sbostic case 'X': 19634226Sbostic digs = "0123456789ABCDEF"; 19734233Sbostic /*FALLTHROUGH*/ 19834226Sbostic case 'x': 199*34314Sbostic GETARG(_ulong); 200*34314Sbostic base = 16; 201*34314Sbostic /* alternate form for hex; leading 0x/X */ 202*34314Sbostic if (alt && _ulong) { 20334263Sbostic PUTC('0'); 20434263Sbostic PUTC(*fmt); 20534233Sbostic } 206*34314Sbostic num: t = buf + sizeof(buf) - 1; 20734233Sbostic do { 208*34314Sbostic *t-- = digs[_ulong % base]; 209*34314Sbostic _ulong /= base; 210*34314Sbostic } while(_ulong); 211*34314Sbostic digs = "0123456789abcdef"; 212*34314Sbostic size = buf + sizeof(buf) - 1 - t; 213*34314Sbostic if (size >= prec) { 214*34314Sbostic /* alternate form for octal; leading 0 */ 215*34314Sbostic if (t[1] != '0' && alt && *fmt == 'o') { 216*34314Sbostic *t-- = '0'; 217*34314Sbostic ++size; 218*34314Sbostic } 219*34314Sbostic } 220*34314Sbostic else 221*34314Sbostic for (; size < prec; ++size) 222*34314Sbostic *t-- = '0'; 223*34314Sbostic if (!ladjust) 224*34314Sbostic while (size++ < width) 22534263Sbostic PUTC(padc); 226*34314Sbostic while (++t < buf + sizeof(buf)) 227*34314Sbostic PUTC(*t); 22834235Sbostic for (; width > size; --width) 22934263Sbostic PUTC(padc); 23034226Sbostic break; 23134233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 232*34314Sbostic return(cnt); 23334226Sbostic default: 23434263Sbostic PUTC(*fmt); 23534226Sbostic } 23634226Sbostic } 237*34314Sbostic /*NOTREACHED*/ 23834226Sbostic } 23934242Sbostic 24034261Sbostic #define EFORMAT 0x01 24134261Sbostic #define FFORMAT 0x02 24234261Sbostic #define GFORMAT 0x04 243*34314Sbostic #define DEFPREC 6 24434261Sbostic 24534261Sbostic static char * 24634261Sbostic _cvt(number, prec, startp, endp, fmtch) 24734242Sbostic double number; 24834261Sbostic register int prec; 24934248Sbostic char *startp, *endp, fmtch; 25034242Sbostic { 25134248Sbostic register char *p; 25234261Sbostic register int expcnt, format; 25334248Sbostic double fract, integer, tmp, modf(); 25434261Sbostic int decpt; 25534248Sbostic char *savep; 25634242Sbostic 257*34314Sbostic if (prec == -1) 25834242Sbostic prec = DEFPREC; 25934242Sbostic 260*34314Sbostic if (number < 0) { 26134248Sbostic *startp++ = '-'; 26234248Sbostic number = -number; 26334248Sbostic } 264*34314Sbostic else if (sign) 26534248Sbostic *startp++ = '+'; 26634242Sbostic 26734261Sbostic switch(fmtch) { 26834261Sbostic case 'e': 26934261Sbostic case 'E': 27034261Sbostic format = EFORMAT; 27134261Sbostic break; 27234261Sbostic case 'f': 27334261Sbostic format = FFORMAT; 27434261Sbostic break; 27534261Sbostic case 'g': 27634261Sbostic case 'G': 27734261Sbostic format = GFORMAT; 27834261Sbostic fmtch -= 2; 27934261Sbostic } 28034261Sbostic 28134248Sbostic /* 28234248Sbostic * if the alternate flag is set, or, at least one digit of precision 28334248Sbostic * was requested, add a decimal point, unless it's the g/G format 284*34314Sbostic * in which case we require two digits of precision, as it counts 28534248Sbostic * precision differently. 28634248Sbostic */ 287*34314Sbostic decpt = alt || prec > 1 || !(format&GFORMAT) && prec; 28834248Sbostic 28934248Sbostic expcnt = 0; 290*34314Sbostic p = endp - 1; 29134248Sbostic fract = modf(number, &integer); 29234248Sbostic if (integer) { 29334248Sbostic register char *p2; 29434248Sbostic 29534248Sbostic /* get integer part of number; count decimal places */ 29634248Sbostic for (; integer; ++expcnt) { 29734248Sbostic tmp = modf(integer / 10, &integer); 29834248Sbostic *p-- = (int)((tmp + .03) * 10) + '0'; 29934242Sbostic } 30034248Sbostic 30134248Sbostic /* copy, in reverse order, to start of buffer */ 30234248Sbostic p2 = startp; 30334248Sbostic *p2++ = *++p; 30434248Sbostic 30534248Sbostic /* 30634248Sbostic * if the format is g/G, and the resulting exponent will be 30734248Sbostic * greater than the precision, use e/E format. If e/E format, 30834248Sbostic * put in a decimal point as needed, and decrement precision 30934248Sbostic * count for each digit after the decimal point. 31034248Sbostic */ 31134248Sbostic if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 31234248Sbostic if (format&GFORMAT) { 31334248Sbostic format |= EFORMAT; 31434248Sbostic 31534248Sbostic /* first digit is precision for g/G format */ 31634248Sbostic if (prec) 31734248Sbostic --prec; 31834248Sbostic } 31934248Sbostic if (decpt) 32034248Sbostic *p2++ = '.'; 32134248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 32234248Sbostic 32334248Sbostic /* precision ran out; round number */ 32434248Sbostic if (p < endp) { 32534248Sbostic if (*p > '4') { 32634248Sbostic for (savep = p2--;; *p2-- = '0') { 32734248Sbostic if (*p2 == '.') 32834248Sbostic --p2; 32934248Sbostic if (++*p2 <= '9') 33034248Sbostic break; 33134248Sbostic } 33234248Sbostic p2 = savep; 33334248Sbostic } 33434248Sbostic fract = 0; 33534248Sbostic } 33634242Sbostic } 33734248Sbostic /* 338*34314Sbostic * g/G in f format; if out of precision, replace digits with 339*34314Sbostic * zeroes, note, have to round first. 34034248Sbostic */ 34134248Sbostic else if (format&GFORMAT) { 34234248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 34334248Sbostic /* precision ran out; round and then add zeroes */ 34434248Sbostic if (p < endp) { 34534248Sbostic if (*p > '4') { 34634248Sbostic for (savep = p2--; ++*p2 > '9'; 34734248Sbostic *p2-- = '0'); 34834248Sbostic p2 = savep; 34934248Sbostic } 35034248Sbostic do { 35134248Sbostic *p2++ = '0'; 35234248Sbostic } while (++p < endp); 35334248Sbostic fract = 0; 35434248Sbostic } 35534248Sbostic if (decpt) 35634248Sbostic *p2++ = '.'; 35734242Sbostic } 35834248Sbostic /* f format */ 35934248Sbostic else { 36034248Sbostic for (; ++p < endp; *p2++ = *p); 36134248Sbostic if (decpt) 36234248Sbostic *p2++ = '.'; 36334248Sbostic } 36434248Sbostic p = p2; 36534248Sbostic } 36634248Sbostic /* 36734248Sbostic * it's unclear from the ANSI X3J11 spec if the g/G format should 36834248Sbostic * just result in an empty string, because it's supposed to remove 36934248Sbostic * trailing zeroes. That seems counter-intuitive, so here it does 37034248Sbostic * what f and e/E do; if no fraction, the number was zero, and if 371*34314Sbostic * no precision, can't show anything after the decimal point. 37234248Sbostic */ 37334248Sbostic else if (!fract || !prec) { 37434248Sbostic *startp++ = '0'; 37534248Sbostic if (decpt) 37634248Sbostic *startp++ = '.'; 37734248Sbostic *startp++ = '\0'; 37834248Sbostic return(startp); 37934248Sbostic } 38034248Sbostic /* 38134248Sbostic * if the format is g/G, and the resulting exponent will be less than 38234248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 38334248Sbostic */ 38434248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 38534248Sbostic format |= EFORMAT; 38634248Sbostic if (fract) 38734248Sbostic for (p = startp; fract;) { 38834248Sbostic fract = modf(fract * 10, &tmp); 38934248Sbostic if (!tmp) { 39034248Sbostic --expcnt; 39134248Sbostic continue; 39234248Sbostic } 39334248Sbostic *p++ = (int)tmp + '0'; 39434248Sbostic break; 39534248Sbostic } 39634242Sbostic else 39734248Sbostic *p++ = '0'; 39834248Sbostic 39934248Sbostic /* g/G format, decrement precision for first digit */ 40034248Sbostic if (format&GFORMAT && prec) 40134248Sbostic --prec; 40234248Sbostic 40334248Sbostic /* add decimal after first non-zero digit */ 40434248Sbostic if (decpt) 40534248Sbostic *p++ = '.'; 40634242Sbostic } 40734248Sbostic /* 40834248Sbostic * f format or g/G printed as f format; don't worry about decimal 40934248Sbostic * point, if g/G format doesn't need it, will get stripped later. 41034248Sbostic */ 41134242Sbostic else { 41234248Sbostic p = startp; 41334248Sbostic *p++ = '0'; 41434248Sbostic *p++ = '.'; 41534248Sbostic } 41634248Sbostic 41734248Sbostic /* finish out requested precision from fractional value */ 41834248Sbostic while (prec--) 41934248Sbostic if (fract) { 42034248Sbostic fract = modf(fract * 10, &tmp); 42134248Sbostic *p++ = (int)tmp + '0'; 42234248Sbostic } 42334248Sbostic else 42434248Sbostic *p++ = '0'; 42534248Sbostic 42634248Sbostic /* 42734248Sbostic * if any fractional value left, "round" it back up to the beginning 42834248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 42934248Sbostic * decimal point. 43034248Sbostic */ 43134248Sbostic if (fract) { 43234248Sbostic (void)modf(fract * 10, &tmp); 43334248Sbostic if (tmp > 4) { 43434248Sbostic for (savep = p--;; *p-- = '0') { 43534248Sbostic if (*p == '.') 43634248Sbostic --p; 43734248Sbostic if (p == startp) { 43834248Sbostic *p = '1'; 43934248Sbostic ++expcnt; 44034248Sbostic break; 44134248Sbostic } 44234248Sbostic if (++*p <= '9') 44334248Sbostic break; 44434242Sbostic } 44534248Sbostic p = savep; 44634242Sbostic } 44734248Sbostic } 44834248Sbostic 44934248Sbostic /* 45034248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 45134248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 45234248Sbostic */ 453*34314Sbostic if (!alt) { 45434248Sbostic if (format&GFORMAT) 45534248Sbostic for (; p[-1] == '0'; --p); 45634248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 45734248Sbostic --p; 45834248Sbostic } 45934248Sbostic 46034248Sbostic /* if an e/E format, add exponent */ 46134248Sbostic if (format&EFORMAT) { 46234248Sbostic *p++ = fmtch; 46334248Sbostic if (--expcnt < 0) { 46434248Sbostic expcnt = -expcnt; 46534248Sbostic *p++ = '-'; 46634242Sbostic } 46734248Sbostic else 46834248Sbostic *p++ = '+'; 46934248Sbostic *p++ = expcnt / 10 + '0'; 47034248Sbostic *p++ = expcnt % 10 + '0'; 47134242Sbostic } 47234248Sbostic return(p); 47334242Sbostic } 474