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