|
|
1.1 ! root 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 the above copyright notice and this paragraph are ! 7: * duplicated in all such forms and that any documentation, ! 8: * advertising materials, and other materials related to such ! 9: * distribution and use acknowledge that the software was developed ! 10: * by the University of California, Berkeley. The name of the ! 11: * University may not be used to endorse or promote products derived ! 12: * from this software without specific prior written permission. ! 13: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ! 14: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ! 15: * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ! 16: */ ! 17: ! 18: #if defined(LIBC_SCCS) && !defined(lint) ! 19: static char sccsid[] = "@(#)doprnt.c 5.35 (Berkeley) 6/27/88"; ! 20: #endif /* LIBC_SCCS and not lint */ ! 21: ! 22: #include <sys/types.h> ! 23: #include <varargs.h> ! 24: #include <stdio.h> ! 25: #include <ctype.h> ! 26: ! 27: /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ ! 28: #define MAXEXP 308 ! 29: /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ ! 30: #define MAXFRACT 39 ! 31: ! 32: #define DEFPREC 6 ! 33: ! 34: #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ ! 35: ! 36: #define PUTC(ch) (void) putc(ch, fp) ! 37: ! 38: #define ARG() \ ! 39: _ulong = flags&LONGINT ? va_arg(argp, long) : \ ! 40: flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); ! 41: ! 42: #define todigit(c) ((c) - '0') ! 43: #define tochar(n) ((n) + '0') ! 44: ! 45: /* have to deal with the negative buffer count kludge */ ! 46: #define NEGATIVE_COUNT_KLUDGE ! 47: ! 48: #define LONGINT 0x01 /* long integer */ ! 49: #define LONGDBL 0x02 /* long double; unimplemented */ ! 50: #define SHORTINT 0x04 /* short integer */ ! 51: #define ALT 0x08 /* alternate form */ ! 52: #define LADJUST 0x10 /* left adjustment */ ! 53: #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ ! 54: #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ ! 55: ! 56: _doprnt(fmt0, argp, fp) ! 57: u_char *fmt0; ! 58: va_list argp; ! 59: register FILE *fp; ! 60: { ! 61: register u_char *fmt; /* format string */ ! 62: register int ch; /* character from fmt */ ! 63: register int cnt; /* return value accumulator */ ! 64: register int n; /* random handy integer */ ! 65: register char *t; /* buffer pointer */ ! 66: double _double; /* double precision arguments %[eEfgG] */ ! 67: u_long _ulong; /* integer arguments %[diouxX] */ ! 68: int base; /* base for [diouxX] conversion */ ! 69: int dprec; /* decimal precision in [diouxX] */ ! 70: int fieldsz; /* field size expanded by sign, etc */ ! 71: int flags; /* flags as above */ ! 72: int fpprec; /* `extra' floating precision in [eEfgG] */ ! 73: int prec; /* precision from format (%.3d), or -1 */ ! 74: int realsz; /* field size expanded by decimal precision */ ! 75: int size; /* size of converted field or string */ ! 76: int width; /* width from format (%8d), or 0 */ ! 77: char sign; /* sign prefix (' ', '+', '-', or \0) */ ! 78: char softsign; /* temporary negative sign for floats */ ! 79: char *digs; /* digits for [diouxX] conversion */ ! 80: char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ ! 81: ! 82: if (fp->_flag & _IORW) { ! 83: fp->_flag |= _IOWRT; ! 84: fp->_flag &= ~(_IOEOF|_IOREAD); ! 85: } ! 86: if ((fp->_flag & _IOWRT) == 0) ! 87: return (EOF); ! 88: ! 89: fmt = fmt0; ! 90: digs = "0123456789abcdef"; ! 91: for (cnt = 0;; ++fmt) { ! 92: n = fp->_cnt; ! 93: for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%'; ! 94: ++cnt, ++fmt) ! 95: if (--n < 0 ! 96: #ifdef NEGATIVE_COUNT_KLUDGE ! 97: && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) ! 98: #endif ! 99: || ch == '\n' && fp->_flag & _IOLBF) { ! 100: fp->_cnt = n; ! 101: fp->_ptr = t; ! 102: (void) _flsbuf((u_char)ch, fp); ! 103: n = fp->_cnt; ! 104: t = (char *)fp->_ptr; ! 105: } else ! 106: *t++ = ch; ! 107: fp->_cnt = n; ! 108: fp->_ptr = t; ! 109: if (!ch) ! 110: return (cnt); ! 111: ! 112: flags = 0; dprec = 0; fpprec = 0; width = 0; ! 113: prec = -1; ! 114: sign = '\0'; ! 115: ! 116: rflag: switch (*++fmt) { ! 117: case ' ': ! 118: /* ! 119: * ``If the space and + flags both appear, the space ! 120: * flag will be ignored.'' ! 121: * -- ANSI X3J11 ! 122: */ ! 123: if (!sign) ! 124: sign = ' '; ! 125: goto rflag; ! 126: case '#': ! 127: flags |= ALT; ! 128: goto rflag; ! 129: case '*': ! 130: /* ! 131: * ``A negative field width argument is taken as a ! 132: * - flag followed by a positive field width.'' ! 133: * -- ANSI X3J11 ! 134: * They don't exclude field widths read from args. ! 135: */ ! 136: if ((width = va_arg(argp, int)) >= 0) ! 137: goto rflag; ! 138: width = -width; ! 139: /* FALLTHROUGH */ ! 140: case '-': ! 141: flags |= LADJUST; ! 142: goto rflag; ! 143: case '+': ! 144: sign = '+'; ! 145: goto rflag; ! 146: case '.': ! 147: if (*++fmt == '*') ! 148: n = va_arg(argp, int); ! 149: else { ! 150: n = 0; ! 151: while (isascii(*fmt) && isdigit(*fmt)) ! 152: n = 10 * n + todigit(*fmt++); ! 153: --fmt; ! 154: } ! 155: prec = n < 0 ? -1 : n; ! 156: goto rflag; ! 157: case '0': ! 158: /* ! 159: * ``Note that 0 is taken as a flag, not as the ! 160: * beginning of a field width.'' ! 161: * -- ANSI X3J11 ! 162: */ ! 163: flags |= ZEROPAD; ! 164: goto rflag; ! 165: case '1': case '2': case '3': case '4': ! 166: case '5': case '6': case '7': case '8': case '9': ! 167: n = 0; ! 168: do { ! 169: n = 10 * n + todigit(*fmt); ! 170: } while (isascii(*++fmt) && isdigit(*fmt)); ! 171: width = n; ! 172: --fmt; ! 173: goto rflag; ! 174: case 'L': ! 175: flags |= LONGDBL; ! 176: goto rflag; ! 177: case 'h': ! 178: flags |= SHORTINT; ! 179: goto rflag; ! 180: case 'l': ! 181: flags |= LONGINT; ! 182: goto rflag; ! 183: case 'c': ! 184: *(t = buf) = va_arg(argp, int); ! 185: size = 1; ! 186: sign = '\0'; ! 187: goto pforw; ! 188: case 'D': ! 189: flags |= LONGINT; ! 190: /*FALLTHROUGH*/ ! 191: case 'd': ! 192: case 'i': ! 193: ARG(); ! 194: if ((long)_ulong < 0) { ! 195: _ulong = -_ulong; ! 196: sign = '-'; ! 197: } ! 198: base = 10; ! 199: goto number; ! 200: case 'e': ! 201: case 'E': ! 202: case 'f': ! 203: case 'g': ! 204: case 'G': ! 205: _double = va_arg(argp, double); ! 206: /* ! 207: * don't do unrealistic precision; just pad it with ! 208: * zeroes later, so buffer size stays rational. ! 209: */ ! 210: if (prec > MAXFRACT) { ! 211: if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) ! 212: fpprec = prec - MAXFRACT; ! 213: prec = MAXFRACT; ! 214: } ! 215: else if (prec == -1) ! 216: prec = DEFPREC; ! 217: /* ! 218: * softsign avoids negative 0 if _double is < 0 and ! 219: * no significant digits will be shown ! 220: */ ! 221: if (_double < 0) { ! 222: softsign = '-'; ! 223: _double = -_double; ! 224: } ! 225: else ! 226: softsign = 0; ! 227: /* ! 228: * cvt may have to round up past the "start" of the ! 229: * buffer, i.e. ``intf("%.2f", (double)9.999);''; ! 230: * if the first char isn't NULL, it did. ! 231: */ ! 232: *buf = NULL; ! 233: size = cvt(_double, prec, flags, &softsign, *fmt, buf, ! 234: buf + sizeof(buf)); ! 235: if (softsign) ! 236: sign = '-'; ! 237: t = *buf ? buf : buf + 1; ! 238: goto pforw; ! 239: case 'n': ! 240: if (flags & LONGINT) ! 241: *va_arg(argp, long *) = cnt; ! 242: else if (flags & SHORTINT) ! 243: *va_arg(argp, short *) = cnt; ! 244: else ! 245: *va_arg(argp, int *) = cnt; ! 246: break; ! 247: case 'O': ! 248: flags |= LONGINT; ! 249: /*FALLTHROUGH*/ ! 250: case 'o': ! 251: ARG(); ! 252: base = 8; ! 253: goto nosign; ! 254: case 'p': ! 255: /* ! 256: * ``The argument shall be a pointer to void. The ! 257: * value of the pointer is converted to a sequence ! 258: * of printable characters, in an implementation- ! 259: * defined manner.'' ! 260: * -- ANSI X3J11 ! 261: */ ! 262: /* NOSTRICT */ ! 263: _ulong = (u_long)va_arg(argp, void *); ! 264: base = 16; ! 265: goto nosign; ! 266: case 's': ! 267: if (!(t = va_arg(argp, char *))) ! 268: t = "(null)"; ! 269: if (prec >= 0) { ! 270: /* ! 271: * can't use strlen; can only look for the ! 272: * NUL in the first `prec' characters, and ! 273: * strlen() will go further. ! 274: */ ! 275: char *p, *memchr(); ! 276: ! 277: if (p = memchr(t, 0, prec)) { ! 278: size = p - t; ! 279: if (size > prec) ! 280: size = prec; ! 281: } else ! 282: size = prec; ! 283: } else ! 284: size = strlen(t); ! 285: sign = '\0'; ! 286: goto pforw; ! 287: case 'U': ! 288: flags |= LONGINT; ! 289: /*FALLTHROUGH*/ ! 290: case 'u': ! 291: ARG(); ! 292: base = 10; ! 293: goto nosign; ! 294: case 'X': ! 295: digs = "0123456789ABCDEF"; ! 296: /* FALLTHROUGH */ ! 297: case 'x': ! 298: ARG(); ! 299: base = 16; ! 300: /* leading 0x/X only if non-zero */ ! 301: if (flags & ALT && _ulong != 0) ! 302: flags |= HEXPREFIX; ! 303: ! 304: /* unsigned conversions */ ! 305: nosign: sign = '\0'; ! 306: /* ! 307: * ``... diouXx conversions ... if a precision is ! 308: * specified, the 0 flag will be ignored.'' ! 309: * -- ANSI X3J11 ! 310: */ ! 311: number: if ((dprec = prec) >= 0) ! 312: flags &= ~ZEROPAD; ! 313: ! 314: /* ! 315: * ``The result of converting a zero value with an ! 316: * explicit precision of zero is no characters.'' ! 317: * -- ANSI X3J11 ! 318: */ ! 319: t = buf + BUF; ! 320: if (_ulong != 0 || prec != 0) { ! 321: do { ! 322: *--t = digs[_ulong % base]; ! 323: _ulong /= base; ! 324: } while (_ulong); ! 325: digs = "0123456789abcdef"; ! 326: if (flags & ALT && base == 8 && *t != '0') ! 327: *--t = '0'; /* octal leading 0 */ ! 328: } ! 329: size = buf + BUF - t; ! 330: ! 331: pforw: ! 332: /* ! 333: * All reasonable formats wind up here. At this point, ! 334: * `t' points to a string which (if not flags&LADJUST) ! 335: * should be padded out to `width' places. If ! 336: * flags&ZEROPAD, it should first be prefixed by any ! 337: * sign or other prefix; otherwise, it should be blank ! 338: * padded before the prefix is emitted. After any ! 339: * left-hand padding and prefixing, emit zeroes ! 340: * required by a decimal [diouxX] precision, then print ! 341: * the string proper, then emit zeroes required by any ! 342: * leftover floating precision; finally, if LADJUST, ! 343: * pad with blanks. ! 344: */ ! 345: ! 346: /* ! 347: * compute actual size, so we know how much to pad ! 348: * fieldsz excludes decimal prec; realsz includes it ! 349: */ ! 350: fieldsz = size + fpprec; ! 351: if (sign) ! 352: fieldsz++; ! 353: if (flags & HEXPREFIX) ! 354: fieldsz += 2; ! 355: realsz = dprec > fieldsz ? dprec : fieldsz; ! 356: ! 357: /* right-adjusting blank padding */ ! 358: if ((flags & (LADJUST|ZEROPAD)) == 0 && width) ! 359: for (n = realsz; n < width; n++) ! 360: PUTC(' '); ! 361: /* prefix */ ! 362: if (sign) ! 363: PUTC(sign); ! 364: if (flags & HEXPREFIX) { ! 365: PUTC('0'); ! 366: PUTC((char)*fmt); ! 367: } ! 368: /* right-adjusting zero padding */ ! 369: if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) ! 370: for (n = realsz; n < width; n++) ! 371: PUTC('0'); ! 372: /* leading zeroes from decimal precision */ ! 373: for (n = fieldsz; n < dprec; n++) ! 374: PUTC('0'); ! 375: ! 376: /* the string or number proper */ ! 377: if (fp->_cnt - (n = size) >= 0 && ! 378: (fp->_flag & _IOLBF) == 0) { ! 379: fp->_cnt -= n; ! 380: bcopy(t, (char *)fp->_ptr, n); ! 381: fp->_ptr += n; ! 382: } else ! 383: while (--n >= 0) ! 384: PUTC(*t++); ! 385: /* trailing f.p. zeroes */ ! 386: while (--fpprec >= 0) ! 387: PUTC('0'); ! 388: /* left-adjusting padding (always blank) */ ! 389: if (flags & LADJUST) ! 390: for (n = realsz; n < width; n++) ! 391: PUTC(' '); ! 392: /* finally, adjust cnt */ ! 393: cnt += width > realsz ? width : realsz; ! 394: break; ! 395: case '\0': /* "%?" prints ?, unless ? is NULL */ ! 396: return (cnt); ! 397: default: ! 398: PUTC((char)*fmt); ! 399: cnt++; ! 400: } ! 401: } ! 402: /* NOTREACHED */ ! 403: } ! 404: ! 405: static ! 406: cvt(number, prec, flags, signp, fmtch, startp, endp) ! 407: double number; ! 408: register int prec; ! 409: int flags; ! 410: u_char fmtch; ! 411: char *signp, *startp, *endp; ! 412: { ! 413: register char *p, *t; ! 414: register double fract; ! 415: int dotrim, expcnt, gformat; ! 416: double integer, tmp, modf(); ! 417: char *exponent(), *round(); ! 418: ! 419: dotrim = expcnt = gformat = 0; ! 420: fract = modf(number, &integer); ! 421: ! 422: /* get an extra slot for rounding. */ ! 423: t = ++startp; ! 424: ! 425: /* ! 426: * get integer portion of number; put into the end of the buffer; the ! 427: * .01 is added for modf(356.0 / 10, &integer) returning .59999999... ! 428: */ ! 429: for (p = endp - 1; integer; ++expcnt) { ! 430: tmp = modf(integer / 10, &integer); ! 431: *p-- = tochar((int)((tmp + .01) * 10)); ! 432: } ! 433: switch(fmtch) { ! 434: case 'f': ! 435: /* reverse integer into beginning of buffer */ ! 436: if (expcnt) ! 437: for (; ++p < endp; *t++ = *p); ! 438: else ! 439: *t++ = '0'; ! 440: /* ! 441: * if precision required or alternate flag set, add in a ! 442: * decimal point. ! 443: */ ! 444: if (prec || flags&ALT) ! 445: *t++ = '.'; ! 446: /* if requires more precision and some fraction left */ ! 447: if (fract) { ! 448: if (prec) ! 449: do { ! 450: fract = modf(fract * 10, &tmp); ! 451: *t++ = tochar((int)tmp); ! 452: } while (--prec && fract); ! 453: if (fract) ! 454: startp = round(fract, (int *)NULL, startp, ! 455: t - 1, (char)0, signp); ! 456: } ! 457: for (; prec--; *t++ = '0'); ! 458: break; ! 459: case 'e': ! 460: case 'E': ! 461: eformat: if (expcnt) { ! 462: *t++ = *++p; ! 463: if (prec || flags&ALT) ! 464: *t++ = '.'; ! 465: /* if requires more precision and some integer left */ ! 466: for (; prec && ++p < endp; --prec) ! 467: *t++ = *p; ! 468: /* ! 469: * if done precision and more of the integer component, ! 470: * round using it; adjust fract so we don't re-round ! 471: * later. ! 472: */ ! 473: if (!prec && ++p < endp) { ! 474: fract = 0; ! 475: startp = round((double)0, &expcnt, startp, ! 476: t - 1, *p, signp); ! 477: } ! 478: /* adjust expcnt for digit in front of decimal */ ! 479: --expcnt; ! 480: } ! 481: /* until first fractional digit, decrement exponent */ ! 482: else if (fract) { ! 483: /* adjust expcnt for digit in front of decimal */ ! 484: for (expcnt = -1;; --expcnt) { ! 485: fract = modf(fract * 10, &tmp); ! 486: if (tmp) ! 487: break; ! 488: } ! 489: *t++ = tochar((int)tmp); ! 490: if (prec || flags&ALT) ! 491: *t++ = '.'; ! 492: } ! 493: else { ! 494: *t++ = '0'; ! 495: if (prec || flags&ALT) ! 496: *t++ = '.'; ! 497: } ! 498: /* if requires more precision and some fraction left */ ! 499: if (fract) { ! 500: if (prec) ! 501: do { ! 502: fract = modf(fract * 10, &tmp); ! 503: *t++ = tochar((int)tmp); ! 504: } while (--prec && fract); ! 505: if (fract) ! 506: startp = round(fract, &expcnt, startp, ! 507: t - 1, (char)0, signp); ! 508: } ! 509: /* if requires more precision */ ! 510: for (; prec--; *t++ = '0'); ! 511: ! 512: /* unless alternate flag, trim any g/G format trailing 0's */ ! 513: if (gformat && !(flags&ALT)) { ! 514: while (t > startp && *--t == '0'); ! 515: if (*t == '.') ! 516: --t; ! 517: ++t; ! 518: } ! 519: t = exponent(t, expcnt, fmtch); ! 520: break; ! 521: case 'g': ! 522: case 'G': ! 523: /* a precision of 0 is treated as a precision of 1. */ ! 524: if (!prec) ! 525: ++prec; ! 526: /* ! 527: * ``The style used depends on the value converted; style e ! 528: * will be used only if the exponent resulting from the ! 529: * conversion is less than -4 or greater than the precision.'' ! 530: * -- ANSI X3J11 ! 531: */ ! 532: if (expcnt > prec || !expcnt && fract && fract < .0001) { ! 533: /* ! 534: * g/G format counts "significant digits, not digits of ! 535: * precision; for the e/E format, this just causes an ! 536: * off-by-one problem, i.e. g/G considers the digit ! 537: * before the decimal point significant and e/E doesn't ! 538: * count it as precision. ! 539: */ ! 540: --prec; ! 541: fmtch -= 2; /* G->E, g->e */ ! 542: gformat = 1; ! 543: goto eformat; ! 544: } ! 545: /* ! 546: * reverse integer into beginning of buffer, ! 547: * note, decrement precision ! 548: */ ! 549: if (expcnt) ! 550: for (; ++p < endp; *t++ = *p, --prec); ! 551: else ! 552: *t++ = '0'; ! 553: /* ! 554: * if precision required or alternate flag set, add in a ! 555: * decimal point. If no digits yet, add in leading 0. ! 556: */ ! 557: if (prec || flags&ALT) { ! 558: dotrim = 1; ! 559: *t++ = '.'; ! 560: } ! 561: else ! 562: dotrim = 0; ! 563: /* if requires more precision and some fraction left */ ! 564: if (fract) { ! 565: if (prec) { ! 566: do { ! 567: fract = modf(fract * 10, &tmp); ! 568: *t++ = tochar((int)tmp); ! 569: } while(!tmp); ! 570: while (--prec && fract) { ! 571: fract = modf(fract * 10, &tmp); ! 572: *t++ = tochar((int)tmp); ! 573: } ! 574: } ! 575: if (fract) ! 576: startp = round(fract, (int *)NULL, startp, ! 577: t - 1, (char)0, signp); ! 578: } ! 579: /* alternate format, adds 0's for precision, else trim 0's */ ! 580: if (flags&ALT) ! 581: for (; prec--; *t++ = '0'); ! 582: else if (dotrim) { ! 583: while (t > startp && *--t == '0'); ! 584: if (*t != '.') ! 585: ++t; ! 586: } ! 587: } ! 588: return(t - startp); ! 589: } ! 590: ! 591: static char * ! 592: round(fract, exp, start, end, ch, signp) ! 593: double fract; ! 594: int *exp; ! 595: register char *start, *end; ! 596: char ch, *signp; ! 597: { ! 598: double tmp; ! 599: ! 600: if (fract) ! 601: (void)modf(fract * 10, &tmp); ! 602: else ! 603: tmp = todigit(ch); ! 604: if (tmp > 4) ! 605: for (;; --end) { ! 606: if (*end == '.') ! 607: --end; ! 608: if (++*end <= '9') ! 609: break; ! 610: *end = '0'; ! 611: if (end == start) { ! 612: if (exp) { /* e/E; increment exponent */ ! 613: *end = '1'; ! 614: ++*exp; ! 615: } ! 616: else { /* f; add extra digit */ ! 617: *--end = '1'; ! 618: --start; ! 619: } ! 620: break; ! 621: } ! 622: } ! 623: /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ ! 624: else if (*signp == '-') ! 625: for (;; --end) { ! 626: if (*end == '.') ! 627: --end; ! 628: if (*end != '0') ! 629: break; ! 630: if (end == start) ! 631: *signp = 0; ! 632: } ! 633: return(start); ! 634: } ! 635: ! 636: static char * ! 637: exponent(p, exp, fmtch) ! 638: register char *p; ! 639: register int exp; ! 640: u_char fmtch; ! 641: { ! 642: register char *t; ! 643: char expbuf[MAXEXP]; ! 644: ! 645: *p++ = fmtch; ! 646: if (exp < 0) { ! 647: exp = -exp; ! 648: *p++ = '-'; ! 649: } ! 650: else ! 651: *p++ = '+'; ! 652: t = expbuf + MAXEXP; ! 653: if (exp > 9) { ! 654: do { ! 655: *--t = tochar(exp % 10); ! 656: } while ((exp /= 10) > 9); ! 657: *--t = tochar(exp); ! 658: for (; t < expbuf + MAXEXP; *p++ = *t++); ! 659: } ! 660: else { ! 661: *p++ = '0'; ! 662: *p++ = tochar(exp); ! 663: } ! 664: return(p); ! 665: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.