xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34242)
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.6 (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 	for (cnt = 0; *fmt; ++fmt) {
52 		if (*fmt != '%') {
53 			PUTC(*fmt, fp);
54 			continue;
55 		}
56 
57 		alternate = ladjust = width = 0;
58 		prec = -1;
59 		padc = ' ';
60 		argsize = printsign = '\0';
61 
62 flags:		switch (*++fmt) {
63 		case '#':
64 			alternate = 1;
65 			goto flags;
66 		case '%':			/* "%#%" prints as "%" */
67 			PUTC('%', fp);
68 			continue;
69 		case '*':
70 			/*
71 			 * ``A negative field width argument is taken as a
72 			 * - flag followed by a  positive field width.''
73 			 *	-- ANSI X3J11
74 			 * They don't exclude field widths read from args.
75 			 */
76 			if ((width = va_arg(argp, int)) >= 0)
77 				goto flags;
78 			width = -width;
79 			/*FALLTHROUGH*/
80 		case '-':
81 			ladjust = 1;
82 			goto flags;
83 		case '+':
84 			printsign = '+';
85 			goto flags;
86 		case '.':
87 			if (*++fmt == '*')
88 				prec = va_arg(argp, int);
89 			else if (isdigit(*fmt)) {
90 				prec = 0;
91 				do {
92 					prec = 10 * prec + *fmt - '0';
93 				} while isdigit(*++fmt);
94 				--fmt;
95 			}
96 			else {
97 				prec = 0;
98 				--fmt;
99 				goto flags;
100 			}
101 			if (prec < 0)
102 				prec = -1;
103 			goto flags;
104 		case '0':
105 			padc = '0';
106 			/*FALLTHROUGH*/
107 		case '1': case '2': case '3': case '4':
108 		case '5': case '6': case '7': case '8': case '9':
109 			do {
110 				width = 10 * width + *fmt - '0';
111 			} while isdigit(*++fmt);
112 			--fmt;
113 		case 'L':
114 			argsize |= LONGDBL;
115 			goto flags;
116 		case 'h':
117 			argsize |= SHORTINT;
118 			goto flags;
119 		case 'l':
120 			argsize |= LONGINT;
121 			goto flags;
122 		}
123 
124 		digs = "0123456789abcdef";
125 
126 		switch (*fmt) {
127 		case 'c':
128 			PUTC(va_arg(argp, int), fp);
129 			break;
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);
149 			goto pbuf;
150 		case 'f':
151 			_double = va_arg(argp, double);
152 			bp = _cvt(_double, prec, buf, FFORMAT, 'f',
153 			    printsign);
154 pbuf:			size = bp - buf;
155 			if (size < width && !ladjust)
156 				do {
157 					PUTC(padc, fp);
158 				} while (--width > size);
159 			for (t = buf; t < bp; ++t)
160 				PUTC(*t, fp);
161 			for (; width > size; --width)
162 				PUTC(padc, fp);
163 			break;
164 		case 'G':
165 		case 'g':
166 			_double = va_arg(argp, double);
167 			bp = _cvt(_double, prec, buf, GFORMAT, *fmt - 2,
168 			    printsign);
169 			goto pbuf;
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 			break;
244 		case '\0':		/* "%?" prints ?, unless ? is NULL */
245 			return(ferror(fp) ? -1 : cnt);
246 		default:
247 			PUTC(*fmt, fp);
248 		}
249 	}
250 	return(ferror(fp) ? -1 : cnt);
251 }
252 
253 char *
254 _cvt(number, prec, bp, format, fmtch, printsign)
255 	double number;
256 	int prec, format;
257 	register char *bp;
258 	char fmtch, printsign;
259 {
260 	int sign, decpt;
261 	register char *t;
262 	register int n;
263 	double fabs();
264 	char *ecvt(), *fcvt();
265 
266 	if (prec == -1)
267 		prec = DEFPREC;
268 	t = fabs(number) < 1 ? ecvt(number, prec + 1, &decpt, &sign) :
269 	    fcvt(number, prec + 1, &decpt, &sign);
270 
271 	if (sign)
272 		*bp++ = '-';
273 	else if (printsign)
274 		*bp++ = printsign;
275 
276 	/* E format */
277 	/* use 'e' format if exponent > precision or less than -4 */
278 	if (format == EFORMAT ||
279 	    format == GFORMAT && (decpt > prec || decpt < -3)) {
280 		*bp++ = *t ? *t++ : '0';
281 		if (format != GFORMAT && prec || prec > 1) {
282 			*bp++ = '.';
283 			while(prec--)
284 				*bp++ = *t ? *t++ : '0';
285 		}
286 		if (*t && *t > '4')
287 			++bp[-1];
288 		if (format == 2) {
289 			for (; bp[-1] == '0'; --bp);
290 			if (*bp == '.')
291 				--bp;
292 		}
293 		*bp++ = fmtch;
294 		if (--decpt < 0) {
295 			decpt = -decpt;
296 			*bp++ = '-';
297 		}
298 		else
299 			*bp++ = '+';
300 		*bp++ = decpt / 10 + '0';
301 		*bp++ = decpt % 10 + '0';
302 	}
303 	/* F format */
304 	else {
305 		if (decpt <= 0) {
306 			*bp++ = '0';
307 			if (prec) {
308 				*bp++ = '.';
309 				if (format == FFORMAT)
310 					while (decpt++ < 0 && prec--)
311 						*bp++ = '0';
312 				else while (decpt++ < 0)
313 					*bp++ = '0';
314 			}
315 		}
316 		else {
317 			for (n = 1; n <= decpt; n++)
318 				*bp++ = *t++;
319 			if (prec)
320 				*bp++ = '.';
321 		}
322 		for (n = 1; n <= prec; n++)
323 			*bp++ = *t ? *t++ : '0';
324 		if (format == GFORMAT) {
325 			for (; bp[-1] == '0'; --bp);
326 			if (bp[-1] == '.')
327 				--bp;
328 		}
329 	}
330 	return(bp);
331 }
332