xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34825)
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 the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #if defined(LIBC_SCCS) && !defined(lint)
19 static char sccsid[] = "@(#)vfprintf.c	5.35 (Berkeley) 06/27/88";
20 #endif /* LIBC_SCCS and not lint */
21 
22 #include <sys/types.h>
23 #include <varargs.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 
27 /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
28 #define	MAXEXP		308
29 /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
30 #define	MAXFRACT	39
31 
32 #define	DEFPREC		6
33 
34 #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
35 
36 #define	PUTC(ch)	(void) putc(ch, fp)
37 
38 #define	ARG() \
39 	_ulong = flags&LONGINT ? va_arg(argp, long) : \
40 	    flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
41 
42 #define	todigit(c)	((c) - '0')
43 #define	tochar(n)	((n) + '0')
44 
45 /* have to deal with the negative buffer count kludge */
46 #define	NEGATIVE_COUNT_KLUDGE
47 
48 #define	LONGINT		0x01		/* long integer */
49 #define	LONGDBL		0x02		/* long double; unimplemented */
50 #define	SHORTINT	0x04		/* short integer */
51 #define	ALT		0x08		/* alternate form */
52 #define	LADJUST		0x10		/* left adjustment */
53 #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
54 #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
55 
56 _doprnt(fmt0, argp, fp)
57 	u_char *fmt0;
58 	va_list argp;
59 	register FILE *fp;
60 {
61 	register u_char *fmt;	/* format string */
62 	register int ch;	/* character from fmt */
63 	register int cnt;	/* return value accumulator */
64 	register int n;		/* random handy integer */
65 	register char *t;	/* buffer pointer */
66 	double _double;		/* double precision arguments %[eEfgG] */
67 	u_long _ulong;		/* integer arguments %[diouxX] */
68 	int base;		/* base for [diouxX] conversion */
69 	int dprec;		/* decimal precision in [diouxX] */
70 	int fieldsz;		/* field size expanded by sign, etc */
71 	int flags;		/* flags as above */
72 	int fpprec;		/* `extra' floating precision in [eEfgG] */
73 	int prec;		/* precision from format (%.3d), or -1 */
74 	int realsz;		/* field size expanded by decimal precision */
75 	int size;		/* size of converted field or string */
76 	int width;		/* width from format (%8d), or 0 */
77 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
78 	char softsign;		/* temporary negative sign for floats */
79 	char *digs;		/* digits for [diouxX] conversion */
80 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
81 
82 	if (fp->_flag & _IORW) {
83 		fp->_flag |= _IOWRT;
84 		fp->_flag &= ~(_IOEOF|_IOREAD);
85 	}
86 	if ((fp->_flag & _IOWRT) == 0)
87 		return (EOF);
88 
89 	fmt = fmt0;
90 	digs = "0123456789abcdef";
91 	for (cnt = 0;; ++fmt) {
92 		n = fp->_cnt;
93 		for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
94 		     ++cnt, ++fmt)
95 			if (--n < 0
96 #ifdef NEGATIVE_COUNT_KLUDGE
97 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
98 #endif
99 			    || ch == '\n' && fp->_flag & _IOLBF) {
100 				fp->_cnt = n;
101 				fp->_ptr = t;
102 				(void) _flsbuf((u_char)ch, fp);
103 				n = fp->_cnt;
104 				t = (char *)fp->_ptr;
105 			} else
106 				*t++ = ch;
107 		fp->_cnt = n;
108 		fp->_ptr = t;
109 		if (!ch)
110 			return (cnt);
111 
112 		flags = 0; dprec = 0; fpprec = 0; width = 0;
113 		prec = -1;
114 		sign = '\0';
115 
116 rflag:		switch (*++fmt) {
117 		case ' ':
118 			/*
119 			 * ``If the space and + flags both appear, the space
120 			 * flag will be ignored.''
121 			 *	-- ANSI X3J11
122 			 */
123 			if (!sign)
124 				sign = ' ';
125 			goto rflag;
126 		case '#':
127 			flags |= ALT;
128 			goto rflag;
129 		case '*':
130 			/*
131 			 * ``A negative field width argument is taken as a
132 			 * - flag followed by a  positive field width.''
133 			 *	-- ANSI X3J11
134 			 * They don't exclude field widths read from args.
135 			 */
136 			if ((width = va_arg(argp, int)) >= 0)
137 				goto rflag;
138 			width = -width;
139 			/* FALLTHROUGH */
140 		case '-':
141 			flags |= LADJUST;
142 			goto rflag;
143 		case '+':
144 			sign = '+';
145 			goto rflag;
146 		case '.':
147 			if (*++fmt == '*')
148 				n = va_arg(argp, int);
149 			else {
150 				n = 0;
151 				while (isascii(*fmt) && isdigit(*fmt))
152 					n = 10 * n + todigit(*fmt++);
153 				--fmt;
154 			}
155 			prec = n < 0 ? -1 : n;
156 			goto rflag;
157 		case '0':
158 			/*
159 			 * ``Note that 0 is taken as a flag, not as the
160 			 * beginning of a field width.''
161 			 *	-- ANSI X3J11
162 			 */
163 			flags |= ZEROPAD;
164 			goto rflag;
165 		case '1': case '2': case '3': case '4':
166 		case '5': case '6': case '7': case '8': case '9':
167 			n = 0;
168 			do {
169 				n = 10 * n + todigit(*fmt);
170 			} while (isascii(*++fmt) && isdigit(*fmt));
171 			width = n;
172 			--fmt;
173 			goto rflag;
174 		case 'L':
175 			flags |= LONGDBL;
176 			goto rflag;
177 		case 'h':
178 			flags |= SHORTINT;
179 			goto rflag;
180 		case 'l':
181 			flags |= LONGINT;
182 			goto rflag;
183 		case 'c':
184 			*(t = buf) = va_arg(argp, int);
185 			size = 1;
186 			sign = '\0';
187 			goto pforw;
188 		case 'D':
189 			flags |= LONGINT;
190 			/*FALLTHROUGH*/
191 		case 'd':
192 		case 'i':
193 			ARG();
194 			if ((long)_ulong < 0) {
195 				_ulong = -_ulong;
196 				sign = '-';
197 			}
198 			base = 10;
199 			goto number;
200 		case 'e':
201 		case 'E':
202 		case 'f':
203 		case 'g':
204 		case 'G':
205 			_double = va_arg(argp, double);
206 			/*
207 			 * don't do unrealistic precision; just pad it with
208 			 * zeroes later, so buffer size stays rational.
209 			 */
210 			if (prec > MAXFRACT) {
211 				if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
212 					fpprec = prec - MAXFRACT;
213 				prec = MAXFRACT;
214 			}
215 			else if (prec == -1)
216 				prec = DEFPREC;
217 			/*
218 			 * softsign avoids negative 0 if _double is < 0 and
219 			 * no significant digits will be shown
220 			 */
221 			if (_double < 0) {
222 				softsign = '-';
223 				_double = -_double;
224 			}
225 			else
226 				softsign = 0;
227 			/*
228 			 * cvt may have to round up past the "start" of the
229 			 * buffer, i.e. ``intf("%.2f", (double)9.999);'';
230 			 * if the first char isn't NULL, it did.
231 			 */
232 			*buf = NULL;
233 			size = cvt(_double, prec, flags, &softsign, *fmt, buf,
234 			    buf + sizeof(buf));
235 			if (softsign)
236 				sign = '-';
237 			t = *buf ? buf : buf + 1;
238 			goto pforw;
239 		case 'n':
240 			if (flags & LONGINT)
241 				*va_arg(argp, long *) = cnt;
242 			else if (flags & SHORTINT)
243 				*va_arg(argp, short *) = cnt;
244 			else
245 				*va_arg(argp, int *) = cnt;
246 			break;
247 		case 'O':
248 			flags |= LONGINT;
249 			/*FALLTHROUGH*/
250 		case 'o':
251 			ARG();
252 			base = 8;
253 			goto nosign;
254 		case 'p':
255 			/*
256 			 * ``The argument shall be a pointer to void.  The
257 			 * value of the pointer is converted to a sequence
258 			 * of printable characters, in an implementation-
259 			 * defined manner.''
260 			 *	-- ANSI X3J11
261 			 */
262 			/* NOSTRICT */
263 			_ulong = (u_long)va_arg(argp, void *);
264 			base = 16;
265 			goto nosign;
266 		case 's':
267 			if (!(t = va_arg(argp, char *)))
268 				t = "(null)";
269 			if (prec >= 0) {
270 				/*
271 				 * can't use strlen; can only look for the
272 				 * NUL in the first `prec' characters, and
273 				 * strlen() will go further.
274 				 */
275 				char *p, *memchr();
276 
277 				if (p = memchr(t, 0, prec)) {
278 					size = p - t;
279 					if (size > prec)
280 						size = prec;
281 				} else
282 					size = prec;
283 			} else
284 				size = strlen(t);
285 			sign = '\0';
286 			goto pforw;
287 		case 'U':
288 			flags |= LONGINT;
289 			/*FALLTHROUGH*/
290 		case 'u':
291 			ARG();
292 			base = 10;
293 			goto nosign;
294 		case 'X':
295 			digs = "0123456789ABCDEF";
296 			/* FALLTHROUGH */
297 		case 'x':
298 			ARG();
299 			base = 16;
300 			/* leading 0x/X only if non-zero */
301 			if (flags & ALT && _ulong != 0)
302 				flags |= HEXPREFIX;
303 
304 			/* unsigned conversions */
305 nosign:			sign = '\0';
306 			/*
307 			 * ``... diouXx conversions ... if a precision is
308 			 * specified, the 0 flag will be ignored.''
309 			 *	-- ANSI X3J11
310 			 */
311 number:			if ((dprec = prec) >= 0)
312 				flags &= ~ZEROPAD;
313 
314 			/*
315 			 * ``The result of converting a zero value with an
316 			 * explicit precision of zero is no characters.''
317 			 *	-- ANSI X3J11
318 			 */
319 			t = buf + BUF;
320 			if (_ulong != 0 || prec != 0) {
321 				do {
322 					*--t = digs[_ulong % base];
323 					_ulong /= base;
324 				} while (_ulong);
325 				digs = "0123456789abcdef";
326 				if (flags & ALT && base == 8 && *t != '0')
327 					*--t = '0'; /* octal leading 0 */
328 			}
329 			size = buf + BUF - t;
330 
331 pforw:
332 			/*
333 			 * All reasonable formats wind up here.  At this point,
334 			 * `t' points to a string which (if not flags&LADJUST)
335 			 * should be padded out to `width' places.  If
336 			 * flags&ZEROPAD, it should first be prefixed by any
337 			 * sign or other prefix; otherwise, it should be blank
338 			 * padded before the prefix is emitted.  After any
339 			 * left-hand padding and prefixing, emit zeroes
340 			 * required by a decimal [diouxX] precision, then print
341 			 * the string proper, then emit zeroes required by any
342 			 * leftover floating precision; finally, if LADJUST,
343 			 * pad with blanks.
344 			 */
345 
346 			/*
347 			 * compute actual size, so we know how much to pad
348 			 * fieldsz excludes decimal prec; realsz includes it
349 			 */
350 			fieldsz = size + fpprec;
351 			if (sign)
352 				fieldsz++;
353 			if (flags & HEXPREFIX)
354 				fieldsz += 2;
355 			realsz = dprec > fieldsz ? dprec : fieldsz;
356 
357 			/* right-adjusting blank padding */
358 			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
359 				for (n = realsz; n < width; n++)
360 					PUTC(' ');
361 			/* prefix */
362 			if (sign)
363 				PUTC(sign);
364 			if (flags & HEXPREFIX) {
365 				PUTC('0');
366 				PUTC((char)*fmt);
367 			}
368 			/* right-adjusting zero padding */
369 			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
370 				for (n = realsz; n < width; n++)
371 					PUTC('0');
372 			/* leading zeroes from decimal precision */
373 			for (n = fieldsz; n < dprec; n++)
374 				PUTC('0');
375 
376 			/* the string or number proper */
377 			if (fp->_cnt - (n = size) >= 0 &&
378 			    (fp->_flag & _IOLBF) == 0) {
379 				fp->_cnt -= n;
380 				bcopy(t, (char *)fp->_ptr, n);
381 				fp->_ptr += n;
382 			} else
383 				while (--n >= 0)
384 					PUTC(*t++);
385 			/* trailing f.p. zeroes */
386 			while (--fpprec >= 0)
387 				PUTC('0');
388 			/* left-adjusting padding (always blank) */
389 			if (flags & LADJUST)
390 				for (n = realsz; n < width; n++)
391 					PUTC(' ');
392 			/* finally, adjust cnt */
393 			cnt += width > realsz ? width : realsz;
394 			break;
395 		case '\0':	/* "%?" prints ?, unless ? is NULL */
396 			return (cnt);
397 		default:
398 			PUTC((char)*fmt);
399 			cnt++;
400 		}
401 	}
402 	/* NOTREACHED */
403 }
404 
405 static
406 cvt(number, prec, flags, signp, fmtch, startp, endp)
407 	double number;
408 	register int prec;
409 	int flags;
410 	u_char fmtch;
411 	char *signp, *startp, *endp;
412 {
413 	register char *p, *t;
414 	register double fract;
415 	int dotrim, expcnt, gformat;
416 	double integer, tmp, modf();
417 	char *exponent(), *round();
418 
419 	dotrim = expcnt = gformat = 0;
420 	fract = modf(number, &integer);
421 
422 	/* get an extra slot for rounding. */
423 	t = ++startp;
424 
425 	/*
426 	 * get integer portion of number; put into the end of the buffer; the
427 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
428 	 */
429 	for (p = endp - 1; integer; ++expcnt) {
430 		tmp = modf(integer / 10, &integer);
431 		*p-- = tochar((int)((tmp + .01) * 10));
432 	}
433 	switch(fmtch) {
434 	case 'f':
435 		/* reverse integer into beginning of buffer */
436 		if (expcnt)
437 			for (; ++p < endp; *t++ = *p);
438 		else
439 			*t++ = '0';
440 		/*
441 		 * if precision required or alternate flag set, add in a
442 		 * decimal point.
443 		 */
444 		if (prec || flags&ALT)
445 			*t++ = '.';
446 		/* if requires more precision and some fraction left */
447 		if (fract) {
448 			if (prec)
449 				do {
450 					fract = modf(fract * 10, &tmp);
451 					*t++ = tochar((int)tmp);
452 				} while (--prec && fract);
453 			if (fract)
454 				startp = round(fract, (int *)NULL, startp,
455 				    t - 1, (char)0, signp);
456 		}
457 		for (; prec--; *t++ = '0');
458 		break;
459 	case 'e':
460 	case 'E':
461 eformat:	if (expcnt) {
462 			*t++ = *++p;
463 			if (prec || flags&ALT)
464 				*t++ = '.';
465 			/* if requires more precision and some integer left */
466 			for (; prec && ++p < endp; --prec)
467 				*t++ = *p;
468 			/*
469 			 * if done precision and more of the integer component,
470 			 * round using it; adjust fract so we don't re-round
471 			 * later.
472 			 */
473 			if (!prec && ++p < endp) {
474 				fract = 0;
475 				startp = round((double)0, &expcnt, startp,
476 				    t - 1, *p, signp);
477 			}
478 			/* adjust expcnt for digit in front of decimal */
479 			--expcnt;
480 		}
481 		/* until first fractional digit, decrement exponent */
482 		else if (fract) {
483 			/* adjust expcnt for digit in front of decimal */
484 			for (expcnt = -1;; --expcnt) {
485 				fract = modf(fract * 10, &tmp);
486 				if (tmp)
487 					break;
488 			}
489 			*t++ = tochar((int)tmp);
490 			if (prec || flags&ALT)
491 				*t++ = '.';
492 		}
493 		else {
494 			*t++ = '0';
495 			if (prec || flags&ALT)
496 				*t++ = '.';
497 		}
498 		/* if requires more precision and some fraction left */
499 		if (fract) {
500 			if (prec)
501 				do {
502 					fract = modf(fract * 10, &tmp);
503 					*t++ = tochar((int)tmp);
504 				} while (--prec && fract);
505 			if (fract)
506 				startp = round(fract, &expcnt, startp,
507 				    t - 1, (char)0, signp);
508 		}
509 		/* if requires more precision */
510 		for (; prec--; *t++ = '0');
511 
512 		/* unless alternate flag, trim any g/G format trailing 0's */
513 		if (gformat && !(flags&ALT)) {
514 			while (t > startp && *--t == '0');
515 			if (*t == '.')
516 				--t;
517 			++t;
518 		}
519 		t = exponent(t, expcnt, fmtch);
520 		break;
521 	case 'g':
522 	case 'G':
523 		/* a precision of 0 is treated as a precision of 1. */
524 		if (!prec)
525 			++prec;
526 		/*
527 		 * ``The style used depends on the value converted; style e
528 		 * will be used only if the exponent resulting from the
529 		 * conversion is less than -4 or greater than the precision.''
530 		 *	-- ANSI X3J11
531 		 */
532 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
533 			/*
534 			 * g/G format counts "significant digits, not digits of
535 			 * precision; for the e/E format, this just causes an
536 			 * off-by-one problem, i.e. g/G considers the digit
537 			 * before the decimal point significant and e/E doesn't
538 			 * count it as precision.
539 			 */
540 			--prec;
541 			fmtch -= 2;		/* G->E, g->e */
542 			gformat = 1;
543 			goto eformat;
544 		}
545 		/*
546 		 * reverse integer into beginning of buffer,
547 		 * note, decrement precision
548 		 */
549 		if (expcnt)
550 			for (; ++p < endp; *t++ = *p, --prec);
551 		else
552 			*t++ = '0';
553 		/*
554 		 * if precision required or alternate flag set, add in a
555 		 * decimal point.  If no digits yet, add in leading 0.
556 		 */
557 		if (prec || flags&ALT) {
558 			dotrim = 1;
559 			*t++ = '.';
560 		}
561 		else
562 			dotrim = 0;
563 		/* if requires more precision and some fraction left */
564 		if (fract) {
565 			if (prec) {
566 				do {
567 					fract = modf(fract * 10, &tmp);
568 					*t++ = tochar((int)tmp);
569 				} while(!tmp);
570 				while (--prec && fract) {
571 					fract = modf(fract * 10, &tmp);
572 					*t++ = tochar((int)tmp);
573 				}
574 			}
575 			if (fract)
576 				startp = round(fract, (int *)NULL, startp,
577 				    t - 1, (char)0, signp);
578 		}
579 		/* alternate format, adds 0's for precision, else trim 0's */
580 		if (flags&ALT)
581 			for (; prec--; *t++ = '0');
582 		else if (dotrim) {
583 			while (t > startp && *--t == '0');
584 			if (*t != '.')
585 				++t;
586 		}
587 	}
588 	return(t - startp);
589 }
590 
591 static char *
592 round(fract, exp, start, end, ch, signp)
593 	double fract;
594 	int *exp;
595 	register char *start, *end;
596 	char ch, *signp;
597 {
598 	double tmp;
599 
600 	if (fract)
601 		(void)modf(fract * 10, &tmp);
602 	else
603 		tmp = todigit(ch);
604 	if (tmp > 4)
605 		for (;; --end) {
606 			if (*end == '.')
607 				--end;
608 			if (++*end <= '9')
609 				break;
610 			*end = '0';
611 			if (end == start) {
612 				if (exp) {	/* e/E; increment exponent */
613 					*end = '1';
614 					++*exp;
615 				}
616 				else {		/* f; add extra digit */
617 					*--end = '1';
618 					--start;
619 				}
620 				break;
621 			}
622 		}
623 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
624 	else if (*signp == '-')
625 		for (;; --end) {
626 			if (*end == '.')
627 				--end;
628 			if (*end != '0')
629 				break;
630 			if (end == start)
631 				*signp = 0;
632 		}
633 	return(start);
634 }
635 
636 static char *
637 exponent(p, exp, fmtch)
638 	register char *p;
639 	register int exp;
640 	u_char fmtch;
641 {
642 	register char *t;
643 	char expbuf[MAXEXP];
644 
645 	*p++ = fmtch;
646 	if (exp < 0) {
647 		exp = -exp;
648 		*p++ = '-';
649 	}
650 	else
651 		*p++ = '+';
652 	t = expbuf + MAXEXP;
653 	if (exp > 9) {
654 		do {
655 			*--t = tochar(exp % 10);
656 		} while ((exp /= 10) > 9);
657 		*--t = tochar(exp);
658 		for (; t < expbuf + MAXEXP; *p++ = *t++);
659 	}
660 	else {
661 		*p++ = '0';
662 		*p++ = tochar(exp);
663 	}
664 	return(p);
665 }
666