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*34263Sbostic static char sccsid[] = "@(#)vfprintf.c 5.10 (Berkeley) 05/11/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 2234261Sbostic #define MAXBUF 120 2334261Sbostic #define DEFPREC 6 2434226Sbostic 25*34263Sbostic #define PUTC(ch) {++cnt; putc(ch, fp);} 2634236Sbostic 2734235Sbostic #define LONGINT 0x01 2834235Sbostic #define LONGDBL 0x02 2934235Sbostic #define SHORTINT 0x04 3034241Sbostic #define GETARG(r) \ 3134241Sbostic r = argsize&LONGINT ? va_arg(argp, long) : \ 3234241Sbostic argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 3334235Sbostic 3434248Sbostic static int alternate; 3534248Sbostic static char printsign; 3634248Sbostic 3734235Sbostic x_doprnt(fmt, argp, fp) 3834233Sbostic register char *fmt; 3934233Sbostic va_list argp; 4034235Sbostic register FILE *fp; 4134226Sbostic { 42*34263Sbostic register int base, cnt; 43*34263Sbostic register char *bp, *t; 4434233Sbostic register u_long reg_ulong; 4534233Sbostic register long reg_long; 4634235Sbostic double _double; 47*34263Sbostic char argsize, padc, *_cvt(), *digs, buf[MAXBUF]; 48*34263Sbostic int n, ladjust, width, prec, size; 4934226Sbostic 5034243Sbostic digs = "0123456789abcdef"; 5134233Sbostic for (cnt = 0; *fmt; ++fmt) { 5234233Sbostic if (*fmt != '%') { 53*34263Sbostic PUTC(*fmt); 5434233Sbostic continue; 5534226Sbostic } 5634226Sbostic 5734235Sbostic alternate = ladjust = width = 0; 5834233Sbostic prec = -1; 5934233Sbostic padc = ' '; 6034235Sbostic argsize = printsign = '\0'; 6134226Sbostic 6234233Sbostic flags: switch (*++fmt) { 6334233Sbostic case '#': 6434233Sbostic alternate = 1; 6534233Sbostic goto flags; 6634233Sbostic case '*': 6734235Sbostic /* 6834235Sbostic * ``A negative field width argument is taken as a 6934235Sbostic * - flag followed by a positive field width.'' 7034235Sbostic * -- ANSI X3J11 7134235Sbostic * They don't exclude field widths read from args. 7234235Sbostic */ 7334235Sbostic if ((width = va_arg(argp, int)) >= 0) 7434235Sbostic goto flags; 7534235Sbostic width = -width; 7634235Sbostic /*FALLTHROUGH*/ 7734235Sbostic case '-': 7834235Sbostic ladjust = 1; 7934233Sbostic goto flags; 8034233Sbostic case '+': 8134233Sbostic printsign = '+'; 8234233Sbostic goto flags; 8334233Sbostic case '.': 8434235Sbostic if (*++fmt == '*') 8534235Sbostic prec = va_arg(argp, int); 8634235Sbostic else if (isdigit(*fmt)) { 8734235Sbostic prec = 0; 8834233Sbostic do { 8934236Sbostic prec = 10 * prec + *fmt - '0'; 9034233Sbostic } while isdigit(*++fmt); 9134233Sbostic --fmt; 9234226Sbostic } 9334235Sbostic else { 9434235Sbostic prec = 0; 9534235Sbostic --fmt; 9634235Sbostic goto flags; 9734235Sbostic } 9834235Sbostic if (prec < 0) 9934235Sbostic prec = -1; 10034233Sbostic goto flags; 10134233Sbostic case '0': 10234233Sbostic padc = '0'; 10334235Sbostic /*FALLTHROUGH*/ 10434233Sbostic case '1': case '2': case '3': case '4': 10534233Sbostic case '5': case '6': case '7': case '8': case '9': 10634233Sbostic do { 10734236Sbostic width = 10 * width + *fmt - '0'; 10834233Sbostic } while isdigit(*++fmt); 10934233Sbostic --fmt; 11034235Sbostic case 'L': 11134235Sbostic argsize |= LONGDBL; 11234235Sbostic goto flags; 11334235Sbostic case 'h': 11434235Sbostic argsize |= SHORTINT; 11534235Sbostic goto flags; 11634233Sbostic case 'l': 11734235Sbostic argsize |= LONGINT; 11834233Sbostic goto flags; 11934243Sbostic case 'c': { 12034243Sbostic char ch; 12134226Sbostic 12234243Sbostic ch = va_arg(argp, int); 123*34263Sbostic PUTC(ch); 12434226Sbostic break; 12534243Sbostic } 12634241Sbostic case 'd': 12734241Sbostic case 'i': 12834241Sbostic GETARG(reg_long); 12934241Sbostic if (reg_long < 0) { 13034241Sbostic reg_ulong = -reg_long; 13134241Sbostic printsign = '-'; 13234241Sbostic } 13334241Sbostic else { 13434241Sbostic reg_ulong = reg_long; 13534241Sbostic } 13634241Sbostic if (printsign) 137*34263Sbostic PUTC(printsign); 13834241Sbostic base = 10; 13934241Sbostic goto num1; 14034261Sbostic case 'e': 14134236Sbostic case 'E': 14234235Sbostic case 'f': 14334261Sbostic case 'g': 14434243Sbostic case 'G': 14534243Sbostic _double = va_arg(argp, double); 14634261Sbostic bp = _cvt(_double, prec, buf, buf + sizeof(buf), *fmt); 14734236Sbostic pbuf: size = bp - buf; 14834235Sbostic if (size < width && !ladjust) 14934235Sbostic do { 150*34263Sbostic PUTC(padc); 15134235Sbostic } while (--width > size); 15234235Sbostic for (t = buf; t < bp; ++t) 153*34263Sbostic PUTC(*t); 15434235Sbostic for (; width > size; --width) 155*34263Sbostic PUTC(padc); 15634235Sbostic break; 15734235Sbostic case 'n': 15834235Sbostic *(va_arg(argp, int *)) = cnt; 15934235Sbostic break; 16034226Sbostic case 'o': 16134235Sbostic GETARG(reg_ulong); 16234226Sbostic base = 8; 16334235Sbostic if (!reg_ulong || !alternate) 16434235Sbostic goto num1; 16534235Sbostic bp = buf + sizeof(buf) - 1; 16634235Sbostic do { 16734235Sbostic *bp-- = digs[reg_ulong % base]; 16834235Sbostic reg_ulong /= base; 16934235Sbostic } while(reg_ulong); 17034235Sbostic size = &buf[sizeof(buf) - 1] - bp; 17134235Sbostic if (size < --width && !ladjust) 17234235Sbostic do { 173*34263Sbostic PUTC(padc); 17434235Sbostic } while (--width > size); 175*34263Sbostic PUTC('0'); 17634236Sbostic goto num2; 17734235Sbostic case 'p': 17834226Sbostic case 's': 17934235Sbostic if (!(bp = va_arg(argp, char *))) 18034235Sbostic bp = "(null)"; 18134235Sbostic if (width > 0 && !ladjust) { 18234233Sbostic char *savep; 18334226Sbostic 18434235Sbostic savep = bp; 18534235Sbostic for (n = 0; *bp && (prec < 0 || n < prec); 18634235Sbostic n++, bp++); 18734235Sbostic bp = savep; 18834235Sbostic while (n++ < width) 189*34263Sbostic PUTC(' '); 19034233Sbostic } 19134235Sbostic for (n = 0; *bp; ++bp) { 19234235Sbostic if (++n > prec && prec >= 0) 19334226Sbostic break; 194*34263Sbostic PUTC(*bp); 19534233Sbostic } 19634235Sbostic if (n < width && ladjust) 19734233Sbostic do { 198*34263Sbostic PUTC(' '); 19934235Sbostic } while (++n < width); 20034226Sbostic break; 20134226Sbostic case 'u': 20234235Sbostic GETARG(reg_ulong); 20334226Sbostic base = 10; 20434235Sbostic goto num1; 20534226Sbostic case 'X': 20634226Sbostic digs = "0123456789ABCDEF"; 20734233Sbostic /*FALLTHROUGH*/ 20834226Sbostic case 'x': 20934235Sbostic GETARG(reg_ulong); 21034233Sbostic if (alternate && reg_ulong) { 211*34263Sbostic PUTC('0'); 212*34263Sbostic PUTC(*fmt); 21334233Sbostic } 21434226Sbostic base = 16; 21534235Sbostic num1: bp = buf + sizeof(buf) - 1; 21634233Sbostic do { 21734235Sbostic *bp-- = digs[reg_ulong % base]; 21834233Sbostic reg_ulong /= base; 21934233Sbostic } while(reg_ulong); 22034235Sbostic size = &buf[sizeof(buf) - 1] - bp; 22134235Sbostic for (; size < prec; *bp-- = '0', ++size); 22234235Sbostic if (size < width && !ladjust) 22334235Sbostic do { 224*34263Sbostic PUTC(padc); 22534235Sbostic } while (--width > size); 22634236Sbostic num2: while (++bp != &buf[MAXBUF]) 227*34263Sbostic PUTC(*bp); 22834235Sbostic for (; width > size; --width) 229*34263Sbostic PUTC(padc); 23034243Sbostic digs = "0123456789abcdef"; 23134226Sbostic break; 23234233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 23334235Sbostic return(ferror(fp) ? -1 : cnt); 23434226Sbostic default: 235*34263Sbostic PUTC(*fmt); 23634226Sbostic } 23734226Sbostic } 23834235Sbostic return(ferror(fp) ? -1 : cnt); 23934226Sbostic } 24034242Sbostic 24134261Sbostic #define EFORMAT 0x01 24234261Sbostic #define FFORMAT 0x02 24334261Sbostic #define GFORMAT 0x04 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 25734248Sbostic if (prec == -1) /* set default precision */ 25834242Sbostic prec = DEFPREC; 25934242Sbostic 26034248Sbostic p = endp - 1; 26134248Sbostic if (number < 0) { /* set sign */ 26234248Sbostic *startp++ = '-'; 26334248Sbostic number = -number; 26434248Sbostic } 26534242Sbostic else if (printsign) 26634248Sbostic *startp++ = '+'; 26734242Sbostic 26834261Sbostic switch(fmtch) { 26934261Sbostic case 'e': 27034261Sbostic case 'E': 27134261Sbostic format = EFORMAT; 27234261Sbostic break; 27334261Sbostic case 'f': 27434261Sbostic format = FFORMAT; 27534261Sbostic break; 27634261Sbostic case 'g': 27734261Sbostic case 'G': 27834261Sbostic format = GFORMAT; 27934261Sbostic fmtch -= 2; 28034261Sbostic } 28134261Sbostic 28234248Sbostic /* 28334248Sbostic * if the alternate flag is set, or, at least one digit of precision 28434248Sbostic * was requested, add a decimal point, unless it's the g/G format 28534248Sbostic * in which case we require two digits of precision, since it counts 28634248Sbostic * precision differently. 28734248Sbostic */ 28834248Sbostic decpt = alternate || prec > 1 || !(format&GFORMAT) && prec; 28934248Sbostic 29034248Sbostic expcnt = 0; 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 /* 33834248Sbostic * g/G in f format; if run out of precision, replace digits 33934248Sbostic * with zeroes, note, have to round first, otherwise lose 34034248Sbostic * rounding point. 34134248Sbostic */ 34234248Sbostic else if (format&GFORMAT) { 34334248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 34434248Sbostic /* precision ran out; round and then add zeroes */ 34534248Sbostic if (p < endp) { 34634248Sbostic if (*p > '4') { 34734248Sbostic for (savep = p2--; ++*p2 > '9'; 34834248Sbostic *p2-- = '0'); 34934248Sbostic p2 = savep; 35034248Sbostic } 35134248Sbostic do { 35234248Sbostic *p2++ = '0'; 35334248Sbostic } while (++p < endp); 35434248Sbostic fract = 0; 35534248Sbostic } 35634248Sbostic if (decpt) 35734248Sbostic *p2++ = '.'; 35834242Sbostic } 35934248Sbostic /* f format */ 36034248Sbostic else { 36134248Sbostic for (; ++p < endp; *p2++ = *p); 36234248Sbostic if (decpt) 36334248Sbostic *p2++ = '.'; 36434248Sbostic } 36534248Sbostic p = p2; 36634248Sbostic } 36734248Sbostic /* 36834248Sbostic * it's unclear from the ANSI X3J11 spec if the g/G format should 36934248Sbostic * just result in an empty string, because it's supposed to remove 37034248Sbostic * trailing zeroes. That seems counter-intuitive, so here it does 37134248Sbostic * what f and e/E do; if no fraction, the number was zero, and if 37234248Sbostic * no precision can't show anything after the decimal point. 37334248Sbostic */ 37434248Sbostic else if (!fract || !prec) { 37534248Sbostic *startp++ = '0'; 37634248Sbostic if (decpt) 37734248Sbostic *startp++ = '.'; 37834248Sbostic *startp++ = '\0'; 37934248Sbostic return(startp); 38034248Sbostic } 38134248Sbostic /* 38234248Sbostic * if the format is g/G, and the resulting exponent will be less than 38334248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 38434248Sbostic */ 38534248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 38634248Sbostic format |= EFORMAT; 38734248Sbostic if (fract) 38834248Sbostic for (p = startp; fract;) { 38934248Sbostic fract = modf(fract * 10, &tmp); 39034248Sbostic if (!tmp) { 39134248Sbostic --expcnt; 39234248Sbostic continue; 39334248Sbostic } 39434248Sbostic *p++ = (int)tmp + '0'; 39534248Sbostic break; 39634248Sbostic } 39734242Sbostic else 39834248Sbostic *p++ = '0'; 39934248Sbostic 40034248Sbostic /* g/G format, decrement precision for first digit */ 40134248Sbostic if (format&GFORMAT && prec) 40234248Sbostic --prec; 40334248Sbostic 40434248Sbostic /* add decimal after first non-zero digit */ 40534248Sbostic if (decpt) 40634248Sbostic *p++ = '.'; 40734242Sbostic } 40834248Sbostic /* 40934248Sbostic * f format or g/G printed as f format; don't worry about decimal 41034248Sbostic * point, if g/G format doesn't need it, will get stripped later. 41134248Sbostic */ 41234242Sbostic else { 41334248Sbostic p = startp; 41434248Sbostic *p++ = '0'; 41534248Sbostic *p++ = '.'; 41634248Sbostic } 41734248Sbostic 41834248Sbostic /* finish out requested precision from fractional value */ 41934248Sbostic while (prec--) 42034248Sbostic if (fract) { 42134248Sbostic fract = modf(fract * 10, &tmp); 42234248Sbostic *p++ = (int)tmp + '0'; 42334248Sbostic } 42434248Sbostic else 42534248Sbostic *p++ = '0'; 42634248Sbostic 42734248Sbostic /* 42834248Sbostic * if any fractional value left, "round" it back up to the beginning 42934248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 43034248Sbostic * decimal point. 43134248Sbostic */ 43234248Sbostic if (fract) { 43334248Sbostic (void)modf(fract * 10, &tmp); 43434248Sbostic if (tmp > 4) { 43534248Sbostic for (savep = p--;; *p-- = '0') { 43634248Sbostic if (*p == '.') 43734248Sbostic --p; 43834248Sbostic if (p == startp) { 43934248Sbostic *p = '1'; 44034248Sbostic ++expcnt; 44134248Sbostic break; 44234248Sbostic } 44334248Sbostic if (++*p <= '9') 44434248Sbostic break; 44534242Sbostic } 44634248Sbostic p = savep; 44734242Sbostic } 44834248Sbostic } 44934248Sbostic 45034248Sbostic /* 45134248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 45234248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 45334248Sbostic */ 45434248Sbostic if (!alternate) { 45534248Sbostic if (format&GFORMAT) 45634248Sbostic for (; p[-1] == '0'; --p); 45734248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 45834248Sbostic --p; 45934248Sbostic } 46034248Sbostic 46134248Sbostic /* if an e/E format, add exponent */ 46234248Sbostic if (format&EFORMAT) { 46334248Sbostic *p++ = fmtch; 46434248Sbostic if (--expcnt < 0) { 46534248Sbostic expcnt = -expcnt; 46634248Sbostic *p++ = '-'; 46734242Sbostic } 46834248Sbostic else 46934248Sbostic *p++ = '+'; 47034248Sbostic *p++ = expcnt / 10 + '0'; 47134248Sbostic *p++ = expcnt % 10 + '0'; 47234242Sbostic } 47334248Sbostic return(p); 47434242Sbostic } 475