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