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