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.25 (Berkeley) 05/18/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 * ``The result of converting a zero value with an 252 * explicit precision of zero is no characters.'' 253 * -- ANSI X3J11 254 */ 255 number: if (!_ulong && !prec) 256 break; 257 258 t = buf + BUF - 1; 259 do { 260 *t-- = digs[_ulong % base]; 261 _ulong /= base; 262 } while(_ulong); 263 for (size = buf + BUF - 1 - t; size < prec; ++size) 264 *t-- = '0'; 265 digs = "0123456789abcdef"; 266 267 /* alternate mode for hex and octal numbers */ 268 if (flags&ALT) 269 switch (base) { 270 case 16: 271 /* avoid "00000x35" */ 272 if (padc == ' ') { 273 *t-- = *fmt; 274 *t-- = '0'; 275 size += 2; 276 } 277 else { 278 PUTC('0'); 279 PUTC(*fmt); 280 width -= 2; 281 } 282 break; 283 case 8: 284 if (t[1] != '0') { 285 *t-- = '0'; 286 ++size; 287 } 288 break; 289 } 290 291 if (sign) { 292 /* avoid "0000-3" */ 293 if (padc == ' ') { 294 *t-- = sign; 295 ++size; 296 } 297 else { 298 PUTC(sign); 299 --width; 300 } 301 } 302 ++t; 303 304 pforw: if (!(flags&LADJUST) && width) 305 for (n = size + fpprec; n++ < width;) 306 PUTC(padc); 307 if (fp->_cnt - (n = size) >= 0) { 308 cnt += n; 309 fp->_cnt -= n; 310 bcopy(t, fp->_ptr, n); 311 fp->_ptr += n; 312 } 313 else for (; n--; ++t) 314 PUTC(*t); 315 while (fpprec--) 316 PUTC('0'); 317 if (flags&LADJUST) 318 for (n = size + fpprec; ++n < width;) 319 PUTC(' '); 320 break; 321 case '\0': /* "%?" prints ?, unless ? is NULL */ 322 return(cnt); 323 default: 324 PUTC(*fmt); 325 } 326 } 327 /*NOTREACHED*/ 328 } 329 330 #define EFORMAT 0x01 331 #define FFORMAT 0x02 332 #define GFORMAT 0x04 333 #define DEFPREC 6 334 335 static char * 336 _cvt(number, prec, flags, fmtch, padc, sign, startp, endp) 337 double number; 338 register int prec; 339 int flags; 340 u_char fmtch; 341 char padc, *sign, *startp, *endp; 342 { 343 register char *p; 344 register int expcnt, format; 345 double fract, integer, tmp, modf(); 346 int decpt; 347 char *savep; 348 349 if (prec == -1) 350 prec = DEFPREC; 351 352 if (number < 0) { 353 *sign = '-'; 354 number = -number; 355 } 356 357 /* if blank padded, add sign in as part of the number */ 358 if (*sign && padc == ' ') 359 *startp++ = *sign; 360 361 switch(fmtch) { 362 case 'e': 363 case 'E': 364 format = EFORMAT; 365 break; 366 case 'f': 367 format = FFORMAT; 368 break; 369 case 'g': 370 case 'G': 371 format = GFORMAT; 372 fmtch -= 2; 373 } 374 375 /* 376 * if the alternate flag is set, or, at least one digit of precision 377 * was requested, add a decimal point, unless it's the g/G format 378 * in which case we require two digits of precision, as it counts 379 * precision differently. 380 */ 381 decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 382 383 expcnt = 0; 384 p = endp - 1; 385 fract = modf(number, &integer); 386 if (integer) { 387 register char *p2; 388 389 /* get integer part of number; count decimal places */ 390 for (; integer; ++expcnt) { 391 tmp = modf(integer / 10, &integer); 392 *p-- = (int)((tmp + .03) * 10) + '0'; 393 } 394 395 /* copy, in reverse order, to start of buffer */ 396 p2 = startp; 397 *p2++ = *++p; 398 399 /* 400 * if the format is g/G, and the resulting exponent will be 401 * greater than the precision, use e/E format. If e/E format, 402 * put in a decimal point as needed, and decrement precision 403 * count for each digit after the decimal point. 404 */ 405 if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 406 if (format&GFORMAT) { 407 format |= EFORMAT; 408 409 /* first digit is precision for g/G format */ 410 if (prec) 411 --prec; 412 } 413 if (decpt) 414 *p2++ = '.'; 415 for (; ++p < endp && prec; --prec, *p2++ = *p); 416 417 /* precision ran out, round */ 418 if (p < endp) { 419 if (*p > '4') { 420 for (savep = p2--;; *p2-- = '0') { 421 if (*p2 == '.') 422 --p2; 423 if (++*p2 <= '9') 424 break; 425 } 426 p2 = savep; 427 } 428 fract = 0; 429 } 430 } 431 /* 432 * g/G in f format; if out of precision, replace digits with 433 * zeroes, note, have to round first. 434 */ 435 else if (format&GFORMAT) { 436 for (; ++p < endp && prec; --prec, *p2++ = *p); 437 /* precision ran out; round and then add zeroes */ 438 if (p < endp) { 439 if (*p > '4') { 440 for (savep = p2--; ++*p2 > '9'; 441 *p2-- = '0'); 442 p2 = savep; 443 } 444 do { 445 *p2++ = '0'; 446 } while (++p < endp); 447 fract = 0; 448 } 449 if (decpt) 450 *p2++ = '.'; 451 } 452 /* f format */ 453 else { 454 for (; ++p < endp; *p2++ = *p); 455 if (decpt) 456 *p2++ = '.'; 457 } 458 p = p2; 459 } 460 /* 461 * if no fraction, the number was zero, and if no precision, can't 462 * show anything after the decimal point. 463 */ 464 else if (!fract || !prec) { 465 *startp++ = '0'; 466 if (decpt && !(format&GFORMAT)) 467 *startp++ = '.'; 468 *startp = '\0'; 469 return(startp); 470 } 471 /* 472 * if the format is g/G, and the resulting exponent will be less than 473 * -4 use e/E format. If e/E format, compute exponent value. 474 */ 475 else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 476 format |= EFORMAT; 477 if (fract) 478 for (p = startp; fract;) { 479 fract = modf(fract * 10, &tmp); 480 if (!tmp) { 481 --expcnt; 482 continue; 483 } 484 *p++ = (int)tmp + '0'; 485 break; 486 } 487 else 488 *p++ = '0'; 489 490 /* g/G format, decrement precision for first digit */ 491 if (format&GFORMAT && prec) 492 --prec; 493 494 /* add decimal after first non-zero digit */ 495 if (decpt) 496 *p++ = '.'; 497 } 498 /* 499 * f format or g/G printed as f format; don't worry about decimal 500 * point, if g/G format doesn't need it, will get stripped later. 501 */ 502 else { 503 p = startp; 504 *p++ = '0'; 505 *p++ = '.'; 506 } 507 508 /* finish out requested precision */ 509 while (fract && prec-- > 0) { 510 fract = modf(fract * 10, &tmp); 511 *p++ = (int)tmp + '0'; 512 } 513 while (prec-- > 0) 514 *p++ = '0'; 515 516 /* 517 * if any fractional value left, "round" it back up to the beginning 518 * of the number, fixing the exponent as necessary, and avoiding the 519 * decimal point. 520 */ 521 if (fract) { 522 (void)modf(fract * 10, &tmp); 523 if (tmp > 4) { 524 for (savep = p--;; *p-- = '0') { 525 if (*p == '.') 526 --p; 527 if (p == startp) { 528 *p = '1'; 529 ++expcnt; 530 break; 531 } 532 if (++*p <= '9') 533 break; 534 } 535 p = savep; 536 } 537 } 538 539 /* 540 * if a g/G format and not alternate flag, lose trailing zeroes, 541 * if e/E or g/G format, and last char is decimal point, lose it. 542 */ 543 if (!(flags&ALT)) { 544 if (format&GFORMAT) 545 for (; p[-1] == '0'; --p); 546 if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 547 --p; 548 } 549 550 /* if an e/E format, add exponent */ 551 if (format&EFORMAT) { 552 *p++ = fmtch; 553 if (--expcnt < 0) { 554 expcnt = -expcnt; 555 *p++ = '-'; 556 } 557 else 558 *p++ = '+'; 559 *p++ = expcnt / 10 + '0'; 560 *p++ = expcnt % 10 + '0'; 561 } 562 return(p); 563 } 564