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*34327Sbostic static char sccsid[] = "@(#)vfprintf.c 5.23 (Berkeley) 05/18/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 2234323Sbostic #define MAXBUF 512 2334226Sbostic 2434323Sbostic #define PUTC(ch) {++cnt; putc((char)ch, fp);} 2534236Sbostic 2634318Sbostic #define ARG() \ 2734318Sbostic _ulong = flags&LONGINT ? va_arg(argp, long) : \ 2834318Sbostic flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 2934235Sbostic 3034318Sbostic /* have to deal with the negative buffer count kludge */ 3134318Sbostic #define NEGATIVE_COUNT_KLUDGE 3234318Sbostic 3334318Sbostic #define LONGINT 0x01 /* long integer */ 3434318Sbostic #define LONGDBL 0x02 /* long double; unimplemented */ 3534318Sbostic #define SHORTINT 0x04 /* short integer */ 3634318Sbostic #define ALT 0x08 /* alternate form */ 3734318Sbostic #define LADJUST 0x10 /* left adjustment */ 3834318Sbostic 3934323Sbostic _doprnt(fmt0, argp, fp) 4034323Sbostic u_char *fmt0; 4134233Sbostic va_list argp; 4234235Sbostic register FILE *fp; 4334226Sbostic { 4434323Sbostic register u_char *fmt; 4534323Sbostic register int ch, cnt, n; 4634323Sbostic register char *t; 4734235Sbostic double _double; 4834314Sbostic u_long _ulong; 4934323Sbostic int base, flags, prec, size, width; 5034323Sbostic char padc, sign, *digs, buf[MAXBUF], *_cvt(); 5134226Sbostic 5234323Sbostic fmt = fmt0; 5334243Sbostic digs = "0123456789abcdef"; 5434322Sbostic for (cnt = 0;; ++fmt) { 5534318Sbostic n = fp->_cnt; 5634318Sbostic for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) 5734318Sbostic if (--n < 0 5834318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE 5934318Sbostic && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 6034318Sbostic #endif 6134318Sbostic || ch == '\n' && fp->_flag&_IOLBF) { 6234318Sbostic fp->_cnt = n; 6334318Sbostic fp->_ptr = t; 6434318Sbostic (void)_flsbuf(ch, fp); 6534318Sbostic n = fp->_cnt; 6634318Sbostic t = fp->_ptr; 6734318Sbostic } 6834318Sbostic else 6934314Sbostic *t++ = ch; 7034318Sbostic fp->_cnt = n; 7134318Sbostic fp->_ptr = t; 7234318Sbostic if (!ch) 7334314Sbostic return(cnt); 7434314Sbostic 7534318Sbostic flags = width = 0; 7634233Sbostic prec = -1; 7734233Sbostic padc = ' '; 7834318Sbostic sign = '\0'; 7934226Sbostic 8034318Sbostic rflag: switch (*++fmt) { 8134318Sbostic case ' ': 8234318Sbostic sign = ' '; 8334318Sbostic goto rflag; 8434233Sbostic case '#': 8534318Sbostic flags |= ALT; 8634318Sbostic goto rflag; 8734233Sbostic case '*': 8834235Sbostic /* 8934235Sbostic * ``A negative field width argument is taken as a 9034235Sbostic * - flag followed by a positive field width.'' 9134235Sbostic * -- ANSI X3J11 9234235Sbostic * They don't exclude field widths read from args. 9334235Sbostic */ 9434235Sbostic if ((width = va_arg(argp, int)) >= 0) 9534318Sbostic goto rflag; 9634235Sbostic width = -width; 9734235Sbostic /*FALLTHROUGH*/ 9834235Sbostic case '-': 9934318Sbostic flags |= LADJUST; 10034318Sbostic goto rflag; 10134233Sbostic case '+': 10234314Sbostic sign = '+'; 10334318Sbostic goto rflag; 10434233Sbostic case '.': 10534235Sbostic if (*++fmt == '*') 10634318Sbostic n = va_arg(argp, int); 10734318Sbostic else if (isascii(*fmt) && isdigit(*fmt)) { 10834318Sbostic n = 0; 10934233Sbostic do { 11034318Sbostic n = 10 * n + *fmt - '0'; 11134318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 11234233Sbostic --fmt; 11334226Sbostic } 11434235Sbostic else { 11534318Sbostic --fmt; 11634235Sbostic prec = 0; 11734318Sbostic goto rflag; 11834235Sbostic } 11934318Sbostic prec = n < 0 ? -1 : n; 12034318Sbostic goto rflag; 12134233Sbostic case '0': 12234233Sbostic padc = '0'; 12334235Sbostic /*FALLTHROUGH*/ 12434233Sbostic case '1': case '2': case '3': case '4': 12534233Sbostic case '5': case '6': case '7': case '8': case '9': 12634318Sbostic n = 0; 12734233Sbostic do { 12834318Sbostic n = 10 * n + *fmt - '0'; 12934318Sbostic } while (isascii(*++fmt) && isdigit(*fmt)); 13034318Sbostic width = n; 13134233Sbostic --fmt; 13234319Sbostic goto rflag; 13334235Sbostic case 'L': 13434318Sbostic /* 13534318Sbostic * C doesn't have a long double; use long for now. 13634318Sbostic * flags |= LONGDBL; 13734318Sbostic */ 13834318Sbostic flags |= LONGINT; 13934318Sbostic goto rflag; 14034235Sbostic case 'h': 14134318Sbostic flags |= SHORTINT; 14234318Sbostic goto rflag; 14334233Sbostic case 'l': 14434318Sbostic flags |= LONGINT; 14534318Sbostic goto rflag; 14634314Sbostic case 'c': 14734319Sbostic buf[0] = va_arg(argp, int); 14834314Sbostic size = 1; 14934319Sbostic t = buf; 15034314Sbostic goto pforw; 15134314Sbostic case 'd': 15234318Sbostic case 'i': 15334318Sbostic ARG(); 15434318Sbostic if ((long)_ulong < 0) { 15534318Sbostic _ulong = -_ulong; 15634314Sbostic sign = '-'; 15734241Sbostic } 15834241Sbostic base = 10; 159*34327Sbostic goto number; 16034261Sbostic case 'e': 16134236Sbostic case 'E': 16234235Sbostic case 'f': 16334261Sbostic case 'g': 16434243Sbostic case 'G': 16534243Sbostic _double = va_arg(argp, double); 166*34327Sbostic size = _cvt(_double, prec, flags, *fmt, padc, &sign, 16734323Sbostic buf, buf + sizeof(buf)) - buf; 16834319Sbostic t = buf; 169*34327Sbostic /* 170*34327Sbostic * zero-padded sign put out here; blank padded sign 171*34327Sbostic * placed in number in _cvt(). 172*34327Sbostic */ 173*34327Sbostic if (sign && padc == '0') { 174*34327Sbostic PUTC(sign); 175*34327Sbostic --width; 176*34327Sbostic } 17734314Sbostic goto pforw; 17834235Sbostic case 'n': 17934318Sbostic if (flags&LONGDBL || flags&LONGINT) 18034318Sbostic *va_arg(argp, long *) = cnt; 18134318Sbostic else if (flags&SHORTINT) 18234318Sbostic *va_arg(argp, short *) = cnt; 18334318Sbostic else 18434318Sbostic *va_arg(argp, int *) = cnt; 18534235Sbostic break; 18634226Sbostic case 'o': 18734318Sbostic ARG(); 18834226Sbostic base = 8; 189*34327Sbostic goto nosign; 19034235Sbostic case 'p': 19134320Sbostic /* 19234321Sbostic * ``The argument shall be a pointer to void. The 19334321Sbostic * value of the pointer is converted to a sequence 19434321Sbostic * of printable characters, in an implementation- 19534321Sbostic * defined manner.'' 19634321Sbostic * -- ANSI X3J11 19734320Sbostic */ 198*34327Sbostic /*NOSTRICT*/ 19934320Sbostic _ulong = (u_long)va_arg(argp, void *); 20034320Sbostic base = 16; 201*34327Sbostic goto nosign; 20234226Sbostic case 's': 20334314Sbostic if (!(t = va_arg(argp, char *))) 20434314Sbostic t = "(null)"; 20534321Sbostic if (prec >= 0) { 20634321Sbostic /* 20734321Sbostic * can't use strlen; can only look for the 20834321Sbostic * NUL in the first `prec' characters, and 20934321Sbostic * strlen() will go further. 21034321Sbostic */ 21134321Sbostic char *p, *memchr(); 21234321Sbostic 21334321Sbostic if (p = memchr(t, 0, prec)) { 21434321Sbostic size = p - t; 21534321Sbostic if (size > prec) 21634321Sbostic size = prec; 21734321Sbostic } 21834321Sbostic else 21934321Sbostic size = prec; 22034321Sbostic } 22134321Sbostic else 22234321Sbostic size = strlen(t); 22334318Sbostic pforw: if (!(flags&LADJUST) && width) 22434314Sbostic for (n = size; n++ < width;) 22534314Sbostic PUTC(padc); 22634314Sbostic if (fp->_cnt - (n = size) >= 0) { 22734314Sbostic cnt += n; 22834314Sbostic fp->_cnt -= n; 22934314Sbostic bcopy(t, fp->_ptr, n); 23034314Sbostic fp->_ptr += n; 23134233Sbostic } 23234314Sbostic else for (; n--; ++t) 23334314Sbostic PUTC(*t); 23434318Sbostic if (flags&LADJUST) 23534314Sbostic while (width-- > size) 23634325Sbostic PUTC(' '); 23734226Sbostic break; 23834226Sbostic case 'u': 23934318Sbostic ARG(); 24034226Sbostic base = 10; 241*34327Sbostic goto nosign; 24234226Sbostic case 'X': 24334226Sbostic digs = "0123456789ABCDEF"; 24434233Sbostic /*FALLTHROUGH*/ 24534226Sbostic case 'x': 24634318Sbostic ARG(); 24734314Sbostic base = 16; 24834326Sbostic /* leading 0x/X only if non-zero */ 24934326Sbostic if (!_ulong) 25034324Sbostic flags &= ~ALT; 251*34327Sbostic 252*34327Sbostic /* unsigned conversions */ 253*34327Sbostic nosign: sign = NULL; 25434326Sbostic /* 25534326Sbostic * ``The result of converting a zero value with an 25634326Sbostic * explicit precision of zero is no characters.'' 25734326Sbostic * -- ANSI X3J11 25834326Sbostic */ 259*34327Sbostic number: if (!_ulong && !prec) 26034326Sbostic break; 261*34327Sbostic 26234326Sbostic t = buf + MAXBUF - 1; 26334233Sbostic do { 26434314Sbostic *t-- = digs[_ulong % base]; 26534314Sbostic _ulong /= base; 26634314Sbostic } while(_ulong); 26734324Sbostic for (size = buf + MAXBUF - 1 - t; size < prec; ++size) 26834324Sbostic *t-- = '0'; 269*34327Sbostic 270*34327Sbostic /* alternate mode for hex and octal numbers */ 27134324Sbostic if (flags&ALT) 27234324Sbostic switch (base) { 27334324Sbostic case 16: 27434324Sbostic /* avoid "00000x35" */ 275*34327Sbostic if (padc == ' ') { 276*34327Sbostic *t-- = *fmt; 277*34327Sbostic *t-- = '0'; 278*34327Sbostic } 279*34327Sbostic else { 28034324Sbostic PUTC('0'); 28134324Sbostic PUTC(*fmt); 28234324Sbostic } 28334324Sbostic width -= 2; 28434324Sbostic break; 28534324Sbostic case 8: 28634324Sbostic if (t[1] != '0') { 28734324Sbostic *t-- = '0'; 28834324Sbostic --width; 28934324Sbostic } 29034324Sbostic break; 29134314Sbostic } 292*34327Sbostic 293*34327Sbostic if (sign) { 294*34327Sbostic /* avoid "0000-3" */ 295*34327Sbostic if (padc == ' ') 296*34327Sbostic *t-- = sign; 297*34327Sbostic else 298*34327Sbostic PUTC(sign); 299*34327Sbostic --width; 300*34327Sbostic } 301*34327Sbostic 30234318Sbostic if (!(flags&LADJUST)) 30334314Sbostic while (size++ < width) 30434263Sbostic PUTC(padc); 30534323Sbostic while (++t < buf + MAXBUF) 30634314Sbostic PUTC(*t); 30734325Sbostic while (width-- > size) 30834325Sbostic PUTC(' '); 309*34327Sbostic digs = "0123456789abcdef"; 31034226Sbostic break; 31134233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 31234314Sbostic return(cnt); 31334226Sbostic default: 31434263Sbostic PUTC(*fmt); 31534226Sbostic } 31634226Sbostic } 31734314Sbostic /*NOTREACHED*/ 31834226Sbostic } 31934242Sbostic 32034261Sbostic #define EFORMAT 0x01 32134261Sbostic #define FFORMAT 0x02 32234261Sbostic #define GFORMAT 0x04 32334314Sbostic #define DEFPREC 6 32434261Sbostic 32534323Sbostic static char * 326*34327Sbostic _cvt(number, prec, flags, fmtch, padc, sign, startp, endp) 32734242Sbostic double number; 32834261Sbostic register int prec; 32934323Sbostic int flags; 33034323Sbostic u_char fmtch; 331*34327Sbostic char padc, *sign, *startp, *endp; 33234242Sbostic { 33334248Sbostic register char *p; 33434261Sbostic register int expcnt, format; 33534248Sbostic double fract, integer, tmp, modf(); 33634261Sbostic int decpt; 33734323Sbostic char *savep; 33834242Sbostic 33934314Sbostic if (prec == -1) 34034242Sbostic prec = DEFPREC; 34134242Sbostic 34234314Sbostic if (number < 0) { 343*34327Sbostic *sign = '-'; 34434248Sbostic number = -number; 34534248Sbostic } 34634242Sbostic 347*34327Sbostic /* if blank padded, add sign in as part of the number */ 348*34327Sbostic if (*sign && padc == ' ') 349*34327Sbostic *startp++ = *sign; 350*34327Sbostic 35134261Sbostic switch(fmtch) { 35234261Sbostic case 'e': 35334261Sbostic case 'E': 35434261Sbostic format = EFORMAT; 35534261Sbostic break; 35634261Sbostic case 'f': 35734261Sbostic format = FFORMAT; 35834261Sbostic break; 35934261Sbostic case 'g': 36034261Sbostic case 'G': 36134261Sbostic format = GFORMAT; 36234261Sbostic fmtch -= 2; 36334261Sbostic } 36434261Sbostic 36534248Sbostic /* 36634248Sbostic * if the alternate flag is set, or, at least one digit of precision 36734248Sbostic * was requested, add a decimal point, unless it's the g/G format 36834314Sbostic * in which case we require two digits of precision, as it counts 36934248Sbostic * precision differently. 37034248Sbostic */ 37134318Sbostic decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 37234248Sbostic 37334248Sbostic expcnt = 0; 37434323Sbostic p = endp - 1; 37534248Sbostic fract = modf(number, &integer); 37634248Sbostic if (integer) { 37734248Sbostic register char *p2; 37834248Sbostic 37934248Sbostic /* get integer part of number; count decimal places */ 38034248Sbostic for (; integer; ++expcnt) { 38134248Sbostic tmp = modf(integer / 10, &integer); 38234248Sbostic *p-- = (int)((tmp + .03) * 10) + '0'; 38334242Sbostic } 38434248Sbostic 38534248Sbostic /* copy, in reverse order, to start of buffer */ 38634248Sbostic p2 = startp; 38734248Sbostic *p2++ = *++p; 38834248Sbostic 38934248Sbostic /* 39034248Sbostic * if the format is g/G, and the resulting exponent will be 39134248Sbostic * greater than the precision, use e/E format. If e/E format, 39234248Sbostic * put in a decimal point as needed, and decrement precision 39334248Sbostic * count for each digit after the decimal point. 39434248Sbostic */ 39534248Sbostic if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 39634248Sbostic if (format&GFORMAT) { 39734248Sbostic format |= EFORMAT; 39834248Sbostic 39934248Sbostic /* first digit is precision for g/G format */ 40034248Sbostic if (prec) 40134248Sbostic --prec; 40234248Sbostic } 40334248Sbostic if (decpt) 40434248Sbostic *p2++ = '.'; 40534248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 40634248Sbostic 40734318Sbostic /* precision ran out, round */ 40834248Sbostic if (p < endp) { 40934248Sbostic if (*p > '4') { 41034248Sbostic for (savep = p2--;; *p2-- = '0') { 41134248Sbostic if (*p2 == '.') 41234248Sbostic --p2; 41334248Sbostic if (++*p2 <= '9') 41434248Sbostic break; 41534248Sbostic } 41634248Sbostic p2 = savep; 41734248Sbostic } 41834248Sbostic fract = 0; 41934248Sbostic } 42034242Sbostic } 42134248Sbostic /* 42234314Sbostic * g/G in f format; if out of precision, replace digits with 42334314Sbostic * zeroes, note, have to round first. 42434248Sbostic */ 42534248Sbostic else if (format&GFORMAT) { 42634248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 42734248Sbostic /* precision ran out; round and then add zeroes */ 42834248Sbostic if (p < endp) { 42934248Sbostic if (*p > '4') { 43034248Sbostic for (savep = p2--; ++*p2 > '9'; 43134248Sbostic *p2-- = '0'); 43234248Sbostic p2 = savep; 43334248Sbostic } 43434248Sbostic do { 43534248Sbostic *p2++ = '0'; 43634248Sbostic } while (++p < endp); 43734248Sbostic fract = 0; 43834248Sbostic } 43934248Sbostic if (decpt) 44034248Sbostic *p2++ = '.'; 44134242Sbostic } 44234248Sbostic /* f format */ 44334248Sbostic else { 44434248Sbostic for (; ++p < endp; *p2++ = *p); 44534248Sbostic if (decpt) 44634248Sbostic *p2++ = '.'; 44734248Sbostic } 44834248Sbostic p = p2; 44934248Sbostic } 45034248Sbostic /* 45134318Sbostic * if no fraction, the number was zero, and if no precision, can't 45234318Sbostic * show anything after the decimal point. 45334248Sbostic */ 45434248Sbostic else if (!fract || !prec) { 45534248Sbostic *startp++ = '0'; 45634318Sbostic if (decpt && !(format&GFORMAT)) 45734248Sbostic *startp++ = '.'; 45834318Sbostic *startp = '\0'; 45934323Sbostic return(startp); 46034248Sbostic } 46134248Sbostic /* 46234248Sbostic * if the format is g/G, and the resulting exponent will be less than 46334248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 46434248Sbostic */ 46534248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 46634248Sbostic format |= EFORMAT; 46734248Sbostic if (fract) 46834248Sbostic for (p = startp; fract;) { 46934248Sbostic fract = modf(fract * 10, &tmp); 47034248Sbostic if (!tmp) { 47134248Sbostic --expcnt; 47234248Sbostic continue; 47334248Sbostic } 47434248Sbostic *p++ = (int)tmp + '0'; 47534248Sbostic break; 47634248Sbostic } 47734242Sbostic else 47834248Sbostic *p++ = '0'; 47934248Sbostic 48034248Sbostic /* g/G format, decrement precision for first digit */ 48134248Sbostic if (format&GFORMAT && prec) 48234248Sbostic --prec; 48334248Sbostic 48434248Sbostic /* add decimal after first non-zero digit */ 48534248Sbostic if (decpt) 48634248Sbostic *p++ = '.'; 48734242Sbostic } 48834248Sbostic /* 48934248Sbostic * f format or g/G printed as f format; don't worry about decimal 49034248Sbostic * point, if g/G format doesn't need it, will get stripped later. 49134248Sbostic */ 49234242Sbostic else { 49334248Sbostic p = startp; 49434248Sbostic *p++ = '0'; 49534248Sbostic *p++ = '.'; 49634248Sbostic } 49734248Sbostic 49834319Sbostic /* finish out requested precision */ 49934319Sbostic while (fract && prec-- > 0) { 50034319Sbostic fract = modf(fract * 10, &tmp); 50134319Sbostic *p++ = (int)tmp + '0'; 50234319Sbostic } 50334319Sbostic while (prec-- > 0) 50434319Sbostic *p++ = '0'; 50534248Sbostic 50634248Sbostic /* 50734248Sbostic * if any fractional value left, "round" it back up to the beginning 50834248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 50934248Sbostic * decimal point. 51034248Sbostic */ 51134248Sbostic if (fract) { 51234248Sbostic (void)modf(fract * 10, &tmp); 51334248Sbostic if (tmp > 4) { 51434248Sbostic for (savep = p--;; *p-- = '0') { 51534248Sbostic if (*p == '.') 51634248Sbostic --p; 51734248Sbostic if (p == startp) { 51834248Sbostic *p = '1'; 51934248Sbostic ++expcnt; 52034248Sbostic break; 52134248Sbostic } 52234248Sbostic if (++*p <= '9') 52334248Sbostic break; 52434242Sbostic } 52534248Sbostic p = savep; 52634242Sbostic } 52734248Sbostic } 52834248Sbostic 52934248Sbostic /* 53034248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 53134248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 53234248Sbostic */ 53334318Sbostic if (!(flags&ALT)) { 53434248Sbostic if (format&GFORMAT) 53534248Sbostic for (; p[-1] == '0'; --p); 53634248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 53734248Sbostic --p; 53834248Sbostic } 53934248Sbostic 54034248Sbostic /* if an e/E format, add exponent */ 54134248Sbostic if (format&EFORMAT) { 54234248Sbostic *p++ = fmtch; 54334248Sbostic if (--expcnt < 0) { 54434248Sbostic expcnt = -expcnt; 54534248Sbostic *p++ = '-'; 54634242Sbostic } 54734248Sbostic else 54834248Sbostic *p++ = '+'; 54934248Sbostic *p++ = expcnt / 10 + '0'; 55034248Sbostic *p++ = expcnt % 10 + '0'; 55134242Sbostic } 55234323Sbostic return(p); 55334242Sbostic } 554