xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34236)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 
13 #if defined(LIBC_SCCS) && !defined(lint)
14 static char sccsid[] = "@(#)vfprintf.c	5.4 (Berkeley) 05/08/88";
15 #endif /* LIBC_SCCS and not lint */
16 
17 #include <sys/param.h>
18 #include <varargs.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 
22 #define	GETARG(r) \
23 	r = argsize&LONGINT ? va_arg(argp, long) : \
24 	    argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
25 
26 #define	MAXBUF		1024			/* should hold any number */
27 #define	MAXEXP		10			/* should hold any exponent */
28 
29 #define	DEFPREC		6			/* default precision */
30 
31 #define	PUTC(ch, fd)	{++cnt; putc(ch, fd);}
32 
33 #define	LONGINT		0x01
34 #define	LONGDBL		0x02
35 #define	SHORTINT	0x04
36 
37 x_doprnt(fmt, argp, fp)
38 	register char *fmt;
39 	va_list argp;
40 	register FILE *fp;
41 {
42 	register u_long reg_ulong;
43 	register long reg_long;
44 	register int base;
45 	register char *digs, *bp, *t, padc;
46 	double _double;
47 	char argsize, printsign, buf[MAXBUF], *ecvt(), *fcvt();
48 	int alternate, cnt, decpt, n, ladjust, width, prec, sign, size;
49 
50 	for (cnt = 0; *fmt; ++fmt) {
51 		if (*fmt != '%') {
52 			PUTC(*fmt, fp);
53 			continue;
54 		}
55 
56 		alternate = ladjust = width = 0;
57 		prec = -1;
58 		padc = ' ';
59 		argsize = printsign = '\0';
60 
61 flags:		switch (*++fmt) {
62 		case '#':
63 			alternate = 1;
64 			goto flags;
65 		case '%':			/* "%#%" prints as "%" */
66 			PUTC('%', fp);
67 			continue;
68 		case '*':
69 			/*
70 			 * ``A negative field width argument is taken as a
71 			 * - flag followed by a  positive field width.''
72 			 *	-- ANSI X3J11
73 			 * They don't exclude field widths read from args.
74 			 */
75 			if ((width = va_arg(argp, int)) >= 0)
76 				goto flags;
77 			width = -width;
78 			/*FALLTHROUGH*/
79 		case '-':
80 			ladjust = 1;
81 			goto flags;
82 		case '+':
83 			printsign = '+';
84 			goto flags;
85 		case '.':
86 			if (*++fmt == '*')
87 				prec = va_arg(argp, int);
88 			else if (isdigit(*fmt)) {
89 				prec = 0;
90 				do {
91 					prec = 10 * prec + *fmt - '0';
92 				} while isdigit(*++fmt);
93 				--fmt;
94 			}
95 			else {
96 				prec = 0;
97 				--fmt;
98 				goto flags;
99 			}
100 			if (prec < 0)
101 				prec = -1;
102 			goto flags;
103 		case '0':
104 			padc = '0';
105 			/*FALLTHROUGH*/
106 		case '1': case '2': case '3': case '4':
107 		case '5': case '6': case '7': case '8': case '9':
108 			do {
109 				width = 10 * width + *fmt - '0';
110 			} while isdigit(*++fmt);
111 			--fmt;
112 		case 'L':
113 			argsize |= LONGDBL;
114 			goto flags;
115 		case 'h':
116 			argsize |= SHORTINT;
117 			goto flags;
118 		case 'l':
119 			argsize |= LONGINT;
120 			goto flags;
121 		}
122 
123 		digs = "0123456789abcdef";
124 
125 		switch (*fmt) {
126 		case 'c':
127 			PUTC(va_arg(argp, int), fp);
128 			break;
129 		case 'E':
130 		case 'e':
131 			if (prec == -1)
132 				prec = DEFPREC;
133 			_double = va_arg(argp, double);
134 			t = ecvt(_double, prec + 1, &decpt, &sign);
135 			bp = buf;
136 			*bp++ = *t ? *t++ : '0';
137 			if (alternate || prec > 0)
138 				*bp++ = '.';
139 			while (prec--)
140 				*bp++ = *t ? *t++ : '0';
141 			*bp++ = *fmt;
142 			*bp++ = (decpt > 0 || !_double) ? '+' : '-';
143 			/* we know exponents <= 99 */
144 			--decpt;
145 			*bp++ = (int)decpt / 10 + '0';
146 			*bp++ = (int)decpt % 10 + '0';
147 			goto pbuf;
148 		case 'f':
149 			if (prec == -1)
150 				prec = DEFPREC;
151 			_double = va_arg(argp, double);
152 			t = fcvt(_double, prec + 1, &decpt, &sign);
153 			bp = buf;
154 			if (decpt >= 0)
155 				for (;;) {
156 					*bp++ = *t ? *t++ : '0';
157 					if (!--decpt)
158 						break;
159 				}
160 			if (alternate || prec > 0)
161 				*bp++ = '.';
162 			while (decpt++) {
163 				*bp++ = *t ? *t++ : '0';
164 				--prec;
165 			}
166 			while (prec--)
167 				*bp++ = *t ? *t++ : '0';
168 pbuf:			size = bp - buf;
169 			if (sign || printsign)
170 				PUTC(sign ? '-' : printsign, fp);
171 			if (size < width && !ladjust)
172 				do {
173 					PUTC(padc, fp);
174 				} while (--width > size);
175 			for (t = buf; t < bp; ++t)
176 				PUTC(*t, fp);
177 			for (; width > size; --width)
178 				PUTC(padc, fp);
179 			break;
180 		case 'd':
181 		case 'i':
182 			GETARG(reg_long);
183 			if (reg_long < 0) {
184 				reg_ulong = -reg_long;
185 				printsign = '-';
186 			}
187 			else {
188 				reg_ulong = reg_long;
189 			}
190 			if (printsign)
191 				PUTC(printsign, fp);
192 			base = 10;
193 			goto num1;
194 		case 'n':
195 			*(va_arg(argp, int *)) = cnt;
196 			break;
197 		case 'o':
198 			GETARG(reg_ulong);
199 			base = 8;
200 			if (!reg_ulong || !alternate)
201 				goto num1;
202 			bp = buf + sizeof(buf) - 1;
203 			do {
204 				*bp-- = digs[reg_ulong % base];
205 				reg_ulong /= base;
206 			} while(reg_ulong);
207 			size = &buf[sizeof(buf) - 1] - bp;
208 			if (size < --width && !ladjust)
209 				do {
210 					PUTC(padc, fp);
211 				} while (--width > size);
212 			PUTC('0', fp);
213 			goto num2;
214 			break;
215 		case 'p':
216 		case 's':
217 			if (!(bp = va_arg(argp, char *)))
218 				bp = "(null)";
219 			if (width > 0 && !ladjust) {
220 				char *savep;
221 
222 				savep = bp;
223 				for (n = 0; *bp && (prec < 0 || n < prec);
224 				    n++, bp++);
225 				bp = savep;
226 				while (n++ < width)
227 					PUTC(' ', fp);
228 			}
229 			for (n = 0; *bp; ++bp) {
230 				if (++n > prec && prec >= 0)
231 					break;
232 				PUTC(*bp, fp);
233 			}
234 			if (n < width && ladjust)
235 				do {
236 					PUTC(' ', fp);
237 				} while (++n < width);
238 			break;
239 		case 'u':
240 			GETARG(reg_ulong);
241 			base = 10;
242 			goto num1;
243 		case 'X':
244 			digs = "0123456789ABCDEF";
245 			/*FALLTHROUGH*/
246 		case 'x':
247 			GETARG(reg_ulong);
248 			if (alternate && reg_ulong) {
249 				PUTC('0', fp);
250 				PUTC(*fmt, fp);
251 			}
252 			base = 16;
253 num1:			bp = buf + sizeof(buf) - 1;
254 			do {
255 				*bp-- = digs[reg_ulong % base];
256 				reg_ulong /= base;
257 			} while(reg_ulong);
258 			size = &buf[sizeof(buf) - 1] - bp;
259 			for (; size < prec; *bp-- = '0', ++size);
260 			if (size < width && !ladjust)
261 				do {
262 					PUTC(padc, fp);
263 				} while (--width > size);
264 num2:			while (++bp != &buf[MAXBUF])
265 				PUTC(*bp, fp);
266 			for (; width > size; --width)
267 				PUTC(padc, fp);
268 			break;
269 		case '\0':		/* "%?" prints ?, unless ? is NULL */
270 			return(ferror(fp) ? -1 : cnt);
271 		default:
272 			PUTC(*fmt, fp);
273 		}
274 	}
275 	return(ferror(fp) ? -1 : cnt);
276 }
277