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