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*34318Sbostic static char sccsid[] = "@(#)vfprintf.c 5.14 (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 2234314Sbostic #define MAXBUF 40 2334226Sbostic 2434263Sbostic #define PUTC(ch) {++cnt; putc(ch, fp);} 2534236Sbostic 26*34318Sbostic #define ARG() \ 27*34318Sbostic _ulong = flags&LONGINT ? va_arg(argp, long) : \ 28*34318Sbostic flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 2934235Sbostic 30*34318Sbostic /* have to deal with the negative buffer count kludge */ 31*34318Sbostic #define NEGATIVE_COUNT_KLUDGE 32*34318Sbostic 33*34318Sbostic #define LONGINT 0x01 /* long integer */ 34*34318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 35*34318Sbostic #define SHORTINT 0x04 /* short integer */ 36*34318Sbostic #define ALT 0x08 /* alternate form */ 37*34318Sbostic #define LADJUST 0x10 /* left adjustment */ 38*34318Sbostic static int flags; 39*34318Sbostic 4034314Sbostic static char sign; 4134248Sbostic 4234235Sbostic x_doprnt(fmt, argp, fp) 4334233Sbostic register char *fmt; 4434233Sbostic va_list argp; 4534235Sbostic register FILE *fp; 4634226Sbostic { 4734314Sbostic register int cnt, n; 4834314Sbostic register char ch, *t; 4934235Sbostic double _double; 5034314Sbostic u_long _ulong; 51*34318Sbostic int base, width, prec, size; 52*34318Sbostic char padc, *digs, *_cvt(), buf[MAXBUF]; 5334226Sbostic 5434243Sbostic digs = "0123456789abcdef"; 5534314Sbostic for (cnt = 0;; ++fmt) { 56*34318Sbostic n = fp->_cnt; 57*34318Sbostic for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) 58*34318Sbostic if (--n < 0 59*34318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE 60*34318Sbostic && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 61*34318Sbostic #endif 62*34318Sbostic || ch == '\n' && fp->_flag&_IOLBF) { 63*34318Sbostic fp->_cnt = n; 64*34318Sbostic fp->_ptr = t; 65*34318Sbostic (void)_flsbuf(ch, fp); 66*34318Sbostic n = fp->_cnt; 67*34318Sbostic t = fp->_ptr; 68*34318Sbostic } 69*34318Sbostic else 7034314Sbostic *t++ = ch; 71*34318Sbostic fp->_cnt = n; 72*34318Sbostic fp->_ptr = t; 73*34318Sbostic if (!ch) 7434314Sbostic return(cnt); 7534314Sbostic 76*34318Sbostic flags = width = 0; 7734233Sbostic prec = -1; 7834233Sbostic padc = ' '; 79*34318Sbostic sign = '\0'; 8034314Sbostic t = buf; 8134226Sbostic 82*34318Sbostic rflag: switch (*++fmt) { 83*34318Sbostic case ' ': 84*34318Sbostic sign = ' '; 85*34318Sbostic goto rflag; 8634233Sbostic case '#': 87*34318Sbostic flags |= ALT; 88*34318Sbostic goto rflag; 8934233Sbostic case '*': 9034235Sbostic /* 9134235Sbostic * ``A negative field width argument is taken as a 9234235Sbostic * - flag followed by a positive field width.'' 9334235Sbostic * -- ANSI X3J11 9434235Sbostic * They don't exclude field widths read from args. 9534235Sbostic */ 9634235Sbostic if ((width = va_arg(argp, int)) >= 0) 97*34318Sbostic goto rflag; 9834235Sbostic width = -width; 9934235Sbostic /*FALLTHROUGH*/ 10034235Sbostic case '-': 101*34318Sbostic flags |= LADJUST; 102*34318Sbostic goto rflag; 10334233Sbostic case '+': 10434314Sbostic sign = '+'; 105*34318Sbostic goto rflag; 10634233Sbostic case '.': 10734235Sbostic if (*++fmt == '*') 108*34318Sbostic n = va_arg(argp, int); 109*34318Sbostic else if (isascii(*fmt) && isdigit(*fmt)) { 110*34318Sbostic n = 0; 11134233Sbostic do { 112*34318Sbostic n = 10 * n + *fmt - '0'; 113*34318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 11434233Sbostic --fmt; 11534226Sbostic } 11634235Sbostic else { 117*34318Sbostic --fmt; 11834235Sbostic prec = 0; 119*34318Sbostic goto rflag; 12034235Sbostic } 121*34318Sbostic prec = n < 0 ? -1 : n; 122*34318Sbostic goto rflag; 12334233Sbostic case '0': 12434233Sbostic padc = '0'; 12534235Sbostic /*FALLTHROUGH*/ 12634233Sbostic case '1': case '2': case '3': case '4': 12734233Sbostic case '5': case '6': case '7': case '8': case '9': 128*34318Sbostic n = 0; 12934233Sbostic do { 130*34318Sbostic n = 10 * n + *fmt - '0'; 131*34318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 132*34318Sbostic width = n; 13334233Sbostic --fmt; 13434235Sbostic case 'L': 135*34318Sbostic /* 136*34318Sbostic * C doesn't have a long double; use long for now. 137*34318Sbostic * flags |= LONGDBL; 138*34318Sbostic */ 139*34318Sbostic flags |= LONGINT; 140*34318Sbostic goto rflag; 14134235Sbostic case 'h': 142*34318Sbostic flags |= SHORTINT; 143*34318Sbostic goto rflag; 14434233Sbostic case 'l': 145*34318Sbostic flags |= LONGINT; 146*34318Sbostic goto rflag; 14734314Sbostic case 'c': 14834314Sbostic *t = va_arg(argp, int); 14934314Sbostic size = 1; 15034314Sbostic goto pforw; 15134314Sbostic case 'd': 152*34318Sbostic case 'i': 153*34318Sbostic ARG(); 154*34318Sbostic if ((long)_ulong < 0) { 155*34318Sbostic _ulong = -_ulong; 15634314Sbostic sign = '-'; 15734241Sbostic } 15834314Sbostic if (sign) 15934314Sbostic PUTC(sign); 16034241Sbostic base = 10; 16134314Sbostic goto num; 16234261Sbostic case 'e': 16334236Sbostic case 'E': 16434235Sbostic case 'f': 16534261Sbostic case 'g': 16634243Sbostic case 'G': 16734243Sbostic _double = va_arg(argp, double); 16834314Sbostic size = _cvt(_double, prec, buf, buf + sizeof(buf), 16934314Sbostic *fmt) - buf; 17034314Sbostic goto pforw; 17134235Sbostic case 'n': 172*34318Sbostic if (flags&LONGDBL || flags&LONGINT) 173*34318Sbostic *va_arg(argp, long *) = cnt; 174*34318Sbostic else if (flags&SHORTINT) 175*34318Sbostic *va_arg(argp, short *) = cnt; 176*34318Sbostic else 177*34318Sbostic *va_arg(argp, int *) = cnt; 17834235Sbostic break; 17934226Sbostic case 'o': 180*34318Sbostic ARG(); 18134226Sbostic base = 8; 18234314Sbostic goto num; 18334235Sbostic case 'p': 18434226Sbostic case 's': 18534314Sbostic if (!(t = va_arg(argp, char *))) 18634314Sbostic t = "(null)"; 18734316Sbostic if ((size = strlen(t)) > prec && prec >= 0) 18834314Sbostic size = prec; 189*34318Sbostic pforw: if (!(flags&LADJUST) && width) 19034314Sbostic for (n = size; n++ < width;) 19134314Sbostic PUTC(padc); 19234314Sbostic if (fp->_cnt - (n = size) >= 0) { 19334314Sbostic cnt += n; 19434314Sbostic fp->_cnt -= n; 19534314Sbostic bcopy(t, fp->_ptr, n); 19634314Sbostic fp->_ptr += n; 19734233Sbostic } 19834314Sbostic else for (; n--; ++t) 19934314Sbostic PUTC(*t); 200*34318Sbostic if (flags&LADJUST) 20134314Sbostic while (width-- > size) 20234314Sbostic PUTC(padc); 20334226Sbostic break; 20434226Sbostic case 'u': 205*34318Sbostic ARG(); 20634226Sbostic base = 10; 20734314Sbostic goto num; 20834226Sbostic case 'X': 20934226Sbostic digs = "0123456789ABCDEF"; 21034233Sbostic /*FALLTHROUGH*/ 21134226Sbostic case 'x': 212*34318Sbostic ARG(); 21334314Sbostic base = 16; 21434314Sbostic /* alternate form for hex; leading 0x/X */ 215*34318Sbostic if (flags&ALT && _ulong) { 21634263Sbostic PUTC('0'); 21734263Sbostic PUTC(*fmt); 21834233Sbostic } 21934314Sbostic num: t = buf + sizeof(buf) - 1; 22034233Sbostic do { 22134314Sbostic *t-- = digs[_ulong % base]; 22234314Sbostic _ulong /= base; 22334314Sbostic } while(_ulong); 22434314Sbostic digs = "0123456789abcdef"; 22534314Sbostic size = buf + sizeof(buf) - 1 - t; 22634314Sbostic if (size >= prec) { 22734314Sbostic /* alternate form for octal; leading 0 */ 228*34318Sbostic if (t[1] != '0' && flags&ALT && *fmt == 'o') { 22934314Sbostic *t-- = '0'; 23034314Sbostic ++size; 23134314Sbostic } 23234314Sbostic } 23334314Sbostic else 23434314Sbostic for (; size < prec; ++size) 23534314Sbostic *t-- = '0'; 236*34318Sbostic if (!(flags&LADJUST)) 23734314Sbostic while (size++ < width) 23834263Sbostic PUTC(padc); 23934314Sbostic while (++t < buf + sizeof(buf)) 24034314Sbostic PUTC(*t); 24134235Sbostic for (; width > size; --width) 24234263Sbostic PUTC(padc); 24334226Sbostic break; 24434233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 24534314Sbostic return(cnt); 24634226Sbostic default: 24734263Sbostic PUTC(*fmt); 24834226Sbostic } 24934226Sbostic } 25034314Sbostic /*NOTREACHED*/ 25134226Sbostic } 25234242Sbostic 25334261Sbostic #define EFORMAT 0x01 25434261Sbostic #define FFORMAT 0x02 25534261Sbostic #define GFORMAT 0x04 25634314Sbostic #define DEFPREC 6 25734261Sbostic 25834261Sbostic static char * 25934261Sbostic _cvt(number, prec, startp, endp, fmtch) 26034242Sbostic double number; 26134261Sbostic register int prec; 26234248Sbostic char *startp, *endp, fmtch; 26334242Sbostic { 26434248Sbostic register char *p; 26534261Sbostic register int expcnt, format; 26634248Sbostic double fract, integer, tmp, modf(); 26734261Sbostic int decpt; 26834248Sbostic char *savep; 26934242Sbostic 27034314Sbostic if (prec == -1) 27134242Sbostic prec = DEFPREC; 27234242Sbostic 27334314Sbostic if (number < 0) { 27434248Sbostic *startp++ = '-'; 27534248Sbostic number = -number; 27634248Sbostic } 27734314Sbostic else if (sign) 278*34318Sbostic *startp++ = sign; 27934242Sbostic 28034261Sbostic switch(fmtch) { 28134261Sbostic case 'e': 28234261Sbostic case 'E': 28334261Sbostic format = EFORMAT; 28434261Sbostic break; 28534261Sbostic case 'f': 28634261Sbostic format = FFORMAT; 28734261Sbostic break; 28834261Sbostic case 'g': 28934261Sbostic case 'G': 29034261Sbostic format = GFORMAT; 29134261Sbostic fmtch -= 2; 29234261Sbostic } 29334261Sbostic 29434248Sbostic /* 29534248Sbostic * if the alternate flag is set, or, at least one digit of precision 29634248Sbostic * was requested, add a decimal point, unless it's the g/G format 29734314Sbostic * in which case we require two digits of precision, as it counts 29834248Sbostic * precision differently. 29934248Sbostic */ 300*34318Sbostic decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 30134248Sbostic 30234248Sbostic expcnt = 0; 30334314Sbostic p = endp - 1; 30434248Sbostic fract = modf(number, &integer); 30534248Sbostic if (integer) { 30634248Sbostic register char *p2; 30734248Sbostic 30834248Sbostic /* get integer part of number; count decimal places */ 30934248Sbostic for (; integer; ++expcnt) { 31034248Sbostic tmp = modf(integer / 10, &integer); 31134248Sbostic *p-- = (int)((tmp + .03) * 10) + '0'; 31234242Sbostic } 31334248Sbostic 31434248Sbostic /* copy, in reverse order, to start of buffer */ 31534248Sbostic p2 = startp; 31634248Sbostic *p2++ = *++p; 31734248Sbostic 31834248Sbostic /* 31934248Sbostic * if the format is g/G, and the resulting exponent will be 32034248Sbostic * greater than the precision, use e/E format. If e/E format, 32134248Sbostic * put in a decimal point as needed, and decrement precision 32234248Sbostic * count for each digit after the decimal point. 32334248Sbostic */ 32434248Sbostic if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 32534248Sbostic if (format&GFORMAT) { 32634248Sbostic format |= EFORMAT; 32734248Sbostic 32834248Sbostic /* first digit is precision for g/G format */ 32934248Sbostic if (prec) 33034248Sbostic --prec; 33134248Sbostic } 33234248Sbostic if (decpt) 33334248Sbostic *p2++ = '.'; 33434248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 33534248Sbostic 336*34318Sbostic /* precision ran out, round */ 33734248Sbostic if (p < endp) { 33834248Sbostic if (*p > '4') { 33934248Sbostic for (savep = p2--;; *p2-- = '0') { 34034248Sbostic if (*p2 == '.') 34134248Sbostic --p2; 34234248Sbostic if (++*p2 <= '9') 34334248Sbostic break; 34434248Sbostic } 34534248Sbostic p2 = savep; 34634248Sbostic } 34734248Sbostic fract = 0; 34834248Sbostic } 34934242Sbostic } 35034248Sbostic /* 35134314Sbostic * g/G in f format; if out of precision, replace digits with 35234314Sbostic * zeroes, note, have to round first. 35334248Sbostic */ 35434248Sbostic else if (format&GFORMAT) { 35534248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 35634248Sbostic /* precision ran out; round and then add zeroes */ 35734248Sbostic if (p < endp) { 35834248Sbostic if (*p > '4') { 35934248Sbostic for (savep = p2--; ++*p2 > '9'; 36034248Sbostic *p2-- = '0'); 36134248Sbostic p2 = savep; 36234248Sbostic } 36334248Sbostic do { 36434248Sbostic *p2++ = '0'; 36534248Sbostic } while (++p < endp); 36634248Sbostic fract = 0; 36734248Sbostic } 36834248Sbostic if (decpt) 36934248Sbostic *p2++ = '.'; 37034242Sbostic } 37134248Sbostic /* f format */ 37234248Sbostic else { 37334248Sbostic for (; ++p < endp; *p2++ = *p); 37434248Sbostic if (decpt) 37534248Sbostic *p2++ = '.'; 37634248Sbostic } 37734248Sbostic p = p2; 37834248Sbostic } 37934248Sbostic /* 380*34318Sbostic * if no fraction, the number was zero, and if no precision, can't 381*34318Sbostic * show anything after the decimal point. 38234248Sbostic */ 38334248Sbostic else if (!fract || !prec) { 38434248Sbostic *startp++ = '0'; 385*34318Sbostic if (decpt && !(format&GFORMAT)) 38634248Sbostic *startp++ = '.'; 387*34318Sbostic *startp = '\0'; 38834248Sbostic return(startp); 38934248Sbostic } 39034248Sbostic /* 39134248Sbostic * if the format is g/G, and the resulting exponent will be less than 39234248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 39334248Sbostic */ 39434248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 39534248Sbostic format |= EFORMAT; 39634248Sbostic if (fract) 39734248Sbostic for (p = startp; fract;) { 39834248Sbostic fract = modf(fract * 10, &tmp); 39934248Sbostic if (!tmp) { 40034248Sbostic --expcnt; 40134248Sbostic continue; 40234248Sbostic } 40334248Sbostic *p++ = (int)tmp + '0'; 40434248Sbostic break; 40534248Sbostic } 40634242Sbostic else 40734248Sbostic *p++ = '0'; 40834248Sbostic 40934248Sbostic /* g/G format, decrement precision for first digit */ 41034248Sbostic if (format&GFORMAT && prec) 41134248Sbostic --prec; 41234248Sbostic 41334248Sbostic /* add decimal after first non-zero digit */ 41434248Sbostic if (decpt) 41534248Sbostic *p++ = '.'; 41634242Sbostic } 41734248Sbostic /* 41834248Sbostic * f format or g/G printed as f format; don't worry about decimal 41934248Sbostic * point, if g/G format doesn't need it, will get stripped later. 42034248Sbostic */ 42134242Sbostic else { 42234248Sbostic p = startp; 42334248Sbostic *p++ = '0'; 42434248Sbostic *p++ = '.'; 42534248Sbostic } 42634248Sbostic 42734248Sbostic /* finish out requested precision from fractional value */ 42834248Sbostic while (prec--) 42934248Sbostic if (fract) { 43034248Sbostic fract = modf(fract * 10, &tmp); 43134248Sbostic *p++ = (int)tmp + '0'; 43234248Sbostic } 43334248Sbostic else 43434248Sbostic *p++ = '0'; 43534248Sbostic 43634248Sbostic /* 43734248Sbostic * if any fractional value left, "round" it back up to the beginning 43834248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 43934248Sbostic * decimal point. 44034248Sbostic */ 44134248Sbostic if (fract) { 44234248Sbostic (void)modf(fract * 10, &tmp); 44334248Sbostic if (tmp > 4) { 44434248Sbostic for (savep = p--;; *p-- = '0') { 44534248Sbostic if (*p == '.') 44634248Sbostic --p; 44734248Sbostic if (p == startp) { 44834248Sbostic *p = '1'; 44934248Sbostic ++expcnt; 45034248Sbostic break; 45134248Sbostic } 45234248Sbostic if (++*p <= '9') 45334248Sbostic break; 45434242Sbostic } 45534248Sbostic p = savep; 45634242Sbostic } 45734248Sbostic } 45834248Sbostic 45934248Sbostic /* 46034248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 46134248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 46234248Sbostic */ 463*34318Sbostic if (!(flags&ALT)) { 46434248Sbostic if (format&GFORMAT) 46534248Sbostic for (; p[-1] == '0'; --p); 46634248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 46734248Sbostic --p; 46834248Sbostic } 46934248Sbostic 47034248Sbostic /* if an e/E format, add exponent */ 47134248Sbostic if (format&EFORMAT) { 47234248Sbostic *p++ = fmtch; 47334248Sbostic if (--expcnt < 0) { 47434248Sbostic expcnt = -expcnt; 47534248Sbostic *p++ = '-'; 47634242Sbostic } 47734248Sbostic else 47834248Sbostic *p++ = '+'; 47934248Sbostic *p++ = expcnt / 10 + '0'; 48034248Sbostic *p++ = expcnt % 10 + '0'; 48134242Sbostic } 48234248Sbostic return(p); 48334242Sbostic } 484