xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34241)
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.5 (Berkeley) 05/09/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	MAXBUF		120			/* should hold any number */
23 #define	DEFPREC		6			/* default precision */
24 
25 #define	PUTC(ch, fd)	{++cnt; putc(ch, fd);}
26 
27 #define	LONGINT		0x01
28 #define	LONGDBL		0x02
29 #define	SHORTINT	0x04
30 #define	GETARG(r) \
31 	r = argsize&LONGINT ? va_arg(argp, long) : \
32 	    argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
33 
34 x_doprnt(fmt, argp, fp)
35 	register char *fmt;
36 	va_list argp;
37 	register FILE *fp;
38 {
39 	register u_long reg_ulong;
40 	register long reg_long;
41 	register int base;
42 	register char *digs, *bp, *t, padc;
43 	double _double;
44 	char argsize, printsign, buf[MAXBUF], *ecvt(), *fcvt();
45 	int alternate, cnt, decpt, n, ladjust, width, prec, sign, size;
46 
47 	for (cnt = 0; *fmt; ++fmt) {
48 		if (*fmt != '%') {
49 			PUTC(*fmt, fp);
50 			continue;
51 		}
52 
53 		alternate = ladjust = width = 0;
54 		prec = -1;
55 		padc = ' ';
56 		argsize = printsign = '\0';
57 
58 flags:		switch (*++fmt) {
59 		case '#':
60 			alternate = 1;
61 			goto flags;
62 		case '%':			/* "%#%" prints as "%" */
63 			PUTC('%', fp);
64 			continue;
65 		case '*':
66 			/*
67 			 * ``A negative field width argument is taken as a
68 			 * - flag followed by a  positive field width.''
69 			 *	-- ANSI X3J11
70 			 * They don't exclude field widths read from args.
71 			 */
72 			if ((width = va_arg(argp, int)) >= 0)
73 				goto flags;
74 			width = -width;
75 			/*FALLTHROUGH*/
76 		case '-':
77 			ladjust = 1;
78 			goto flags;
79 		case '+':
80 			printsign = '+';
81 			goto flags;
82 		case '.':
83 			if (*++fmt == '*')
84 				prec = va_arg(argp, int);
85 			else if (isdigit(*fmt)) {
86 				prec = 0;
87 				do {
88 					prec = 10 * prec + *fmt - '0';
89 				} while isdigit(*++fmt);
90 				--fmt;
91 			}
92 			else {
93 				prec = 0;
94 				--fmt;
95 				goto flags;
96 			}
97 			if (prec < 0)
98 				prec = -1;
99 			goto flags;
100 		case '0':
101 			padc = '0';
102 			/*FALLTHROUGH*/
103 		case '1': case '2': case '3': case '4':
104 		case '5': case '6': case '7': case '8': case '9':
105 			do {
106 				width = 10 * width + *fmt - '0';
107 			} while isdigit(*++fmt);
108 			--fmt;
109 		case 'L':
110 			argsize |= LONGDBL;
111 			goto flags;
112 		case 'h':
113 			argsize |= SHORTINT;
114 			goto flags;
115 		case 'l':
116 			argsize |= LONGINT;
117 			goto flags;
118 		}
119 
120 		digs = "0123456789abcdef";
121 
122 		switch (*fmt) {
123 		case 'c':
124 			PUTC(va_arg(argp, int), fp);
125 			break;
126 		case 'd':
127 		case 'i':
128 			GETARG(reg_long);
129 			if (reg_long < 0) {
130 				reg_ulong = -reg_long;
131 				printsign = '-';
132 			}
133 			else {
134 				reg_ulong = reg_long;
135 			}
136 			if (printsign)
137 				PUTC(printsign, fp);
138 			base = 10;
139 			goto num1;
140 		case 'E':
141 		case 'e':
142 			if (prec == -1)
143 				prec = DEFPREC;
144 			_double = va_arg(argp, double);
145 			t = fcvt(_double, prec + 1, &decpt, &sign);
146 gise:			bp = buf;
147 			*bp++ = *t ? *t++ : '0';
148 			if (alternate || prec > 0)
149 				*bp++ = '.';
150 			while (prec--)
151 				*bp++ = *t ? *t++ : '0';
152 			*bp++ = *fmt;
153 			if (decpt > 0 || !_double) {
154 				*bp++ = '+';
155 				--decpt;
156 			}
157 			else {
158 				*bp++ = '-';
159 				decpt = -decpt + 1;
160 			}
161 			/* exponents <= 99 in ANSI X3J11 */
162 			*bp++ = (int)(decpt / 10) + '0';
163 			*bp++ = (int)(decpt % 10) + '0';
164 			goto pbuf;
165 		case 'f':
166 			if (prec == -1)
167 				prec = DEFPREC;
168 			_double = va_arg(argp, double);
169 			t = fcvt(_double, prec + 1, &decpt, &sign);
170 gisf:			bp = buf;
171 			if (decpt >= 0)
172 				for (;;) {
173 					*bp++ = *t ? *t++ : '0';
174 					if (!--decpt)
175 						break;
176 				}
177 			if (alternate || prec > 0) {
178 				if (decpt < 0)
179 					*bp++ = '0';
180 				*bp++ = '.';
181 			}
182 			while (decpt++) {
183 				*bp++ = '0';
184 				--prec;
185 			}
186 			while (prec--)
187 				*bp++ = *t ? *t++ : '0';
188 pbuf:			size = bp - buf;
189 			if (sign || printsign)
190 				PUTC(sign ? '-' : printsign, fp);
191 			if (size < width && !ladjust)
192 				do {
193 					PUTC(padc, fp);
194 				} while (--width > size);
195 			for (t = buf; t < bp; ++t)
196 				PUTC(*t, fp);
197 			for (; width > size; --width)
198 				PUTC(padc, fp);
199 			break;
200 		case 'G':
201 		case 'g': {
202 			int gotoe;
203 
204 			if (prec == -1)
205 				prec = DEFPREC;
206 			_double = va_arg(argp, double);
207 			t = fcvt(_double, prec + 1, &decpt, &sign);
208 			gotoe = decpt > prec;
209 			if (!alternate) {
210 				for (bp = t + prec + decpt; prec &&
211 				    *--bp == '0'; --prec);
212 			}
213 			if (gotoe || decpt < -3) {
214 				*fmt -= 2;
215 				goto gise;
216 			}
217 			--*fmt;
218 			goto gisf;
219 		}
220 		case 'n':
221 			*(va_arg(argp, int *)) = cnt;
222 			break;
223 		case 'o':
224 			GETARG(reg_ulong);
225 			base = 8;
226 			if (!reg_ulong || !alternate)
227 				goto num1;
228 			bp = buf + sizeof(buf) - 1;
229 			do {
230 				*bp-- = digs[reg_ulong % base];
231 				reg_ulong /= base;
232 			} while(reg_ulong);
233 			size = &buf[sizeof(buf) - 1] - bp;
234 			if (size < --width && !ladjust)
235 				do {
236 					PUTC(padc, fp);
237 				} while (--width > size);
238 			PUTC('0', fp);
239 			goto num2;
240 			break;
241 		case 'p':
242 		case 's':
243 			if (!(bp = va_arg(argp, char *)))
244 				bp = "(null)";
245 			if (width > 0 && !ladjust) {
246 				char *savep;
247 
248 				savep = bp;
249 				for (n = 0; *bp && (prec < 0 || n < prec);
250 				    n++, bp++);
251 				bp = savep;
252 				while (n++ < width)
253 					PUTC(' ', fp);
254 			}
255 			for (n = 0; *bp; ++bp) {
256 				if (++n > prec && prec >= 0)
257 					break;
258 				PUTC(*bp, fp);
259 			}
260 			if (n < width && ladjust)
261 				do {
262 					PUTC(' ', fp);
263 				} while (++n < width);
264 			break;
265 		case 'u':
266 			GETARG(reg_ulong);
267 			base = 10;
268 			goto num1;
269 		case 'X':
270 			digs = "0123456789ABCDEF";
271 			/*FALLTHROUGH*/
272 		case 'x':
273 			GETARG(reg_ulong);
274 			if (alternate && reg_ulong) {
275 				PUTC('0', fp);
276 				PUTC(*fmt, fp);
277 			}
278 			base = 16;
279 num1:			bp = buf + sizeof(buf) - 1;
280 			do {
281 				*bp-- = digs[reg_ulong % base];
282 				reg_ulong /= base;
283 			} while(reg_ulong);
284 			size = &buf[sizeof(buf) - 1] - bp;
285 			for (; size < prec; *bp-- = '0', ++size);
286 			if (size < width && !ladjust)
287 				do {
288 					PUTC(padc, fp);
289 				} while (--width > size);
290 num2:			while (++bp != &buf[MAXBUF])
291 				PUTC(*bp, fp);
292 			for (; width > size; --width)
293 				PUTC(padc, fp);
294 			break;
295 		case '\0':		/* "%?" prints ?, unless ? is NULL */
296 			return(ferror(fp) ? -1 : cnt);
297 		default:
298 			PUTC(*fmt, fp);
299 		}
300 	}
301 	return(ferror(fp) ? -1 : cnt);
302 }
303