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