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