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