xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34318)
134226Sbostic /*
234233Sbostic  * Copyright (c) 1988 Regents of the University of California.
334233Sbostic  * All rights reserved.
434226Sbostic  *
534233Sbostic  * Redistribution and use in source and binary forms are permitted
634233Sbostic  * provided that this notice is preserved and that due credit is given
734233Sbostic  * to the University of California at Berkeley. The name of the University
834233Sbostic  * may not be used to endorse or promote products derived from this
934233Sbostic  * software without specific prior written permission. This software
1034233Sbostic  * is provided ``as is'' without express or implied warranty.
1134226Sbostic  */
1234226Sbostic 
1334233Sbostic #if defined(LIBC_SCCS) && !defined(lint)
14*34318Sbostic static char sccsid[] = "@(#)vfprintf.c	5.14 (Berkeley) 05/17/88";
1534233Sbostic #endif /* LIBC_SCCS and not lint */
1634233Sbostic 
1734261Sbostic #include <sys/types.h>
1834233Sbostic #include <varargs.h>
1934226Sbostic #include <stdio.h>
2034233Sbostic #include <ctype.h>
2134226Sbostic 
2234314Sbostic #define	MAXBUF	40
2334226Sbostic 
2434263Sbostic #define	PUTC(ch)	{++cnt; putc(ch, fp);}
2534236Sbostic 
26*34318Sbostic #define	ARG() \
27*34318Sbostic 	_ulong = flags&LONGINT ? va_arg(argp, long) : \
28*34318Sbostic 	    flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
2934235Sbostic 
30*34318Sbostic /* have to deal with the negative buffer count kludge */
31*34318Sbostic #define	NEGATIVE_COUNT_KLUDGE
32*34318Sbostic 
33*34318Sbostic #define	LONGINT		0x01		/* long integer */
34*34318Sbostic #define	LONGDBL		0x02		/* long double; unimplemented */
35*34318Sbostic #define	SHORTINT	0x04		/* short integer */
36*34318Sbostic #define	ALT		0x08		/* alternate form */
37*34318Sbostic #define	LADJUST		0x10		/* left adjustment */
38*34318Sbostic static int flags;
39*34318Sbostic 
4034314Sbostic static char sign;
4134248Sbostic 
4234235Sbostic x_doprnt(fmt, argp, fp)
4334233Sbostic 	register char *fmt;
4434233Sbostic 	va_list argp;
4534235Sbostic 	register FILE *fp;
4634226Sbostic {
4734314Sbostic 	register int cnt, n;
4834314Sbostic 	register char ch, *t;
4934235Sbostic 	double _double;
5034314Sbostic 	u_long _ulong;
51*34318Sbostic 	int base, width, prec, size;
52*34318Sbostic 	char padc, *digs, *_cvt(), buf[MAXBUF];
5334226Sbostic 
5434243Sbostic 	digs = "0123456789abcdef";
5534314Sbostic 	for (cnt = 0;; ++fmt) {
56*34318Sbostic 		n = fp->_cnt;
57*34318Sbostic 		for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
58*34318Sbostic 			if (--n < 0
59*34318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
60*34318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
61*34318Sbostic #endif
62*34318Sbostic 			    || ch == '\n' && fp->_flag&_IOLBF) {
63*34318Sbostic 				fp->_cnt = n;
64*34318Sbostic 				fp->_ptr = t;
65*34318Sbostic 				(void)_flsbuf(ch, fp);
66*34318Sbostic 				n = fp->_cnt;
67*34318Sbostic 				t = fp->_ptr;
68*34318Sbostic 			}
69*34318Sbostic 			else
7034314Sbostic 				*t++ = ch;
71*34318Sbostic 		fp->_cnt = n;
72*34318Sbostic 		fp->_ptr = t;
73*34318Sbostic 		if (!ch)
7434314Sbostic 			return(cnt);
7534314Sbostic 
76*34318Sbostic 		flags = width = 0;
7734233Sbostic 		prec = -1;
7834233Sbostic 		padc = ' ';
79*34318Sbostic 		sign = '\0';
8034314Sbostic 		t = buf;
8134226Sbostic 
82*34318Sbostic rflag:		switch (*++fmt) {
83*34318Sbostic 		case ' ':
84*34318Sbostic 			sign = ' ';
85*34318Sbostic 			goto rflag;
8634233Sbostic 		case '#':
87*34318Sbostic 			flags |= ALT;
88*34318Sbostic 			goto rflag;
8934233Sbostic 		case '*':
9034235Sbostic 			/*
9134235Sbostic 			 * ``A negative field width argument is taken as a
9234235Sbostic 			 * - flag followed by a  positive field width.''
9334235Sbostic 			 *	-- ANSI X3J11
9434235Sbostic 			 * They don't exclude field widths read from args.
9534235Sbostic 			 */
9634235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
97*34318Sbostic 				goto rflag;
9834235Sbostic 			width = -width;
9934235Sbostic 			/*FALLTHROUGH*/
10034235Sbostic 		case '-':
101*34318Sbostic 			flags |= LADJUST;
102*34318Sbostic 			goto rflag;
10334233Sbostic 		case '+':
10434314Sbostic 			sign = '+';
105*34318Sbostic 			goto rflag;
10634233Sbostic 		case '.':
10734235Sbostic 			if (*++fmt == '*')
108*34318Sbostic 				n = va_arg(argp, int);
109*34318Sbostic 			else if (isascii(*fmt) && isdigit(*fmt)) {
110*34318Sbostic 				n = 0;
11134233Sbostic 				do {
112*34318Sbostic 					n = 10 * n + *fmt - '0';
113*34318Sbostic 				} while (isascii(*++fmt) && isdigit(*fmt));
11434233Sbostic 				--fmt;
11534226Sbostic 			}
11634235Sbostic 			else {
117*34318Sbostic 				--fmt;
11834235Sbostic 				prec = 0;
119*34318Sbostic 				goto rflag;
12034235Sbostic 			}
121*34318Sbostic 			prec = n < 0 ? -1 : n;
122*34318Sbostic 			goto rflag;
12334233Sbostic 		case '0':
12434233Sbostic 			padc = '0';
12534235Sbostic 			/*FALLTHROUGH*/
12634233Sbostic 		case '1': case '2': case '3': case '4':
12734233Sbostic 		case '5': case '6': case '7': case '8': case '9':
128*34318Sbostic 			n = 0;
12934233Sbostic 			do {
130*34318Sbostic 				n = 10 * n + *fmt - '0';
131*34318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
132*34318Sbostic 			width = n;
13334233Sbostic 			--fmt;
13434235Sbostic 		case 'L':
135*34318Sbostic 			/*
136*34318Sbostic 			 * C doesn't have a long double; use long for now.
137*34318Sbostic 			 * flags |= LONGDBL;
138*34318Sbostic 			 */
139*34318Sbostic 			flags |= LONGINT;
140*34318Sbostic 			goto rflag;
14134235Sbostic 		case 'h':
142*34318Sbostic 			flags |= SHORTINT;
143*34318Sbostic 			goto rflag;
14434233Sbostic 		case 'l':
145*34318Sbostic 			flags |= LONGINT;
146*34318Sbostic 			goto rflag;
14734314Sbostic 		case 'c':
14834314Sbostic 			*t = va_arg(argp, int);
14934314Sbostic 			size = 1;
15034314Sbostic 			goto pforw;
15134314Sbostic 		case 'd':
152*34318Sbostic 		case 'i':
153*34318Sbostic 			ARG();
154*34318Sbostic 			if ((long)_ulong < 0) {
155*34318Sbostic 				_ulong = -_ulong;
15634314Sbostic 				sign = '-';
15734241Sbostic 			}
15834314Sbostic 			if (sign)
15934314Sbostic 				PUTC(sign);
16034241Sbostic 			base = 10;
16134314Sbostic 			goto num;
16234261Sbostic 		case 'e':
16334236Sbostic 		case 'E':
16434235Sbostic 		case 'f':
16534261Sbostic 		case 'g':
16634243Sbostic 		case 'G':
16734243Sbostic 			_double = va_arg(argp, double);
16834314Sbostic 			size = _cvt(_double, prec, buf, buf + sizeof(buf),
16934314Sbostic 			    *fmt) - buf;
17034314Sbostic 			goto pforw;
17134235Sbostic 		case 'n':
172*34318Sbostic 			if (flags&LONGDBL || flags&LONGINT)
173*34318Sbostic 				*va_arg(argp, long *) = cnt;
174*34318Sbostic 			else if (flags&SHORTINT)
175*34318Sbostic 				*va_arg(argp, short *) = cnt;
176*34318Sbostic 			else
177*34318Sbostic 				*va_arg(argp, int *) = cnt;
17834235Sbostic 			break;
17934226Sbostic 		case 'o':
180*34318Sbostic 			ARG();
18134226Sbostic 			base = 8;
18234314Sbostic 			goto num;
18334235Sbostic 		case 'p':
18434226Sbostic 		case 's':
18534314Sbostic 			if (!(t = va_arg(argp, char *)))
18634314Sbostic 				t = "(null)";
18734316Sbostic 			if ((size = strlen(t)) > prec && prec >= 0)
18834314Sbostic 				size = prec;
189*34318Sbostic pforw:			if (!(flags&LADJUST) && width)
19034314Sbostic 				for (n = size; n++ < width;)
19134314Sbostic 					PUTC(padc);
19234314Sbostic 			if (fp->_cnt - (n = size) >= 0) {
19334314Sbostic 				cnt += n;
19434314Sbostic 				fp->_cnt -= n;
19534314Sbostic 				bcopy(t, fp->_ptr, n);
19634314Sbostic 				fp->_ptr += n;
19734233Sbostic 			}
19834314Sbostic 			else for (; n--; ++t)
19934314Sbostic 				PUTC(*t);
200*34318Sbostic 			if (flags&LADJUST)
20134314Sbostic 				while (width-- > size)
20234314Sbostic 					PUTC(padc);
20334226Sbostic 			break;
20434226Sbostic 		case 'u':
205*34318Sbostic 			ARG();
20634226Sbostic 			base = 10;
20734314Sbostic 			goto num;
20834226Sbostic 		case 'X':
20934226Sbostic 			digs = "0123456789ABCDEF";
21034233Sbostic 			/*FALLTHROUGH*/
21134226Sbostic 		case 'x':
212*34318Sbostic 			ARG();
21334314Sbostic 			base = 16;
21434314Sbostic 			/* alternate form for hex; leading 0x/X */
215*34318Sbostic 			if (flags&ALT && _ulong) {
21634263Sbostic 				PUTC('0');
21734263Sbostic 				PUTC(*fmt);
21834233Sbostic 			}
21934314Sbostic num:			t = buf + sizeof(buf) - 1;
22034233Sbostic 			do {
22134314Sbostic 				*t-- = digs[_ulong % base];
22234314Sbostic 				_ulong /= base;
22334314Sbostic 			} while(_ulong);
22434314Sbostic 			digs = "0123456789abcdef";
22534314Sbostic 			size = buf + sizeof(buf) - 1 - t;
22634314Sbostic 			if (size >= prec) {
22734314Sbostic 				/* alternate form for octal; leading 0 */
228*34318Sbostic 				if (t[1] != '0' && flags&ALT && *fmt == 'o') {
22934314Sbostic 					*t-- = '0';
23034314Sbostic 					++size;
23134314Sbostic 				}
23234314Sbostic 			}
23334314Sbostic 			else
23434314Sbostic 				for (; size < prec; ++size)
23534314Sbostic 					*t-- = '0';
236*34318Sbostic 			if (!(flags&LADJUST))
23734314Sbostic 				while (size++ < width)
23834263Sbostic 					PUTC(padc);
23934314Sbostic 			while (++t < buf + sizeof(buf))
24034314Sbostic 				PUTC(*t);
24134235Sbostic 			for (; width > size; --width)
24234263Sbostic 				PUTC(padc);
24334226Sbostic 			break;
24434233Sbostic 		case '\0':		/* "%?" prints ?, unless ? is NULL */
24534314Sbostic 			return(cnt);
24634226Sbostic 		default:
24734263Sbostic 			PUTC(*fmt);
24834226Sbostic 		}
24934226Sbostic 	}
25034314Sbostic 	/*NOTREACHED*/
25134226Sbostic }
25234242Sbostic 
25334261Sbostic #define	EFORMAT	0x01
25434261Sbostic #define	FFORMAT	0x02
25534261Sbostic #define	GFORMAT	0x04
25634314Sbostic #define	DEFPREC	6
25734261Sbostic 
25834261Sbostic static char *
25934261Sbostic _cvt(number, prec, startp, endp, fmtch)
26034242Sbostic 	double number;
26134261Sbostic 	register int prec;
26234248Sbostic 	char *startp, *endp, fmtch;
26334242Sbostic {
26434248Sbostic 	register char *p;
26534261Sbostic 	register int expcnt, format;
26634248Sbostic 	double fract, integer, tmp, modf();
26734261Sbostic 	int decpt;
26834248Sbostic 	char *savep;
26934242Sbostic 
27034314Sbostic 	if (prec == -1)
27134242Sbostic 		prec = DEFPREC;
27234242Sbostic 
27334314Sbostic 	if (number < 0) {
27434248Sbostic 		*startp++ = '-';
27534248Sbostic 		number = -number;
27634248Sbostic 	}
27734314Sbostic 	else if (sign)
278*34318Sbostic 		*startp++ = sign;
27934242Sbostic 
28034261Sbostic 	switch(fmtch) {
28134261Sbostic 	case 'e':
28234261Sbostic 	case 'E':
28334261Sbostic 		format = EFORMAT;
28434261Sbostic 		break;
28534261Sbostic 	case 'f':
28634261Sbostic 		format = FFORMAT;
28734261Sbostic 		break;
28834261Sbostic 	case 'g':
28934261Sbostic 	case 'G':
29034261Sbostic 		format = GFORMAT;
29134261Sbostic 		fmtch -= 2;
29234261Sbostic 	}
29334261Sbostic 
29434248Sbostic 	/*
29534248Sbostic 	 * if the alternate flag is set, or, at least one digit of precision
29634248Sbostic 	 * was requested, add a decimal point, unless it's the g/G format
29734314Sbostic 	 * in which case we require two digits of precision, as it counts
29834248Sbostic 	 * precision differently.
29934248Sbostic 	 */
300*34318Sbostic 	decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
30134248Sbostic 
30234248Sbostic 	expcnt = 0;
30334314Sbostic 	p = endp - 1;
30434248Sbostic 	fract = modf(number, &integer);
30534248Sbostic 	if (integer) {
30634248Sbostic 		register char *p2;
30734248Sbostic 
30834248Sbostic 		/* get integer part of number; count decimal places */
30934248Sbostic 		for (; integer; ++expcnt) {
31034248Sbostic 			tmp = modf(integer / 10, &integer);
31134248Sbostic 			*p-- = (int)((tmp + .03) * 10) + '0';
31234242Sbostic 		}
31334248Sbostic 
31434248Sbostic 		/* copy, in reverse order, to start of buffer */
31534248Sbostic 		p2 = startp;
31634248Sbostic 		*p2++ = *++p;
31734248Sbostic 
31834248Sbostic 		/*
31934248Sbostic 		 * if the format is g/G, and the resulting exponent will be
32034248Sbostic 		 * greater than the precision, use e/E format.  If e/E format,
32134248Sbostic 		 * put in a decimal point as needed, and decrement precision
32234248Sbostic 		 * count for each digit after the decimal point.
32334248Sbostic 		 */
32434248Sbostic 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
32534248Sbostic 			if (format&GFORMAT) {
32634248Sbostic 				format |= EFORMAT;
32734248Sbostic 
32834248Sbostic 				/* first digit is precision for g/G format */
32934248Sbostic 				if (prec)
33034248Sbostic 					--prec;
33134248Sbostic 			}
33234248Sbostic 			if (decpt)
33334248Sbostic 				*p2++ = '.';
33434248Sbostic 			for (; ++p < endp && prec; --prec, *p2++ = *p);
33534248Sbostic 
336*34318Sbostic 			/* precision ran out, round */
33734248Sbostic 			if (p < endp) {
33834248Sbostic 				if (*p > '4') {
33934248Sbostic 					for (savep = p2--;; *p2-- = '0') {
34034248Sbostic 						if (*p2 == '.')
34134248Sbostic 							--p2;
34234248Sbostic 						if (++*p2 <= '9')
34334248Sbostic 							break;
34434248Sbostic 					}
34534248Sbostic 					p2 = savep;
34634248Sbostic 				}
34734248Sbostic 				fract = 0;
34834248Sbostic 			}
34934242Sbostic 		}
35034248Sbostic 		/*
35134314Sbostic 		 * g/G in f format; if out of precision, replace digits with
35234314Sbostic 		 * zeroes, note, have to round first.
35334248Sbostic 		 */
35434248Sbostic 		else if (format&GFORMAT) {
35534248Sbostic 			for (; ++p < endp && prec; --prec, *p2++ = *p);
35634248Sbostic 			/* precision ran out; round and then add zeroes */
35734248Sbostic 			if (p < endp) {
35834248Sbostic 				if (*p > '4') {
35934248Sbostic 					for (savep = p2--; ++*p2 > '9';
36034248Sbostic 					    *p2-- = '0');
36134248Sbostic 					p2 = savep;
36234248Sbostic 				}
36334248Sbostic 				do {
36434248Sbostic 					*p2++ = '0';
36534248Sbostic 				} while (++p < endp);
36634248Sbostic 				fract = 0;
36734248Sbostic 			}
36834248Sbostic 			if (decpt)
36934248Sbostic 				*p2++ = '.';
37034242Sbostic 		}
37134248Sbostic 		/* f format */
37234248Sbostic 		else {
37334248Sbostic 			for (; ++p < endp; *p2++ = *p);
37434248Sbostic 			if (decpt)
37534248Sbostic 				*p2++ = '.';
37634248Sbostic 		}
37734248Sbostic 		p = p2;
37834248Sbostic 	}
37934248Sbostic 	/*
380*34318Sbostic 	 * if no fraction, the number was zero, and if no precision, can't
381*34318Sbostic 	 * show anything after the decimal point.
38234248Sbostic 	 */
38334248Sbostic 	else if (!fract || !prec) {
38434248Sbostic 		*startp++ = '0';
385*34318Sbostic 		if (decpt && !(format&GFORMAT))
38634248Sbostic 			*startp++ = '.';
387*34318Sbostic 		*startp = '\0';
38834248Sbostic 		return(startp);
38934248Sbostic 	}
39034248Sbostic 	/*
39134248Sbostic 	 * if the format is g/G, and the resulting exponent will be less than
39234248Sbostic 	 * -4 use e/E format.  If e/E format, compute exponent value.
39334248Sbostic 	 */
39434248Sbostic 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
39534248Sbostic 		format |= EFORMAT;
39634248Sbostic 		if (fract)
39734248Sbostic 			for (p = startp; fract;) {
39834248Sbostic 				fract = modf(fract * 10, &tmp);
39934248Sbostic 				if (!tmp) {
40034248Sbostic 					--expcnt;
40134248Sbostic 					continue;
40234248Sbostic 				}
40334248Sbostic 				*p++ = (int)tmp + '0';
40434248Sbostic 				break;
40534248Sbostic 			}
40634242Sbostic 		else
40734248Sbostic 			*p++ = '0';
40834248Sbostic 
40934248Sbostic 		/* g/G format, decrement precision for first digit */
41034248Sbostic 		if (format&GFORMAT && prec)
41134248Sbostic 			--prec;
41234248Sbostic 
41334248Sbostic 		/* add decimal after first non-zero digit */
41434248Sbostic 		if (decpt)
41534248Sbostic 			*p++ = '.';
41634242Sbostic 	}
41734248Sbostic 	/*
41834248Sbostic 	 * f format or g/G printed as f format; don't worry about decimal
41934248Sbostic 	 * point, if g/G format doesn't need it, will get stripped later.
42034248Sbostic 	 */
42134242Sbostic 	else {
42234248Sbostic 		p = startp;
42334248Sbostic 		*p++ = '0';
42434248Sbostic 		*p++ = '.';
42534248Sbostic 	}
42634248Sbostic 
42734248Sbostic 	/* finish out requested precision from fractional value */
42834248Sbostic 	while (prec--)
42934248Sbostic 		if (fract) {
43034248Sbostic 			fract = modf(fract * 10, &tmp);
43134248Sbostic 			*p++ = (int)tmp + '0';
43234248Sbostic 		}
43334248Sbostic 		else
43434248Sbostic 			*p++ = '0';
43534248Sbostic 
43634248Sbostic 	/*
43734248Sbostic 	 * if any fractional value left, "round" it back up to the beginning
43834248Sbostic 	 * of the number, fixing the exponent as necessary, and avoiding the
43934248Sbostic 	 * decimal point.
44034248Sbostic 	 */
44134248Sbostic 	if (fract) {
44234248Sbostic 		(void)modf(fract * 10, &tmp);
44334248Sbostic 		if (tmp > 4) {
44434248Sbostic 			for (savep = p--;; *p-- = '0') {
44534248Sbostic 				if (*p == '.')
44634248Sbostic 					--p;
44734248Sbostic 				if (p == startp) {
44834248Sbostic 					*p = '1';
44934248Sbostic 					++expcnt;
45034248Sbostic 					break;
45134248Sbostic 				}
45234248Sbostic 				if (++*p <= '9')
45334248Sbostic 					break;
45434242Sbostic 			}
45534248Sbostic 			p = savep;
45634242Sbostic 		}
45734248Sbostic 	}
45834248Sbostic 
45934248Sbostic 	/*
46034248Sbostic 	 * if a g/G format and not alternate flag, lose trailing zeroes,
46134248Sbostic 	 * if e/E or g/G format, and last char is decimal point, lose it.
46234248Sbostic 	 */
463*34318Sbostic 	if (!(flags&ALT)) {
46434248Sbostic 		if (format&GFORMAT)
46534248Sbostic 			for (; p[-1] == '0'; --p);
46634248Sbostic 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
46734248Sbostic 			--p;
46834248Sbostic 	}
46934248Sbostic 
47034248Sbostic 	/* if an e/E format, add exponent */
47134248Sbostic 	if (format&EFORMAT) {
47234248Sbostic 		*p++ = fmtch;
47334248Sbostic 		if (--expcnt < 0) {
47434248Sbostic 			expcnt = -expcnt;
47534248Sbostic 			*p++ = '-';
47634242Sbostic 		}
47734248Sbostic 		else
47834248Sbostic 			*p++ = '+';
47934248Sbostic 		*p++ = expcnt / 10 + '0';
48034248Sbostic 		*p++ = expcnt % 10 + '0';
48134242Sbostic 	}
48234248Sbostic 	return(p);
48334242Sbostic }
484