|
|
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.