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*34248Sbostic static char sccsid[] = "@(#)vfprintf.c 5.8 (Berkeley) 05/10/88"; 1534233Sbostic #endif /* LIBC_SCCS and not lint */ 1634233Sbostic 1734233Sbostic #include <sys/param.h> 1834233Sbostic #include <varargs.h> 1934226Sbostic #include <stdio.h> 2034233Sbostic #include <ctype.h> 2134226Sbostic 2234242Sbostic #define MAXBUF 120 2334242Sbostic #define DEFPREC 6 2434226Sbostic 2534236Sbostic #define PUTC(ch, fd) {++cnt; putc(ch, fd);} 2634236Sbostic 27*34248Sbostic #define EFORMAT 0x01 28*34248Sbostic #define FFORMAT 0x02 29*34248Sbostic #define GFORMAT 0x04 3034242Sbostic 3134235Sbostic #define LONGINT 0x01 3234235Sbostic #define LONGDBL 0x02 3334235Sbostic #define SHORTINT 0x04 3434241Sbostic #define GETARG(r) \ 3534241Sbostic r = argsize&LONGINT ? va_arg(argp, long) : \ 3634241Sbostic argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 3734235Sbostic 38*34248Sbostic static int alternate; 39*34248Sbostic static char printsign; 40*34248Sbostic 4134235Sbostic x_doprnt(fmt, argp, fp) 4234233Sbostic register char *fmt; 4334233Sbostic va_list argp; 4434235Sbostic register FILE *fp; 4534226Sbostic { 4634233Sbostic register u_long reg_ulong; 4734233Sbostic register long reg_long; 4834233Sbostic register int base; 4934235Sbostic register char *digs, *bp, *t, padc; 5034235Sbostic double _double; 51*34248Sbostic char argsize, *_cvt(), buf[MAXBUF]; 52*34248Sbostic int cnt, n, ladjust, width, prec, size; 5334226Sbostic 5434243Sbostic digs = "0123456789abcdef"; 5534233Sbostic for (cnt = 0; *fmt; ++fmt) { 5634233Sbostic if (*fmt != '%') { 5734235Sbostic PUTC(*fmt, fp); 5834233Sbostic continue; 5934226Sbostic } 6034226Sbostic 6134235Sbostic alternate = ladjust = width = 0; 6234233Sbostic prec = -1; 6334233Sbostic padc = ' '; 6434235Sbostic argsize = printsign = '\0'; 6534226Sbostic 6634233Sbostic flags: switch (*++fmt) { 6734233Sbostic case '#': 6834233Sbostic alternate = 1; 6934233Sbostic goto flags; 7034233Sbostic case '*': 7134235Sbostic /* 7234235Sbostic * ``A negative field width argument is taken as a 7334235Sbostic * - flag followed by a positive field width.'' 7434235Sbostic * -- ANSI X3J11 7534235Sbostic * They don't exclude field widths read from args. 7634235Sbostic */ 7734235Sbostic if ((width = va_arg(argp, int)) >= 0) 7834235Sbostic goto flags; 7934235Sbostic width = -width; 8034235Sbostic /*FALLTHROUGH*/ 8134235Sbostic case '-': 8234235Sbostic ladjust = 1; 8334233Sbostic goto flags; 8434233Sbostic case '+': 8534233Sbostic printsign = '+'; 8634233Sbostic goto flags; 8734233Sbostic case '.': 8834235Sbostic if (*++fmt == '*') 8934235Sbostic prec = va_arg(argp, int); 9034235Sbostic else if (isdigit(*fmt)) { 9134235Sbostic prec = 0; 9234233Sbostic do { 9334236Sbostic prec = 10 * prec + *fmt - '0'; 9434233Sbostic } while isdigit(*++fmt); 9534233Sbostic --fmt; 9634226Sbostic } 9734235Sbostic else { 9834235Sbostic prec = 0; 9934235Sbostic --fmt; 10034235Sbostic goto flags; 10134235Sbostic } 10234235Sbostic if (prec < 0) 10334235Sbostic prec = -1; 10434233Sbostic goto flags; 10534233Sbostic case '0': 10634233Sbostic padc = '0'; 10734235Sbostic /*FALLTHROUGH*/ 10834233Sbostic case '1': case '2': case '3': case '4': 10934233Sbostic case '5': case '6': case '7': case '8': case '9': 11034233Sbostic do { 11134236Sbostic width = 10 * width + *fmt - '0'; 11234233Sbostic } while isdigit(*++fmt); 11334233Sbostic --fmt; 11434235Sbostic case 'L': 11534235Sbostic argsize |= LONGDBL; 11634235Sbostic goto flags; 11734235Sbostic case 'h': 11834235Sbostic argsize |= SHORTINT; 11934235Sbostic goto flags; 12034233Sbostic case 'l': 12134235Sbostic argsize |= LONGINT; 12234233Sbostic goto flags; 12334243Sbostic case '%': /* "%#%" prints as "%" */ 12434243Sbostic PUTC('%', fp); 12534243Sbostic break; 12634243Sbostic case 'c': { 12734243Sbostic char ch; 12834226Sbostic 12934243Sbostic ch = va_arg(argp, int); 13034243Sbostic PUTC(ch, fp); 13134226Sbostic break; 13234243Sbostic } 13334241Sbostic case 'd': 13434241Sbostic case 'i': 13534241Sbostic GETARG(reg_long); 13634241Sbostic if (reg_long < 0) { 13734241Sbostic reg_ulong = -reg_long; 13834241Sbostic printsign = '-'; 13934241Sbostic } 14034241Sbostic else { 14134241Sbostic reg_ulong = reg_long; 14234241Sbostic } 14334241Sbostic if (printsign) 14434241Sbostic PUTC(printsign, fp); 14534241Sbostic base = 10; 14634241Sbostic goto num1; 14734236Sbostic case 'E': 14834236Sbostic case 'e': 14934236Sbostic _double = va_arg(argp, double); 150*34248Sbostic bp = _cvt(_double, prec, EFORMAT, buf, 151*34248Sbostic buf + sizeof(buf), *fmt); 15234236Sbostic goto pbuf; 15334235Sbostic case 'f': 15434235Sbostic _double = va_arg(argp, double); 155*34248Sbostic bp = _cvt(_double, prec, FFORMAT, buf, 156*34248Sbostic buf + sizeof(buf), 'f'); 15734243Sbostic goto pbuf; 15834243Sbostic case 'G': 15934243Sbostic case 'g': 16034243Sbostic _double = va_arg(argp, double); 161*34248Sbostic bp = _cvt(_double, prec, GFORMAT, buf, 162*34248Sbostic buf + sizeof(buf), *fmt - 2); 16334236Sbostic pbuf: size = bp - buf; 16434235Sbostic if (size < width && !ladjust) 16534235Sbostic do { 16634235Sbostic PUTC(padc, fp); 16734235Sbostic } while (--width > size); 16834235Sbostic for (t = buf; t < bp; ++t) 16934235Sbostic PUTC(*t, fp); 17034235Sbostic for (; width > size; --width) 17134235Sbostic PUTC(padc, fp); 17234235Sbostic break; 17334235Sbostic case 'n': 17434235Sbostic *(va_arg(argp, int *)) = cnt; 17534235Sbostic break; 17634226Sbostic case 'o': 17734235Sbostic GETARG(reg_ulong); 17834226Sbostic base = 8; 17934235Sbostic if (!reg_ulong || !alternate) 18034235Sbostic goto num1; 18134235Sbostic bp = buf + sizeof(buf) - 1; 18234235Sbostic do { 18334235Sbostic *bp-- = digs[reg_ulong % base]; 18434235Sbostic reg_ulong /= base; 18534235Sbostic } while(reg_ulong); 18634235Sbostic size = &buf[sizeof(buf) - 1] - bp; 18734235Sbostic if (size < --width && !ladjust) 18834235Sbostic do { 18934235Sbostic PUTC(padc, fp); 19034235Sbostic } while (--width > size); 19134235Sbostic PUTC('0', fp); 19234236Sbostic goto num2; 19334235Sbostic case 'p': 19434226Sbostic case 's': 19534235Sbostic if (!(bp = va_arg(argp, char *))) 19634235Sbostic bp = "(null)"; 19734235Sbostic if (width > 0 && !ladjust) { 19834233Sbostic char *savep; 19934226Sbostic 20034235Sbostic savep = bp; 20134235Sbostic for (n = 0; *bp && (prec < 0 || n < prec); 20234235Sbostic n++, bp++); 20334235Sbostic bp = savep; 20434235Sbostic while (n++ < width) 20534235Sbostic PUTC(' ', fp); 20634233Sbostic } 20734235Sbostic for (n = 0; *bp; ++bp) { 20834235Sbostic if (++n > prec && prec >= 0) 20934226Sbostic break; 21034235Sbostic PUTC(*bp, fp); 21134233Sbostic } 21234235Sbostic if (n < width && ladjust) 21334233Sbostic do { 21434235Sbostic PUTC(' ', fp); 21534235Sbostic } while (++n < width); 21634226Sbostic break; 21734226Sbostic case 'u': 21834235Sbostic GETARG(reg_ulong); 21934226Sbostic base = 10; 22034235Sbostic goto num1; 22134226Sbostic case 'X': 22234226Sbostic digs = "0123456789ABCDEF"; 22334233Sbostic /*FALLTHROUGH*/ 22434226Sbostic case 'x': 22534235Sbostic GETARG(reg_ulong); 22634233Sbostic if (alternate && reg_ulong) { 22734235Sbostic PUTC('0', fp); 22834235Sbostic PUTC(*fmt, fp); 22934233Sbostic } 23034226Sbostic base = 16; 23134235Sbostic num1: bp = buf + sizeof(buf) - 1; 23234233Sbostic do { 23334235Sbostic *bp-- = digs[reg_ulong % base]; 23434233Sbostic reg_ulong /= base; 23534233Sbostic } while(reg_ulong); 23634235Sbostic size = &buf[sizeof(buf) - 1] - bp; 23734235Sbostic for (; size < prec; *bp-- = '0', ++size); 23834235Sbostic if (size < width && !ladjust) 23934235Sbostic do { 24034235Sbostic PUTC(padc, fp); 24134235Sbostic } while (--width > size); 24234236Sbostic num2: while (++bp != &buf[MAXBUF]) 24334235Sbostic PUTC(*bp, fp); 24434235Sbostic for (; width > size; --width) 24534235Sbostic PUTC(padc, fp); 24634243Sbostic digs = "0123456789abcdef"; 24734226Sbostic break; 24834233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 24934235Sbostic return(ferror(fp) ? -1 : cnt); 25034226Sbostic default: 25134235Sbostic PUTC(*fmt, fp); 25234226Sbostic } 25334226Sbostic } 25434235Sbostic return(ferror(fp) ? -1 : cnt); 25534226Sbostic } 25634242Sbostic 25734242Sbostic char * 258*34248Sbostic _cvt(number, prec, format, startp, endp, fmtch) 25934242Sbostic double number; 260*34248Sbostic int prec, format; 261*34248Sbostic char *startp, *endp, fmtch; 26234242Sbostic { 263*34248Sbostic register char *p; 264*34248Sbostic double fract, integer, tmp, modf(); 265*34248Sbostic int decpt, expcnt; 266*34248Sbostic char *savep; 26734242Sbostic 268*34248Sbostic if (prec == -1) /* set default precision */ 26934242Sbostic prec = DEFPREC; 27034242Sbostic 271*34248Sbostic p = endp - 1; 272*34248Sbostic if (number < 0) { /* set sign */ 273*34248Sbostic *startp++ = '-'; 274*34248Sbostic number = -number; 275*34248Sbostic } 27634242Sbostic else if (printsign) 277*34248Sbostic *startp++ = '+'; 27834242Sbostic 279*34248Sbostic /* 280*34248Sbostic * if the alternate flag is set, or, at least one digit of precision 281*34248Sbostic * was requested, add a decimal point, unless it's the g/G format 282*34248Sbostic * in which case we require two digits of precision, since it counts 283*34248Sbostic * precision differently. 284*34248Sbostic */ 285*34248Sbostic decpt = alternate || prec > 1 || !(format&GFORMAT) && prec; 286*34248Sbostic 287*34248Sbostic expcnt = 0; 288*34248Sbostic fract = modf(number, &integer); 289*34248Sbostic if (integer) { 290*34248Sbostic register char *p2; 291*34248Sbostic 292*34248Sbostic /* get integer part of number; count decimal places */ 293*34248Sbostic for (; integer; ++expcnt) { 294*34248Sbostic tmp = modf(integer / 10, &integer); 295*34248Sbostic *p-- = (int)((tmp + .03) * 10) + '0'; 29634242Sbostic } 297*34248Sbostic 298*34248Sbostic /* copy, in reverse order, to start of buffer */ 299*34248Sbostic p2 = startp; 300*34248Sbostic *p2++ = *++p; 301*34248Sbostic 302*34248Sbostic /* 303*34248Sbostic * if the format is g/G, and the resulting exponent will be 304*34248Sbostic * greater than the precision, use e/E format. If e/E format, 305*34248Sbostic * put in a decimal point as needed, and decrement precision 306*34248Sbostic * count for each digit after the decimal point. 307*34248Sbostic */ 308*34248Sbostic if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 309*34248Sbostic if (format&GFORMAT) { 310*34248Sbostic format |= EFORMAT; 311*34248Sbostic 312*34248Sbostic /* first digit is precision for g/G format */ 313*34248Sbostic if (prec) 314*34248Sbostic --prec; 315*34248Sbostic } 316*34248Sbostic if (decpt) 317*34248Sbostic *p2++ = '.'; 318*34248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 319*34248Sbostic 320*34248Sbostic /* precision ran out; round number */ 321*34248Sbostic if (p < endp) { 322*34248Sbostic if (*p > '4') { 323*34248Sbostic for (savep = p2--;; *p2-- = '0') { 324*34248Sbostic if (*p2 == '.') 325*34248Sbostic --p2; 326*34248Sbostic if (++*p2 <= '9') 327*34248Sbostic break; 328*34248Sbostic } 329*34248Sbostic p2 = savep; 330*34248Sbostic } 331*34248Sbostic fract = 0; 332*34248Sbostic } 33334242Sbostic } 334*34248Sbostic /* 335*34248Sbostic * g/G in f format; if run out of precision, replace digits 336*34248Sbostic * with zeroes, note, have to round first, otherwise lose 337*34248Sbostic * rounding point. 338*34248Sbostic */ 339*34248Sbostic else if (format&GFORMAT) { 340*34248Sbostic for (; ++p < endp && prec; --prec, *p2++ = *p); 341*34248Sbostic /* precision ran out; round and then add zeroes */ 342*34248Sbostic if (p < endp) { 343*34248Sbostic if (*p > '4') { 344*34248Sbostic for (savep = p2--; ++*p2 > '9'; 345*34248Sbostic *p2-- = '0'); 346*34248Sbostic p2 = savep; 347*34248Sbostic } 348*34248Sbostic do { 349*34248Sbostic *p2++ = '0'; 350*34248Sbostic } while (++p < endp); 351*34248Sbostic fract = 0; 352*34248Sbostic } 353*34248Sbostic if (decpt) 354*34248Sbostic *p2++ = '.'; 35534242Sbostic } 356*34248Sbostic /* f format */ 357*34248Sbostic else { 358*34248Sbostic for (; ++p < endp; *p2++ = *p); 359*34248Sbostic if (decpt) 360*34248Sbostic *p2++ = '.'; 361*34248Sbostic } 362*34248Sbostic p = p2; 363*34248Sbostic } 364*34248Sbostic /* 365*34248Sbostic * it's unclear from the ANSI X3J11 spec if the g/G format should 366*34248Sbostic * just result in an empty string, because it's supposed to remove 367*34248Sbostic * trailing zeroes. That seems counter-intuitive, so here it does 368*34248Sbostic * what f and e/E do; if no fraction, the number was zero, and if 369*34248Sbostic * no precision can't show anything after the decimal point. 370*34248Sbostic */ 371*34248Sbostic else if (!fract || !prec) { 372*34248Sbostic *startp++ = '0'; 373*34248Sbostic if (decpt) 374*34248Sbostic *startp++ = '.'; 375*34248Sbostic *startp++ = '\0'; 376*34248Sbostic return(startp); 377*34248Sbostic } 378*34248Sbostic /* 379*34248Sbostic * if the format is g/G, and the resulting exponent will be less than 380*34248Sbostic * -4 use e/E format. If e/E format, compute exponent value. 381*34248Sbostic */ 382*34248Sbostic else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 383*34248Sbostic format |= EFORMAT; 384*34248Sbostic if (fract) 385*34248Sbostic for (p = startp; fract;) { 386*34248Sbostic fract = modf(fract * 10, &tmp); 387*34248Sbostic if (!tmp) { 388*34248Sbostic --expcnt; 389*34248Sbostic continue; 390*34248Sbostic } 391*34248Sbostic *p++ = (int)tmp + '0'; 392*34248Sbostic break; 393*34248Sbostic } 39434242Sbostic else 395*34248Sbostic *p++ = '0'; 396*34248Sbostic 397*34248Sbostic /* g/G format, decrement precision for first digit */ 398*34248Sbostic if (format&GFORMAT && prec) 399*34248Sbostic --prec; 400*34248Sbostic 401*34248Sbostic /* add decimal after first non-zero digit */ 402*34248Sbostic if (decpt) 403*34248Sbostic *p++ = '.'; 40434242Sbostic } 405*34248Sbostic /* 406*34248Sbostic * f format or g/G printed as f format; don't worry about decimal 407*34248Sbostic * point, if g/G format doesn't need it, will get stripped later. 408*34248Sbostic */ 40934242Sbostic else { 410*34248Sbostic p = startp; 411*34248Sbostic *p++ = '0'; 412*34248Sbostic *p++ = '.'; 413*34248Sbostic } 414*34248Sbostic 415*34248Sbostic /* finish out requested precision from fractional value */ 416*34248Sbostic while (prec--) 417*34248Sbostic if (fract) { 418*34248Sbostic fract = modf(fract * 10, &tmp); 419*34248Sbostic *p++ = (int)tmp + '0'; 420*34248Sbostic } 421*34248Sbostic else 422*34248Sbostic *p++ = '0'; 423*34248Sbostic 424*34248Sbostic /* 425*34248Sbostic * if any fractional value left, "round" it back up to the beginning 426*34248Sbostic * of the number, fixing the exponent as necessary, and avoiding the 427*34248Sbostic * decimal point. 428*34248Sbostic */ 429*34248Sbostic if (fract) { 430*34248Sbostic (void)modf(fract * 10, &tmp); 431*34248Sbostic if (tmp > 4) { 432*34248Sbostic for (savep = p--;; *p-- = '0') { 433*34248Sbostic if (*p == '.') 434*34248Sbostic --p; 435*34248Sbostic if (p == startp) { 436*34248Sbostic *p = '1'; 437*34248Sbostic ++expcnt; 438*34248Sbostic break; 439*34248Sbostic } 440*34248Sbostic if (++*p <= '9') 441*34248Sbostic break; 44234242Sbostic } 443*34248Sbostic p = savep; 44434242Sbostic } 445*34248Sbostic } 446*34248Sbostic 447*34248Sbostic /* 448*34248Sbostic * if a g/G format and not alternate flag, lose trailing zeroes, 449*34248Sbostic * if e/E or g/G format, and last char is decimal point, lose it. 450*34248Sbostic */ 451*34248Sbostic if (!alternate) { 452*34248Sbostic if (format&GFORMAT) 453*34248Sbostic for (; p[-1] == '0'; --p); 454*34248Sbostic if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 455*34248Sbostic --p; 456*34248Sbostic } 457*34248Sbostic 458*34248Sbostic /* if an e/E format, add exponent */ 459*34248Sbostic if (format&EFORMAT) { 460*34248Sbostic *p++ = fmtch; 461*34248Sbostic if (--expcnt < 0) { 462*34248Sbostic expcnt = -expcnt; 463*34248Sbostic *p++ = '-'; 46434242Sbostic } 465*34248Sbostic else 466*34248Sbostic *p++ = '+'; 467*34248Sbostic *p++ = expcnt / 10 + '0'; 468*34248Sbostic *p++ = expcnt % 10 + '0'; 46934242Sbostic } 470*34248Sbostic return(p); 47134242Sbostic } 472