Annotation of prex/usr/lib/libc/stdio/vfprintf.c, Revision 1.1
1.1 ! nbrk 1: /*-
! 2: * Copyright (c) 1990, 1993
! 3: * The Regents of the University of California. All rights reserved.
! 4: *
! 5: * This code is derived from software contributed to Berkeley by
! 6: * Chris Torek.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. Neither the name of the University nor the names of its contributors
! 17: * may be used to endorse or promote products derived from this software
! 18: * without specific prior written permission.
! 19: *
! 20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 30: * SUCH DAMAGE.
! 31: */
! 32:
! 33: /*
! 34: * Actual printf innards.
! 35: *
! 36: * This code is large and complicated...
! 37: */
! 38:
! 39: #include <sys/types.h>
! 40:
! 41: #include <limits.h>
! 42: #include <stdio.h>
! 43: #include <stdlib.h>
! 44: #include <string.h>
! 45: #include <stdarg.h>
! 46:
! 47: #include "local.h"
! 48: #include "fvwrite.h"
! 49:
! 50: /*
! 51: * Flush out all the vectors defined by the given uio,
! 52: * then reset it so that it can be reused.
! 53: */
! 54: static int
! 55: __sprint(FILE *fp, struct __suio *uio)
! 56: {
! 57: int err;
! 58:
! 59: if (uio->uio_resid == 0) {
! 60: uio->uio_iovcnt = 0;
! 61: return (0);
! 62: }
! 63: err = __sfvwrite(fp, uio);
! 64: uio->uio_resid = 0;
! 65: uio->uio_iovcnt = 0;
! 66: return (err);
! 67: }
! 68:
! 69: /*
! 70: * Helper function for `fprintf to unbuffered unix file': creates a
! 71: * temporary buffer. We only work on write-only files; this avoids
! 72: * worries about ungetc buffers and so forth.
! 73: */
! 74: static int
! 75: __sbprintf(FILE *fp, const char *fmt, va_list ap)
! 76: {
! 77: int ret;
! 78: FILE fake;
! 79: unsigned char buf[BUFSIZ];
! 80:
! 81: /* copy the important variables */
! 82: fake._flags = fp->_flags & ~__SNBF;
! 83: fake._file = fp->_file;
! 84:
! 85: /* set up the buffer */
! 86: fake._bf._base = fake._p = buf;
! 87: fake._bf._size = fake._w = sizeof(buf);
! 88:
! 89: /* do the work, then copy any error status */
! 90: ret = vfprintf(&fake, fmt, ap);
! 91: if (ret >= 0 && fflush(&fake))
! 92: ret = EOF;
! 93: if (fake._flags & __SERR)
! 94: fp->_flags |= __SERR;
! 95: return (ret);
! 96: }
! 97:
! 98: /*
! 99: * Macros for converting digits to letters and vice versa
! 100: */
! 101: #define to_digit(c) ((c) - '0')
! 102: #define is_digit(c) ((unsigned)to_digit(c) <= 9)
! 103: #define to_char(n) ((n) + '0')
! 104:
! 105: /*
! 106: * Convert an unsigned long to ASCII for printf purposes, returning
! 107: * a pointer to the first character of the string representation.
! 108: * Octal numbers can be forced to have a leading zero; hex numbers
! 109: * use the given digits.
! 110: */
! 111: static char *
! 112: __ultoa(u_long val, char *endp, int base, int octzero, char *xdigs)
! 113: {
! 114: char *cp = endp;
! 115: long sval;
! 116:
! 117: /*
! 118: * Handle the three cases separately, in the hope of getting
! 119: * better/faster code.
! 120: */
! 121: switch (base) {
! 122: case 10:
! 123: if (val < 10) { /* many numbers are 1 digit */
! 124: *--cp = to_char(val);
! 125: return (cp);
! 126: }
! 127: /*
! 128: * On many machines, unsigned arithmetic is harder than
! 129: * signed arithmetic, so we do at most one unsigned mod and
! 130: * divide; this is sufficient to reduce the range of
! 131: * the incoming value to where signed arithmetic works.
! 132: */
! 133: if (val > LONG_MAX) {
! 134: *--cp = to_char(val % 10);
! 135: sval = val / 10;
! 136: } else
! 137: sval = val;
! 138: do {
! 139: *--cp = to_char(sval % 10);
! 140: sval /= 10;
! 141: } while (sval != 0);
! 142: break;
! 143:
! 144: case 8:
! 145: do {
! 146: *--cp = to_char(val & 7);
! 147: val >>= 3;
! 148: } while (val);
! 149: if (octzero && *cp != '0')
! 150: *--cp = '0';
! 151: break;
! 152:
! 153: case 16:
! 154: do {
! 155: *--cp = xdigs[val & 15];
! 156: val >>= 4;
! 157: } while (val);
! 158: break;
! 159:
! 160: default: /* oops */
! 161: abort();
! 162: }
! 163: return (cp);
! 164: }
! 165:
! 166:
! 167: #define BUF 32
! 168:
! 169:
! 170: /*
! 171: * Flags used during conversion.
! 172: */
! 173: #define ALT 0x001 /* alternate form */
! 174: #define HEXPREFIX 0x002 /* add 0x or 0X prefix */
! 175: #define LADJUST 0x004 /* left adjustment */
! 176: #define LONGDBL 0x008 /* long double; unimplemented */
! 177: #define LONGINT 0x010 /* long integer */
! 178: #define SHORTINT 0x040 /* short integer */
! 179: #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
! 180: int
! 181: vfprintf(fp, fmt0, ap)
! 182: FILE *fp;
! 183: const char *fmt0;
! 184: va_list ap;
! 185: {
! 186: char *fmt; /* format string */
! 187: int ch; /* character from fmt */
! 188: int n; /* handy integer (short term usage) */
! 189: char *cp; /* handy char pointer (short term usage) */
! 190: struct __siov *iovp; /* for PRINT macro */
! 191: int flags; /* flags as above */
! 192: int ret; /* return value accumulator */
! 193: int width; /* width from format (%8d), or 0 */
! 194: int prec; /* precision from format (%.3d), or -1 */
! 195: char sign; /* sign prefix (' ', '+', '-', or \0) */
! 196: u_long ulval; /* integer arguments %[diouxX] */
! 197: int base; /* base for [diouxX] conversion */
! 198: int dprec; /* a copy of prec if [diouxX], 0 otherwise */
! 199: int fieldsz; /* field size expanded by sign, etc */
! 200: int realsz; /* field size expanded by dprec */
! 201: int size; /* size of converted field or string */
! 202: char *xdigs = NULL; /* digits for [xX] conversion */
! 203: #define NIOV 8
! 204: struct __suio uio; /* output information: summary */
! 205: struct __siov iov[NIOV];/* ... and individual io vectors */
! 206: char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
! 207: char ox[2]; /* space for 0x hex-prefix */
! 208:
! 209: /*
! 210: * Choose PADSIZE to trade efficiency vs. size. If larger printf
! 211: * fields occur frequently, increase PADSIZE and make the initialisers
! 212: * below longer.
! 213: */
! 214: #define PADSIZE 16 /* pad chunk size */
! 215: static char blanks[PADSIZE] =
! 216: {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
! 217: static char zeroes[PADSIZE] =
! 218: {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
! 219:
! 220: /*
! 221: * BEWARE, these `goto error' on error, and PAD uses `n'.
! 222: */
! 223: #define PRINT(ptr, len) { \
! 224: iovp->iov_base = (ptr); \
! 225: iovp->iov_len = (len); \
! 226: uio.uio_resid += (len); \
! 227: iovp++; \
! 228: if (++uio.uio_iovcnt >= NIOV) { \
! 229: if (__sprint(fp, &uio)) \
! 230: goto error; \
! 231: iovp = iov; \
! 232: } \
! 233: }
! 234: #define PAD(howmany, with) { \
! 235: if ((n = (howmany)) > 0) { \
! 236: while (n > PADSIZE) { \
! 237: PRINT(with, PADSIZE); \
! 238: n -= PADSIZE; \
! 239: } \
! 240: PRINT(with, n); \
! 241: } \
! 242: }
! 243: #define FLUSH() { \
! 244: if (uio.uio_resid && __sprint(fp, &uio)) \
! 245: goto error; \
! 246: uio.uio_iovcnt = 0; \
! 247: iovp = iov; \
! 248: }
! 249:
! 250: /*
! 251: * To extend shorts properly, we need both signed and unsigned
! 252: * argument extraction methods.
! 253: */
! 254: #define SARG() \
! 255: (flags&LONGINT ? va_arg(ap, long) : \
! 256: flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
! 257: (long)va_arg(ap, int))
! 258: #define UARG() \
! 259: (flags&LONGINT ? va_arg(ap, u_long) : \
! 260: flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
! 261: (u_long)va_arg(ap, u_int))
! 262:
! 263: /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
! 264: if (cantwrite(fp))
! 265: return (EOF);
! 266:
! 267: /* optimise fprintf(stderr) (and other unbuffered Unix files) */
! 268: if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
! 269: fp->_file >= 0)
! 270: return (__sbprintf(fp, fmt0, ap));
! 271:
! 272: fmt = (char *)fmt0;
! 273: uio.uio_iov = iovp = iov;
! 274: uio.uio_resid = 0;
! 275: uio.uio_iovcnt = 0;
! 276: ret = 0;
! 277:
! 278: /*
! 279: * Scan the format for conversions (`%' character).
! 280: */
! 281: for (;;) {
! 282: for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
! 283: /* void */;
! 284: if ((n = fmt - cp) != 0) {
! 285: PRINT(cp, n);
! 286: ret += n;
! 287: }
! 288: if (ch == '\0')
! 289: goto done;
! 290: fmt++; /* skip over '%' */
! 291:
! 292: flags = 0;
! 293: dprec = 0;
! 294: width = 0;
! 295: prec = -1;
! 296: sign = '\0';
! 297:
! 298: rflag: ch = *fmt++;
! 299: reswitch: switch (ch) {
! 300: case ' ':
! 301: /*
! 302: * ``If the space and + flags both appear, the space
! 303: * flag will be ignored.''
! 304: * -- ANSI X3J11
! 305: */
! 306: if (!sign)
! 307: sign = ' ';
! 308: goto rflag;
! 309: case '#':
! 310: flags |= ALT;
! 311: goto rflag;
! 312: case '*':
! 313: /*
! 314: * ``A negative field width argument is taken as a
! 315: * - flag followed by a positive field width.''
! 316: * -- ANSI X3J11
! 317: * They don't exclude field widths read from args.
! 318: */
! 319: if ((width = va_arg(ap, int)) >= 0)
! 320: goto rflag;
! 321: width = -width;
! 322: /* FALLTHROUGH */
! 323: case '-':
! 324: flags |= LADJUST;
! 325: goto rflag;
! 326: case '+':
! 327: sign = '+';
! 328: goto rflag;
! 329: case '.':
! 330: if ((ch = *fmt++) == '*') {
! 331: n = va_arg(ap, int);
! 332: prec = n < 0 ? -1 : n;
! 333: goto rflag;
! 334: }
! 335: n = 0;
! 336: while (is_digit(ch)) {
! 337: n = 10 * n + to_digit(ch);
! 338: ch = *fmt++;
! 339: }
! 340: prec = n < 0 ? -1 : n;
! 341: goto reswitch;
! 342: case '0':
! 343: /*
! 344: * ``Note that 0 is taken as a flag, not as the
! 345: * beginning of a field width.''
! 346: * -- ANSI X3J11
! 347: */
! 348: flags |= ZEROPAD;
! 349: goto rflag;
! 350: case '1': case '2': case '3': case '4':
! 351: case '5': case '6': case '7': case '8': case '9':
! 352: n = 0;
! 353: do {
! 354: n = 10 * n + to_digit(ch);
! 355: ch = *fmt++;
! 356: } while (is_digit(ch));
! 357: width = n;
! 358: goto reswitch;
! 359: case 'h':
! 360: flags |= SHORTINT;
! 361: goto rflag;
! 362: case 'l':
! 363: flags |= LONGINT;
! 364: goto rflag;
! 365: case 'c':
! 366: *(cp = buf) = va_arg(ap, int);
! 367: size = 1;
! 368: sign = '\0';
! 369: break;
! 370: case 'D':
! 371: flags |= LONGINT;
! 372: /*FALLTHROUGH*/
! 373: case 'd':
! 374: case 'i':
! 375: ulval = SARG();
! 376: if ((long)ulval < 0) {
! 377: ulval = -ulval;
! 378: sign = '-';
! 379: }
! 380: base = 10;
! 381: goto number;
! 382: case 'n':
! 383: if (flags & LONGINT)
! 384: *va_arg(ap, long *) = ret;
! 385: else if (flags & SHORTINT)
! 386: *va_arg(ap, short *) = ret;
! 387: else
! 388: *va_arg(ap, int *) = ret;
! 389: continue; /* no output */
! 390: case 'O':
! 391: flags |= LONGINT;
! 392: /*FALLTHROUGH*/
! 393: case 'o':
! 394: ulval = UARG();
! 395: base = 8;
! 396: goto nosign;
! 397: case 'p':
! 398: /*
! 399: * ``The argument shall be a pointer to void. The
! 400: * value of the pointer is converted to a sequence
! 401: * of printable characters, in an implementation-
! 402: * defined manner.''
! 403: * -- ANSI X3J11
! 404: */
! 405: ulval = (u_long)va_arg(ap, void *);
! 406: base = 16;
! 407: xdigs = "0123456789abcdef";
! 408: flags |= HEXPREFIX;
! 409: ch = 'x';
! 410: goto nosign;
! 411: case 's':
! 412: if ((cp = va_arg(ap, char *)) == NULL)
! 413: cp = "(null)";
! 414: if (prec >= 0) {
! 415: /*
! 416: * can't use strlen; can only look for the
! 417: * NUL in the first `prec' characters, and
! 418: * strlen() will go further.
! 419: */
! 420: char *p = memchr(cp, 0, prec);
! 421:
! 422: if (p != NULL) {
! 423: size = p - cp;
! 424: if (size > prec)
! 425: size = prec;
! 426: } else
! 427: size = prec;
! 428: } else
! 429: size = strlen(cp);
! 430: sign = '\0';
! 431: break;
! 432: case 'U':
! 433: flags |= LONGINT;
! 434: /*FALLTHROUGH*/
! 435: case 'u':
! 436: ulval = UARG();
! 437: base = 10;
! 438: goto nosign;
! 439: case 'X':
! 440: xdigs = "0123456789ABCDEF";
! 441: goto hex;
! 442: case 'x':
! 443: xdigs = "0123456789abcdef";
! 444: hex:
! 445: ulval = UARG();
! 446: base = 16;
! 447: /* leading 0x/X only if non-zero */
! 448: if (flags & ALT && ulval != 0)
! 449: flags |= HEXPREFIX;
! 450:
! 451: /* unsigned conversions */
! 452: nosign: sign = '\0';
! 453: /*
! 454: * ``... diouXx conversions ... if a precision is
! 455: * specified, the 0 flag will be ignored.''
! 456: * -- ANSI X3J11
! 457: */
! 458: number: if ((dprec = prec) >= 0)
! 459: flags &= ~ZEROPAD;
! 460:
! 461: /*
! 462: * ``The result of converting a zero value with an
! 463: * explicit precision of zero is no characters.''
! 464: * -- ANSI X3J11
! 465: */
! 466: cp = buf + BUF;
! 467: if (ulval != 0 || prec != 0)
! 468: cp = __ultoa(ulval, cp, base,
! 469: flags & ALT, xdigs);
! 470: size = buf + BUF - cp;
! 471: break;
! 472: default: /* "%?" prints ?, unless ? is NUL */
! 473: if (ch == '\0')
! 474: goto done;
! 475: /* pretend it was %c with argument ch */
! 476: cp = buf;
! 477: *cp = ch;
! 478: size = 1;
! 479: sign = '\0';
! 480: break;
! 481: }
! 482:
! 483: /*
! 484: * All reasonable formats wind up here. At this point, `cp'
! 485: * points to a string which (if not flags&LADJUST) should be
! 486: * padded out to `width' places. If flags&ZEROPAD, it should
! 487: * first be prefixed by any sign or other prefix; otherwise,
! 488: * it should be blank padded before the prefix is emitted.
! 489: * After any left-hand padding and prefixing, emit zeroes
! 490: * required by a decimal [diouxX] precision, then print the
! 491: * string proper, then emit zeroes required by any leftover
! 492: * floating precision; finally, if LADJUST, pad with blanks.
! 493: *
! 494: * Compute actual size, so we know how much to pad.
! 495: * fieldsz excludes decimal prec; realsz includes it.
! 496: */
! 497: fieldsz = size;
! 498: if (sign)
! 499: fieldsz++;
! 500: else if (flags & HEXPREFIX)
! 501: fieldsz += 2;
! 502: realsz = dprec > fieldsz ? dprec : fieldsz;
! 503:
! 504: /* right-adjusting blank padding */
! 505: if ((flags & (LADJUST|ZEROPAD)) == 0)
! 506: PAD(width - realsz, blanks);
! 507:
! 508: /* prefix */
! 509: if (sign) {
! 510: PRINT(&sign, 1);
! 511: } else if (flags & HEXPREFIX) {
! 512: ox[0] = '0';
! 513: ox[1] = ch;
! 514: PRINT(ox, 2);
! 515: }
! 516:
! 517: /* right-adjusting zero padding */
! 518: if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
! 519: PAD(width - realsz, zeroes);
! 520:
! 521: /* leading zeroes from decimal precision */
! 522: PAD(dprec - fieldsz, zeroes);
! 523:
! 524: /* the string or number proper */
! 525: PRINT(cp, size);
! 526:
! 527: /* left-adjusting padding (always blank) */
! 528: if (flags & LADJUST)
! 529: PAD(width - realsz, blanks);
! 530:
! 531: /* finally, adjust ret */
! 532: ret += width > realsz ? width : realsz;
! 533:
! 534: FLUSH(); /* copy out the I/O vectors */
! 535: }
! 536: done:
! 537: FLUSH();
! 538: error:
! 539: return (__sferror(fp) ? EOF : ret);
! 540: /* NOTREACHED */
! 541: }
CVSweb