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