1 /* 2 * Copyright (c) 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #if defined(LIBC_SCCS) && !defined(lint) 14 static char sccsid[] = "@(#)vfprintf.c 5.18 (Berkeley) 05/17/88"; 15 #endif /* LIBC_SCCS and not lint */ 16 17 #include <sys/types.h> 18 #include <varargs.h> 19 #include <stdio.h> 20 #include <ctype.h> 21 22 /* 23 * To handle arbitrary floating point precision, the buffer has to hold the 24 * number, a decimal point, and N precision digits. We can't just truncate 25 * at some point is that the lower-level math routines may very well be 26 * repeatedly returning some small fraction. A 128 bit fraction can be 27 * represented in 39 decimal digits. Guess a max of 40 digits of precision, 28 * and add one for the decimal point. 29 */ 30 #define MAXFRAC 39 31 #define MAXPREC 40 32 #define MAXDIGIT (MAXFRAC + MAXPREC + 1) 33 34 #define PUTC(ch) {++cnt; putc(ch, fp);} 35 36 #define ARG() \ 37 _ulong = flags&LONGINT ? va_arg(argp, long) : \ 38 flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 39 40 /* have to deal with the negative buffer count kludge */ 41 #define NEGATIVE_COUNT_KLUDGE 42 43 #define LONGINT 0x01 /* long integer */ 44 #define LONGDBL 0x02 /* long double; unimplemented */ 45 #define SHORTINT 0x04 /* short integer */ 46 #define ALT 0x08 /* alternate form */ 47 #define LADJUST 0x10 /* left adjustment */ 48 static int flags; 49 50 static char sign, *buf; 51 52 x_doprnt(fmt, argp, fp) 53 register char *fmt; 54 va_list argp; 55 register FILE *fp; 56 { 57 register int cnt, n; 58 register char ch, *t; 59 double _double; 60 u_long _ulong; 61 int base, width, prec, size; 62 char padc, *digs, sbuf[MAXDIGIT]; 63 64 digs = "0123456789abcdef"; 65 if (!buf) 66 buf = sbuf; 67 for (cnt = 0;; ++fmt) { 68 n = fp->_cnt; 69 for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) 70 if (--n < 0 71 #ifdef NEGATIVE_COUNT_KLUDGE 72 && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 73 #endif 74 || ch == '\n' && fp->_flag&_IOLBF) { 75 fp->_cnt = n; 76 fp->_ptr = t; 77 (void)_flsbuf(ch, fp); 78 n = fp->_cnt; 79 t = fp->_ptr; 80 } 81 else 82 *t++ = ch; 83 fp->_cnt = n; 84 fp->_ptr = t; 85 if (!ch) 86 return(cnt); 87 88 flags = width = 0; 89 prec = -1; 90 padc = ' '; 91 sign = '\0'; 92 93 rflag: switch (*++fmt) { 94 case ' ': 95 sign = ' '; 96 goto rflag; 97 case '#': 98 flags |= ALT; 99 goto rflag; 100 case '*': 101 /* 102 * ``A negative field width argument is taken as a 103 * - flag followed by a positive field width.'' 104 * -- ANSI X3J11 105 * They don't exclude field widths read from args. 106 */ 107 if ((width = va_arg(argp, int)) >= 0) 108 goto rflag; 109 width = -width; 110 /*FALLTHROUGH*/ 111 case '-': 112 flags |= LADJUST; 113 goto rflag; 114 case '+': 115 sign = '+'; 116 goto rflag; 117 case '.': 118 if (*++fmt == '*') 119 n = va_arg(argp, int); 120 else if (isascii(*fmt) && isdigit(*fmt)) { 121 n = 0; 122 do { 123 n = 10 * n + *fmt - '0'; 124 } while (isascii(*++fmt) && isdigit(*fmt)); 125 --fmt; 126 } 127 else { 128 --fmt; 129 prec = 0; 130 goto rflag; 131 } 132 prec = n < 0 ? -1 : n; 133 goto rflag; 134 case '0': 135 padc = '0'; 136 /*FALLTHROUGH*/ 137 case '1': case '2': case '3': case '4': 138 case '5': case '6': case '7': case '8': case '9': 139 n = 0; 140 do { 141 n = 10 * n + *fmt - '0'; 142 } while (isascii(*++fmt) && isdigit(*fmt)); 143 width = n; 144 --fmt; 145 goto rflag; 146 case 'L': 147 /* 148 * C doesn't have a long double; use long for now. 149 * flags |= LONGDBL; 150 */ 151 flags |= LONGINT; 152 goto rflag; 153 case 'h': 154 flags |= SHORTINT; 155 goto rflag; 156 case 'l': 157 flags |= LONGINT; 158 goto rflag; 159 case 'c': 160 buf[0] = va_arg(argp, int); 161 size = 1; 162 t = buf; 163 goto pforw; 164 case 'd': 165 case 'i': 166 ARG(); 167 if ((long)_ulong < 0) { 168 _ulong = -_ulong; 169 sign = '-'; 170 } 171 if (sign) 172 PUTC(sign); 173 base = 10; 174 goto num; 175 case 'e': 176 case 'E': 177 case 'f': 178 case 'g': 179 case 'G': 180 _double = va_arg(argp, double); 181 size = _cvt(_double, prec, *fmt); 182 t = buf; 183 goto pforw; 184 case 'n': 185 if (flags&LONGDBL || flags&LONGINT) 186 *va_arg(argp, long *) = cnt; 187 else if (flags&SHORTINT) 188 *va_arg(argp, short *) = cnt; 189 else 190 *va_arg(argp, int *) = cnt; 191 break; 192 case 'o': 193 ARG(); 194 base = 8; 195 goto num; 196 case 'p': 197 /* 198 * ``The argument shall be a pointer to void. The 199 * value of the pointer is converted to a sequence 200 * of printable characters, in an implementation- 201 * defined manner.'' 202 * -- ANSI X3J11 203 */ 204 _ulong = (u_long)va_arg(argp, void *); 205 base = 16; 206 goto num; 207 case 's': 208 if (!(t = va_arg(argp, char *))) 209 t = "(null)"; 210 if (prec >= 0) { 211 /* 212 * can't use strlen; can only look for the 213 * NUL in the first `prec' characters, and 214 * strlen() will go further. 215 */ 216 char *p, *memchr(); 217 218 if (p = memchr(t, 0, prec)) { 219 size = p - t; 220 if (size > prec) 221 size = prec; 222 } 223 else 224 size = prec; 225 } 226 else 227 size = strlen(t); 228 pforw: if (!(flags&LADJUST) && width) 229 for (n = size; n++ < width;) 230 PUTC(padc); 231 if (fp->_cnt - (n = size) >= 0) { 232 cnt += n; 233 fp->_cnt -= n; 234 bcopy(t, fp->_ptr, n); 235 fp->_ptr += n; 236 } 237 else for (; n--; ++t) 238 PUTC(*t); 239 if (flags&LADJUST) 240 while (width-- > size) 241 PUTC(padc); 242 break; 243 case 'u': 244 ARG(); 245 base = 10; 246 goto num; 247 case 'X': 248 digs = "0123456789ABCDEF"; 249 /*FALLTHROUGH*/ 250 case 'x': 251 ARG(); 252 base = 16; 253 /* alternate form for hex; leading 0x/X */ 254 if (flags&ALT && _ulong) { 255 PUTC('0'); 256 PUTC(*fmt); 257 } 258 num: t = buf + MAXDIGIT - 1; 259 do { 260 *t-- = digs[_ulong % base]; 261 _ulong /= base; 262 } while(_ulong); 263 digs = "0123456789abcdef"; 264 size = buf + MAXDIGIT - 1 - t; 265 if (size >= prec) { 266 /* alternate form for octal; leading 0 */ 267 if (t[1] != '0' && flags&ALT && *fmt == 'o') { 268 *t-- = '0'; 269 ++size; 270 } 271 } 272 else 273 for (; size < prec; ++size) 274 *t-- = '0'; 275 if (!(flags&LADJUST)) 276 while (size++ < width) 277 PUTC(padc); 278 while (++t < buf + MAXDIGIT) 279 PUTC(*t); 280 for (; width > size; --width) 281 PUTC(padc); 282 break; 283 case '\0': /* "%?" prints ?, unless ? is NULL */ 284 return(cnt); 285 default: 286 PUTC(*fmt); 287 } 288 } 289 /*NOTREACHED*/ 290 } 291 292 #define EFORMAT 0x01 293 #define FFORMAT 0x02 294 #define GFORMAT 0x04 295 #define DEFPREC 6 296 297 static 298 _cvt(number, prec, fmtch) 299 double number; 300 register int prec; 301 char fmtch; 302 { 303 register char *p; 304 register int expcnt, format; 305 static int maxprec = MAXPREC; 306 double fract, integer, tmp, modf(); 307 int decpt; 308 char *endp, *savep, *startp, *malloc(); 309 310 if (prec == -1) 311 prec = DEFPREC; 312 313 /* allocate space for large precision */ 314 if (prec > maxprec) 315 buf = malloc((u_int)((maxprec = prec) + MAXFRAC + 1)); 316 317 startp = buf; 318 if (number < 0) { 319 *startp++ = '-'; 320 number = -number; 321 } 322 else if (sign) 323 *startp++ = sign; 324 325 switch(fmtch) { 326 case 'e': 327 case 'E': 328 format = EFORMAT; 329 break; 330 case 'f': 331 format = FFORMAT; 332 break; 333 case 'g': 334 case 'G': 335 format = GFORMAT; 336 fmtch -= 2; 337 } 338 339 /* 340 * if the alternate flag is set, or, at least one digit of precision 341 * was requested, add a decimal point, unless it's the g/G format 342 * in which case we require two digits of precision, as it counts 343 * precision differently. 344 */ 345 decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 346 347 expcnt = 0; 348 p = buf + maxprec + MAXFRAC; 349 endp = p + 1; 350 fract = modf(number, &integer); 351 if (integer) { 352 register char *p2; 353 354 /* get integer part of number; count decimal places */ 355 for (; integer; ++expcnt) { 356 tmp = modf(integer / 10, &integer); 357 *p-- = (int)((tmp + .03) * 10) + '0'; 358 } 359 360 /* copy, in reverse order, to start of buffer */ 361 p2 = startp; 362 *p2++ = *++p; 363 364 /* 365 * if the format is g/G, and the resulting exponent will be 366 * greater than the precision, use e/E format. If e/E format, 367 * put in a decimal point as needed, and decrement precision 368 * count for each digit after the decimal point. 369 */ 370 if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 371 if (format&GFORMAT) { 372 format |= EFORMAT; 373 374 /* first digit is precision for g/G format */ 375 if (prec) 376 --prec; 377 } 378 if (decpt) 379 *p2++ = '.'; 380 for (; ++p < endp && prec; --prec, *p2++ = *p); 381 382 /* precision ran out, round */ 383 if (p < endp) { 384 if (*p > '4') { 385 for (savep = p2--;; *p2-- = '0') { 386 if (*p2 == '.') 387 --p2; 388 if (++*p2 <= '9') 389 break; 390 } 391 p2 = savep; 392 } 393 fract = 0; 394 } 395 } 396 /* 397 * g/G in f format; if out of precision, replace digits with 398 * zeroes, note, have to round first. 399 */ 400 else if (format&GFORMAT) { 401 for (; ++p < endp && prec; --prec, *p2++ = *p); 402 /* precision ran out; round and then add zeroes */ 403 if (p < endp) { 404 if (*p > '4') { 405 for (savep = p2--; ++*p2 > '9'; 406 *p2-- = '0'); 407 p2 = savep; 408 } 409 do { 410 *p2++ = '0'; 411 } while (++p < endp); 412 fract = 0; 413 } 414 if (decpt) 415 *p2++ = '.'; 416 } 417 /* f format */ 418 else { 419 for (; ++p < endp; *p2++ = *p); 420 if (decpt) 421 *p2++ = '.'; 422 } 423 p = p2; 424 } 425 /* 426 * if no fraction, the number was zero, and if no precision, can't 427 * show anything after the decimal point. 428 */ 429 else if (!fract || !prec) { 430 *startp++ = '0'; 431 if (decpt && !(format&GFORMAT)) 432 *startp++ = '.'; 433 *startp = '\0'; 434 return(startp - buf); 435 } 436 /* 437 * if the format is g/G, and the resulting exponent will be less than 438 * -4 use e/E format. If e/E format, compute exponent value. 439 */ 440 else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 441 format |= EFORMAT; 442 if (fract) 443 for (p = startp; fract;) { 444 fract = modf(fract * 10, &tmp); 445 if (!tmp) { 446 --expcnt; 447 continue; 448 } 449 *p++ = (int)tmp + '0'; 450 break; 451 } 452 else 453 *p++ = '0'; 454 455 /* g/G format, decrement precision for first digit */ 456 if (format&GFORMAT && prec) 457 --prec; 458 459 /* add decimal after first non-zero digit */ 460 if (decpt) 461 *p++ = '.'; 462 } 463 /* 464 * f format or g/G printed as f format; don't worry about decimal 465 * point, if g/G format doesn't need it, will get stripped later. 466 */ 467 else { 468 p = startp; 469 *p++ = '0'; 470 *p++ = '.'; 471 } 472 473 /* finish out requested precision */ 474 while (fract && prec-- > 0) { 475 fract = modf(fract * 10, &tmp); 476 *p++ = (int)tmp + '0'; 477 } 478 while (prec-- > 0) 479 *p++ = '0'; 480 481 /* 482 * if any fractional value left, "round" it back up to the beginning 483 * of the number, fixing the exponent as necessary, and avoiding the 484 * decimal point. 485 */ 486 if (fract) { 487 (void)modf(fract * 10, &tmp); 488 if (tmp > 4) { 489 for (savep = p--;; *p-- = '0') { 490 if (*p == '.') 491 --p; 492 if (p == startp) { 493 *p = '1'; 494 ++expcnt; 495 break; 496 } 497 if (++*p <= '9') 498 break; 499 } 500 p = savep; 501 } 502 } 503 504 /* 505 * if a g/G format and not alternate flag, lose trailing zeroes, 506 * if e/E or g/G format, and last char is decimal point, lose it. 507 */ 508 if (!(flags&ALT)) { 509 if (format&GFORMAT) 510 for (; p[-1] == '0'; --p); 511 if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 512 --p; 513 } 514 515 /* if an e/E format, add exponent */ 516 if (format&EFORMAT) { 517 *p++ = fmtch; 518 if (--expcnt < 0) { 519 expcnt = -expcnt; 520 *p++ = '-'; 521 } 522 else 523 *p++ = '+'; 524 *p++ = expcnt / 10 + '0'; 525 *p++ = expcnt % 10 + '0'; 526 } 527 return(p - buf); 528 } 529