xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34321)
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.17 (Berkeley) 05/17/88";
15 #endif /* LIBC_SCCS and not lint */
16 
17 #include <sys/types.h>
18 #include <varargs.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 
22 /*
23  * To handle arbitrary floating point precision, the buffer has to hold the
24  * number, a decimal point, and N precision digits.  We can't just truncate
25  * at some point is that the lower-level math routines may very well be
26  * repeatedly returning some small fraction.  A 128 bit fraction can be
27  * represented in 39 decimal digits.  Guess a max of 40 digits of precision,
28  * and add one for the decimal point.
29  */
30 #define	MAXFRAC		39
31 #define	MAXPREC		40
32 #define	MAXDIGIT	(MAXFRAC + MAXPREC + 1)
33 
34 #define	PUTC(ch)	{++cnt; putc(ch, fp);}
35 
36 #define	ARG() \
37 	_ulong = flags&LONGINT ? va_arg(argp, long) : \
38 	    flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
39 
40 /* have to deal with the negative buffer count kludge */
41 #define	NEGATIVE_COUNT_KLUDGE
42 
43 #define	LONGINT		0x01		/* long integer */
44 #define	LONGDBL		0x02		/* long double; unimplemented */
45 #define	SHORTINT	0x04		/* short integer */
46 #define	ALT		0x08		/* alternate form */
47 #define	LADJUST		0x10		/* left adjustment */
48 static int flags;
49 
50 static char sign, *buf;
51 
52 x_doprnt(fmt, argp, fp)
53 	register char *fmt;
54 	va_list argp;
55 	register FILE *fp;
56 {
57 	register int cnt, n;
58 	register char ch, *t;
59 	double _double;
60 	u_long _ulong;
61 	int base, width, prec, size;
62 	char padc, *digs, sbuf[MAXDIGIT];
63 
64 	digs = "0123456789abcdef";
65 	for (buf = sbuf, cnt = 0;; ++fmt) {
66 		n = fp->_cnt;
67 		for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
68 			if (--n < 0
69 #ifdef NEGATIVE_COUNT_KLUDGE
70 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
71 #endif
72 			    || ch == '\n' && fp->_flag&_IOLBF) {
73 				fp->_cnt = n;
74 				fp->_ptr = t;
75 				(void)_flsbuf(ch, fp);
76 				n = fp->_cnt;
77 				t = fp->_ptr;
78 			}
79 			else
80 				*t++ = ch;
81 		fp->_cnt = n;
82 		fp->_ptr = t;
83 		if (!ch)
84 			return(cnt);
85 
86 		flags = width = 0;
87 		prec = -1;
88 		padc = ' ';
89 		sign = '\0';
90 
91 rflag:		switch (*++fmt) {
92 		case ' ':
93 			sign = ' ';
94 			goto rflag;
95 		case '#':
96 			flags |= ALT;
97 			goto rflag;
98 		case '*':
99 			/*
100 			 * ``A negative field width argument is taken as a
101 			 * - flag followed by a  positive field width.''
102 			 *	-- ANSI X3J11
103 			 * They don't exclude field widths read from args.
104 			 */
105 			if ((width = va_arg(argp, int)) >= 0)
106 				goto rflag;
107 			width = -width;
108 			/*FALLTHROUGH*/
109 		case '-':
110 			flags |= LADJUST;
111 			goto rflag;
112 		case '+':
113 			sign = '+';
114 			goto rflag;
115 		case '.':
116 			if (*++fmt == '*')
117 				n = va_arg(argp, int);
118 			else if (isascii(*fmt) && isdigit(*fmt)) {
119 				n = 0;
120 				do {
121 					n = 10 * n + *fmt - '0';
122 				} while (isascii(*++fmt) && isdigit(*fmt));
123 				--fmt;
124 			}
125 			else {
126 				--fmt;
127 				prec = 0;
128 				goto rflag;
129 			}
130 			prec = n < 0 ? -1 : n;
131 			goto rflag;
132 		case '0':
133 			padc = '0';
134 			/*FALLTHROUGH*/
135 		case '1': case '2': case '3': case '4':
136 		case '5': case '6': case '7': case '8': case '9':
137 			n = 0;
138 			do {
139 				n = 10 * n + *fmt - '0';
140 			} while (isascii(*++fmt) && isdigit(*fmt));
141 			width = n;
142 			--fmt;
143 			goto rflag;
144 		case 'L':
145 			/*
146 			 * C doesn't have a long double; use long for now.
147 			 * flags |= LONGDBL;
148 			 */
149 			flags |= LONGINT;
150 			goto rflag;
151 		case 'h':
152 			flags |= SHORTINT;
153 			goto rflag;
154 		case 'l':
155 			flags |= LONGINT;
156 			goto rflag;
157 		case 'c':
158 			buf[0] = va_arg(argp, int);
159 			size = 1;
160 			t = buf;
161 			goto pforw;
162 		case 'd':
163 		case 'i':
164 			ARG();
165 			if ((long)_ulong < 0) {
166 				_ulong = -_ulong;
167 				sign = '-';
168 			}
169 			if (sign)
170 				PUTC(sign);
171 			base = 10;
172 			goto num;
173 		case 'e':
174 		case 'E':
175 		case 'f':
176 		case 'g':
177 		case 'G':
178 			_double = va_arg(argp, double);
179 			size = _cvt(_double, prec, *fmt);
180 			t = buf;
181 			goto pforw;
182 		case 'n':
183 			if (flags&LONGDBL || flags&LONGINT)
184 				*va_arg(argp, long *) = cnt;
185 			else if (flags&SHORTINT)
186 				*va_arg(argp, short *) = cnt;
187 			else
188 				*va_arg(argp, int *) = cnt;
189 			break;
190 		case 'o':
191 			ARG();
192 			base = 8;
193 			goto num;
194 		case 'p':
195 			/*
196 			 * ``The argument shall be a pointer to void.  The
197 			 * value of the pointer is converted to a sequence
198 			 * of printable characters, in an implementation-
199 			 * defined manner.''
200 			 *	-- ANSI X3J11
201 			 */
202 			_ulong = (u_long)va_arg(argp, void *);
203 			base = 16;
204 			goto num;
205 		case 's':
206 			if (!(t = va_arg(argp, char *)))
207 				t = "(null)";
208 			if (prec >= 0) {
209 				/*
210 				 * can't use strlen; can only look for the
211 				 * NUL in the first `prec' characters, and
212 				 * strlen() will go further.
213 				 */
214 				char *p, *memchr();
215 
216 				if (p = memchr(t, 0, prec)) {
217 					size = p - t;
218 					if (size > prec)
219 						size = prec;
220 				}
221 				else
222 					size = prec;
223 			}
224 			else
225 				size = strlen(t);
226 pforw:			if (!(flags&LADJUST) && width)
227 				for (n = size; n++ < width;)
228 					PUTC(padc);
229 			if (fp->_cnt - (n = size) >= 0) {
230 				cnt += n;
231 				fp->_cnt -= n;
232 				bcopy(t, fp->_ptr, n);
233 				fp->_ptr += n;
234 			}
235 			else for (; n--; ++t)
236 				PUTC(*t);
237 			if (flags&LADJUST)
238 				while (width-- > size)
239 					PUTC(padc);
240 			break;
241 		case 'u':
242 			ARG();
243 			base = 10;
244 			goto num;
245 		case 'X':
246 			digs = "0123456789ABCDEF";
247 			/*FALLTHROUGH*/
248 		case 'x':
249 			ARG();
250 			base = 16;
251 			/* alternate form for hex; leading 0x/X */
252 			if (flags&ALT && _ulong) {
253 				PUTC('0');
254 				PUTC(*fmt);
255 			}
256 num:			t = buf + MAXDIGIT - 1;
257 			do {
258 				*t-- = digs[_ulong % base];
259 				_ulong /= base;
260 			} while(_ulong);
261 			digs = "0123456789abcdef";
262 			size = buf + MAXDIGIT - 1 - t;
263 			if (size >= prec) {
264 				/* alternate form for octal; leading 0 */
265 				if (t[1] != '0' && flags&ALT && *fmt == 'o') {
266 					*t-- = '0';
267 					++size;
268 				}
269 			}
270 			else
271 				for (; size < prec; ++size)
272 					*t-- = '0';
273 			if (!(flags&LADJUST))
274 				while (size++ < width)
275 					PUTC(padc);
276 			while (++t < buf + MAXDIGIT)
277 				PUTC(*t);
278 			for (; width > size; --width)
279 				PUTC(padc);
280 			break;
281 		case '\0':		/* "%?" prints ?, unless ? is NULL */
282 			return(cnt);
283 		default:
284 			PUTC(*fmt);
285 		}
286 	}
287 	/*NOTREACHED*/
288 }
289 
290 #define	EFORMAT	0x01
291 #define	FFORMAT	0x02
292 #define	GFORMAT	0x04
293 #define	DEFPREC	6
294 
295 static
296 _cvt(number, prec, fmtch)
297 	double number;
298 	register int prec;
299 	char fmtch;
300 {
301 	register char *p;
302 	register int expcnt, format;
303 	static int maxprec = MAXPREC;
304 	double fract, integer, tmp, modf();
305 	int decpt;
306 	char *endp, *savep, *startp, *malloc();
307 
308 	if (prec == -1)
309 		prec = DEFPREC;
310 
311 	/* allocate space for large precision */
312 	if (prec > maxprec)
313 		buf = malloc((u_int)((maxprec = prec) + MAXFRAC + 1));
314 
315 	startp = buf;
316 	if (number < 0) {
317 		*startp++ = '-';
318 		number = -number;
319 	}
320 	else if (sign)
321 		*startp++ = sign;
322 
323 	switch(fmtch) {
324 	case 'e':
325 	case 'E':
326 		format = EFORMAT;
327 		break;
328 	case 'f':
329 		format = FFORMAT;
330 		break;
331 	case 'g':
332 	case 'G':
333 		format = GFORMAT;
334 		fmtch -= 2;
335 	}
336 
337 	/*
338 	 * if the alternate flag is set, or, at least one digit of precision
339 	 * was requested, add a decimal point, unless it's the g/G format
340 	 * in which case we require two digits of precision, as it counts
341 	 * precision differently.
342 	 */
343 	decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
344 
345 	expcnt = 0;
346 	p = buf + maxprec + MAXFRAC;
347 	endp = p + 1;
348 	fract = modf(number, &integer);
349 	if (integer) {
350 		register char *p2;
351 
352 		/* get integer part of number; count decimal places */
353 		for (; integer; ++expcnt) {
354 			tmp = modf(integer / 10, &integer);
355 			*p-- = (int)((tmp + .03) * 10) + '0';
356 		}
357 
358 		/* copy, in reverse order, to start of buffer */
359 		p2 = startp;
360 		*p2++ = *++p;
361 
362 		/*
363 		 * if the format is g/G, and the resulting exponent will be
364 		 * greater than the precision, use e/E format.  If e/E format,
365 		 * put in a decimal point as needed, and decrement precision
366 		 * count for each digit after the decimal point.
367 		 */
368 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
369 			if (format&GFORMAT) {
370 				format |= EFORMAT;
371 
372 				/* first digit is precision for g/G format */
373 				if (prec)
374 					--prec;
375 			}
376 			if (decpt)
377 				*p2++ = '.';
378 			for (; ++p < endp && prec; --prec, *p2++ = *p);
379 
380 			/* precision ran out, round */
381 			if (p < endp) {
382 				if (*p > '4') {
383 					for (savep = p2--;; *p2-- = '0') {
384 						if (*p2 == '.')
385 							--p2;
386 						if (++*p2 <= '9')
387 							break;
388 					}
389 					p2 = savep;
390 				}
391 				fract = 0;
392 			}
393 		}
394 		/*
395 		 * g/G in f format; if out of precision, replace digits with
396 		 * zeroes, note, have to round first.
397 		 */
398 		else if (format&GFORMAT) {
399 			for (; ++p < endp && prec; --prec, *p2++ = *p);
400 			/* precision ran out; round and then add zeroes */
401 			if (p < endp) {
402 				if (*p > '4') {
403 					for (savep = p2--; ++*p2 > '9';
404 					    *p2-- = '0');
405 					p2 = savep;
406 				}
407 				do {
408 					*p2++ = '0';
409 				} while (++p < endp);
410 				fract = 0;
411 			}
412 			if (decpt)
413 				*p2++ = '.';
414 		}
415 		/* f format */
416 		else {
417 			for (; ++p < endp; *p2++ = *p);
418 			if (decpt)
419 				*p2++ = '.';
420 		}
421 		p = p2;
422 	}
423 	/*
424 	 * if no fraction, the number was zero, and if no precision, can't
425 	 * show anything after the decimal point.
426 	 */
427 	else if (!fract || !prec) {
428 		*startp++ = '0';
429 		if (decpt && !(format&GFORMAT))
430 			*startp++ = '.';
431 		*startp = '\0';
432 		return(startp - buf);
433 	}
434 	/*
435 	 * if the format is g/G, and the resulting exponent will be less than
436 	 * -4 use e/E format.  If e/E format, compute exponent value.
437 	 */
438 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
439 		format |= EFORMAT;
440 		if (fract)
441 			for (p = startp; fract;) {
442 				fract = modf(fract * 10, &tmp);
443 				if (!tmp) {
444 					--expcnt;
445 					continue;
446 				}
447 				*p++ = (int)tmp + '0';
448 				break;
449 			}
450 		else
451 			*p++ = '0';
452 
453 		/* g/G format, decrement precision for first digit */
454 		if (format&GFORMAT && prec)
455 			--prec;
456 
457 		/* add decimal after first non-zero digit */
458 		if (decpt)
459 			*p++ = '.';
460 	}
461 	/*
462 	 * f format or g/G printed as f format; don't worry about decimal
463 	 * point, if g/G format doesn't need it, will get stripped later.
464 	 */
465 	else {
466 		p = startp;
467 		*p++ = '0';
468 		*p++ = '.';
469 	}
470 
471 	/* finish out requested precision */
472 	while (fract && prec-- > 0) {
473 		fract = modf(fract * 10, &tmp);
474 		*p++ = (int)tmp + '0';
475 	}
476 	while (prec-- > 0)
477 		*p++ = '0';
478 
479 	/*
480 	 * if any fractional value left, "round" it back up to the beginning
481 	 * of the number, fixing the exponent as necessary, and avoiding the
482 	 * decimal point.
483 	 */
484 	if (fract) {
485 		(void)modf(fract * 10, &tmp);
486 		if (tmp > 4) {
487 			for (savep = p--;; *p-- = '0') {
488 				if (*p == '.')
489 					--p;
490 				if (p == startp) {
491 					*p = '1';
492 					++expcnt;
493 					break;
494 				}
495 				if (++*p <= '9')
496 					break;
497 			}
498 			p = savep;
499 		}
500 	}
501 
502 	/*
503 	 * if a g/G format and not alternate flag, lose trailing zeroes,
504 	 * if e/E or g/G format, and last char is decimal point, lose it.
505 	 */
506 	if (!(flags&ALT)) {
507 		if (format&GFORMAT)
508 			for (; p[-1] == '0'; --p);
509 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
510 			--p;
511 	}
512 
513 	/* if an e/E format, add exponent */
514 	if (format&EFORMAT) {
515 		*p++ = fmtch;
516 		if (--expcnt < 0) {
517 			expcnt = -expcnt;
518 			*p++ = '-';
519 		}
520 		else
521 			*p++ = '+';
522 		*p++ = expcnt / 10 + '0';
523 		*p++ = expcnt % 10 + '0';
524 	}
525 	return(p - buf);
526 }
527