|
|
1.1 ! root 1: /* ! 2: * libc/stdio/vfprintf.c ! 3: * ANSI-compliant C standard i/o library. ! 4: * vfprintf() ! 5: * ANSI 4.9.6.7, 4.9.6.1. ! 6: * Do the work for fprintf(), printf(), sprintf(), vprintf(), vsprintf(). ! 7: * Thanks to the ANSI committee for all the complication. ! 8: * ! 9: * Implementation-defined behavior: ! 10: * "%p" format is same as "%#.<PPREC>X" or "%#.<PPREC>lX" ! 11: */ ! 12: ! 13: #include <stdio.h> ! 14: #include <stdlib.h> ! 15: #include <stdarg.h> ! 16: #include <limits.h> ! 17: ! 18: /* Compile-time options. */ ! 19: #if _I386 ! 20: #define PPREC 8 /* precision for %p format */ ! 21: #else ! 22: #define PPREC 4 /* precision for %p format */ ! 23: #endif ! 24: #define LONGDOUBLE 0 /* iff sizeof(double) != sizeof(long double) */ ! 25: #define VA_PURE 0 /* iff va_list treated purely for doubles */ ! 26: ! 27: static char *convert(); ! 28: ! 29: /* Manifest constants. */ ! 30: /* ANSI says any conversion item can be up to 509 characters. The only */ ! 31: /* case where cbuf needs to be big is for floating point (%f). */ ! 32: #define CBUFMAX 512 /* conversion buffer size */ ! 33: #define NDIGITS 12 /* ASCII digits in octal long */ ! 34: /* i.e. "37777777777\0" */ ! 35: #define PRINTNULL "{NULL}" /* for %s with NULL arg */ ! 36: ! 37: int ! 38: vfprintf(fp, format, args) FILE *fp; register char *format; va_list args; ! 39: { ! 40: char cbuf[CBUFMAX]; ! 41: register char * cbp; ! 42: char * cbs; ! 43: char * s; ! 44: register int count, c; ! 45: long l; ! 46: int fwidth, prec, base, len; ! 47: int leftjustify, plusflag, spaceflag, altflag, longflag; ! 48: int padchar, padwidth, issigned, prefix, ispfx, nzeros; ! 49: va_list rargs; ! 50: #if VA_PURE ! 51: double d; ! 52: #if LONGDOUBLE ! 53: long double ld; ! 54: #endif ! 55: #endif ! 56: ! 57: count = 0; /* characters printed */ ! 58: for (;;) { ! 59: ! 60: /* Nonconversion characters. */ ! 61: while ((c = *format++) != '%') { ! 62: if (c == '\0') ! 63: return count; ! 64: ++count; ! 65: putc(c, fp); ! 66: } ! 67: ! 68: /* Optional flags "-+ #0". */ ! 69: leftjustify = plusflag = spaceflag = altflag = 0; ! 70: padchar = ' '; ! 71: for (;;) { ! 72: switch(c = *format++) { ! 73: case '-': ++leftjustify; continue; ! 74: case '+': ++plusflag; continue; ! 75: case ' ': ++spaceflag; continue; ! 76: case '#': ++altflag; continue; ! 77: case '0': padchar = '0'; continue; ! 78: default: break; ! 79: } ! 80: break; ! 81: } ! 82: ! 83: /* Optional field width ('*' or decimal integer). */ ! 84: if (c == '*') { ! 85: if ((fwidth = va_arg(args, int)) < 0) { ! 86: leftjustify = 1; ! 87: fwidth = -fwidth; ! 88: } ! 89: c = *format++; ! 90: } else ! 91: for (fwidth = 0; c>='0' && c<='9'; c = *format++) ! 92: fwidth = fwidth*10 + c-'0'; ! 93: ! 94: /* Optional precision ('.' followed by '*' or decimal integer). */ ! 95: if (c == '.') { ! 96: c = *format++; ! 97: if (c == '*') { ! 98: prec = va_arg(args, int); ! 99: if (prec < 0) ! 100: prec = -1; ! 101: c = *format++; ! 102: } else ! 103: for (prec=0; c>='0' && c<='9'; c = *format++) ! 104: prec = prec*10 + c-'0'; ! 105: } else ! 106: prec = -1; ! 107: ! 108: /* Optional 'h', 'l' or 'L' flag. */ ! 109: if (c == 'l' || c == 'h' || c == 'L') { ! 110: longflag = c; ! 111: c = *format++; ! 112: } else ! 113: longflag = 0; ! 114: ! 115: /* Convert an item. */ ! 116: cbp = cbs = cbuf; ! 117: issigned = nzeros = prefix = ispfx = 0; ! 118: switch (c) { ! 119: ! 120: case 'd': ! 121: case 'i': ! 122: base = 10; ! 123: if (longflag=='l') ! 124: l = va_arg(args, long); ! 125: else ! 126: l = (long) va_arg(args, int); ! 127: if (longflag == 'h') ! 128: l = (short) l; ! 129: if (l < 0L) { ! 130: l = -l; ! 131: --issigned; /* -1 for negative */ ! 132: } else ! 133: ++issigned; /* +1 for positive */ ! 134: goto conv; ! 135: ! 136: case 'o': ! 137: base = 8; ! 138: goto unsconv; ! 139: ! 140: case 'u': ! 141: base = 10; ! 142: goto unsconv; ! 143: ! 144: case 'x': ! 145: case 'X': ! 146: base = 16; ! 147: unsconv: ! 148: if (longflag=='l') ! 149: l = va_arg(args, long); ! 150: else ! 151: #if _I386 ! 152: /* The i8086 compiler sign-extends this. */ ! 153: l = (unsigned long) va_arg(args, int); ! 154: #else ! 155: /* Kludge. */ ! 156: l = va_arg(args, int) & 0x0000FFFFL; ! 157: #endif ! 158: if (longflag == 'h') ! 159: l = (unsigned short) l; ! 160: if (altflag && ((l != 0L && base == 8) || base == 16)) ! 161: prefix = c; /* 'o', 'x' or 'X' */ ! 162: conv: ! 163: if (prec == 0 && l == 0L) ! 164: break; /* ANSI says so */ ! 165: if (prec != -1) ! 166: padchar = ' '; /* ignore 0 flag */ ! 167: cbp = convert(cbp, l, base, c); ! 168: if ((nzeros = prec - (cbp - cbs)) < 0) /* number of leading '0's */ ! 169: nzeros = 0; ! 170: break; ! 171: ! 172: /* ! 173: * Floating point output. ! 174: * A simple floating point operation may have considerable ! 175: * overhead in some environments (e.g. MSDOS, where a large ! 176: * 8087 emulator gets linked into executables which require ! 177: * software floating point). But there is only one ! 178: * ideologically pure way to get the fp arg from the arg list, ! 179: * namely with va_arg(), and that requires a fp fetch. ! 180: * The code below is conditionalized to do it the pure way ! 181: * (generating a fp fetch) or with a pointer to double (no fp). ! 182: */ ! 183: case 'f': ! 184: case 'e': ! 185: case 'E': ! 186: case 'g': ! 187: case 'G': ! 188: #if VA_PURE ! 189: #if LONGDOUBLE ! 190: if (longflag == 'L') { ! 191: ld = va_arg(args, long double); ! 192: cbp = _ldtefg(cbp, &ld, c, prec, altflag, &issigned); ! 193: break; ! 194: } ! 195: #endif ! 196: d = va_arg(args, double); ! 197: cbp = _dtefg(cbp, &d, c, prec, altflag, &issigned); ! 198: break; ! 199: #else ! 200: #if LONGDOUBLE ! 201: if (longflag == 'L') { ! 202: cbp = _ldtefg(cbp, (long double *)args, c, ! 203: prec, altflag, &issigned); ! 204: args = ((char *)args) + sizeof(long double); ! 205: break; ! 206: } ! 207: #endif ! 208: cbp = _dtefg(cbp, (double *)args, c, prec, altflag, &issigned); ! 209: args = ((char *)args) + sizeof(double); ! 210: break; ! 211: #endif ! 212: case 'c': ! 213: *cbp++ = (unsigned char) va_arg(args, int); ! 214: break; ! 215: ! 216: case 's': ! 217: if ((s = va_arg(args, char *)) == NULL) ! 218: s = PRINTNULL; /* not strictly ANSI */ ! 219: cbp = cbs = s; ! 220: while (*cbp++ != '\0') ! 221: if (prec>=0 && cbp-s>prec) ! 222: break; ! 223: cbp--; ! 224: break; ! 225: ! 226: /* Implementation-defined '%p' format: %#.<PPREC>X or %#.<PPREC>lX */ ! 227: case 'p': ! 228: #if _LARGE ! 229: longflag = 'l'; ! 230: #else ! 231: longflag = 0; ! 232: #endif ! 233: prec = PPREC; ! 234: ++altflag; ! 235: c = 'X'; ! 236: base = 16; ! 237: goto unsconv; ! 238: ! 239: case 'n': ! 240: if (longflag == 'h') ! 241: *(va_arg(args, short *)) = (short)count; ! 242: else if (longflag == 'l') ! 243: *(va_arg(args, long *)) = (long)count; ! 244: else ! 245: *(va_arg(args, int *)) = count; ! 246: break; ! 247: ! 248: /* ! 249: * The '%r' recursive printf conversion is non-ANSI. ! 250: * The item is a va_list; its first member is the format. ! 251: */ ! 252: case 'r': ! 253: rargs = va_arg(args, va_list); ! 254: s = va_arg(rargs, char *); ! 255: count += vfprintf(fp, s, rargs); ! 256: break; ! 257: ! 258: default: ! 259: ++count; ! 260: putc(c, fp); ! 261: continue; ! 262: } ! 263: ! 264: /* Output an item. */ ! 265: len = cbp - cbs; /* length of converted item */ ! 266: if (issigned && (issigned == -1 || plusflag || spaceflag)) { ! 267: ++len; /* for '-', '+', ' ' */ ! 268: ++ispfx; ! 269: } ! 270: if (prefix) { ! 271: ++len; /* for '0' */ ! 272: ++ispfx; ! 273: if (prefix != 'o') ! 274: ++len; /* for 'x', 'X' */ ! 275: } ! 276: if ((padwidth = fwidth - nzeros - len) < 0) ! 277: padwidth = 0; /* length of padding required */ ! 278: count += len + padwidth + nzeros; ! 279: if (!leftjustify && padwidth > 0) { ! 280: if (ispfx && padchar == '0') ! 281: nzeros += padwidth; /* prefix before 0-padding */ ! 282: else ! 283: while (padwidth-- > 0) /* pad on the left */ ! 284: putc(padchar, fp); ! 285: } ! 286: if (issigned) { ! 287: if (issigned == -1) ! 288: putc('-', fp); ! 289: else if (plusflag) ! 290: putc('+', fp); ! 291: else if (spaceflag) ! 292: putc(' ', fp); ! 293: } ! 294: if (prefix) { ! 295: putc('0', fp); ! 296: if (prefix != 'o') ! 297: putc(prefix, fp); ! 298: } ! 299: while (nzeros-- > 0) ! 300: putc('0', fp); /* leading '0's */ ! 301: len = cbp - cbs; ! 302: while (len-- > 0) ! 303: putc(*cbs++, fp); /* converted item */ ! 304: if (leftjustify) ! 305: while (padwidth-- > 0) ! 306: putc(' ', fp); /* pad on the right */ ! 307: } ! 308: } ! 309: ! 310: static const char digits[] = { ! 311: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ! 312: 'A', 'B', 'C', 'D', 'E', 'F' ! 313: }; ! 314: static const char ldigits[] = { ! 315: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ! 316: 'a', 'b', 'c', 'd', 'e', 'f' ! 317: }; ! 318: ! 319: /* ! 320: * Convert unsigned long 'n' to ASCII in base 'b' for conversion 'c'. ! 321: * Store the result through 'cp' and return a pointer past the end. ! 322: */ ! 323: static ! 324: char * ! 325: convert(cp, n, b, c) register char *cp; unsigned long n; unsigned int b; int c; ! 326: { ! 327: register char * ep; ! 328: char * dp; ! 329: unsigned int u; ! 330: char pbuf[NDIGITS]; ! 331: ! 332: dp = (c == 'x') ? &ldigits[0] : &digits[0]; ! 333: ep = &pbuf[NDIGITS-1]; ! 334: *ep = '\0'; /* NUL-terminate */ ! 335: if (n <= UINT_MAX) { ! 336: u = n; /* Use int arithmetic for efficiency */ ! 337: do { /* Store next digit */ ! 338: *--ep = dp[u%b]; ! 339: } while ((u /= b) != 0); ! 340: } else { ! 341: do { /* Store next digit */ ! 342: *--ep = dp[n%b]; ! 343: } while ((n /= b) != 0); ! 344: } ! 345: while (*ep) ! 346: *cp++ = *ep++; /* Copy result */ ! 347: return cp; ! 348: } ! 349: ! 350: ! 351: /* end of libc/stdio/vfprintf.c */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.