xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34248)
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.8 (Berkeley) 05/10/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		0x01
28 #define	FFORMAT		0x02
29 #define	GFORMAT		0x04
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 static int alternate;
39 static char printsign;
40 
41 x_doprnt(fmt, argp, fp)
42 	register char *fmt;
43 	va_list argp;
44 	register FILE *fp;
45 {
46 	register u_long reg_ulong;
47 	register long reg_long;
48 	register int base;
49 	register char *digs, *bp, *t, padc;
50 	double _double;
51 	char argsize, *_cvt(), buf[MAXBUF];
52 	int cnt, n, ladjust, width, prec, size;
53 
54 	digs = "0123456789abcdef";
55 	for (cnt = 0; *fmt; ++fmt) {
56 		if (*fmt != '%') {
57 			PUTC(*fmt, fp);
58 			continue;
59 		}
60 
61 		alternate = ladjust = width = 0;
62 		prec = -1;
63 		padc = ' ';
64 		argsize = printsign = '\0';
65 
66 flags:		switch (*++fmt) {
67 		case '#':
68 			alternate = 1;
69 			goto flags;
70 		case '*':
71 			/*
72 			 * ``A negative field width argument is taken as a
73 			 * - flag followed by a  positive field width.''
74 			 *	-- ANSI X3J11
75 			 * They don't exclude field widths read from args.
76 			 */
77 			if ((width = va_arg(argp, int)) >= 0)
78 				goto flags;
79 			width = -width;
80 			/*FALLTHROUGH*/
81 		case '-':
82 			ladjust = 1;
83 			goto flags;
84 		case '+':
85 			printsign = '+';
86 			goto flags;
87 		case '.':
88 			if (*++fmt == '*')
89 				prec = va_arg(argp, int);
90 			else if (isdigit(*fmt)) {
91 				prec = 0;
92 				do {
93 					prec = 10 * prec + *fmt - '0';
94 				} while isdigit(*++fmt);
95 				--fmt;
96 			}
97 			else {
98 				prec = 0;
99 				--fmt;
100 				goto flags;
101 			}
102 			if (prec < 0)
103 				prec = -1;
104 			goto flags;
105 		case '0':
106 			padc = '0';
107 			/*FALLTHROUGH*/
108 		case '1': case '2': case '3': case '4':
109 		case '5': case '6': case '7': case '8': case '9':
110 			do {
111 				width = 10 * width + *fmt - '0';
112 			} while isdigit(*++fmt);
113 			--fmt;
114 		case 'L':
115 			argsize |= LONGDBL;
116 			goto flags;
117 		case 'h':
118 			argsize |= SHORTINT;
119 			goto flags;
120 		case 'l':
121 			argsize |= LONGINT;
122 			goto flags;
123 		case '%':			/* "%#%" prints as "%" */
124 			PUTC('%', fp);
125 			break;
126 		case 'c': {
127 			char ch;
128 
129 			ch = va_arg(argp, int);
130 			PUTC(ch, fp);
131 			break;
132 		}
133 		case 'd':
134 		case 'i':
135 			GETARG(reg_long);
136 			if (reg_long < 0) {
137 				reg_ulong = -reg_long;
138 				printsign = '-';
139 			}
140 			else {
141 				reg_ulong = reg_long;
142 			}
143 			if (printsign)
144 				PUTC(printsign, fp);
145 			base = 10;
146 			goto num1;
147 		case 'E':
148 		case 'e':
149 			_double = va_arg(argp, double);
150 			bp = _cvt(_double, prec, EFORMAT, buf,
151 			    buf + sizeof(buf), *fmt);
152 			goto pbuf;
153 		case 'f':
154 			_double = va_arg(argp, double);
155 			bp = _cvt(_double, prec, FFORMAT, buf,
156 			    buf + sizeof(buf), 'f');
157 			goto pbuf;
158 		case 'G':
159 		case 'g':
160 			_double = va_arg(argp, double);
161 			bp = _cvt(_double, prec, GFORMAT, buf,
162 			    buf + sizeof(buf), *fmt - 2);
163 pbuf:			size = bp - buf;
164 			if (size < width && !ladjust)
165 				do {
166 					PUTC(padc, fp);
167 				} while (--width > size);
168 			for (t = buf; t < bp; ++t)
169 				PUTC(*t, fp);
170 			for (; width > size; --width)
171 				PUTC(padc, fp);
172 			break;
173 		case 'n':
174 			*(va_arg(argp, int *)) = cnt;
175 			break;
176 		case 'o':
177 			GETARG(reg_ulong);
178 			base = 8;
179 			if (!reg_ulong || !alternate)
180 				goto num1;
181 			bp = buf + sizeof(buf) - 1;
182 			do {
183 				*bp-- = digs[reg_ulong % base];
184 				reg_ulong /= base;
185 			} while(reg_ulong);
186 			size = &buf[sizeof(buf) - 1] - bp;
187 			if (size < --width && !ladjust)
188 				do {
189 					PUTC(padc, fp);
190 				} while (--width > size);
191 			PUTC('0', fp);
192 			goto num2;
193 		case 'p':
194 		case 's':
195 			if (!(bp = va_arg(argp, char *)))
196 				bp = "(null)";
197 			if (width > 0 && !ladjust) {
198 				char *savep;
199 
200 				savep = bp;
201 				for (n = 0; *bp && (prec < 0 || n < prec);
202 				    n++, bp++);
203 				bp = savep;
204 				while (n++ < width)
205 					PUTC(' ', fp);
206 			}
207 			for (n = 0; *bp; ++bp) {
208 				if (++n > prec && prec >= 0)
209 					break;
210 				PUTC(*bp, fp);
211 			}
212 			if (n < width && ladjust)
213 				do {
214 					PUTC(' ', fp);
215 				} while (++n < width);
216 			break;
217 		case 'u':
218 			GETARG(reg_ulong);
219 			base = 10;
220 			goto num1;
221 		case 'X':
222 			digs = "0123456789ABCDEF";
223 			/*FALLTHROUGH*/
224 		case 'x':
225 			GETARG(reg_ulong);
226 			if (alternate && reg_ulong) {
227 				PUTC('0', fp);
228 				PUTC(*fmt, fp);
229 			}
230 			base = 16;
231 num1:			bp = buf + sizeof(buf) - 1;
232 			do {
233 				*bp-- = digs[reg_ulong % base];
234 				reg_ulong /= base;
235 			} while(reg_ulong);
236 			size = &buf[sizeof(buf) - 1] - bp;
237 			for (; size < prec; *bp-- = '0', ++size);
238 			if (size < width && !ladjust)
239 				do {
240 					PUTC(padc, fp);
241 				} while (--width > size);
242 num2:			while (++bp != &buf[MAXBUF])
243 				PUTC(*bp, fp);
244 			for (; width > size; --width)
245 				PUTC(padc, fp);
246 			digs = "0123456789abcdef";
247 			break;
248 		case '\0':		/* "%?" prints ?, unless ? is NULL */
249 			return(ferror(fp) ? -1 : cnt);
250 		default:
251 			PUTC(*fmt, fp);
252 		}
253 	}
254 	return(ferror(fp) ? -1 : cnt);
255 }
256 
257 char *
258 _cvt(number, prec, format, startp, endp, fmtch)
259 	double number;
260 	int prec, format;
261 	char *startp, *endp, fmtch;
262 {
263 	register char *p;
264 	double fract, integer, tmp, modf();
265 	int decpt, expcnt;
266 	char *savep;
267 
268 	if (prec == -1)				/* set default precision */
269 		prec = DEFPREC;
270 
271 	p = endp - 1;
272 	if (number < 0) {			/* set sign */
273 		*startp++ = '-';
274 		number = -number;
275 	}
276 	else if (printsign)
277 		*startp++ = '+';
278 
279 	/*
280 	 * if the alternate flag is set, or, at least one digit of precision
281 	 * was requested, add a decimal point, unless it's the g/G format
282 	 * in which case we require two digits of precision, since it counts
283 	 * precision differently.
284 	 */
285 	decpt = alternate || prec > 1 || !(format&GFORMAT) && prec;
286 
287 	expcnt = 0;
288 	fract = modf(number, &integer);
289 	if (integer) {
290 		register char *p2;
291 
292 		/* get integer part of number; count decimal places */
293 		for (; integer; ++expcnt) {
294 			tmp = modf(integer / 10, &integer);
295 			*p-- = (int)((tmp + .03) * 10) + '0';
296 		}
297 
298 		/* copy, in reverse order, to start of buffer */
299 		p2 = startp;
300 		*p2++ = *++p;
301 
302 		/*
303 		 * if the format is g/G, and the resulting exponent will be
304 		 * greater than the precision, use e/E format.  If e/E format,
305 		 * put in a decimal point as needed, and decrement precision
306 		 * count for each digit after the decimal point.
307 		 */
308 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
309 			if (format&GFORMAT) {
310 				format |= EFORMAT;
311 
312 				/* first digit is precision for g/G format */
313 				if (prec)
314 					--prec;
315 			}
316 			if (decpt)
317 				*p2++ = '.';
318 			for (; ++p < endp && prec; --prec, *p2++ = *p);
319 
320 			/* precision ran out; round number */
321 			if (p < endp) {
322 				if (*p > '4') {
323 					for (savep = p2--;; *p2-- = '0') {
324 						if (*p2 == '.')
325 							--p2;
326 						if (++*p2 <= '9')
327 							break;
328 					}
329 					p2 = savep;
330 				}
331 				fract = 0;
332 			}
333 		}
334 		/*
335 		 * g/G in f format; if run out of precision, replace digits
336 		 * with zeroes, note, have to round first, otherwise lose
337 		 * rounding point.
338 		 */
339 		else if (format&GFORMAT) {
340 			for (; ++p < endp && prec; --prec, *p2++ = *p);
341 			/* precision ran out; round and then add zeroes */
342 			if (p < endp) {
343 				if (*p > '4') {
344 					for (savep = p2--; ++*p2 > '9';
345 					    *p2-- = '0');
346 					p2 = savep;
347 				}
348 				do {
349 					*p2++ = '0';
350 				} while (++p < endp);
351 				fract = 0;
352 			}
353 			if (decpt)
354 				*p2++ = '.';
355 		}
356 		/* f format */
357 		else {
358 			for (; ++p < endp; *p2++ = *p);
359 			if (decpt)
360 				*p2++ = '.';
361 		}
362 		p = p2;
363 	}
364 	/*
365 	 * it's unclear from the ANSI X3J11 spec if the g/G format should
366 	 * just result in an empty string, because it's supposed to remove
367 	 * trailing zeroes.  That seems counter-intuitive, so here it does
368 	 * what f and e/E do; if no fraction, the number was zero, and if
369 	 * no precision can't show anything after the decimal point.
370 	 */
371 	else if (!fract || !prec) {
372 		*startp++ = '0';
373 		if (decpt)
374 			*startp++ = '.';
375 		*startp++ = '\0';
376 		return(startp);
377 	}
378 	/*
379 	 * if the format is g/G, and the resulting exponent will be less than
380 	 * -4 use e/E format.  If e/E format, compute exponent value.
381 	 */
382 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
383 		format |= EFORMAT;
384 		if (fract)
385 			for (p = startp; fract;) {
386 				fract = modf(fract * 10, &tmp);
387 				if (!tmp) {
388 					--expcnt;
389 					continue;
390 				}
391 				*p++ = (int)tmp + '0';
392 				break;
393 			}
394 		else
395 			*p++ = '0';
396 
397 		/* g/G format, decrement precision for first digit */
398 		if (format&GFORMAT && prec)
399 			--prec;
400 
401 		/* add decimal after first non-zero digit */
402 		if (decpt)
403 			*p++ = '.';
404 	}
405 	/*
406 	 * f format or g/G printed as f format; don't worry about decimal
407 	 * point, if g/G format doesn't need it, will get stripped later.
408 	 */
409 	else {
410 		p = startp;
411 		*p++ = '0';
412 		*p++ = '.';
413 	}
414 
415 	/* finish out requested precision from fractional value */
416 	while (prec--)
417 		if (fract) {
418 			fract = modf(fract * 10, &tmp);
419 			*p++ = (int)tmp + '0';
420 		}
421 		else
422 			*p++ = '0';
423 
424 	/*
425 	 * if any fractional value left, "round" it back up to the beginning
426 	 * of the number, fixing the exponent as necessary, and avoiding the
427 	 * decimal point.
428 	 */
429 	if (fract) {
430 		(void)modf(fract * 10, &tmp);
431 		if (tmp > 4) {
432 			for (savep = p--;; *p-- = '0') {
433 				if (*p == '.')
434 					--p;
435 				if (p == startp) {
436 					*p = '1';
437 					++expcnt;
438 					break;
439 				}
440 				if (++*p <= '9')
441 					break;
442 			}
443 			p = savep;
444 		}
445 	}
446 
447 	/*
448 	 * if a g/G format and not alternate flag, lose trailing zeroes,
449 	 * if e/E or g/G format, and last char is decimal point, lose it.
450 	 */
451 	if (!alternate) {
452 		if (format&GFORMAT)
453 			for (; p[-1] == '0'; --p);
454 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
455 			--p;
456 	}
457 
458 	/* if an e/E format, add exponent */
459 	if (format&EFORMAT) {
460 		*p++ = fmtch;
461 		if (--expcnt < 0) {
462 			expcnt = -expcnt;
463 			*p++ = '-';
464 		}
465 		else
466 			*p++ = '+';
467 		*p++ = expcnt / 10 + '0';
468 		*p++ = expcnt % 10 + '0';
469 	}
470 	return(p);
471 }
472