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