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