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